/* 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.rescale; import static org.junit.Assert.*; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.io.IOException; import javax.media.jai.PlanarImage; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import org.junit.BeforeClass; import org.junit.Test; import com.sun.media.jai.util.ImageUtil; 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 class is used for checking the functionalities of the Rescale operation on all the possible data types. The various tests are performed * by taking the destination image tile, and comparing it with the related source tile pixels. The comparison is made by taking each source pixel, * rescaling it and ensuring that it is equal to the associated destination pixel. If the pixel is outside the ROI or it is a NoData, then it is * skipped. These tests are done with and without No Data, with and without ROI, and, if ROI is present, with and without ROI RasterAccessor. If the * user wants to see the result of the operation in a particular condition must set to true the JVM parameter JAI.Ext.Interactive and associate to the * JVM parameter JAI.Ext.TestSelector an integer associated with the operation: * <ul> * <li>0 without ROI and NoData</li> * <li>1 with ROI RasterAccessor and without NoData</li> * <li>2 with ROI and without NoData</li> * <li>3 with ROI RasterAccessor and with NoData</li> * <li>4 without ROI and with NoData</li> * <li>5 with ROI and NoData</li> * </ul> */ public class RescaleTest extends TestBase { /** Tolerance value used for comparison between double and float */ private static final double TOLERANCE = 0.01d; /** Source images array, one for each data type */ private static RenderedImage[] sourceIMG; /** Roi data used for test */ private static ROI roi; /** No data value for Byte data */ private static byte noDataB; /** No data value for UShort data */ private static short noDataU; /** No data value for Short data */ private static short noDataS; /** No data value for Integer data */ private static int noDataI; /** No data value for Float data */ private static float noDataF; /** No data value for Double data */ private static double noDataD; /** Range of NoData for Byte values */ private static Range noDataByte; /** Range of NoData for UShort values */ private static Range noDataUShort; /** Range of NoData for Short values */ private static Range noDataShort; /** Range of NoData for Integer values */ private static Range noDataInt; /** Range of NoData for Float values */ private static Range noDataFloat; /** Range of NoData for Double values */ private static Range noDataDouble; /** Scale factors used */ private static double[] scales; /** Offset parameters used */ private static double[] offsets; /** Destination No Data used for replacing data where the input vale is a NoData */ private static double destNoData; /** ROI Bounds used for checking if each pixel is inside or outside the ROI */ private static Rectangle roiBounds; @BeforeClass public static void initialSetup() { // ROI creation roiBounds = new Rectangle(5, 5, DEFAULT_WIDTH / 4, DEFAULT_HEIGHT / 4); roi = new ROIShape(roiBounds); // No Data Range creation // No Data values noDataB = 50; noDataU = 50; noDataS = 50; noDataI = 50; noDataF = 50; noDataD = 50; boolean minIncluded = true; boolean maxIncluded = 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, true); noDataDouble = RangeFactory.create(noDataD, minIncluded, noDataD, maxIncluded, true); // Image creations IMAGE_FILLER = true; sourceIMG = new RenderedImage[6]; sourceIMG[0] = createTestImage(DataBuffer.TYPE_BYTE, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataB, false); sourceIMG[1] = createTestImage(DataBuffer.TYPE_USHORT, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataU, false); sourceIMG[2] = createTestImage(DataBuffer.TYPE_SHORT, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataS, false); sourceIMG[3] = createTestImage(DataBuffer.TYPE_INT, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataI, false); sourceIMG[4] = createTestImage(DataBuffer.TYPE_FLOAT, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataF, false); sourceIMG[5] = createTestImage(DataBuffer.TYPE_DOUBLE, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataD, false); IMAGE_FILLER = false; // Scale values scales = new double[] { 10, 20, 30 }; // Offset values offsets = new double[] { 0, 1, 2 }; // Destination No Data destNoData = 0.0d; } // This test checks if the Rescale is correct in absence of No Data and ROI @Test public void testNoRangeNoRoi() { boolean roiUsed = false; boolean noDataRangeUsed = false; boolean useROIAccessor = false; TestSelection select = TestSelection.NO_ROI_ONLY_DATA; // Byte data Type testRescale(sourceIMG[0], roiUsed, noDataRangeUsed, useROIAccessor, select); // Ushort data Type testRescale(sourceIMG[1], roiUsed, noDataRangeUsed, useROIAccessor, select); // Short data Type testRescale(sourceIMG[2], roiUsed, noDataRangeUsed, useROIAccessor, select); // Integer data Type testRescale(sourceIMG[3], roiUsed, noDataRangeUsed, useROIAccessor, select); // Float data Type testRescale(sourceIMG[4], roiUsed, noDataRangeUsed, useROIAccessor, select); // Double data Type testRescale(sourceIMG[5], roiUsed, noDataRangeUsed, useROIAccessor, select); } // This test checks if the Rescale is correct in absence of No Data but with ROI (ROI RasterAccessor not used) @Test public void testRoiBounds() { boolean roiUsed = true; boolean noDataRangeUsed = false; boolean useROIAccessor = false; TestSelection select = TestSelection.ROI_ONLY_DATA; // Byte data Type testRescale(sourceIMG[0], roiUsed, noDataRangeUsed, useROIAccessor, select); // Ushort data Type testRescale(sourceIMG[1], roiUsed, noDataRangeUsed, useROIAccessor, select); // Short data Type testRescale(sourceIMG[2], roiUsed, noDataRangeUsed, useROIAccessor, select); // Integer data Type testRescale(sourceIMG[3], roiUsed, noDataRangeUsed, useROIAccessor, select); // Float data Type testRescale(sourceIMG[4], roiUsed, noDataRangeUsed, useROIAccessor, select); // Double data Type testRescale(sourceIMG[5], roiUsed, noDataRangeUsed, useROIAccessor, select); } // This test checks if the Rescale is correct in absence of No Data but with ROI (ROI RasterAccessor used) @Test public void testRoiAccessor() { boolean roiUsed = true; boolean noDataRangeUsed = false; boolean useROIAccessor = true; TestSelection select = TestSelection.ROI_ACCESSOR_ONLY_DATA; // Byte data Type testRescale(sourceIMG[0], roiUsed, noDataRangeUsed, useROIAccessor, select); // Ushort data Type testRescale(sourceIMG[1], roiUsed, noDataRangeUsed, useROIAccessor, select); // Short data Type testRescale(sourceIMG[2], roiUsed, noDataRangeUsed, useROIAccessor, select); // Integer data Type testRescale(sourceIMG[3], roiUsed, noDataRangeUsed, useROIAccessor, select); // Float data Type testRescale(sourceIMG[4], roiUsed, noDataRangeUsed, useROIAccessor, select); // Double data Type testRescale(sourceIMG[5], roiUsed, noDataRangeUsed, useROIAccessor, select); } // This test checks if the Rescale is correct in absence of ROI but with No Data @Test public void testNoData() { boolean roiUsed = false; boolean noDataRangeUsed = true; boolean useROIAccessor = false; TestSelection select = TestSelection.NO_ROI_NO_DATA; // Byte data Type testRescale(sourceIMG[0], roiUsed, noDataRangeUsed, useROIAccessor, select); // Ushort data Type testRescale(sourceIMG[1], roiUsed, noDataRangeUsed, useROIAccessor, select); // Short data Type testRescale(sourceIMG[2], roiUsed, noDataRangeUsed, useROIAccessor, select); // Integer data Type testRescale(sourceIMG[3], roiUsed, noDataRangeUsed, useROIAccessor, select); // Float data Type testRescale(sourceIMG[4], roiUsed, noDataRangeUsed, useROIAccessor, select); // Double data Type testRescale(sourceIMG[5], roiUsed, noDataRangeUsed, useROIAccessor, select); } // This test checks if the Rescale is correct in presence of No Data and ROI (ROI RasterAccessor not used) @Test public void testRoiNoData() { boolean roiUsed = true; boolean noDataRangeUsed = true; boolean useROIAccessor = false; TestSelection select = TestSelection.ROI_NO_DATA; // Byte data Type testRescale(sourceIMG[0], roiUsed, noDataRangeUsed, useROIAccessor, select); // Ushort data Type testRescale(sourceIMG[1], roiUsed, noDataRangeUsed, useROIAccessor, select); // Short data Type testRescale(sourceIMG[2], roiUsed, noDataRangeUsed, useROIAccessor, select); // Integer data Type testRescale(sourceIMG[3], roiUsed, noDataRangeUsed, useROIAccessor, select); // Float data Type testRescale(sourceIMG[4], roiUsed, noDataRangeUsed, useROIAccessor, select); // Double data Type testRescale(sourceIMG[5], roiUsed, noDataRangeUsed, useROIAccessor, select); } // This test checks if the Rescale is correct in presence of No Data and ROI (ROI RasterAccessor used) @Test public void testRoiAccessorNoData() { boolean roiUsed = true; boolean noDataRangeUsed = true; boolean useROIAccessor = true; TestSelection select = TestSelection.ROI_ACCESSOR_NO_DATA; // Byte data Type testRescale(sourceIMG[0], roiUsed, noDataRangeUsed, useROIAccessor, select); // Ushort data Type testRescale(sourceIMG[1], roiUsed, noDataRangeUsed, useROIAccessor, select); // Short data Type testRescale(sourceIMG[2], roiUsed, noDataRangeUsed, useROIAccessor, select); // Integer data Type testRescale(sourceIMG[3], roiUsed, noDataRangeUsed, useROIAccessor, select); // Float data Type testRescale(sourceIMG[4], roiUsed, noDataRangeUsed, useROIAccessor, select); // Double data Type testRescale(sourceIMG[5], roiUsed, noDataRangeUsed, useROIAccessor, select); } public void testRescale(RenderedImage source, boolean roiUsed, boolean noDataUsed, boolean useRoiAccessor, TestSelection select) { // The precalculated roi is used, if selected by the related boolean. ROI roiData; if (roiUsed) { roiData = roi; } else { roiData = null; } // The precalculated NoData Range is used, if selected by the related boolean. Range noDataRange; // Image data type int dataType = source.getSampleModel().getDataType(); if (noDataUsed) { 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; } // Rescale operation PlanarImage rescaled = RescaleDescriptor.create(source, scales, offsets, roiData, noDataRange, useRoiAccessor, destNoData, null); // Display Image if (INTERACTIVE && TEST_SELECTOR == select.getType()) { RenderedImageBrowser.showChain(rescaled, false, roiUsed); try { System.in.read(); } catch (IOException e) { e.printStackTrace(); } } else { // Calculation of all the image tiles rescaled.getTiles(); } // Rescale control on the first band int tileMinX = rescaled.getMinTileX(); int tileMinY = rescaled.getMinTileY(); // Selection of the source and destination first tile Raster tileDest = rescaled.getTile(tileMinX, tileMinY); Raster tileSource = source.getTile(tileMinX, tileMinY); int tileMinXpix = tileDest.getMinX(); int tileMinYpix = tileDest.getMinY(); int tileMaxXpix = tileDest.getWidth() + tileMinXpix; int tileMaxYpix = tileDest.getHeight() + tileMinYpix; double scaleFactor = scales[0]; double offset = offsets[0]; // loop through the tile pixels for (int i = tileMinXpix; i < tileMaxXpix; i++) { for (int j = tileMinYpix; j < tileMaxYpix; j++) { switch (dataType) { case DataBuffer.TYPE_BYTE: // selection of the rescaled pixel byte destValueB = (byte) tileDest.getSample(i, j, 0); // rescale operation on the source pixel int srcValueB = tileSource.getSample(i, j, 0) & 0xFF; byte calculationB = ImageUtil.clampRoundByte(srcValueB * scaleFactor + offset); // comparison if (roiUsed && noDataUsed) { if (roiBounds.contains(i, j) && !noDataRange.contains((byte) srcValueB)) { assertEquals(calculationB, destValueB); } } else if (roiUsed) { if (roiBounds.contains(i, j)) { assertEquals(calculationB, destValueB); } } else if (noDataUsed) { if (!noDataRange.contains((byte) srcValueB)) { assertEquals(calculationB, destValueB); } } else { assertEquals(calculationB, destValueB); } break; case DataBuffer.TYPE_USHORT: short destValueU = (short) tileDest.getSample(i, j, 0); int srcValueU = tileSource.getSample(i, j, 0) & 0xFFFF; short calculationU = ImageUtil.clampRoundUShort(srcValueU * scaleFactor + offset); if (roiUsed && noDataUsed) { if (roiBounds.contains(i, j) && !noDataRange.contains((short) srcValueU)) { assertEquals(calculationU, destValueU); } } else if (roiUsed) { if (roiBounds.contains(i, j)) { assertEquals(calculationU, destValueU); } } else if (noDataUsed) { if (!noDataRange.contains((short) srcValueU)) { assertEquals(calculationU, destValueU); } } else { assertEquals(calculationU, destValueU); } break; case DataBuffer.TYPE_SHORT: short destValueS = (short) tileDest.getSample(i, j, 0); short srcValueS = (short) tileSource.getSample(i, j, 0); short calculationS = ImageUtil .clampRoundShort(srcValueS * scaleFactor + offset); if (roiUsed && noDataUsed) { if (roiBounds.contains(i, j) && !noDataRange.contains(srcValueS)) { assertEquals(calculationS, destValueS); } } else if (roiUsed) { if (roiBounds.contains(i, j)) { assertEquals(calculationS, destValueS); } } else if (noDataUsed) { if (!noDataRange.contains(srcValueS)) { assertEquals(calculationS, destValueS); } } else { assertEquals(calculationS, destValueS); } break; case DataBuffer.TYPE_INT: int destValueI = tileDest.getSample(i, j, 0); int srcValueI = tileSource.getSample(i, j, 0); int calculationI = ImageUtil.clampRoundInt(srcValueI * scaleFactor + offset); if (roiUsed && noDataUsed) { if (roiBounds.contains(i, j) && !noDataRange.contains(srcValueI)) { assertEquals(calculationI, destValueI); } } else if (roiUsed) { if (roiBounds.contains(i, j)) { assertEquals(calculationI, destValueI); } } else if (noDataUsed) { if (!noDataRange.contains(srcValueI)) { assertEquals(calculationI, destValueI); } } else { assertEquals(calculationI, destValueI); } break; case DataBuffer.TYPE_FLOAT: float destValueF = tileDest.getSampleFloat(i, j, 0); float srcValueF = tileSource.getSampleFloat(i, j, 0); float calculationF = (float) ((srcValueF * scaleFactor) + offset); if (roiUsed && noDataUsed) { if (roiBounds.contains(i, j) && !noDataRange.contains(srcValueF)) { assertEquals(calculationF, destValueF, TOLERANCE); } } else if (roiUsed) { if (roiBounds.contains(i, j)) { assertEquals(calculationF, destValueF, TOLERANCE); } } else if (noDataUsed) { if (!noDataRange.contains(srcValueF)) { assertEquals(calculationF, destValueF, TOLERANCE); } } else { assertEquals(calculationF, destValueF, TOLERANCE); } break; case DataBuffer.TYPE_DOUBLE: double destValueD = tileDest.getSampleDouble(i, j, 0); double srcValueD = tileSource.getSampleDouble(i, j, 0); double calculationD = ((srcValueD * scaleFactor) + offset); if (roiUsed && noDataUsed) { if (roiBounds.contains(i, j) && !noDataRange.contains(srcValueD)) { assertEquals(calculationD, destValueD, TOLERANCE); } } else if (roiUsed) { if (roiBounds.contains(i, j)) { assertEquals(calculationD, destValueD, TOLERANCE); } } else if (noDataUsed) { if (!noDataRange.contains(srcValueD)) { assertEquals(calculationD, destValueD, TOLERANCE); } } else { assertEquals(calculationD, destValueD, TOLERANCE); } break; default: throw new IllegalArgumentException("Wrong data type"); } } } } }