Search in sources :

Example 1 with Compression

use of it.geosolutions.imageio.plugins.jp2k.JP2KKakaduImageWriteParam.Compression in project imageio-ext by geosolutions-it.

the class JP2KKakaduImageWriter method write.

@Override
public void write(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) throws IOException {
    // ////////////////////////////////////////////////////////////////////
    // 
    // Variables initialization
    // 
    // ////////////////////////////////////////////////////////////////////
    final String fileName = outputFile.getAbsolutePath();
    JP2KKakaduImageWriteParam jp2Kparam;
    final boolean writeCodeStreamOnly;
    final double quality;
    int cLayers = 1;
    int cLevels;
    final boolean cycc;
    byte[] geoJp2 = null;
    final boolean orgGen_plt;
    int orgGen_tlm = JP2KKakaduImageWriteParam.UNSPECIFIED_ORG_GEN_TLM;
    int qGuard = -1;
    String orgT_parts = null;
    String cPrecincts = null;
    boolean setTiling = false;
    int tileW = Integer.MIN_VALUE;
    int tileH = Integer.MIN_VALUE;
    ProgressionOrder cOrder = null;
    double[] bitRates = null;
    boolean addCommentMarker = ADD_COMMENT_MARKER;
    int sProfile = JP2KKakaduImageWriteParam.DEFAULT_SPROFILE;
    Compression compression = Compression.UNDEFINED;
    // //
    if (param == null) {
        param = getDefaultWriteParam();
    }
    if (param instanceof JP2KKakaduImageWriteParam) {
        jp2Kparam = (JP2KKakaduImageWriteParam) param;
        writeCodeStreamOnly = jp2Kparam.isWriteCodeStreamOnly();
        bitRates = jp2Kparam.getQualityLayersBitRates();
        double q = jp2Kparam.getQuality();
        if (q < 0.01) {
            q = 0.01;
            if (LOGGER.isLoggable(Level.FINE))
                LOGGER.fine("Quality level should be in the range 0.01 - 1. /n Reassigning it to 0.01");
        }
        quality = q;
        geoJp2 = jp2Kparam.getGeoJp2();
        setTiling = jp2Kparam.getTilingMode() == ImageWriteParam.MODE_EXPLICIT;
        if (setTiling) {
            tileH = jp2Kparam.getTileHeight();
            tileW = jp2Kparam.getTileWidth();
        }
        // COD PARAMS
        cOrder = jp2Kparam.getcOrder();
        cPrecincts = jp2Kparam.getcPrecincts();
        cLevels = jp2Kparam.getCLevels();
        cLayers = jp2Kparam.getQualityLayers();
        // ORG PARAMS
        orgGen_plt = jp2Kparam.isOrgGen_plt();
        orgGen_tlm = jp2Kparam.getOrgGen_tlm();
        orgT_parts = jp2Kparam.getOrgT_parts();
        qGuard = jp2Kparam.getqGuard();
        addCommentMarker &= jp2Kparam.isAddCommentMarker();
        sProfile = jp2Kparam.getsProfile();
        compression = jp2Kparam.getCompression();
        if (bitRates != null && bitRates.length != cLayers) {
            throw new IllegalArgumentException(" Specified bitRates parameter's length " + bitRates.length + " should match the quality layers parameter " + "(cLayers): " + cLayers);
        }
        if (compression != null) {
            switch(compression) {
                case LOSSY:
                    if (bitRates != null) {
                        if (LOGGER.isLoggable(Level.FINE)) {
                            LOGGER.fine("Applying lossy compression leveraging on provided quality bit rates");
                        }
                    } else {
                        if (LOGGER.isLoggable(Level.FINE)) {
                            LOGGER.fine("Applying lossy compression leveraging on quality factor");
                        }
                    }
                    break;
                case NUMERICALLY_LOSSLESS:
                    if (bitRates != null) {
                        if (LOGGER.isLoggable(Level.FINE)) {
                            LOGGER.fine("Applying numerically lossless compression leveraging on " + "provided quality bit rates");
                        }
                        if (KakaduUtilities.notEqual(bitRates[bitRates.length - 1], 0)) {
                            throw new IllegalArgumentException("Asking for a Numerically Lossless " + "but the last quality layer's bit rate should be 0 " + " instead of " + bitRates[bitRates.length - 1]);
                        }
                    } else {
                        if (LOGGER.isLoggable(Level.FINE)) {
                            LOGGER.fine("Applying numerically lossless compression");
                        }
                    }
                    break;
            }
        } else {
            compression = Compression.UNDEFINED;
        }
    } else {
        orgGen_plt = false;
        writeCodeStreamOnly = true;
        quality = JP2KKakaduImageWriteParam.DEFAULT_QUALITY;
        cLevels = JP2KKakaduImageWriteParam.DEFAULT_C_LEVELS;
    }
    // ////////////////////////////////////////////////////////////////////
    // 
    // Image properties initialization
    // 
    // ////////////////////////////////////////////////////////////////////
    final RenderedImage inputRenderedImage = image.getRenderedImage();
    final int sourceWidth = inputRenderedImage.getWidth();
    final int sourceHeight = inputRenderedImage.getHeight();
    final int sourceMinX = inputRenderedImage.getMinX();
    final int sourceMinY = inputRenderedImage.getMinY();
    final SampleModel sm = inputRenderedImage.getSampleModel();
    final int dataType = sm.getDataType();
    final boolean isDataSigned = (dataType != DataBuffer.TYPE_USHORT && dataType != DataBuffer.TYPE_BYTE);
    final ColorModel colorModel = inputRenderedImage.getColorModel();
    final boolean hasPalette = colorModel instanceof IndexColorModel ? true : false;
    final int[] numberOfBits = colorModel.getComponentSize();
    // The number of bytes used by header, markers, boxes
    int bytesOverHead = 0;
    // We suppose all bands share the same bitDepth
    final int bits = numberOfBits[0];
    int nComponents = sm.getNumBands();
    // Array to store optional look up table entries
    byte[] reds = null;
    byte[] greens = null;
    byte[] blues = null;
    // //
    if (hasPalette) {
        cycc = false;
        cLevels = 1;
        IndexColorModel icm = (IndexColorModel) colorModel;
        final int lutSize = icm.getMapSize();
        final int numColorComponents = colorModel.getNumColorComponents();
        // Updating the number of components to write as RGB (3 bands)
        if (writeCodeStreamOnly) {
            nComponents = numColorComponents;
            // //
            // 
            // Caching look up table for future accesses.
            // 
            // //
            reds = new byte[lutSize];
            blues = new byte[lutSize];
            greens = new byte[lutSize];
            icm.getReds(reds);
            icm.getGreens(greens);
            icm.getBlues(blues);
        } else {
            // adding pclr and cmap boxes overhead bytes
            // NE + NC + Bi
            bytesOverHead += (4 + 2 + numColorComponents + 1);
            // pclr LUT
            bytesOverHead += lutSize * numColorComponents + 4;
            // cmap
            bytesOverHead += 20;
        }
    } else if (quality == 1) {
        cycc = false;
    } else {
        cycc = true;
    }
    // //
    // 
    // Setting regions and sizes and retrieving parameters
    // 
    // //
    final int xSubsamplingFactor = param.getSourceXSubsampling();
    final int ySubsamplingFactor = param.getSourceYSubsampling();
    final Rectangle originalBounds = new Rectangle(sourceMinX, sourceMinY, sourceWidth, sourceHeight);
    final Rectangle imageBounds = (Rectangle) originalBounds.clone();
    final Dimension destSize = new Dimension();
    KakaduUtilities.computeRegions(imageBounds, destSize, param);
    boolean resampleInputImage = false;
    if (xSubsamplingFactor != 1 || ySubsamplingFactor != 1 || !imageBounds.equals(originalBounds)) {
        resampleInputImage = true;
    }
    // Destination sizes
    final int destinationWidth = destSize.width;
    final int destinationHeight = destSize.height;
    final int rowSize = (destinationWidth * nComponents);
    final int bandSize = destinationHeight * destinationWidth;
    final int imageSize = bandSize * nComponents;
    // ////////////////////////////////////////////////////////////////////
    // 
    // Kakadu objects initialization
    // 
    // ////////////////////////////////////////////////////////////////////
    Kdu_compressed_target outputTarget = null;
    Jp2_target target = null;
    Jp2_family_tgt familyTarget = null;
    // Setting decomposition levels
    cLevels = setDecompositionLevels(cLevels, destinationWidth);
    try {
        if (writeCodeStreamOnly) {
            // Open a simple file target
            outputTarget = new Kdu_simple_file_target();
            ((Kdu_simple_file_target) outputTarget).Open(fileName);
            final int extensionIndex = fileName.lastIndexOf(".");
            final String suffix = fileName.substring(extensionIndex, fileName.length());
            if (suffix.equalsIgnoreCase(".jp2") && LOGGER.isLoggable(Level.FINE)) {
                LOGGER.fine("When writing codestreams, the \".j2c\" file suffix is suggested instead of \".jp2\"");
            }
        } else {
            familyTarget = new Jp2_family_tgt();
            familyTarget.Open(fileName);
            target = new Jp2_target();
            target.Open(familyTarget);
            // Adding signature, fileType, image header, Jp2Header box bytes
            // + jp2c marker.
            bytesOverHead += 84;
            if (geoJp2 != null) {
                bytesOverHead += geoJp2.length;
            }
        }
        bytesOverHead += addMarkerBytes(nComponents, destinationWidth, destinationHeight, tileW, tileH, orgGen_tlm, addCommentMarker);
        if (bytesOverHead >= imageSize) {
            bytesOverHead = 0;
        }
        final long qualityLayersSize = bitRates != null ? imageSize : (long) ((imageSize - bytesOverHead) * quality * bits * KakaduUtilities.BIT_TO_BYTE_FACTOR);
        // //
        // 
        // Parameters initialization
        // 
        // //
        final Kdu_codestream codeStream = new Kdu_codestream();
        Siz_params params = new Siz_params();
        initializeParams(params, destinationWidth, destinationHeight, bits, nComponents, isDataSigned, tileW, tileH, sProfile);
        // Create a codestream on the proper output object.
        if (writeCodeStreamOnly) {
            codeStream.Create(params, outputTarget, null);
        } else {
            codeStream.Create(params, target, null);
        }
        if (!initializeCodestream(codeStream, cycc, cLevels, quality, cLayers, colorModel, writeCodeStreamOnly, dataType, target, geoJp2, orgGen_plt, orgGen_tlm, orgT_parts, qGuard, cOrder, cPrecincts, compression)) {
            throw new IOException("Unable to initialize the codestream due to a missing " + "Jp2_target object");
        }
        // //
        // 
        // Preparing parameters for stripe compression
        // 
        // //
        final Kdu_stripe_compressor compressor = new Kdu_stripe_compressor();
        // Array with one entry for each image component, identifying the
        // number of lines supplied for that component in the present call.
        // All entries must be non-negative.
        final int[] stripeHeights = new int[nComponents];
        // Array with one entry for each image component, identifying
        // the separation between horizontally adjacent samples within the
        // corresponding stripe buffer found in the stripe_bufs array.
        final int[] sampleGaps = new int[nComponents];
        // Array with one entry for each image component, identifying
        // the separation between vertically adjacent samples within the
        // corresponding stripe buffer found in the stripe_bufs array.
        final int[] rowGaps = new int[nComponents];
        // Array with one entry for each image component, identifying the
        // position of the first sample of that component within the buffer array.
        final int[] sampleOffsets = new int[nComponents];
        // Array with one entry for each image component, identifying the
        // number of significant bits used to represent each sample.
        final int[] precisions = new int[nComponents];
        initializeStripeCompressor(compressor, codeStream, quality, cLayers, qualityLayersSize, bitRates, compression, rowSize, destinationHeight, destinationWidth, nComponents, stripeHeights, sampleGaps, rowGaps, sampleOffsets, precisions, numberOfBits, addCommentMarker);
        // ////////////////////////////////////////////////////////////////
        // 
        // Pushing Stripes
        // 
        // ////////////////////////////////////////////////////////////////
        pushStripes(compressor, inputRenderedImage, imageBounds, originalBounds, isDataSigned, resampleInputImage, writeCodeStreamOnly, hasPalette, nComponents, bits, destinationWidth, destinationHeight, xSubsamplingFactor, ySubsamplingFactor, stripeHeights, sampleGaps, rowGaps, sampleOffsets, precisions, reds, greens, blues);
        // ////////////////////////////////////////////////////////////////
        // 
        // Kakadu Objects Finalization
        // 
        // ////////////////////////////////////////////////////////////////
        compressor.Finish();
        compressor.Native_destroy();
        codeStream.Destroy();
    } catch (KduException e) {
        throw (IOException) new IOException("Error caused by a Kakadu exception during write operation").initCause(e);
    } finally {
        // //
        if (!writeCodeStreamOnly && target != null) {
            try {
                if (target.Exists())
                    target.Close();
            } catch (Throwable e) {
            // Does Nothing
            }
            try {
                target.Native_destroy();
            } catch (Throwable e) {
            // Does Nothing
            }
            if (familyTarget != null) {
                try {
                    if (familyTarget.Exists())
                        familyTarget.Close();
                } catch (Throwable e) {
                // Does Nothing
                }
                try {
                    familyTarget.Native_destroy();
                } catch (Throwable e) {
                // Does Nothing
                }
            }
        } else if (writeCodeStreamOnly && outputTarget != null) {
            try {
                outputTarget.Close();
            } catch (Throwable e) {
            // Does Nothing
            }
            try {
                outputTarget.Native_destroy();
            } catch (Throwable e) {
            // Does Nothing
            }
        }
        // //
        if (outputStream != null) {
            writeOnStream();
        }
    }
}
Also used : ProgressionOrder(it.geosolutions.imageio.plugins.jp2k.JP2KKakaduImageWriteParam.ProgressionOrder) Compression(it.geosolutions.imageio.plugins.jp2k.JP2KKakaduImageWriteParam.Compression) Rectangle(java.awt.Rectangle) Kdu_stripe_compressor(kdu_jni.Kdu_stripe_compressor) Kdu_simple_file_target(kdu_jni.Kdu_simple_file_target) IndexColorModel(java.awt.image.IndexColorModel) ColorModel(java.awt.image.ColorModel) Kdu_compressed_target(kdu_jni.Kdu_compressed_target) Kdu_codestream(kdu_jni.Kdu_codestream) IndexColorModel(java.awt.image.IndexColorModel) KduException(kdu_jni.KduException) Siz_params(kdu_jni.Siz_params) Dimension(java.awt.Dimension) IOException(java.io.IOException) Jp2_target(kdu_jni.Jp2_target) SampleModel(java.awt.image.SampleModel) Jp2_family_tgt(kdu_jni.Jp2_family_tgt) RenderedImage(java.awt.image.RenderedImage)

Aggregations

Compression (it.geosolutions.imageio.plugins.jp2k.JP2KKakaduImageWriteParam.Compression)1 ProgressionOrder (it.geosolutions.imageio.plugins.jp2k.JP2KKakaduImageWriteParam.ProgressionOrder)1 Dimension (java.awt.Dimension)1 Rectangle (java.awt.Rectangle)1 ColorModel (java.awt.image.ColorModel)1 IndexColorModel (java.awt.image.IndexColorModel)1 RenderedImage (java.awt.image.RenderedImage)1 SampleModel (java.awt.image.SampleModel)1 IOException (java.io.IOException)1 Jp2_family_tgt (kdu_jni.Jp2_family_tgt)1 Jp2_target (kdu_jni.Jp2_target)1 KduException (kdu_jni.KduException)1 Kdu_codestream (kdu_jni.Kdu_codestream)1 Kdu_compressed_target (kdu_jni.Kdu_compressed_target)1 Kdu_simple_file_target (kdu_jni.Kdu_simple_file_target)1 Kdu_stripe_compressor (kdu_jni.Kdu_stripe_compressor)1 Siz_params (kdu_jni.Siz_params)1