/* JAI-Ext - OpenSource Java Advanced Image Extensions Library
* http://www.geo-solutions.it/
* Copyright 2014 GeoSolutions
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package it.geosolutions.jaiext.border;
import static org.junit.Assert.*;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.io.IOException;
import javax.media.jai.BorderExtender;
import javax.media.jai.RenderedOp;
import org.junit.BeforeClass;
import org.junit.Test;
import it.geosolutions.jaiext.range.Range;
import it.geosolutions.jaiext.range.RangeFactory;
import it.geosolutions.jaiext.testclasses.TestBase;
import it.geosolutions.rendered.viewer.RenderedImageBrowser;
/**
* This test is used for checking if the Border operation is performed correctly. The tests are executed for all the JAI allowed data types, with and
* withou No Data and with one of these BorderExtender objects:
* <ul>
* <li>BorderExtenderZero</li>
* <li>BorderExtenderCopy</li>
* <li>BorderExtenderReflect</li>
* <li>BorderExtenderWrap</li>
* </ul>
*
* If the user wants to see the result of the Border operation must set the following JVM parameters:
* <ul>
* <li>JAI.Ext.Interactive=true</li>
* <li>JAI.Ext.RangeUsed=true(for showing the calculation with NoData, false otherwise)</li>
* <li>JAI.Ext.TestSelector=0/1/2/3(Each value is associated with one of the 4 possible BorderExtender types)</li>
* </ul>
*
* @author geosolutions
*
*/
public class BorderTest extends TestBase {
/** Tolerance value used for double comparison */
private static final double TOLERANCE = 0.1d;
/** Boolean indicating if No Data calculation must be showed(useful only if INTERACTIVE value is set to true) */
public static boolean RANGE_USED = Boolean.getBoolean("JAI.Ext.RangeUsed");
/** Input test images */
private static RenderedImage[] sourceIMG;
/** Range associated with the Byte image */
private static Range noDataByte;
/** Range associated with the UShort image */
private static Range noDataUShort;
/** Range associated with the Short image */
private static Range noDataShort;
/** Range associated with the Integer image */
private static Range noDataInt;
/** Range associated with the Float image */
private static Range noDataFloat;
/** Range associated with the Double image */
private static Range noDataDouble;
/** BorderExtender array */
private static BorderExtender[] extender;
/** Output value for the No Data */
private static double destNoData;
/** Left padding parameter */
private static int leftPad;
/** Right padding parameter */
private static int rightPad;
/** Top padding parameter */
private static int topPad;
/** Bottom padding parameter */
private static int bottomPad;
@BeforeClass
public static void initialSetup() {
// No Data values
byte noDataB = 50;
short noDataU = 50;
short noDataS = 50;
int noDataI = 50;
float noDataF = 50f;
double noDataD = 50d;
// Range parameters
boolean minIncluded = true;
boolean maxIncluded = true;
boolean nanIncluded = true;
noDataByte = RangeFactory.create(noDataB, minIncluded, noDataB, maxIncluded);
noDataUShort = RangeFactory.createU(noDataU, minIncluded, noDataU, maxIncluded);
noDataShort = RangeFactory.create(noDataS, minIncluded, noDataS, maxIncluded);
noDataInt = RangeFactory.create(noDataI, minIncluded, noDataI, maxIncluded);
noDataFloat = RangeFactory.create(noDataF, minIncluded, noDataF, maxIncluded, nanIncluded);
noDataDouble = RangeFactory.create(noDataD, minIncluded, noDataD, maxIncluded, nanIncluded);
// Image array creation
sourceIMG = new RenderedImage[DataBuffer.TYPE_DOUBLE + 1];
// Image filler parameter must be set to true for creating images without only few values different from 0
IMAGE_FILLER = true;
sourceIMG[DataBuffer.TYPE_BYTE] = createTestImage(DataBuffer.TYPE_BYTE, DEFAULT_WIDTH,
DEFAULT_HEIGHT, noDataB, false, 1);
sourceIMG[DataBuffer.TYPE_USHORT] = createTestImage(DataBuffer.TYPE_USHORT, DEFAULT_WIDTH,
DEFAULT_HEIGHT, noDataU, false, 1);
sourceIMG[DataBuffer.TYPE_SHORT] = createTestImage(DataBuffer.TYPE_SHORT, DEFAULT_WIDTH,
DEFAULT_HEIGHT, noDataS, false, 1);
sourceIMG[DataBuffer.TYPE_INT] = createTestImage(DataBuffer.TYPE_INT, DEFAULT_WIDTH,
DEFAULT_HEIGHT, noDataI, false, 1);
sourceIMG[DataBuffer.TYPE_FLOAT] = createTestImage(DataBuffer.TYPE_FLOAT, DEFAULT_WIDTH,
DEFAULT_HEIGHT, noDataF, false, 1);
sourceIMG[DataBuffer.TYPE_DOUBLE] = createTestImage(DataBuffer.TYPE_DOUBLE, DEFAULT_WIDTH,
DEFAULT_HEIGHT, noDataD, false, 1);
IMAGE_FILLER = false;
// Creation of the BorderExtender array
extender = new BorderExtender[BorderExtender.BORDER_WRAP + 1];
extender[BorderExtender.BORDER_ZERO] = BorderExtender
.createInstance(BorderExtender.BORDER_ZERO);
extender[BorderExtender.BORDER_COPY] = BorderExtender
.createInstance(BorderExtender.BORDER_COPY);
extender[BorderExtender.BORDER_REFLECT] = BorderExtender
.createInstance(BorderExtender.BORDER_REFLECT);
extender[BorderExtender.BORDER_WRAP] = BorderExtender
.createInstance(BorderExtender.BORDER_WRAP);
// Destination NoData value
destNoData = 100d;
// Padding values
leftPad = 2;
rightPad = 2;
topPad = 2;
bottomPad = 2;
}
// This method tests the Border operation using the BorderExtenderZero with and without NoData
@Test
public void testBorderZero() {
boolean noDataRangeUsed = false;
int borderType = BorderExtender.BORDER_ZERO;
testBorder(DataBuffer.TYPE_BYTE, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_USHORT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_SHORT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_INT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_FLOAT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_DOUBLE, noDataRangeUsed, borderType);
noDataRangeUsed = true;
testBorder(DataBuffer.TYPE_BYTE, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_USHORT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_SHORT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_INT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_FLOAT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_DOUBLE, noDataRangeUsed, borderType);
}
// This method tests the Border operation using the BorderExtenderCopy with and without NoData
@Test
public void testBorderCopy() {
boolean noDataRangeUsed = false;
int borderType = BorderExtender.BORDER_COPY;
testBorder(DataBuffer.TYPE_BYTE, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_USHORT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_SHORT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_INT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_FLOAT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_DOUBLE, noDataRangeUsed, borderType);
noDataRangeUsed = true;
testBorder(DataBuffer.TYPE_BYTE, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_USHORT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_SHORT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_INT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_FLOAT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_DOUBLE, noDataRangeUsed, borderType);
}
// This method tests the Border operation using the BorderExtenderReflect with and without NoData
@Test
public void testBorderReflect() {
boolean noDataRangeUsed = false;
int borderType = BorderExtender.BORDER_REFLECT;
testBorder(DataBuffer.TYPE_BYTE, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_USHORT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_SHORT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_INT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_FLOAT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_DOUBLE, noDataRangeUsed, borderType);
noDataRangeUsed = true;
testBorder(DataBuffer.TYPE_BYTE, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_USHORT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_SHORT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_INT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_FLOAT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_DOUBLE, noDataRangeUsed, borderType);
}
// This method tests the Border operation using the BorderExtenderWrap with and without NoData
@Test
public void testBorderWrap() {
boolean noDataRangeUsed = false;
int borderType = BorderExtender.BORDER_WRAP;
testBorder(DataBuffer.TYPE_BYTE, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_USHORT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_SHORT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_INT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_FLOAT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_DOUBLE, noDataRangeUsed, borderType);
noDataRangeUsed = true;
testBorder(DataBuffer.TYPE_BYTE, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_USHORT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_SHORT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_INT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_FLOAT, noDataRangeUsed, borderType);
testBorder(DataBuffer.TYPE_DOUBLE, noDataRangeUsed, borderType);
}
private void testBorder(int dataType, boolean noDataRangeUsed, int borderType) {
// Selection of the source
RenderedImage source = sourceIMG[dataType];
// Selection of the BorderExtender
BorderExtender extend = extender[borderType];
// The precalculated NoData Range is used, if selected by the related boolean.
Range noDataRange;
if (noDataRangeUsed) {
switch (dataType) {
case DataBuffer.TYPE_BYTE:
noDataRange = noDataByte;
break;
case DataBuffer.TYPE_USHORT:
noDataRange = noDataUShort;
break;
case DataBuffer.TYPE_SHORT:
noDataRange = noDataShort;
break;
case DataBuffer.TYPE_INT:
noDataRange = noDataInt;
break;
case DataBuffer.TYPE_FLOAT:
noDataRange = noDataFloat;
break;
case DataBuffer.TYPE_DOUBLE:
noDataRange = noDataDouble;
break;
default:
throw new IllegalArgumentException("Wrong data type");
}
} else {
noDataRange = null;
}
// Border operation execution
RenderedOp borderIMG = BorderDescriptor.create(source, leftPad, rightPad, topPad,
bottomPad, extend, noDataRange, destNoData, null);
// If the parameters are set, the image is printed to the screen
if (INTERACTIVE && dataType == DataBuffer.TYPE_BYTE && TEST_SELECTOR == borderType
&& noDataRangeUsed == RANGE_USED) {
RenderedImageBrowser.showChain(borderIMG, false, false);
try {
System.in.read();
} catch (IOException e) {
}
} else {
// Else all the image tiles are calculated
borderIMG.getTiles();
}
// Calculation of all the variables for iterating on the image borders
int minX = borderIMG.getMinX();
int minY = borderIMG.getMinY();
int maxXPadding = minX + leftPad;
int maxYPadding = minY + topPad;
int maxX = borderIMG.getMaxX();
int maxY = borderIMG.getMaxY();
int width = DEFAULT_WIDTH / 2;
int height = DEFAULT_HEIGHT / 2;
int widthReduced = width - rightPad;
int heightReduced = height - bottomPad;
int topPaddingBorder = topPad + minY;
int leftPaddingBorder = leftPad + minX;
int xIndex = 0;
int yIndex = 0;
int tileX = 0;
int tileY = 0;
int xWrap = 0;
int yWrap = 0;
// Test for the top padding
for (int x = minX; x < width; x++) {
tileX = borderIMG.XToTileX(x);
if (x > leftPad && x < widthReduced) {
for (int y = minY; y < topPaddingBorder; y++) {
tileY = borderIMG.YToTileY(y);
Raster tile = borderIMG.getTile(tileX, tileY);
double value = tile.getSampleDouble(x, y, 0);
switch (borderType) {
case BorderExtender.BORDER_ZERO:
assertEquals(value, 0, TOLERANCE);
break;
case BorderExtender.BORDER_COPY:
double valueCopy = tile.getSampleDouble(x, maxYPadding, 0);
if (noDataRangeUsed) {
if (noDataDouble.contains(valueCopy)) {
assertEquals(value, destNoData, TOLERANCE);
} else {
assertEquals(value, valueCopy, TOLERANCE);
}
} else {
assertEquals(value, valueCopy, TOLERANCE);
}
break;
case BorderExtender.BORDER_REFLECT:
yIndex = y - minY;
double valueReflect = tile.getSampleDouble(x, (topPad - yIndex - 1), 0);
if (noDataRangeUsed) {
if (noDataDouble.contains(valueReflect)) {
assertEquals(value, destNoData, TOLERANCE);
} else {
assertEquals(value, valueReflect, TOLERANCE);
}
} else {
assertEquals(value, valueReflect, TOLERANCE);
}
break;
case BorderExtender.BORDER_WRAP:
yIndex = topPad - (y - minY);
yWrap = maxY - bottomPad - yIndex;
tileY = borderIMG.YToTileY(yWrap);
Raster tileWrap = borderIMG.getTile(tileX, tileY);
double valueWrap = tileWrap.getSampleDouble(x, yWrap, 0);
if (noDataRangeUsed) {
if (noDataDouble.contains(valueWrap)) {
assertEquals(value, destNoData, TOLERANCE);
} else {
assertEquals(value, valueWrap, TOLERANCE);
}
} else {
assertEquals(value, valueWrap, TOLERANCE);
}
break;
default:
throw new IllegalArgumentException("Wrong BorderExtender type");
}
}
}
}
// Test for the left padding
for (int y = minY; y < height; y++) {
tileY = borderIMG.YToTileY(y);
if (y > topPad && y < heightReduced) {
for (int x = minX; x < leftPaddingBorder; x++) {
tileX = borderIMG.XToTileX(x);
Raster tile = borderIMG.getTile(tileX, tileY);
double value = tile.getSampleDouble(x, y, 0);
switch (borderType) {
case BorderExtender.BORDER_ZERO:
assertEquals(value, 0, TOLERANCE);
break;
case BorderExtender.BORDER_COPY:
double valueCopy = tile.getSampleDouble(maxXPadding, y, 0);
if (noDataRangeUsed) {
if (noDataDouble.contains(valueCopy)) {
assertEquals(value, destNoData, TOLERANCE);
} else {
assertEquals(value, valueCopy, TOLERANCE);
}
} else {
assertEquals(value, valueCopy, TOLERANCE);
}
break;
case BorderExtender.BORDER_REFLECT:
xIndex = x - minX;
double valueReflect = tile.getSampleDouble((leftPad - xIndex - 1), y, 0);
if (noDataRangeUsed) {
if (noDataDouble.contains(valueReflect)) {
assertEquals(value, destNoData, TOLERANCE);
} else {
assertEquals(value, valueReflect, TOLERANCE);
}
} else {
assertEquals(value, valueReflect, TOLERANCE);
}
break;
case BorderExtender.BORDER_WRAP:
xIndex = leftPad - (x - minX);
xWrap = maxX - rightPad - xIndex;
tileX = borderIMG.XToTileX(xWrap);
Raster tileWrap = borderIMG.getTile(tileX, tileY);
double valueWrap = tileWrap.getSampleDouble(xWrap, y, 0);
if (noDataRangeUsed) {
if (noDataDouble.contains(valueWrap)) {
assertEquals(value, destNoData, TOLERANCE);
} else {
assertEquals(value, valueWrap, TOLERANCE);
}
} else {
assertEquals(value, valueWrap, TOLERANCE);
}
break;
default:
throw new IllegalArgumentException("Wrong BorderExtender type");
}
}
}
}
}
}