/* 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.orderdither; import static org.junit.Assert.assertEquals; import it.geosolutions.jaiext.iterators.RandomIterFactory; 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 java.util.Arrays; import javax.media.jai.ColorCube; import javax.media.jai.KernelJAI; import javax.media.jai.PlanarImage; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.RenderedOp; import javax.media.jai.iterator.RandomIter; import org.junit.BeforeClass; import org.junit.Test; /** * Unit test for testing OrderedDither operation with ROI and No Data. */ public class OrderedDitherTest extends TestBase { /** Tolerance value for double comparison */ public static final double TOLERANCE = 0.01d; /** Images used for testing */ private static RenderedImage[] testImages; /** NoData Range for Byte */ private static Range noDataByte; /** NoData Range for Ushort */ private static Range noDataUShort; /** NoData Range for Short */ private static Range noDataShort; /** NoData Range for Int */ private static Range noDataInt; /** NoData Range for Float */ private static Range noDataFloat; /** NoData Range for Double */ private static Range noDataDouble; /** Input ROI */ private static ROI roiObject; /** Output value for NoData */ private static double destNoData; @BeforeClass public static void initialSetup() { byte noDataB = 50; short noDataS = 50; int noDataI = 50; float noDataF = 50; double noDataD = 50; testImages = new RenderedImage[7]; IMAGE_FILLER = true; testImages[DataBuffer.TYPE_BYTE] = createTestImage(DataBuffer.TYPE_BYTE, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataB, false, 1, (byte) (64)); testImages[DataBuffer.TYPE_DOUBLE + 1] = createTestImage(DataBuffer.TYPE_BYTE, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataB, false, 3, (byte) (64)); testImages[DataBuffer.TYPE_USHORT] = createTestImage(DataBuffer.TYPE_USHORT, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataS, false, 1, (short) (Short.MAX_VALUE / 4)); testImages[DataBuffer.TYPE_SHORT] = createTestImage(DataBuffer.TYPE_SHORT, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataS, false, 1, (short) (-49)); testImages[DataBuffer.TYPE_INT] = createTestImage(DataBuffer.TYPE_INT, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataI, false, 1, (int) (105)); testImages[DataBuffer.TYPE_FLOAT] = createTestImage(DataBuffer.TYPE_FLOAT, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataF, false, 1, (float) ((255 / 2) * 5)); testImages[DataBuffer.TYPE_DOUBLE] = createTestImage(DataBuffer.TYPE_DOUBLE, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataD, false, 1, (255 / 7) * 13); IMAGE_FILLER = false; // No Data Ranges boolean minIncluded = true; boolean maxIncluded = true; noDataByte = RangeFactory.create(noDataB, minIncluded, noDataB, maxIncluded); noDataUShort = RangeFactory.createU(noDataS, minIncluded, noDataS, maxIncluded); noDataShort = RangeFactory.create(noDataS, minIncluded, noDataS, maxIncluded); noDataInt = RangeFactory.create(noDataI, minIncluded, noDataI, maxIncluded); noDataFloat = RangeFactory.create(noDataF, minIncluded, noDataF, maxIncluded, true); noDataDouble = RangeFactory.create(noDataD, minIncluded, noDataD, maxIncluded, true); // ROI creation Rectangle roiBounds = new Rectangle(5, 5, DEFAULT_WIDTH / 4, DEFAULT_HEIGHT / 4); roiObject = new ROIShape(roiBounds); destNoData = 10; } @Test public void testValidData() { boolean roiUsed = false; boolean noDataUsed = false; for (int i = 0; i < 6; i++) { if (i == 0) { testType(testImages[i], noDataUsed, roiUsed, true, true); testType(testImages[i], noDataUsed, roiUsed, true, false); } testType(testImages[i], noDataUsed, roiUsed, false, false); } } @Test public void testRoi() { boolean roiUsed = true; boolean noDataUsed = false; for (int i = 0; i < 6; i++) { if (i == 0) { testType(testImages[i], noDataUsed, roiUsed, true, true); testType(testImages[i], noDataUsed, roiUsed, true, false); } testType(testImages[i], noDataUsed, roiUsed, false, false); } } @Test public void testNoData() { boolean roiUsed = false; boolean noDataUsed = true; for (int i = 0; i < 6; i++) { if (i == 0) { testType(testImages[i], noDataUsed, roiUsed, true, true); testType(testImages[i], noDataUsed, roiUsed, true, false); } testType(testImages[i], noDataUsed, roiUsed, false, false); } } @Test public void testRoiNoData() { boolean roiUsed = true; boolean noDataUsed = true; for (int i = 0; i < 6; i++) { if (i == 0) { testType(testImages[i], noDataUsed, roiUsed, true, true); testType(testImages[i], noDataUsed, roiUsed, true, false); } testType(testImages[i], noDataUsed, roiUsed, false, false); } } private void testType(RenderedImage src, boolean nodataUsed, boolean roiUsed, boolean byteOptimized, boolean opt3p3) { if (byteOptimized && opt3p3) { src = testImages[DataBuffer.TYPE_DOUBLE + 1]; } // Optional No Data Range used Range noData; // Source image data type int dataType = src.getSampleModel().getDataType(); // If no Data are present, the No Data Range associated is used if (nodataUsed) { switch (dataType) { case DataBuffer.TYPE_BYTE: noData = noDataByte; break; case DataBuffer.TYPE_USHORT: noData = noDataUShort; break; case DataBuffer.TYPE_SHORT: noData = noDataShort; break; case DataBuffer.TYPE_INT: noData = noDataInt; break; case DataBuffer.TYPE_FLOAT: noData = noDataFloat; break; case DataBuffer.TYPE_DOUBLE: noData = noDataDouble; break; default: throw new IllegalArgumentException("Wrong data type"); } } else { noData = null; } // ROI setting ROI roi; if (roiUsed) { roi = roiObject; } else { roi = null; } // Getting the ColorMap ColorCube colorMap; if (byteOptimized && opt3p3) { colorMap = ColorCube.BYTE_855; } else { colorMap = ColorCube.createColorCube(dataType, new int[] { 8 }); } // Getting the dithering mask int numBands = src.getSampleModel().getNumBands(); KernelJAI[] k = new KernelJAI[numBands]; int width; int height; float[] data; // Different Mask in case of Byte optimization if (byteOptimized) { width = 8; height = 8; data = new float[width * height]; Arrays.fill(data, 0.5f); k[0] = new KernelJAI(width, height, data); if (opt3p3) { k[1] = new KernelJAI(width, height, data); k[2] = new KernelJAI(width, height, data); } } else { width = 64; height = 64; data = new float[width * height]; Arrays.fill(data, 0.5f); k[0] = new KernelJAI(width, height, data); } // Ordered Dither operation RenderedOp orderedDither = OrderedDitherDescriptor.create(src, colorMap, k, null, roi, noData, destNoData + colorMap.getAdjustedOffset()); checkNoDataROI(orderedDither, src, roi, noData, colorMap); // Disposal of the output image orderedDither.dispose(); } /** * Method for checking if ROI and NoData are handled correctly * * @param finalimage * @param image * @param roi * @param nodata * @param colorCube */ private void checkNoDataROI(RenderedOp finalimage, RenderedImage image, ROI roi, Range nodata, ColorCube colorCube) { // Ensure the dimensions are the same assertEquals(finalimage.getMinX(), image.getMinX()); assertEquals(finalimage.getMinY(), image.getMinY()); assertEquals(finalimage.getWidth(), image.getWidth()); assertEquals(finalimage.getHeight(), image.getHeight()); boolean roiExists = roi != null; boolean nodataExists = nodata != null; // Simply ensure no exception is thrown if (!nodataExists && !roiExists) { finalimage.getTiles(); } if (nodataExists) { nodata = RangeFactory.convertToDoubleRange(nodata); } RandomIter roiIter = null; Rectangle roiBounds = null; if (roiExists) { PlanarImage roiIMG = roi.getAsImage(); roiIter = RandomIterFactory.create(roiIMG, finalimage.getBounds(), true, true); roiBounds = roi.getBounds(); } // Else check ROI and NoData RandomIter sourceIter = RandomIterFactory.create(image, null, true, true); RandomIter destIter = RandomIterFactory.create(finalimage, null, true, true); // Start the iteration (we iterate only the first band) int w = image.getWidth(); int h = image.getHeight(); int minX = image.getMinX(); int minY = image.getMinY(); int maxX = minX + w; int maxY = minY + h; double noDataValue = destNoData + colorCube.getAdjustedOffset(); for (int y = minY; y < maxY; y++) { for (int x = minX; x < maxX; x++) { double src = sourceIter.getSampleDouble(x, y, 0); double dest = destIter.getSampleDouble(x, y, 0); boolean valid = true; // ROI Check if (roiExists && !(roiBounds.contains(x, y) && roiIter.getSample(x, y, 0) > 0)) { valid = false; } // NoData Check if (nodataExists && nodata.contains(src)) { valid = false; } if (!valid) { assertEquals(noDataValue, dest, TOLERANCE); } } } } }