/* JP2K Kakadu Image Writer V. 1.0 * * (c) 2008 Quality Nighthawk Teleradiology Group, Inc. * Contact: info@qualitynighthawk.com * * Produced by GeoSolutions, Eng. Daniele Romagnoli and Eng. Simone Giannecchini * GeoSolutions S.A.S. --- Via Carignoni 51, 55041 Camaiore (LU) Italy * Contact: info@geo-solutions.it * * Released under the Gnu Lesser General Public License version 3. * All rights otherwise reserved. * * JP2K Kakadu Image Writer is distributed on an "AS IS" basis, * WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU Lesser General Public License version 3 for more details. * http://www.fsf.org/licensing/licenses/lgpl.html */ package it.geosolutions.imageio.plugins.jp2k; import it.geosolutions.imageio.plugins.jp2k.JP2KKakaduImageWriteParam.Compression; import it.geosolutions.imageio.plugins.jp2k.JP2KKakaduImageWriteParam.ProgressionOrder; import java.awt.image.RenderedImage; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.logging.Logger; import javax.imageio.IIOImage; import javax.imageio.ImageReader; import javax.imageio.ImageWriteParam; import javax.imageio.stream.FileImageInputStream; import javax.imageio.stream.FileImageOutputStream; import javax.media.jai.JAI; import javax.media.jai.operator.ExtremaDescriptor; import javax.media.jai.operator.SubtractDescriptor; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; import com.sun.media.imageioimpl.plugins.tiff.TIFFImageReaderSpi; import com.sun.media.jai.operator.ImageReadDescriptor; public class JP2KKakaduQualityLayersWriteTest extends Assert { final private static String inputFileName = "c:\\11AUG04063808-S3DS_R29C5-052565658010_01_P001.ntf.JP2"; public enum CompressionProfile { NPJE, EPJE } private final static double BPPPB[] = new double[] { 0.03125, 0.0625, 0.125, 0.25, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.5, 1.7, 2.0, 2.3, 3.5, 3.9, 0 }; private final static double THRESHOLD = 1E-6; private final static JP2KKakaduImageReaderSpi JP2K_RSPI = new JP2KKakaduImageReaderSpi(); private final static JP2KKakaduImageWriterSpi JP2K_WSPI = new JP2KKakaduImageWriterSpi(); // private final static TIFFImageReaderSpi TIFF_SPI = new TIFFImageReaderSpi(); /** The LOGGER for this class. */ private static final Logger LOGGER = Logger .getLogger("it.geosolutions.imageio.plugins.jp2k"); static { JAI.getDefaultInstance().getTileCache().setMemoryCapacity(768*1024*1024); }; @Test @Ignore public void testWriteCompressionProfiles() throws IOException { final File file = new File(inputFileName); FileImageInputStream fis = null; new FileImageInputStream(file); ImageReader reader = null; try { fis = new FileImageInputStream(file); reader = JP2K_RSPI.createReaderInstance(); RenderedImage ri = ImageReadDescriptor.create(fis, 0, false, false, false, null, null, null, reader, null); // ImageIO.write(ri, "TIFF", new File("c:\\readback.tif")); write(inputFileName, ri, Compression.NUMERICALLY_LOSSLESS, CompressionProfile.NPJE); write(inputFileName, ri, Compression.NUMERICALLY_LOSSLESS, CompressionProfile.EPJE); write(inputFileName, ri, Compression.LOSSY, CompressionProfile.NPJE); write(inputFileName, ri, Compression.LOSSY, CompressionProfile.EPJE); } finally { if (fis != null){ try { fis.close(); } catch (Throwable t){ } } if (reader != null){ try { reader.dispose(); } catch (Throwable t){ } } } } private void write( final String inputFileName, final RenderedImage originalImage, final Compression type, final CompressionProfile profile) throws IOException { JP2KKakaduImageWriter kakaduWriter = (JP2KKakaduImageWriter) JP2K_WSPI.createWriterInstance(); ImageReader reader = JP2K_RSPI.createReaderInstance(); FileImageOutputStream fos = null; FileImageInputStream fis = null; String suffix; switch (type) { case NUMERICALLY_LOSSLESS: suffix = "NL"; break; case LOSSY: suffix = "VL"; break; default: suffix = "Lossy"; } final String outputFileName = inputFileName + "rewrittenAs_" + suffix + "_" + profile + ".jp2"; final File outputFile = new File(outputFileName); try { fos = new FileImageOutputStream(outputFile); kakaduWriter.setOutput(fos); JP2KKakaduImageWriteParam param = setupWriteParameter(kakaduWriter, type, profile); kakaduWriter.write(null, new IIOImage(originalImage, null, null), param); kakaduWriter.dispose(); kakaduWriter = null; fis = new FileImageInputStream(outputFile); RenderedImage readBack = ImageReadDescriptor.create(fis, 0, false, false, false, null, null, null, reader, null); RenderedImage difference = SubtractDescriptor.create(readBack, originalImage, null); double[][] extrema = (double[][]) ExtremaDescriptor.create(difference, null, 1, 1, false, 1, null).getProperty("Extrema"); if (type == Compression.NUMERICALLY_LOSSLESS){ for (int i = 0; i < extrema.length; i++){ for (int j = 0; j < extrema[i].length; j++){ assertEquals(extrema[i][j], 0, THRESHOLD); } } LOGGER.info("Numerically LossLess successfull: No differences with " + "respect to the original image"); } else { StringBuilder sb = new StringBuilder( "Extrema values on VISUALLY_LOSSLESS (LOSSY)" + " compressions for: " + outputFileName); for (int i = 0; i < extrema.length; i++){ for (int j = 0; j < extrema[i].length; j++){ sb.append(extrema[i][j]).append(" "); } } LOGGER.info(sb.toString()); } } finally { if (fos != null){ try { fos.close(); } catch (Throwable t){ } } if (kakaduWriter != null){ try { kakaduWriter.dispose(); } catch (Throwable t){ } } if (fis != null){ try { fis.close(); } catch (Throwable t){ } } if (reader != null){ try { reader.dispose(); } catch (Throwable t){ } } } } private JP2KKakaduImageWriteParam setupWriteParameter( final JP2KKakaduImageWriter kakaduWriter, final Compression type, final CompressionProfile profile) { JP2KKakaduImageWriteParam param = (JP2KKakaduImageWriteParam) kakaduWriter.getDefaultWriteParam(); param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); double bitRates[] = null; switch (type){ case NUMERICALLY_LOSSLESS: param.setQuality(1); param.setQualityLayers(20); param.setCompression(Compression.NUMERICALLY_LOSSLESS); bitRates = new double[20]; System.arraycopy(BPPPB, 0, bitRates, 0, 20); break; case LOSSY: param.setQualityLayers(19); bitRates = new double[19]; param.setCompression(Compression.LOSSY); System.arraycopy(BPPPB, 0, bitRates, 0, 19); break; default: param.setQualityLayers(19); break; } switch (profile){ case NPJE: param.setcOrder(ProgressionOrder.LRCP); break; case EPJE: param.setcOrder(ProgressionOrder.RLCP); break; } param.setCLevels(5); param.setTilingMode(ImageWriteParam.MODE_EXPLICIT); param.setTiling(1024, 1024, 0,0); param.setsProfile(1); param.setOrgGen_plt(true); param.setOrgGen_tlm(1); param.setQualityLayersBitRates(bitRates); param.setWriteCodeStreamOnly(true); param.setAddCommentMarker(true); return param; } @Test @Ignore public void extractJP2() throws IOException { final String in = "SPECIFY A NITF FILE WITH JP2 COMPRESSION"; final String out = in + ".jp2"; FileInputStream fis = new FileInputStream(new File(in)); FileOutputStream fos = new FileOutputStream(new File(out)); // Provide start and end bytes within the stream (use an HEX editor by looking for // SOC marker (FF4F) and EOC marker (FFD9) within the NITF file) final int start = Integer.parseInt("e7e" ,16); final int end = Integer.parseInt("1681cf0a" ,16); final int toBeRead = end - start; byte b[] = new byte [65536]; fis.skip(start); int readd = 0; int totalRead = start + readd; while ((readd = (fis.read(b))) != -1){ totalRead += readd; if (totalRead > toBeRead){ fos.write(b, 0, totalRead - toBeRead); break; } else { fos.write(b, 0, readd); } } fis.close(); fos.close(); } }