/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2006-2015, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.image; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import java.awt.Color; import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.DirectColorModel; import java.awt.image.IndexColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.Random; import java.util.zip.GZIPInputStream; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.JAI; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.RasterFactory; import javax.media.jai.RenderedOp; import javax.media.jai.WarpAffine; import javax.media.jai.operator.ConstantDescriptor; import javax.media.jai.operator.MosaicDescriptor; import org.apache.commons.io.IOUtils; import org.geotools.TestData; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.grid.GridCoverageFactory; import org.geotools.coverage.grid.Viewer; import org.geotools.coverage.processing.GridProcessingTestBase; import org.geotools.geometry.jts.JTS; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.CRS; import org.geotools.referencing.crs.DefaultGeographicCRS; import org.geotools.resources.image.ComponentColorModelJAI; import it.geosolutions.jaiext.vectorbin.ROIGeometry; import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import com.sun.media.imageioimpl.common.PackageUtil; import com.vividsolutions.jts.geom.Envelope; import it.geosolutions.imageio.utilities.ImageIOUtilities; import it.geosolutions.imageioimpl.plugins.tiff.TIFFImageReaderSpi; import it.geosolutions.jaiext.JAIExt; import it.geosolutions.jaiext.lookup.LookupTable; import it.geosolutions.jaiext.lookup.LookupTableFactory; import it.geosolutions.jaiext.range.NoDataContainer; import it.geosolutions.jaiext.range.Range; import it.geosolutions.jaiext.range.RangeFactory; /** * Tests the {@link ImageWorker} implementation. * * * * @source $URL$ * @version $Id$ * @author Simone Giannecchini (GeoSolutions) * @author Martin Desruisseaux (Geomatys) */ public final class ImageWorkerTest extends GridProcessingTestBase { private final static String GOOGLE_MERCATOR_WKT="PROJCS[\"WGS 84 / Pseudo-Mercator\","+ "GEOGCS[\"Popular Visualisation CRS\","+ "DATUM[\"Popular_Visualisation_Datum\","+ "SPHEROID[\"Popular Visualisation Sphere\",6378137,0,"+ "AUTHORITY[\"EPSG\",\"7059\"]],"+ "TOWGS84[0,0,0,0,0,0,0],"+ "AUTHORITY[\"EPSG\",\"6055\"]],"+ "PRIMEM[\"Greenwich\",0,"+ "AUTHORITY[\"EPSG\",\"8901\"]],"+ "UNIT[\"degree\",0.01745329251994328,"+ "AUTHORITY[\"EPSG\",\"9122\"]],"+ "AUTHORITY[\"EPSG\",\"4055\"]],"+ "UNIT[\"metre\",1,"+ "AUTHORITY[\"EPSG\",\"9001\"]],"+ "PROJECTION[\"Mercator_1SP\"],"+ "PARAMETER[\"central_meridian\",0],"+ "PARAMETER[\"scale_factor\",1],"+ "PARAMETER[\"false_easting\",0],"+ "PARAMETER[\"false_northing\",0],"+ "AUTHORITY[\"EPSG\",\"3785\"],"+ "AXIS[\"X\",EAST],"+ "AXIS[\"Y\",NORTH]]"; /** * Image to use for testing purpose. */ private static RenderedImage sstImage, worldImage, chlImage, bathy, smallWorld, gray, grayAlpha; /** * {@code true} if the image should be visualized. */ private static final boolean SHOW = TestData.isInteractiveTest(); private static BufferedImage worldDEMImage = null; @BeforeClass public static void setupJaiExt() { JAIExt.initJAIEXT(true); } @AfterClass public static void teardownJaiExt() { JAIExt.initJAIEXT(false); } /** * Creates a simple 128x128 {@link RenderedImage} for testing purposes. * * @param maximum * @return */ private static RenderedImage getSynthetic(final double maximum) { final int width = 128; final int height = 128; final WritableRaster raster = RasterFactory.createBandedRaster( DataBuffer.TYPE_DOUBLE, width, height, 1, null); final Random random = new Random(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { raster.setSample(x, y, 0,Math.ceil(random.nextDouble()*maximum) ); } } final ColorModel cm = new ComponentColorModelJAI(ColorSpace .getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, DataBuffer.TYPE_DOUBLE); final BufferedImage image = new BufferedImage(cm, raster, false, null); return image; } /** * Creates a test image in RGB with either {@link ComponentColorModel} or {@link DirectColorModel}. * * @param direct <code>true</code> when we request a {@link DirectColorModel}, <code>false</code> otherwise. * @return */ private static BufferedImage getSyntheticRGB(final boolean direct) { final int width = 128; final int height = 128; final BufferedImage image= direct?new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR) : new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); final WritableRaster raster =(WritableRaster) image.getData(); final Random random = new Random(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { raster.setSample(x, y, 0,random.nextInt(256)); } } return image; } /** * Creates a test image in RGB with either {@link ComponentColorModel} or {@link DirectColorModel}. * * @param direct <code>true</code> when we request a {@link DirectColorModel}, <code>false</code> otherwise. * @return */ private static BufferedImage getSyntheticRGB(Color color) { final int width = 128; final int height = 128; final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); final WritableRaster raster = image.getRaster(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { raster.setSample(x, y, 0, color.getRed()); raster.setSample(x, y, 1, color.getGreen()); raster.setSample(x, y, 2, color.getBlue()); } } return image; } /** * Creates a test image in RGB with either {@link ComponentColorModel} or {@link DirectColorModel}. * * @param direct <code>true</code> when we request a {@link DirectColorModel}, <code>false</code> otherwise. * @return */ private static BufferedImage getSyntheticSolidGray(byte gray) { final int width = 128; final int height = 128; final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); final WritableRaster raster = image.getRaster(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { raster.setSample(x, y, 0, gray); } } return image; } /** * Creates a test paletted image with translucency. * * @return */ private static BufferedImage getSyntheticTranslucentIndexed() { final byte bb[] = new byte[256]; for (int i = 0; i < 256; i++) bb[i] = (byte) i; final IndexColorModel icm = new IndexColorModel(8, 256, bb, bb, bb, bb); final WritableRaster raster = RasterFactory .createWritableRaster(icm.createCompatibleSampleModel(1024, 1024), null); for (int i = raster.getMinX(); i < raster.getMinX() + raster.getWidth(); i++) for (int j = raster.getMinY(); j < raster.getMinY() + raster.getHeight(); j++) raster.setSample(i, j, 0, (i + j) / 32); return new BufferedImage(icm, raster, false, null); } /** * Creates a test paletted image with a given number of entries in the map * * @return */ private static BufferedImage getSyntheticGrayIndexed(int entries) { final byte bb[] = new byte[entries]; for (int i = 0; i < entries; i++) bb[i] = (byte) i; final IndexColorModel icm = new IndexColorModel(8, entries, bb, bb, bb, bb); final WritableRaster raster = RasterFactory .createWritableRaster(icm.createCompatibleSampleModel(512, 512), null); for (int i = raster.getMinX(); i < raster.getMinX() + raster.getWidth(); i++) for (int j = raster.getMinY(); j < raster.getMinY() + raster.getHeight(); j++) raster.setSample(i, j, 0, (i + j) / 32); return new BufferedImage(icm, raster, false, null); } /** * Loads the image (if not already loaded) and creates the worker instance. * * @throws IOException If the image was not found. */ @Before public void setUp() throws IOException { if (sstImage == null) { final InputStream input = TestData.openStream(GridCoverage2D.class, "QL95209.png"); sstImage = ImageIO.read(input); input.close(); } if (worldImage == null) { final InputStream input = TestData.openStream(GridCoverage2D.class, "world.png"); worldImage = ImageIO.read(input); input.close(); } if (worldDEMImage == null) { final InputStream input = TestData.openStream(GridCoverage2D.class, "world_dem.gif"); worldDEMImage = ImageIO.read(input); input.close(); } if (chlImage == null) { final InputStream input = TestData.openStream(GridCoverage2D.class, "CHL01195.png"); chlImage = ImageIO.read(input); input.close(); } if (bathy == null) { final InputStream input = TestData.openStream(GridCoverage2D.class, "BATHY.png"); bathy = ImageIO.read(input); input.close(); } if (smallWorld == null) { final InputStream input = TestData.openStream(GridCoverage2D.class, "small_world.png"); smallWorld = ImageIO.read(input); input.close(); } if (gray == null) { final InputStream input = TestData.openStream(GridCoverage2D.class, "gray.png"); gray = ImageIO.read(input); input.close(); } if (grayAlpha == null) { final InputStream input = TestData.openStream(GridCoverage2D.class, "gray-alpha.png"); grayAlpha = ImageIO.read(input); input.close(); } } @Test public void testBitmask(){ assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); ImageWorker worker = new ImageWorker(sstImage); worker.forceBitmaskIndexColorModel(); assertEquals( 1, worker.getNumBands()); assertEquals( -1, worker.getTransparentPixel()); assertTrue ( worker.isBytes()); assertFalse ( worker.isBinary()); assertTrue ( worker.isIndexed()); assertTrue ( worker.isColorSpaceRGB()); assertFalse ( worker.isColorSpaceGRAYScale()); assertFalse ( worker.isTranslucent()); assertNoData(worker, null); final BufferedImage directRGB= getSyntheticRGB(true); worker = new ImageWorker(directRGB); worker.forceBitmaskIndexColorModel(); assertEquals( 1, worker.getNumBands()); assertEquals( -1, worker.getTransparentPixel()); assertTrue ( worker.isBytes()); assertFalse ( worker.isBinary()); assertTrue ( worker.isIndexed()); assertTrue ( worker.isColorSpaceRGB()); assertFalse ( worker.isColorSpaceGRAYScale()); assertFalse ( worker.isTranslucent()); assertNoData(worker, null); final BufferedImage componentRGB= getSyntheticRGB(false); worker = new ImageWorker(componentRGB); worker.forceBitmaskIndexColorModel(); assertEquals( 1, worker.getNumBands()); assertEquals( -1, worker.getTransparentPixel()); assertTrue ( worker.isBytes()); assertFalse ( worker.isBinary()); assertTrue ( worker.isIndexed()); assertTrue ( worker.isColorSpaceRGB()); assertFalse ( worker.isColorSpaceGRAYScale()); assertFalse ( worker.isTranslucent()); assertNoData(worker, null); final BufferedImage translucentIndexed= getSyntheticTranslucentIndexed(); worker=new ImageWorker(translucentIndexed); assertTrue ( worker.isBytes()); assertFalse ( worker.isBinary()); assertTrue ( worker.isIndexed()); assertTrue ( worker.isColorSpaceRGB()); assertTrue ( worker.isTranslucent()); assertNoData(worker, null); worker.forceIndexColorModelForGIF(true); assertEquals( 1, worker.getNumBands()); assertEquals( 0, worker.getTransparentPixel()); assertTrue ( worker.isBytes()); assertFalse ( worker.isBinary()); assertTrue ( worker.isIndexed()); assertTrue ( worker.isColorSpaceRGB()); assertFalse ( worker.isTranslucent()); assertNoData(worker, null); } /** * Tests capability to write GIF image. * * @throws IOException If an error occured while writting the image. */ @Test public void testGIFImageWrite() throws IOException { assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); // Get the image of the world with transparency. ImageWorker worker = new ImageWorker(worldDEMImage); show(worker, "Input GIF"); RenderedImage image = worker.getRenderedImage(); ColorModel cm = image.getColorModel(); assertTrue("wrong color model", cm instanceof IndexColorModel); assertEquals("wrong transparency model", Transparency.OPAQUE, cm.getTransparency()); // Writes it out as GIF on a file using index color model with final File outFile = TestData.temp(this, "temp.gif"); worker.writeGIF(outFile, "LZW", 0.75f); // Read it back final ImageWorker readWorker = new ImageWorker(ImageIO.read(outFile)); show(readWorker, "GIF to file"); image = readWorker.getRenderedImage(); cm = image.getColorModel(); assertTrue("wrong color model", cm instanceof IndexColorModel); assertEquals("wrong transparency model", Transparency.OPAQUE, cm.getTransparency()); // Write on an output streams. final OutputStream os = new FileOutputStream(outFile); worker = new ImageWorker(worldImage); worker.forceIndexColorModelForGIF(true); worker.writeGIF(os, "LZW", 0.75f); // Read it back. readWorker.setImage(ImageIO.read(outFile)); show(readWorker, "GIF to output stream"); image = readWorker.getRenderedImage(); cm = image.getColorModel(); assertTrue("wrong color model", cm instanceof IndexColorModel); assertEquals("wrong transparency model", Transparency.BITMASK, cm.getTransparency()); assertEquals("wrong transparent color index", 255, ((IndexColorModel)cm).getTransparentPixel()); outFile.delete(); } /** * Testing JPEG capabilities. * * @throws IOException If an error occured while writting the image. */ @Test public void testJPEGWrite() throws IOException { assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); // get the image of the world with transparency final ImageWorker worker = new ImageWorker(getSyntheticRGB(true)); show(worker, "Input JPEG"); // ///////////////////////////////////////////////////////////////////// // nativeJPEG with compression JPEG-LS // //////////////////////////////////////////////////////////////////// final File outFile = TestData.temp(this, "temp.jpeg"); ImageWorker readWorker; if(PackageUtil.isCodecLibAvailable()){ worker.writeJPEG(outFile, "JPEG-LS", 0.75f, true); readWorker = new ImageWorker(ImageIO.read(outFile)); show(readWorker, "Native JPEG LS"); } else { try{ worker.writeJPEG(outFile, "JPEG-LS", 0.75f, true); assertFalse(true); } catch (Exception e) { // TODO: handle exception } } // ///////////////////////////////////////////////////////////////////// // native JPEG compression // ///////////////////////////////////////////////////////////////////// worker.setImage(worldImage); worker.writeJPEG(outFile, "JPEG", 0.75f, true); readWorker = new ImageWorker(ImageIO.read(outFile)); show(readWorker, "native JPEG"); // ///////////////////////////////////////////////////////////////////// // pure java JPEG compression // ///////////////////////////////////////////////////////////////////// worker.setImage(worldImage); worker.writeJPEG(outFile, "JPEG", 0.75f, false); readWorker.setImage(ImageIO.read(outFile)); show(readWorker, "Pure Java JPEG"); outFile.delete(); } /** * Testing PNG capabilities. * * @throws IOException If an error occured while writting the image. */ @Test public void testPNGWrite() throws IOException { assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); // Get the image of the world with transparency. final ImageWorker worker = new ImageWorker(worldImage); show(worker, "Input file"); // ///////////////////////////////////////////////////////////////////// // native png filtered compression 24 bits // ///////////////////////////////////////////////////////////////////// final File outFile = TestData.temp(this, "temp.png"); worker.writePNG(outFile, "FILTERED", 0.75f, true,false); final ImageWorker readWorker = new ImageWorker(ImageIO.read(outFile)); show(readWorker, "Native PNG24"); // ///////////////////////////////////////////////////////////////////// // native png filtered compression 8 bits // ///////////////////////////////////////////////////////////////////// worker.setImage(worldImage); worker.writePNG(outFile, "FILTERED", 0.75f, true,true); readWorker.setImage(ImageIO.read(outFile)); show(readWorker, "native PNG8"); // ///////////////////////////////////////////////////////////////////// // pure java png 24 // ///////////////////////////////////////////////////////////////////// worker.setImage(worldImage); worker.writePNG(outFile, "FILTERED", 0.75f, false,false); readWorker.setImage(ImageIO.read(outFile)); show(readWorker, "Pure PNG24"); // ///////////////////////////////////////////////////////////////////// // pure java png 8 // ///////////////////////////////////////////////////////////////////// worker.setImage(worldImage); worker.writePNG(outFile, "FILTERED", 0.75f, false,true); readWorker.setImage(ImageIO.read(outFile)); show(readWorker, "Pure PNG8"); outFile.delete(); // Check we are not expanding to RGB a paletted image worker.setImage(sstImage); assertTrue(sstImage.getColorModel() instanceof IndexColorModel); worker.writePNG(outFile, "FILTERED", 0.75f, false, false); readWorker.setImage(ImageIO.read(outFile)); assertTrue(readWorker.getRenderedImage().getColorModel() instanceof IndexColorModel); } @Test public void test16BitGIF() throws Exception { // the resource has been compressed since the palette is way larger than the image itself, // and the palette does not get compressed InputStream gzippedStream = ImageWorkerTest.class.getResource("test-data/sf-sfdem.tif.gz").openStream(); GZIPInputStream is = new GZIPInputStream(gzippedStream); try { ImageInputStream iis = ImageIO.createImageInputStream(is); ImageReader reader = new TIFFImageReaderSpi().createReaderInstance(iis); reader.setInput(iis); BufferedImage bi = reader.read(0); if(TestData.isInteractiveTest()){ ImageIOUtilities.visualize(bi,"before"); } reader.dispose(); iis.close(); IndexColorModel icm = (IndexColorModel) bi.getColorModel(); assertEquals(65536, icm.getMapSize()); final File outFile = TestData.temp(this, "temp.gif"); ImageWorker worker = new ImageWorker(bi); worker.writeGIF(outFile, "LZW", 0.75f); // Read it back. bi=ImageIO.read(outFile); if(TestData.isInteractiveTest()){ ImageIOUtilities.visualize(bi,"after"); } ColorModel cm = bi.getColorModel(); assertTrue("wrong color model", cm instanceof IndexColorModel); assertEquals("wrong transparency model", Transparency.OPAQUE, cm.getTransparency()); final IndexColorModel indexColorModel = (IndexColorModel)cm; assertEquals("wrong transparent color index", -1, indexColorModel.getTransparentPixel()); assertEquals("wrong component size", 8, indexColorModel.getComponentSize(0)); outFile.delete(); } finally { is.close(); } } @Test public void test16BitPNG() throws Exception { // the resource has been compressed since the palette is way larger than the image itself, // and the palette does not get compressed InputStream gzippedStream = ImageWorkerTest.class.getResource("test-data/sf-sfdem.tif.gz").openStream(); GZIPInputStream is = new GZIPInputStream(gzippedStream); ImageInputStream iis = null; try { iis = ImageIO.createImageInputStream(is); ImageReader reader = new TIFFImageReaderSpi().createReaderInstance(iis); reader.setInput(iis); BufferedImage bi = reader.read(0); reader.dispose(); IndexColorModel icm = (IndexColorModel) bi.getColorModel(); assertEquals(65536, icm.getMapSize()); final File outFile = TestData.temp(this, "temp.png"); ImageWorker worker = new ImageWorker(bi); worker.writePNG(outFile, "FILTERED", 0.75f, true, false); worker.dispose(); // make sure we can read it BufferedImage back = ImageIO.read(outFile); // we expect a RGB one ComponentColorModel ccm = (ComponentColorModel) back.getColorModel(); assertEquals(3, ccm.getNumColorComponents()); // now ask to write paletted worker = new ImageWorker(bi); worker.writePNG(outFile, "FILTERED", 0.75f, true, true); worker.dispose(); // make sure we can read it back = ImageIO.read(outFile); // we expect a RGB one icm = (IndexColorModel) back.getColorModel(); assertEquals(3, icm.getNumColorComponents()); assertTrue(icm.getMapSize() <= 256); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(iis); } } @Test public void test4BitPNG() throws Exception { // create test image IndexColorModel icm =new IndexColorModel( 4, 16, new byte[]{(byte)255,0, 0, 0,16,32,64,(byte)128,1,2,3,4,5,6,7,8}, new byte[]{0, (byte)255,0, 0,16,32,64,(byte)128,1,2,3,4,5,6,7,8}, new byte[]{0, 0, (byte)255,0,16,32,64,(byte)128,1,2,3,4,5,6,7,8}); assertEquals(16, icm.getMapSize()); // create random data WritableRaster data = com.sun.media.jai.codecimpl.util.RasterFactory.createWritableRaster( icm.createCompatibleSampleModel(32,32), new Point(0,0)); for(int x=data.getMinX();x<data.getMinX()+data.getWidth();x++){ for(int y=data.getMinY();y<data.getMinY()+data.getHeight();y++){ data.setSample(x, y, 0, (x+y)%8); } } final BufferedImage bi = new BufferedImage( icm, data, false, null); assertEquals(16, ((IndexColorModel)bi.getColorModel()).getMapSize()); assertEquals(4, bi.getSampleModel().getSampleSize(0)); bi.setData(data); if(TestData.isInteractiveTest()){ ImageIOUtilities.visualize(bi,"before"); } // encode as png ImageWorker worker = new ImageWorker(bi); final File outFile = TestData.temp(this, "temp4.png"); worker.writePNG(outFile, "FILTERED", 0.75f, true, false); worker.dispose(); // make sure we can read it BufferedImage back = ImageIO.read(outFile); // we expect an IndexColorMolde one matching the old one IndexColorModel ccm = (IndexColorModel) back.getColorModel(); assertEquals(3, ccm.getNumColorComponents()); assertEquals(16, ccm.getMapSize()); assertEquals(4, ccm.getPixelSize()); if(TestData.isInteractiveTest()){ ImageIOUtilities.visualize(back,"after"); } } /** * Testing TIFF capabilities. * * @throws IOException If an error occured while writting the image. */ @Test public void testTIFFWrite() throws IOException { assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); // Get the image of the world with transparency. final ImageWorker worker = new ImageWorker(worldImage); show(worker, "Input file"); // ///////////////////////////////////////////////////////////////////// // tiff deflated untiled // ///////////////////////////////////////////////////////////////////// final File outFile = TestData.temp(this, "temp.tiff"); worker.writeTIFF(outFile, "Deflate", 0.75f, -1, -1); final ImageWorker readWorker = new ImageWorker(ImageIO.read(outFile)); show(readWorker, "Tiff untiled"); // ///////////////////////////////////////////////////////////////////// // tiff deflated compressed tiled // ///////////////////////////////////////////////////////////////////// worker.setImage(worldImage); worker.writeTIFF(outFile, "Deflate", 0.75f, 32, 32); readWorker.setImage(ImageIO.read(outFile)); show(readWorker, "Tiff jpeg compressed, tiled"); outFile.delete(); } /** * Tests the conversion between RGB and indexed color model. */ @Test public void testRGB2Palette(){ assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); final ImageWorker worker = new ImageWorker(worldImage); show(worker, "Input file"); worker.forceIndexColorModelForGIF(true); assertNoData(worker, null); // Convert to to index color bitmask ColorModel cm = worker.getRenderedImage().getColorModel(); assertTrue("wrong color model", cm instanceof IndexColorModel); assertEquals("wrong transparency model", Transparency.BITMASK, cm.getTransparency()); assertEquals("wrong transparency index", 255, ((IndexColorModel) cm).getTransparentPixel()); show(worker, "Paletted bitmask"); // Go back to rgb. worker.forceComponentColorModel(); cm = worker.getRenderedImage().getColorModel(); assertTrue("wrong color model", cm instanceof ComponentColorModel); assertEquals("wrong bands number", 4, cm.getNumComponents()); assertNoData(worker, null); show(worker, "RGB translucent"); assertEquals("wrong transparency model", Transparency.TRANSLUCENT, cm.getTransparency()); show(worker, "RGB translucent"); } /** * Tests the {@link #rescaleToBytes()} operation. */ @Test public void rescaleToBytes(){ assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); // set up synthetic images for testing final RenderedImage test1= ConstantDescriptor.create(128.0f, 128.0f, new Double[]{20000.0}, null); final RenderedImage test2= ConstantDescriptor.create(128.0f, 128.0f, new Double[]{255.0}, null); final RenderedImage test3= getSynthetic(20000); final RenderedImage test4= getSynthetic(255); // starting to check the results // single band value exceed the byte upper bound and is constant final ImageWorker test1I=new ImageWorker(test1).rescaleToBytes(); Assert.assertEquals("Format",test1I.getRenderedOperation().getOperationName()); final double[] maximums1 = test1I.getMaximums(); Assert.assertTrue(maximums1.length==1); Assert.assertEquals(255.0,maximums1[0],1E-10); final double[] minimums1 = test1I.getMinimums(); Assert.assertTrue(minimums1.length==1); Assert.assertEquals(255.0,minimums1[0],1E-10); assertNoData(test1I, null); // single band value does not exceed the byte upper bound and is constant final ImageWorker test2I=new ImageWorker(test2).rescaleToBytes(); Assert.assertEquals("Format",test2I.getRenderedOperation().getOperationName()); final double[] maximums2 = test1I.getMaximums(); Assert.assertTrue(maximums2.length==1); Assert.assertEquals(255.0,maximums2[0],1E-10); final double[] minimums2 = test1I.getMinimums(); Assert.assertTrue(minimums2.length==1); Assert.assertEquals(255.0,minimums2[0],1E-10); assertNoData(test2I, null); // single band value exceed the byte upper bound ImageWorker test3I=new ImageWorker(test3); final double[] maximums3a = test3I.getMaximums(); final double[] minimums3a = test3I.getMinimums(); test3I.rescaleToBytes(); Assert.assertEquals("Rescale",test3I.getRenderedOperation().getOperationName()); final double[] maximums3b = test3I.getMaximums(); final double[] minimums3b = test3I.getMinimums(); assertNoData(test3I, null); if(maximums3a[0]>255) { Assert.assertTrue(Math.abs(maximums3a[0]-maximums3b[0])>1E-10); Assert.assertTrue(Math.abs(255.0-maximums3b[0])>=0); } if(minimums3a[0]<0) { Assert.assertTrue(minimums3b[0]>=0); } // single band value does not exceed the byte upper bound ImageWorker test4I=new ImageWorker(test4); final double[] maximums4a = test4I.getMaximums(); final double[] minimums4a = test4I.getMinimums(); test4I.rescaleToBytes(); Assert.assertEquals("Format",test4I.getRenderedOperation().getOperationName()); final double[] maximums4b = test4I.getMaximums(); final double[] minimums4b = test4I.getMinimums(); Assert.assertEquals(maximums4a[0],maximums4b[0],1E-10); Assert.assertEquals(minimums4a[0],minimums4b[0],1E-10); assertNoData(test4I, null); // now test multibands case ParameterBlock pb = new ParameterBlock(); pb.addSource(test2).addSource(test3); final RenderedImage multiband=JAI.create("BandMerge", pb, null);//BandMergeDescriptor.create(test2, test3, null); ImageWorker testmultibandI=new ImageWorker(multiband); final double[] maximums5a = testmultibandI.getMaximums(); final double[] minimums5a = testmultibandI.getMinimums(); testmultibandI.rescaleToBytes().setNoData(null); final double[] maximums5b = testmultibandI.getMaximums(); final double[] minimums5b = testmultibandI.getMinimums(); Assert.assertEquals(maximums5a[0],maximums5b[0],1E-10); Assert.assertEquals(minimums5a[0],minimums5b[0],1E-10); assertNoData(testmultibandI, null); Assert.assertTrue(Math.abs(maximums5a[1]-maximums5b[1])>1E-10); Assert.assertTrue(Math.abs(minimums5a[1]-minimums5b[1])>1E-10); } @Test public void rescaleToBytesNoData(){ assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); // set up synthetic images for testing final RenderedImage test3 = getSynthetic(20000); ImageWorker worker = new ImageWorker(test3); Range noData = RangeFactory.convert(RangeFactory.create(-10, -10), test3.getSampleModel().getDataType()); worker.setNoData(noData); worker.rescaleToBytes(); RenderedImage image = worker.getRenderedImage(); // check the nodata has been actually set Object property = image.getProperty(NoDataContainer.GC_NODATA); assertNotEquals(property, Image.UndefinedProperty); assertThat(property, instanceOf(NoDataContainer.class)); NoDataContainer nd = (NoDataContainer) property; assertEquals(-10, nd.getAsSingleValue(), 0d); } /** * Tests the {@link ImageWorker#makeColorTransparent} methods. * Some trivial tests are performed before. * @throws IOException * @throws FileNotFoundException * @throws IllegalStateException */ @Test public void testMakeColorTransparent() throws IllegalStateException, FileNotFoundException, IOException { assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); ImageWorker worker = new ImageWorker(sstImage); assertSame(sstImage, worker.getRenderedImage()); assertEquals( 1, worker.getNumBands()); assertEquals( -1, worker.getTransparentPixel()); assertTrue ( worker.isBytes()); assertFalse ( worker.isBinary()); assertTrue ( worker.isIndexed()); assertTrue ( worker.isColorSpaceRGB()); assertFalse ( worker.isColorSpaceGRAYScale()); assertFalse ( worker.isTranslucent()); assertNoData(worker, null); assertSame("Expected no operation.", sstImage, worker.forceIndexColorModel(false).getRenderedImage()); assertSame("Expected no operation.", sstImage, worker.forceIndexColorModel(true ).getRenderedImage()); assertSame("Expected no operation.", sstImage, worker.forceColorSpaceRGB() .getRenderedImage()); assertSame("Expected no operation.", sstImage, worker.retainFirstBand() .getRenderedImage()); assertSame("Expected no operation.", sstImage, worker.retainLastBand() .getRenderedImage()); // Following will change image, so we need to test after the above assertions. assertEquals( 0, worker.getMinimums()[0], 0); assertEquals(255, worker.getMaximums()[0], 0); assertNotSame(sstImage, worker.getRenderedImage()); assertSame("Expected same databuffer, i.e. pixels should not be duplicated.", sstImage.getTile(0,0).getDataBuffer(), worker.getRenderedImage().getTile(0,0).getDataBuffer()); assertSame(worker, worker.makeColorTransparent(Color.WHITE)); assertEquals(255, worker.getTransparentPixel()); assertFalse ( worker.isTranslucent()); assertSame("Expected same databuffer, i.e. pixels should not be duplicated.", sstImage.getTile(0,0).getDataBuffer(), worker.getRenderedImage().getTile(0,0).getDataBuffer()); assertNoData(worker, null); // INDEX TO INDEX-ALPHA worker=new ImageWorker(chlImage).makeColorTransparent(Color.black); show(worker, "CHL01195.png"); assertEquals( 1, worker.getNumBands()); assertEquals( 0, worker.getTransparentPixel()); assertTrue ( worker.isBytes()); assertTrue ( worker.isIndexed()); assertTrue ( worker.isColorSpaceRGB()); assertFalse ( worker.isColorSpaceGRAYScale()); assertFalse ( worker.isTranslucent()); RenderedImage image= worker.getRenderedImage(); assertTrue ( image.getColorModel() instanceof IndexColorModel); IndexColorModel iColorModel=(IndexColorModel) image.getColorModel(); int transparentColor=iColorModel.getRGB(worker.getTransparentPixel())&0x00ffffff; assertTrue ( transparentColor==0); assertNoData(image, null); // INDEX TO INDEX-ALPHA worker=new ImageWorker(bathy).makeColorTransparent(Color.WHITE); show(worker, "BATHY.png"); assertEquals( 1, worker.getNumBands()); assertEquals( 206, worker.getTransparentPixel()); assertTrue ( worker.isBytes()); assertTrue ( worker.isIndexed()); assertTrue ( worker.isColorSpaceRGB()); assertFalse ( worker.isColorSpaceGRAYScale()); assertFalse ( worker.isTranslucent()); image= worker.getRenderedImage(); assertTrue ( image.getColorModel() instanceof IndexColorModel); iColorModel=(IndexColorModel) image.getColorModel(); transparentColor=iColorModel.getRGB(worker.getTransparentPixel())&0x00ffffff; assertTrue ( transparentColor==(Color.WHITE.getRGB()&0x00ffffff)); assertNoData(image, null); // RGB TO RGBA worker=new ImageWorker(smallWorld).makeColorTransparent(new Color(11,10,50)); show(worker, "small_world.png"); assertEquals( 4, worker.getNumBands()); assertEquals( -1, worker.getTransparentPixel()); assertTrue ( worker.isBytes()); assertFalse ( worker.isIndexed()); assertTrue ( worker.isColorSpaceRGB()); assertFalse ( worker.isColorSpaceGRAYScale()); assertTrue ( worker.isTranslucent()); image= worker.getRenderedImage(); assertTrue ( image.getColorModel() instanceof ComponentColorModel); assertNoData(image, null); // RGBA to RGBA worker=new ImageWorker(worldImage).makeColorTransparent(Color.white); show(worker, "world.png"); assertEquals( 4, worker.getNumBands()); assertEquals( -1, worker.getTransparentPixel()); assertTrue ( worker.isBytes()); assertFalse ( worker.isIndexed()); assertTrue ( worker.isColorSpaceRGB()); assertFalse ( worker.isColorSpaceGRAYScale()); assertTrue ( worker.isTranslucent()); image= worker.getRenderedImage(); assertTrue ( image.getColorModel() instanceof ComponentColorModel); assertNoData(image, null); // GRAY TO GRAY-ALPHA worker=new ImageWorker(gray).makeColorTransparent(Color.black); show(worker, "gray.png"); assertEquals( 2, worker.getNumBands()); assertEquals( -1, worker.getTransparentPixel()); assertTrue ( worker.isBytes()); assertFalse ( worker.isIndexed()); assertFalse ( worker.isColorSpaceRGB()); assertTrue ( worker.isColorSpaceGRAYScale()); assertTrue ( worker.isTranslucent()); image= worker.getRenderedImage(); assertTrue ( image.getColorModel() instanceof ComponentColorModel); assertNoData(image, null); // GRAY-ALPHA TO GRAY-ALPHA. worker=new ImageWorker(grayAlpha).makeColorTransparent(Color.black); show(worker, "gray-alpha.png"); assertEquals( 2, worker.getNumBands()); assertEquals( -1, worker.getTransparentPixel()); assertTrue ( worker.isBytes()); assertFalse ( worker.isIndexed()); assertFalse ( worker.isColorSpaceRGB()); assertTrue ( worker.isColorSpaceGRAYScale()); assertTrue ( worker.isTranslucent()); image= worker.getRenderedImage(); assertTrue ( image.getColorModel() instanceof ComponentColorModel); assertNoData(image, null); } /** * Tests the {@link ImageWorker#tile()} methods. * Some trivial tests are performed before. */ @Test public void testReTile() { assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); ImageWorker worker = new ImageWorker(worldImage); assertSame(worldImage, worker.getRenderedImage()); assertNoData(worker.getRenderedImage(), null); assertEquals( 4, worker.getNumBands()); assertEquals( -1, worker.getTransparentPixel()); assertTrue ( worker.isBytes()); assertFalse ( worker.isBinary()); assertFalse ( worker.isIndexed()); assertTrue ( worker.isColorSpaceRGB()); assertFalse ( worker.isColorSpaceGRAYScale()); assertTrue ( worker.isTranslucent()); assertSame("Expected no operation.", worldImage, worker.rescaleToBytes() .getRenderedImage()); assertSame("Expected no operation.", worldImage, worker.forceComponentColorModel().getRenderedImage()); assertSame("Expected no operation.", worldImage, worker.forceColorSpaceRGB() .getRenderedImage()); assertSame("Expected no operation.", worldImage, worker.retainBands(4) .getRenderedImage()); // Following will change image, so we need to test after the above assertions. worker.setRenderingHint(JAI.KEY_IMAGE_LAYOUT, new ImageLayout().setTileGridXOffset(0).setTileGridYOffset(0).setTileHeight(64).setTileWidth(64)); worker.tile(); assertSame("Expected 64.", 64, worker.getRenderedImage().getTileWidth()); assertSame("Expected 64.", 64, worker.getRenderedImage().getTileHeight()); } /** * Visualize the content of given image if {@link #SHOW} is {@code true}. * * @param worker The worker for which to visualize the image. * @param title The title to be given to the windows. */ private static void show(final ImageWorker worker, final String title) { if (SHOW) { Viewer.show(worker.getRenderedImage(), title); } else { assertNotNull(worker.getRenderedImage().getTile(worker.getRenderedImage().getMinTileX(), worker.getRenderedImage().getMinTileY())); // Force computation. } } @Test public void testOpacityAlphaRGBComponent() { testAlphaRGB(false); } @Test public void testOpacityAlphaRGBDirect() { testAlphaRGB(true); } @Test public void testYCbCr() { assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); // check the presence of the PYCC.pf file that contains the profile for the YCbCr color space if(ImageWorker.CS_PYCC==null){ System.out.println("testYCbCr disabled since we are unable to locate the YCbCr color profile"); return; } // RGB component color model ImageWorker worker = new ImageWorker(getSyntheticRGB(false)); RenderedImage image = worker.getRenderedImage(); assertNoData(image, null); assertTrue(image.getColorModel() instanceof ComponentColorModel); assertTrue(!image.getColorModel().hasAlpha()); int sample = image.getTile(0, 0).getSample(0, 0, 2); assertEquals(0, sample); assertFalse(worker.isColorSpaceYCbCr()); worker.forceColorSpaceYCbCr(); assertTrue(worker.isColorSpaceYCbCr()); worker.forceColorSpaceRGB(); assertFalse(worker.isColorSpaceYCbCr()); assertTrue(worker.isColorSpaceRGB()); // RGB Palette worker.forceBitmaskIndexColorModel(); image = worker.getRenderedImage(); assertNoData(image, null); assertTrue(image.getColorModel() instanceof IndexColorModel); assertTrue(!image.getColorModel().hasAlpha()); assertFalse(worker.isColorSpaceYCbCr()); worker.forceColorSpaceYCbCr(); assertTrue(worker.isColorSpaceYCbCr()); worker.forceColorSpaceRGB(); assertFalse(worker.isColorSpaceYCbCr()); assertTrue(worker.isColorSpaceRGB()); // RGB DirectColorModel worker = new ImageWorker(getSyntheticRGB(true)); image = worker.getRenderedImage(); assertNoData(image, null); assertTrue(image.getColorModel() instanceof DirectColorModel); assertTrue(!image.getColorModel().hasAlpha()); sample = image.getTile(0, 0).getSample(0, 0, 2); assertEquals(0, sample); assertFalse(worker.isColorSpaceYCbCr()); worker.forceColorSpaceYCbCr(); assertTrue(worker.isColorSpaceYCbCr()); worker.forceColorSpaceRGB(); assertFalse(worker.isColorSpaceYCbCr()); assertTrue(worker.isColorSpaceRGB()); } private void testAlphaRGB(boolean direct) { assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); ImageWorker worker = new ImageWorker(getSyntheticRGB(direct)); worker.applyOpacity(0.5f); RenderedImage image = worker.getRenderedImage(); assertTrue(image.getColorModel() instanceof ComponentColorModel); assertTrue(image.getColorModel().hasAlpha()); assertNoData(image, null); int sample = image.getTile(0, 0).getSample(0, 0, 3); assertEquals(128, sample); } @Test public void testOpacityRGBA() { assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); assertTrue(worldImage.getColorModel().hasAlpha()); assertTrue(worldImage.getColorModel() instanceof ComponentColorModel); ImageWorker worker = new ImageWorker(worldImage); worker.applyOpacity(0.5f); RenderedImage image = worker.getRenderedImage(); assertTrue(image.getColorModel() instanceof ComponentColorModel); assertTrue(image.getColorModel().hasAlpha()); assertNoData(image, null); Raster tile = worldImage.getTile(0, 0); Raster outputTile = image.getTile(0, 0); for(int i = 0; i < tile.getWidth(); i++) { for(int j = 0; j < tile.getHeight(); j++) { int original = tile.getSample(i, j, 3); int result = outputTile.getSample(i, j, 3); assertEquals(Math.round(original * 0.5), result); } } } @Test public void testOpacityGray() { assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); ImageWorker worker = new ImageWorker(gray); worker.applyOpacity(0.5f); RenderedImage image = worker.getRenderedImage(); assertTrue(image.getColorModel() instanceof ComponentColorModel); assertTrue(image.getColorModel().hasAlpha()); int sample = image.getTile(0, 0).getSample(0, 0, 1); assertEquals(128, sample); assertNoData(image, null); } @Test public void testOpacityGrayROI() { assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); ImageWorker worker = new ImageWorker(gray); worker.setROI(new ROIShape(new Rectangle(1, 1 , 1, 1))); worker.applyOpacity(0.5f); RenderedImage image = worker.getRenderedImage(); assertTrue(image.getColorModel() instanceof ComponentColorModel); assertTrue(image.getColorModel().hasAlpha()); int sample = image.getTile(0, 0).getSample(0, 0, 1); assertEquals(0, sample); assertNoData(image, null); } @Test public void testOpacityGrayNoData() { assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); ImageWorker worker = new ImageWorker(gray); Range noData = RangeFactory.convert(RangeFactory.create(255, 255), gray.getSampleModel().getDataType()); worker.setNoData(noData); worker.applyOpacity(0.5f); RenderedImage image = worker.getRenderedImage(); assertTrue(image.getColorModel() instanceof ComponentColorModel); assertTrue(image.getColorModel().hasAlpha()); int sample = image.getTile(0, 0).getSample(0, 0, 1); assertEquals(0, sample); assertNoData(image, noData); } @Test public void testOpacityGrayAlpha() { assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); ImageWorker worker = new ImageWorker(gray); worker.applyOpacity(0.5f); RenderedImage image = worker.getRenderedImage(); assertTrue(image.getColorModel() instanceof ComponentColorModel); assertTrue(image.getColorModel().hasAlpha()); assertNoData(image, null); Raster tile = gray.getTile(0, 0); Raster outputTile = image.getTile(0, 0); for(int i = 0; i < tile.getWidth(); i++) { for(int j = 0; j < tile.getHeight(); j++) { int original = tile.getSample(i, j, 1); int result = outputTile.getSample(i, j, 1); assertEquals(Math.round(original * 0.5), result); } } } @Test public void testOpacityIndexed() { assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); assertFalse(worldDEMImage.getColorModel().hasAlpha()); ImageWorker worker = new ImageWorker(worldDEMImage); worker.applyOpacity(0.5f); RenderedImage image = worker.getRenderedImage(); assertTrue(image.getColorModel() instanceof IndexColorModel); assertTrue(image.getColorModel().hasAlpha()); assertNoData(image, null); // check the resulting palette IndexColorModel index = (IndexColorModel) image.getColorModel(); for (int i = 0; i < index.getMapSize(); i++) { assertEquals(128, index.getAlpha(i)); } } @Test public void testOpacityIndexedTranslucent() { assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); assertFalse(worldDEMImage.getColorModel().hasAlpha()); final BufferedImage input = getSyntheticTranslucentIndexed(); ImageWorker worker = new ImageWorker(input); worker.applyOpacity(0.5f); RenderedImage image = worker.getRenderedImage(); assertTrue(image.getColorModel() instanceof IndexColorModel); assertTrue(image.getColorModel().hasAlpha()); assertNoData(image, null); // check the resulting palette IndexColorModel outputCM = (IndexColorModel) image.getColorModel(); IndexColorModel inputCM = (IndexColorModel) input.getColorModel(); for (int i = 0; i < inputCM.getMapSize(); i++) { assertEquals(Math.round(inputCM.getAlpha(i) * 0.5), outputCM.getAlpha(i)); } } @Test public void testOptimizeAffine() throws Exception { BufferedImage bi = new BufferedImage(100, 100, BufferedImage.TYPE_3BYTE_BGR); ImageWorker iw = new ImageWorker(bi); // apply straight translation AffineTransform at = AffineTransform.getTranslateInstance(100, 100); iw.affine(at, null, null); RenderedImage t1 = iw.getRenderedImage(); assertEquals(100, t1.getMinX()); assertEquals(100, t1.getMinY()); assertNoData(t1, null); // now go back AffineTransform atInverse = AffineTransform.getTranslateInstance(-100, -100); iw.affine(atInverse, null, null); RenderedImage t2 = iw.getRenderedImage(); assertEquals(0, t2.getMinX()); assertEquals(0, t2.getMinY()); assertSame(bi, t2); assertNoData(t2, null); } @Test public void testTileSizeScale() throws Exception { // apply straight translation AffineTransform at = AffineTransform.getScaleInstance(1000, 1000); testTileSize(at); } @Test public void testTileSizeGenericAffine() throws Exception { // apply straight translation AffineTransform at = new AffineTransform(100, 0.5, -0.5, 100, 20, 20); testTileSize(at); } private void testTileSize(AffineTransform at) { BufferedImage bi = new BufferedImage(4, 4, BufferedImage.TYPE_3BYTE_BGR); ImageWorker iw = new ImageWorker(bi); iw.affine(at, null, null); RenderedImage t1 = iw.getRenderedImage(); assertEquals(512, t1.getTileWidth()); assertEquals(512, t1.getTileHeight()); } @Test public void testAffineNegative() throws Exception { BufferedImage bi = new BufferedImage(100, 100, BufferedImage.TYPE_3BYTE_BGR); ImageWorker iw = new ImageWorker(bi); // flipping tx, not a scale, used to blow AffineTransform at = AffineTransform.getScaleInstance(-1, -1); iw.affine(at, null, null); RenderedImage t1 = iw.getRenderedImage(); assertEquals(-100, t1.getMinX()); assertEquals(-100, t1.getMinY()); assertNoData(t1, null); } @Test public void testOptimizedWarp() throws Exception { // do it again, make sure the image does not turn black since GridCoverage2D ushortCoverage = EXAMPLES.get(5); GridCoverage2D coverage = project(ushortCoverage, CRS.parseWKT(GOOGLE_MERCATOR_WKT), null, "nearest", null); RenderedImage ri = coverage.getRenderedImage(); ImageWorker.WARP_REDUCTION_ENABLED = false; AffineTransform at = new AffineTransform(0.4, 0, 0, 0.5, -200, -200); RenderedOp fullChain = (RenderedOp) new ImageWorker(ri).affine(at, Interpolation.getInstance(Interpolation.INTERP_NEAREST), new double[] { 0 }) .getRenderedImage(); assertEquals("Scale", fullChain.getOperationName()); fullChain.getTiles(); assertNoData(fullChain, null); ImageWorker.WARP_REDUCTION_ENABLED = true; RenderedOp reduced = (RenderedOp) new ImageWorker(ri).affine(at, Interpolation.getInstance(Interpolation.INTERP_NEAREST), new double[] { 0 }) .getRenderedImage(); // force computation, to make sure it does not throw exceptions reduced.getTiles(); // check the chain has been reduced assertEquals("Warp", reduced.getOperationName()); assertEquals(1, reduced.getSources().size()); assertSame(ushortCoverage.getRenderedImage(), reduced.getSourceImage(0)); assertNoData(reduced, null); // check the bounds of the output image has not changed assertEquals(fullChain.getBounds(), reduced.getBounds()); // check we are getting a reasonable tile size and origin (JAI warp_affine will generate // odd results otherwise assertEquals(0, reduced.getTileGridXOffset()); assertEquals(0, reduced.getTileGridYOffset()); assertEquals(ushortCoverage.getRenderedImage().getTileWidth(), reduced.getTileWidth()); assertEquals(ushortCoverage.getRenderedImage().getTileHeight(), reduced.getTileHeight()); } @Test public void testWarpAffinePreserveBackgorund() throws Exception { // pick a RGB image, do warp and then affine double[] background = new double[] {1,2,3}; GridCoverage2D warped = project(EXAMPLES.get(0), CRS.parseWKT(GOOGLE_MERCATOR_WKT), null, "nearest", background, null); ImageWorker iwa = new ImageWorker(warped.getRenderedImage()); iwa.affine(AffineTransform.getScaleInstance(0.5, 0.5), Interpolation.getInstance(Interpolation.INTERP_NEAREST), null); RenderedOp reduced = (RenderedOp) iwa.getRenderedImage(); assertEquals("Warp", reduced.getOperationName()); assertEquals(background, reduced.getParameterBlock().getObjectParameter(2)); } @Test public void testOptimizedWarpOnLargeUpscale() throws Exception { BufferedImage bi = new BufferedImage(4, 4, BufferedImage.TYPE_BYTE_INDEXED); GridCoverage2D source = new GridCoverageFactory().create("Test", bi, new ReferencedEnvelope(0, 1, 0, 1, DefaultGeographicCRS.WGS84)); GridCoverage2D coverage = project(source, CRS.parseWKT(GOOGLE_MERCATOR_WKT), null, "nearest", null); RenderedImage ri = coverage.getRenderedImage(); checkTileSize(ri, AffineTransform.getScaleInstance(100, 100)); checkTileSize(ri, new AffineTransform(100, 0, 0, 100, 10, 10)); } private void checkTileSize(RenderedImage ri, AffineTransform scale) { final Interpolation interpolation = Interpolation.getInstance(Interpolation.INTERP_NEAREST); RenderedOp reduced = (RenderedOp) new ImageWorker(ri).affine(scale, interpolation, new double[] { 0 }) .getRenderedImage(); // the tile size is not 4x4 assertEquals(400, reduced.getTileWidth()); assertEquals(400, reduced.getTileHeight()); } @Test public void testRescaleNoData() { // Getting input gray scale image ImageWorker w = new ImageWorker(gray); // Removing optional Alpha band w.retainFirstBand(); // Formatting to int (avoid to convert values greater to 127 into negative values during rescaling) w.format(DataBuffer.TYPE_INT); // Setting NoData Range noData = RangeFactory.create(0, 0); w.setNoData(noData); // Setting background to 10 w.setBackground(new double[] { 10d }); // Rescaling data w.rescale(new double[] { 2 }, new double[] { 2 }); // Getting Minimum value, It cannot be equal or lower than the offset value (2) double minimum = w.getMinimums()[0]; assertTrue(minimum > 2); assertNoData(w.getRenderedImage(), noData); } @Test public void testLookupROI() { // Getting input Palette image ImageWorker w = new ImageWorker(chlImage); // Forcing component colormodel w.forceComponentColorModel(); // Applying a lookup table byte[] data = new byte[256]; // Setting all the values to 50 Arrays.fill(data, (byte) 50); LookupTable table = LookupTableFactory.create(data); // Add a ROI ROI roi = new ROIShape(new Rectangle(chlImage.getMinX(), chlImage.getMinY(), chlImage.getWidth() / 2, chlImage.getHeight() / 2)); w.setROI(roi); // Setting Background to 0 w.setBackground(new double[] { 0 }); // Appliyng lookup w.lookup(table); // Removing NoData and ROI and calculate the statistics on the whole image w.setNoData(null); w.setROI(null); // Calculating the minimum and maximum double min = w.getMinimums()[0]; double max = w.getMaximums()[0]; // Ensuring minimum is 0 and maximum 50 assertEquals(min, 0, 1E-7); assertEquals(max, 50, 1E-7); assertNoData(w.getRenderedImage(), null); } @Test public void testDoubleCrop() { ImageWorker iw = new ImageWorker(gray); iw.crop(10, 10, 50, 50); RenderedImage ri1 = iw.getRenderedImage(); assertEquals(10, ri1.getMinX()); assertEquals(10, ri1.getMinY()); assertEquals(50, ri1.getWidth()); assertEquals(50, ri1.getHeight()); // the crop area overlaps with the image iw.crop(30, 30, 60, 60); RenderedImage ri2 = iw.getRenderedImage(); assertEquals(30, ri2.getMinX()); assertEquals(30, ri2.getMinY()); assertEquals(30, ri2.getWidth()); assertEquals(30, ri2.getHeight()); // check intermediate crop elimination RenderedOp op = (RenderedOp) ri2; assertEquals(gray, op.getSourceObject(0)); assertNoData(op, null); } @Test public void testAddBands() { ImageWorker iw = new ImageWorker(gray).retainBands(1); RenderedImage input = iw.getRenderedImage(); RenderedImage image = iw.addBands(new RenderedImage[]{input, input, input, input}, false, null).getRenderedImage(); assertEquals(4, image.getTile(0, 0).getSampleModel().getNumBands()); assertNoData(image, null); } @Test public void testBandMerge() { ImageWorker iw = new ImageWorker(gray).retainBands(1); RenderedImage image = iw.bandMerge(4).getRenderedImage(); assertEquals(4, image.getTile(0, 0).getSampleModel().getNumBands()); assertNoData(image, null); } static void assertNoData(ImageWorker worker, Range nodata) { assertNoData(worker.getRenderedImage(), nodata); } static void assertNoData(RenderedImage image, Range nodata) { Object property = image.getProperty(NoDataContainer.GC_NODATA); if(nodata == null) { // image properties return an instance of Object in case the property is not found assertEquals("We expect lack of noData, but one was found", Object.class, property.getClass()); } else { NoDataContainer container = (NoDataContainer) property; assertEquals(nodata, container.getAsRange()); } } @Test public void testMosaicRasterROI() throws Exception { BufferedImage red = getSyntheticRGB(Color.RED); ROI redROI = new ROI(new ROIShape(new Rectangle2D.Double(0, 0, 64, 64)).getAsImage()); BufferedImage blue = getSyntheticRGB(Color.BLUE); ROI blueROI = new ROI(new ROIShape(new Rectangle2D.Double(63, 63, 64, 64)).getAsImage()); testMosaicRedBlue(red, redROI, blue, blueROI); } @Test public void testMosaicRasterROI2() throws Exception { BufferedImage red = getSyntheticRGB(Color.RED); ROI redROI = new ROI(getSyntheticSolidGray((byte) 1), 1).subtract(new ROIShape(new Rectangle(0, 64, 128, 128))).subtract(new ROIShape(new Rectangle(64, 0, 64, 128))); BufferedImage blue = getSyntheticRGB(Color.BLUE); ROI blueROI = new ROI(new ROIShape(new Rectangle2D.Double(63, 63, 64, 64)).getAsImage()); testMosaicRedBlue(red, redROI, blue, blueROI); } @Test public void testMosaicShapeROI() throws Exception { BufferedImage red = getSyntheticRGB(Color.RED); ROI redROI = new ROIShape(new Rectangle2D.Double(0, 0, 64, 64)); BufferedImage blue = getSyntheticRGB(Color.BLUE); ROI blueROI = new ROIShape(new Rectangle2D.Double(63, 63, 64, 64)); testMosaicRedBlue(red, redROI, blue, blueROI); } @Test public void testMosaicShapeRasterROI() throws Exception { BufferedImage red = getSyntheticRGB(Color.RED); ROI redROI = new ROIShape(new Rectangle2D.Double(0, 0, 64, 64)); BufferedImage blue = getSyntheticRGB(Color.BLUE); ROI blueROI = new ROI(new ROIShape(new Rectangle2D.Double(63, 63, 64, 64)).getAsImage()); testMosaicRedBlue(red, redROI, blue, blueROI); } @Test public void testMosaicRasterShapeROI() throws Exception { BufferedImage red = getSyntheticRGB(Color.RED); ROI redROI = new ROI(new ROIShape(new Rectangle2D.Double(0, 0, 64, 64)).getAsImage()); BufferedImage blue = getSyntheticRGB(Color.BLUE); ROI blueROI = new ROIShape(new Rectangle2D.Double(63, 63, 64, 64)); testMosaicRedBlue(red, redROI, blue, blueROI); } @Test public void testMosaicGeometryROI() throws Exception { BufferedImage red = getSyntheticRGB(Color.RED); ROI redROI = new ROIGeometry(JTS.toGeometry(new Envelope(0, 64, 0, 64))); BufferedImage blue = getSyntheticRGB(Color.BLUE); ROI blueROI = new ROIGeometry(JTS.toGeometry(new Envelope(63, 127, 63, 127))); testMosaicRedBlue(red, redROI, blue, blueROI); } @Test public void testMosaicGeometryShapeROI() throws Exception { BufferedImage red = getSyntheticRGB(Color.RED); ROI redROI = new ROIGeometry(JTS.toGeometry(new Envelope(0, 64, 0, 64))); BufferedImage blue = getSyntheticRGB(Color.BLUE); ROI blueROI = new ROIShape(new Rectangle2D.Double(63, 63, 64, 64)); testMosaicRedBlue(red, redROI, blue, blueROI); } @Test public void testMosaicShapeGeometryROI() throws Exception { BufferedImage red = getSyntheticRGB(Color.RED); ROI redROI = new ROIShape(new Rectangle2D.Double(0, 0, 64, 64)); BufferedImage blue = getSyntheticRGB(Color.BLUE); ROI blueROI = new ROIGeometry(JTS.toGeometry(new Envelope(63, 127, 63, 127))); testMosaicRedBlue(red, redROI, blue, blueROI); } private void testMosaicRedBlue(BufferedImage red, ROI redROI, BufferedImage blue, ROI blueROI) { ImageWorker iw = new ImageWorker(); iw.mosaic(new RenderedImage[] {red, blue}, MosaicDescriptor.MOSAIC_TYPE_OVERLAY, null, new ROI[] {redROI, blueROI}, null, null); RenderedImage mosaicked = iw.getRenderedImage(); Object roiProperty = mosaicked.getProperty("ROI"); assertThat(roiProperty, instanceOf(ROI.class)); ROI roi = (ROI) roiProperty; // check ROI assertTrue(roi.contains(20, 20)); assertTrue(roi.contains(120, 120)); assertFalse(roi.contains(20, 120)); assertFalse(roi.contains(120, 20)); } @Test public void testMosaicRasterGeometry() throws Exception { BufferedImage red = getSyntheticRGB(Color.RED); ROI redROI = new ROI(new ROIShape(new Rectangle2D.Double(0, 0, 64, 64)).getAsImage()); BufferedImage blue = getSyntheticRGB(Color.BLUE); ROI blueROI = new ROIGeometry(JTS.toGeometry(new Envelope(63, 127, 63, 127))); testMosaicRedBlue(red, redROI, blue, blueROI); } @Test public void testMosaicBackgroundColor() { BufferedImage red = getSyntheticRGB(Color.RED); ROI redROI = new ROI(new ROIShape(new Rectangle2D.Double(0, 0, 64, 64)).getAsImage()); BufferedImage blue = getSyntheticRGB(Color.BLUE); ROI blueROI = new ROIGeometry(JTS.toGeometry(new Envelope(63, 127, 63, 127))); ImageWorker iw = new ImageWorker(); iw.setBackground(new double[] {255, 255, 255}); iw.mosaic(new RenderedImage[] {red, blue}, MosaicDescriptor.MOSAIC_TYPE_OVERLAY, null, new ROI[] {redROI, blueROI}, null, null); RenderedImage mosaicked = iw.getRenderedImage(); Object roiProperty = mosaicked.getProperty("ROI"); assertThat(roiProperty, not((instanceOf(ROI.class)))); } @Test public void testMosaicIndexedBackgroundColor() { BufferedImage gray = getSyntheticGrayIndexed(128); // test the case where the color is in the palette ImageWorker iw = new ImageWorker(); iw.setBackground(new double[] {10, 10, 10}); iw.mosaic(new RenderedImage[] {gray}, MosaicDescriptor.MOSAIC_TYPE_OVERLAY, null, null, null, null); RenderedImage ri = iw.getRenderedImage(); assertThat(ri.getColorModel(), instanceOf(IndexColorModel.class)); // and the case where it's not and we have to expand iw = new ImageWorker(); iw.setBackground(new double[] {255, 255, 255}); iw.mosaic(new RenderedImage[] {gray}, MosaicDescriptor.MOSAIC_TYPE_OVERLAY, null, null, null, null); ri = iw.getRenderedImage(); assertThat(ri.getColorModel(), instanceOf(ComponentColorModel.class)); } @Test public void testForceComponentColorModelIncompletePalette() throws IOException { // small palette, but the image has values above it BufferedImage bi = getSyntheticGrayIndexed(10); ImageWorker iw = new ImageWorker(bi); iw.setBackground(new double[] {255, 255, 255}); iw.forceComponentColorModel(); // used to crash here RenderedImage ri = iw.getRenderedImage(); int[] pixel = new int[2]; ri.getData().getPixel(256, 256, pixel); assertEquals(255, pixel[0]); assertEquals(255, pixel[1]); } @Test public void testWarpROITileSize() { assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); ImageWorker worker = new ImageWorker(gray); worker.setROI(new ROIShape(new Rectangle(0, 0, gray.getWidth(), gray.getHeight()))); RenderedImage image = worker.getRenderedImage(); assertNotEquals(java.awt.Image.UndefinedProperty, image.getProperty("ROI")); ImageWorker iw = new ImageWorker(image); iw.warp(new WarpAffine(AffineTransform.getScaleInstance(0.5, 0.5)), Interpolation.getInstance(Interpolation.INTERP_NEAREST)); RenderedImage warped = iw.getRenderedImage(); assertNotEquals(java.awt.Image.UndefinedProperty, warped.getProperty("ROI")); ROI warpedROI = (ROI) warped.getProperty("ROI"); // turned into raster, the ROI has the same tile structure as the image assertEquals(ROI.class, warpedROI.getClass()); assertEquals(warped.getTileWidth(), warpedROI.getAsImage().getTileWidth()); assertEquals(warped.getTileHeight(), warpedROI.getAsImage().getTileHeight()); } @Test public void testAffineROITileSize() { assertTrue("Assertions should be enabled.", ImageWorker.class.desiredAssertionStatus()); ImageWorker worker = new ImageWorker(gray); ROIShape roiShape = new ROIShape(new Rectangle(0, 0, gray.getWidth(), gray.getHeight())); ROI roi = new ROI(roiShape.getAsImage()); worker.setROI(roi); RenderedImage image = worker.getRenderedImage(); assertNotEquals(java.awt.Image.UndefinedProperty, image.getProperty("ROI")); ImageWorker iw = new ImageWorker(image); iw.affine(AffineTransform.getScaleInstance(0.5, 0.5), Interpolation.getInstance(Interpolation.INTERP_NEAREST), null); RenderedImage scaled = iw.getRenderedImage(); assertNotEquals(java.awt.Image.UndefinedProperty, scaled.getProperty("ROI")); ROI scaledROI = (ROI) scaled.getProperty("ROI"); assertEquals(ROI.class, scaledROI.getClass()); assertEquals(scaled.getTileWidth(), scaledROI.getAsImage().getTileWidth()); assertEquals(scaled.getTileHeight(), scaledROI.getAsImage().getTileHeight()); } @Test public void testStatsAfterCrop() throws IOException { final int width = 100; final int height = 100; final WritableRaster raster = RasterFactory.createBandedRaster( DataBuffer.TYPE_BYTE, width, height, 1, null); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { raster.setSample(x, y, 0, x+y); } } final ColorModel cm = new ComponentColorModelJAI(ColorSpace .getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); final BufferedImage image = new BufferedImage(cm, raster, false, null); ImageWorker worker = new ImageWorker(image); double maxs[] = worker.getMaximums(); assertEquals(width + height - 2, (int)maxs[0]); worker = worker.crop(0, 0, width/2, height/2); maxs = worker.getMaximums(); assertEquals(width/2 + height/2 - 2, (int)maxs[0]); } }