/* 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.rlookup; import static org.junit.Assert.assertEquals; import it.geosolutions.jaiext.range.Range; import it.geosolutions.jaiext.range.RangeFactory; import it.geosolutions.jaiext.testclasses.TestBase; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import javax.media.jai.JAI; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.RenderedOp; import javax.media.jai.iterator.RectIter; import javax.media.jai.iterator.RectIterFactory; import org.junit.BeforeClass; import org.junit.Test; /** * Tests for the RangeLookup operation. * * @author Michael Bedward */ public class RangeLookupTest extends TestBase { /** Image Size */ private static final int WIDTH = 10; /** Tolerance value for double comparison */ private static final double TOLERANCE = 1E-6; /** Images to test */ private static RenderedImage[] images; /** ROI used for testing */ private static ROIShape roi; /** Default value */ private static Double defaultV; @BeforeClass public static void initialSetup() { images = new RenderedImage[6]; IMAGE_FILLER = true; images[0] = createTestImage(DataBuffer.TYPE_BYTE, WIDTH, WIDTH, (byte) 0, false); images[1] = createTestImage(DataBuffer.TYPE_USHORT, WIDTH, WIDTH, (short) 0, false); images[2] = createTestImage(DataBuffer.TYPE_SHORT, WIDTH, WIDTH, (short) 0, false); images[3] = createTestImage(DataBuffer.TYPE_INT, WIDTH, WIDTH, 0, false); images[4] = createTestImage(DataBuffer.TYPE_FLOAT, WIDTH, WIDTH, 0f, false); images[5] = createTestImage(DataBuffer.TYPE_DOUBLE, WIDTH, WIDTH, 0d, false); IMAGE_FILLER = false; roi = new ROIShape(new Rectangle(2, 2, 3, 3)); defaultV = new Double(13); } @Test public void byteToByte() { Byte[] breaks = { 2, 4, 6, 8 }; Byte[] values = { 0, 1, 2, 3, 4 }; RenderedImage srcImg = images[0]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_BYTE); } @Test public void byteToByteROI() { Byte[] breaks = { 2, 4, 6, 8 }; Byte[] values = { 0, 1, 2, 3, 4 }; RenderedImage srcImg = images[0]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_BYTE, roi, defaultV); } @Test public void byteToShort() { Byte[] breaks = { 2, 4, 6, 8 }; Short[] values = { -50, -10, 0, 10, 50 }; RenderedImage srcImg = images[0]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_SHORT); } @Test public void byteToInt() { Byte[] breaks = { 2, 4, 6, 8 }; Integer[] values = { -50, -10, 0, 10, 50 }; RenderedImage srcImg = images[0]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_INT); } @Test public void byteToFloat() { Byte[] breaks = { 2, 4, 6, 8 }; Float[] values = { -50f, -10f, 0f, 10f, 50f }; RenderedImage srcImg = images[0]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_FLOAT); } @Test public void byteToDouble() { Byte[] breaks = { 2, 4, 6, 8 }; Double[] values = { -50d, -10d, 0d, 10d, 50d }; RenderedImage srcImg = images[0]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_DOUBLE); } @Test public void shortToByte() { Short[] breaks = { 2, 4, 6, 8 }; Byte[] values = { 0, 1, 2, 3, 4 }; RenderedImage srcImg = images[2]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_BYTE); } @Test public void shortToShort() { Short[] breaks = { 2, 4, 6, 8 }; Short[] values = { -50, -10, 0, 10, 50 }; RenderedImage srcImg = images[2]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_SHORT); } @Test public void shortToShortROI() { Short[] breaks = { 2, 4, 6, 8 }; Short[] values = { -50, -10, 0, 10, 50 }; RenderedImage srcImg = images[2]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_SHORT, roi, defaultV); } @Test public void shortToInt() { Short[] breaks = { 2, 4, 6, 8 }; Integer[] values = { -50, -10, 0, 10, 50 }; RenderedImage srcImg = images[2]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_INT); } @Test public void shortToFloat() { Short[] breaks = { 2, 4, 6, 8 }; Float[] values = { -50f, -10f, 0f, 10f, 50f }; RenderedImage srcImg = images[2]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_FLOAT); } @Test public void shortToDouble() { Short[] breaks = { 2, 4, 6, 8 }; Double[] values = { -50d, -10d, 0d, 10d, 50d }; RenderedImage srcImg = images[2]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_DOUBLE); } @Test public void ushortToByte() { Short[] breaks = { 2, 4, 6, 8 }; Byte[] values = { 0, 1, 2, 3, 4 }; RenderedImage srcImg = images[1]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_BYTE); } @Test public void ushortToShort() { Short[] breaks = { 2, 4, 6, 8 }; Short[] values = { -50, -10, 0, 10, 50 }; RenderedImage srcImg = images[1]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_SHORT); } @Test public void ushortToInt() { Short[] breaks = { 2, 4, 6, 8 }; Integer[] values = { -50, -10, 0, 10, 50 }; RenderedImage srcImg = images[1]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_INT); } @Test public void ushortToFloat() { Short[] breaks = { 2, 4, 6, 8 }; Float[] values = { -50f, -10f, 0f, 10f, 50f }; RenderedImage srcImg = images[1]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_FLOAT); } @Test public void ushortToDouble() { Short[] breaks = { 2, 4, 6, 8 }; Double[] values = { -50d, -10d, 0d, 10d, 50d }; RenderedImage srcImg = images[1]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_DOUBLE); } @Test public void intToByte() { Integer[] breaks = { 2, 4, 6, 8 }; Byte[] values = { 0, 1, 2, 3, 4 }; RenderedImage srcImg = images[3]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_BYTE); } @Test public void intToShort() { Integer[] breaks = { 2, 4, 6, 8 }; Short[] values = { -50, -10, 0, 10, 50 }; RenderedImage srcImg = images[3]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_SHORT); } @Test public void intToInt() { Integer[] breaks = { 2, 4, 6, 8 }; Integer[] values = { -50, -10, 0, 10, 50 }; RenderedImage srcImg = images[3]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_INT); } @Test public void intToIntROI() { Integer[] breaks = { 2, 4, 6, 8 }; Integer[] values = { -50, -10, 0, 10, 50 }; RenderedImage srcImg = images[3]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_INT, roi, defaultV); } @Test public void intToFloat() { Integer[] breaks = { 2, 4, 6, 8 }; Float[] values = { -50f, -10f, 0f, 10f, 50f }; RenderedImage srcImg = images[3]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_FLOAT); } @Test public void intToDouble() { Integer[] breaks = { 2, 4, 6, 8 }; Double[] values = { -50d, -10d, 0d, 10d, 50d }; RenderedImage srcImg = images[3]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_DOUBLE); } @Test public void floatToByte() { Float[] breaks = { 2f, 4f, 6f, 8f }; Byte[] values = { 0, 1, 2, 3, 4 }; RenderedImage srcImg = images[4]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_BYTE); } @Test public void floatToShort() { Float[] breaks = { 2f, 4f, 6f, 8f }; Short[] values = { -50, -10, 0, 10, 50 }; RenderedImage srcImg = images[4]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_SHORT); } @Test public void floatToInt() { Float[] breaks = { 2f, 4f, 6f, 8f }; Integer[] values = { -50, -10, 0, 10, 50 }; RenderedImage srcImg = images[4]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_INT); } @Test public void floatToFloat() { Float[] breaks = { 2f, 4f, 6f, 8f }; Float[] values = { -50f, -10f, 0f, 10f, 50f }; RenderedImage srcImg = images[4]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_FLOAT); } @Test public void floatToFloatROI() { Float[] breaks = { 2f, 4f, 6f, 8f }; Float[] values = { -50f, -10f, 0f, 10f, 50f }; RenderedImage srcImg = images[4]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_FLOAT, roi, defaultV); } @Test public void floatToDouble() { Float[] breaks = { 2f, 4f, 6f, 8f }; Double[] values = { -50d, -10d, 0d, 10d, 50d }; RenderedImage srcImg = images[4]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_DOUBLE); } @Test public void doubleToByte() { Double[] breaks = { 2d, 4d, 6d, 8d }; Byte[] values = { 0, 1, 2, 3, 4 }; RenderedImage srcImg = images[5]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_BYTE); } @Test public void doubleToShort() { Double[] breaks = { 2d, 4d, 6d, 8d }; Short[] values = { -50, -10, 0, 10, 50 }; RenderedImage srcImg = images[5]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_SHORT); } @Test public void doubleToInt() { Double[] breaks = { 2d, 4d, 6d, 8d }; Integer[] values = { -50, -10, 0, 10, 50 }; RenderedImage srcImg = images[5]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_INT); } @Test public void doubleToFloat() { Double[] breaks = { 2d, 4d, 6d, 8d }; Float[] values = { -50f, -10f, 0f, 10f, 50f }; RenderedImage srcImg = images[5]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_FLOAT); } @Test public void doubleToDouble() { Double[] breaks = { 2d, 4d, 6d, 8d }; Double[] values = { -50d, -10d, 0d, 10d, 50d }; RenderedImage srcImg = images[5]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_DOUBLE); } @Test public void doubleToDoubleROI() { Double[] breaks = { 2d, 4d, 6d, 8d }; Double[] values = { -50d, -10d, 0d, 10d, 50d }; RenderedImage srcImg = images[5]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_DOUBLE, roi, defaultV); } @Test public void shortToShortWithNoNegativeValues() throws Exception { Short[] breaks = { 2, 4, 6, 8 }; Short[] values = { 0, 1, 2, 3, 4 }; RenderedImage srcImg = images[2]; // The destination image shoule be TYPE_USHORT assertLookup(breaks, values, srcImg, DataBuffer.TYPE_USHORT); } @Test public void ushortToUShort() throws Exception { Short[] breaks = { 2, 4, 6, 8 }; Short[] values = { 0, 1, 2, 3, 4 }; RenderedImage srcImg = images[1]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_USHORT); } @Test public void ushortToUShortROI() throws Exception { Short[] breaks = { 2, 4, 6, 8 }; Short[] values = { 0, 1, 2, 3, 4 }; RenderedImage srcImg = images[1]; assertLookup(breaks, values, srcImg, DataBuffer.TYPE_USHORT, roi, defaultV); } @Test public void ushortSourceWithNegativeDestValues() throws Exception { Short[] breaks = { 2, 4, 6, 8 }; Short[] values = { -50, -10, 0, 10, 50 }; RenderedImage srcImg = images[1]; // The destination image should be TYPE_SHORT assertLookup(breaks, values, srcImg, DataBuffer.TYPE_SHORT); } /** * Runs the lookup operation and tests destination image values. * * @param breaks source image breakpoints * @param values lookup values * @param srcImg source image * @param destType expected destination image data type */ private <T extends Number & Comparable<? super T>, U extends Number & Comparable<? super U>> void assertLookup( T[] breaks, U[] values, RenderedImage srcImg, int destType) { assertLookup(breaks, values, srcImg, destType, null, null); } private <T extends Number & Comparable<? super T>, U extends Number & Comparable<? super U>> void assertLookup( T[] breaks, U[] values, RenderedImage srcImg, int destType, ROI roi, Double defaultValue) { RangeLookupTable<T, U> table = createTable(breaks, values); RenderedOp destImg = doOp(srcImg, table, roi, defaultValue); // check data type assertEquals(destType, destImg.getSampleModel().getDataType()); assertImageValues(srcImg, table, destImg, roi, defaultValue); } public static <T extends Number & Comparable<? super T>, U extends Number & Comparable<? super U>> RangeLookupTable<T, U> createTable( T[] breaks, U[] values) { final int N = breaks.length; if (values.length != N + 1) { throw new IllegalArgumentException( "values array length should be breaks array length + 1"); } RangeLookupTable.Builder<T, U> builder = new RangeLookupTable.Builder<T, U>(); Range r; r = RangeFactory.create(Double.NEGATIVE_INFINITY, false, breaks[0].doubleValue(), false); builder.add(r, values[0]); for (int i = 1; i < N; i++) { r = RangeFactory.create(breaks[i - 1].doubleValue(), true, breaks[i].doubleValue(), false); builder.add(r, values[i]); } r = RangeFactory.create(breaks[N - 1].doubleValue(), true, Double.POSITIVE_INFINITY, false); builder.add(r, values[N]); return builder.build(); } private RenderedOp doOp(RenderedImage srcImg, RangeLookupTable table, ROI roi, Double defaultValue) { ParameterBlockJAI pb = new ParameterBlockJAI("RLookup"); pb.setSource("source0", srcImg); pb.setParameter("table", table); pb.setParameter("roi", roi); pb.setParameter("default", defaultValue); return JAI.create("RLookup", pb); } /** * Tests that a destination image contains expected values given the source image and lookup table. * * @param srcImg source image * @param table lookup table * @param destImg destination image */ private void assertImageValues(RenderedImage srcImg, RangeLookupTable table, RenderedImage destImg, ROI roi, Double defaultValue) { final int srcType = srcImg.getSampleModel().getDataType(); final int destType = destImg.getSampleModel().getDataType(); RectIter srcIter = RectIterFactory.create(srcImg, null); RectIter destIter = RectIterFactory.create(destImg, null); int x = 0; int y = 0; boolean roiExists = roi != null; Rectangle bounds = roiExists ? roi.getBounds() : null; do { do { Number srcVal = getSourceImageValue(srcIter, srcType); Number expectedVal = table.getLookupItem(srcVal).getValue(); // ROI Check if (roiExists && !(bounds.contains(x, y) && roi.contains(x, y))) { expectedVal = defaultValue; } switch (destType) { case DataBuffer.TYPE_BYTE: assertEquals(expectedVal.byteValue(), (byte) destIter.getSample()); break; case DataBuffer.TYPE_SHORT: assertEquals(expectedVal.shortValue(), (short) destIter.getSample()); break; case DataBuffer.TYPE_INT: assertEquals(expectedVal.intValue(), destIter.getSample()); break; case DataBuffer.TYPE_FLOAT: assertEquals(expectedVal.floatValue(), destIter.getSampleFloat(), TOLERANCE); break; case DataBuffer.TYPE_DOUBLE: assertEquals(expectedVal.doubleValue(), destIter.getSampleDouble(), TOLERANCE); break; } srcIter.nextPixelDone(); x++; } while (!destIter.nextPixelDone()); srcIter.nextLineDone(); srcIter.startPixels(); destIter.startPixels(); y++; x = 0; } while (!destIter.nextLineDone()); } /** * Helper method for {@link #assertImageValues}. * * @param srcIter source image iterator * @param srcType source image data type * * @return source image value as a Number */ private Number getSourceImageValue(RectIter srcIter, int srcType) { Number val = null; switch (srcType) { case DataBuffer.TYPE_BYTE: val = (byte) (srcIter.getSample() & 0xff); break; case DataBuffer.TYPE_SHORT: val = (short) srcIter.getSample(); break; case DataBuffer.TYPE_USHORT: val = (short) (srcIter.getSample() & 0xffff); break; case DataBuffer.TYPE_INT: val = srcIter.getSample(); break; case DataBuffer.TYPE_FLOAT: val = srcIter.getSampleFloat(); break; case DataBuffer.TYPE_DOUBLE: val = (short) srcIter.getSampleDouble(); break; default: throw new IllegalArgumentException("Unknown image type"); } return val; } }