All Downloads are FREE. Search and download functionalities are using the official Maven repository.

icc.lut.MatrixBasedTransformTosRGB Maven / Gradle / Ivy

Go to download

Fork of jpeg2k code from https://code.google.com/p/jj2000/. This is a dependency for support of compression in Grib2 files in netCDF-java and TDS. We welcome bug fixes and other contributions to this code.

The newest version!
/*****************************************************************************
 *
 * $Id: MatrixBasedTransformTosRGB.java,v 1.1 2002/07/25 14:56:49 grosbois Exp $
 *
 * Copyright Eastman Kodak Company, 343 State Street, Rochester, NY 14650
 * $Date $
 *****************************************************************************/

package icc.lut;

import colorspace .ColorSpace;
import icc .ICCProfile;
import icc .RestrictedICCProfile;
import icc .tags.ICCXYZType;
import icc .lut.LookUpTableFP;
import jj2000.j2k.image.DataBlkInt;
import jj2000.j2k.image.DataBlkFloat;

/**
 * Transform for applying ICCProfiling to an input DataBlk
 * 
 * @see		jj2000.j2k.image.DataBlkInt
 * @see		jj2000.j2k.image.DataBlkFloat
 * @version	1.0
 * @author	Bruce A. Kern
 */
public class MatrixBasedTransformTosRGB {

    private static final String eol = System.getProperty ("line.separator");

    // Start of contant definitions:

    private static final int               // Convenience
        RED   = ICCProfile.RED,
        GREEN = ICCProfile.GREEN,
        BLUE  = ICCProfile.BLUE;

    private static final double            // Define the PCS to linear sRGB matrix coefficients
        SRGB00 =  3.1337,
        SRGB01 = -1.6173,
        SRGB02 = -0.4907,
        SRGB10 = -0.9785,
        SRGB11 =  1.9162,
        SRGB12 =  0.0334,
        SRGB20 =  0.0720,
        SRGB21 = -0.2290,
        SRGB22 =  1.4056;
    
    // Define constants representing the indices into the matrix array
    private static final int M00 = 0;
    private static final int M01 = 1;
    private static final int M02 = 2;
    private static final int M10 = 3;
    private static final int M11 = 4;
    private static final int M12 = 5;
    private static final int M20 = 6;
    private static final int M21 = 7;
    private static final int M22 = 8;

    private static final double ksRGBExponent		 = (1.0 / 2.4);
    private static final double ksRGBScaleAfterExp	 = 1.055;
    private static final double ksRGBReduceAfterExp  = 0.055;
    private static final double ksRGBShadowCutoff	 = 0.0031308;
    private static final double ksRGBShadowSlope     = 12.92; 

    // End of contant definitions:

    private final double [] matrix;      // Matrix coefficients 

    private LookUpTableFP [] fLut = new LookUpTableFP [3];			
    private LookUpTable32LinearSRGBtoSRGB lut; // Linear sRGB to sRGB LUT

    private final int [] dwMaxValue;
    private final int [] dwShiftValue;

    private int            dwMaxCols = 0;			// Maximum number of columns that can be processed
    private int            dwMaxRows = 0;			// Maximum number of rows that can be processed

    private float [][] fBuf = null;      // Intermediate output of the first LUT operation.

    /**
     * String representation of class
     * @return suitable representation for class 
     */
    public String toString () {
        int i,j;

        StringBuffer rep = new StringBuffer ("[MatrixBasedTransformTosRGB: ");

        StringBuffer body = new StringBuffer ("  ");
        body.append(eol).append("ksRGBExponent= ").append(String.valueOf(ksRGBExponent));
        body.append(eol).append("ksRGBScaleAfterExp= ").append(String.valueOf(ksRGBScaleAfterExp));
        body.append(eol).append("ksRGBReduceAfterExp= ").append(String.valueOf(ksRGBReduceAfterExp));

        
        body.append(eol)
            .append("dwMaxValues= ")
            .append(String.valueOf(dwMaxValue[0])).append(", ")
            .append(String.valueOf(dwMaxValue[1])).append(", ")
            .append(String.valueOf(dwMaxValue[2]));

        body.append(eol)
            .append("dwShiftValues= ")
            .append(String.valueOf(dwShiftValue[0])).append(", ")
            .append(String.valueOf(dwShiftValue[1])).append(", ")
            .append(String.valueOf(dwShiftValue[2]));

        body.append(eol)
            .append(eol).append("fLut= ")
            .append(eol).append(ColorSpace.indent ("  ", "fLut[RED]=  "+ fLut[0].toString()))
            .append(eol).append(ColorSpace.indent ("  ", "fLut[GRN]=  "+ fLut[1].toString()))
            .append(eol).append(ColorSpace.indent ("  ", "fLut[BLU]=  "+ fLut[2].toString()));

        // Print the matrix
        body .append(eol).append(eol).append("[matrix ");
        for (i=0; i<3; ++i) {
            body .append(eol).append("  ");
            for (j=0; j<3; ++j) {
                body .append(matrix [3*i+j] + "   "); }}
        body  .append("]");
        
        
        // Print the LinearSRGBtoSRGB lut.
        body.append(eol).append(eol).append(lut.toString());

        rep.append(ColorSpace.indent("  ",body)).append("]");
        return rep.append("]").toString(); }


    /**
     * Construct a 3 component transform based on an input RestricedICCProfile
     * This transform will pass the input throught a floating point lut (LookUpTableFP),
     * apply a matrix to the output and finally pass the intermediate buffer through
     * a 8-bit lut (LookUpTable8).  This operation will be designated (LFP*M*L8) * Data
     * The operators (LFP*M*L8) are constructed here.  Although the data for
     * only one component is returned, the transformation must be done for all
     * components, because the matrix application involves a linear combination of
     * component input to produce the output.
     *   @param ricc input profile
     *   @param dwMaxValue clipping value for output.
     *   @param dwMaxCols number of columns to transform
     *   @param dwMaxRows number of rows to transform
     */
    public MatrixBasedTransformTosRGB 
        (RestrictedICCProfile  ricc,
         int dwMaxValue [],
         int dwShiftValue []) {

        // Assure the proper type profile for this xform.
        if (ricc.getType() != RestrictedICCProfile.kThreeCompInput)
            throw new IllegalArgumentException ("MatrixBasedTransformTosRGB: wrong type ICCProfile supplied");

        int c;					     				// component index.
        this .dwMaxValue   = dwMaxValue;
        this .dwShiftValue = dwShiftValue;

        // Create the LUTFP from the input profile.
        for (c=0; c<3; ++c) {
            fLut[c] = LookUpTableFP.createInstance (ricc.trc[c], dwMaxValue[c]+1); }

        // Create the Input linear to PCS matrix
        matrix = createMatrix(ricc, dwMaxValue);      // Create and matrix from the ICC profile.

        // Create the final LUT32
        lut = LookUpTable32LinearSRGBtoSRGB.createInstance
            (dwMaxValue[0], dwMaxValue[0], 
             ksRGBShadowCutoff, ksRGBShadowSlope, 
             ksRGBScaleAfterExp, ksRGBExponent, ksRGBReduceAfterExp); }


    private double [] createMatrix (RestrictedICCProfile ricc, int [] maxValues) {

        // Coefficients from the input linear to PCS matrix
        double dfPCS00 = ICCXYZType.XYZToDouble(ricc.colorant[RED].x);
        double dfPCS01 = ICCXYZType.XYZToDouble(ricc.colorant[GREEN].x);
        double dfPCS02 = ICCXYZType.XYZToDouble(ricc.colorant[BLUE].x);
        double dfPCS10 = ICCXYZType.XYZToDouble(ricc.colorant[RED].y);
        double dfPCS11 = ICCXYZType.XYZToDouble(ricc.colorant[GREEN].y);
        double dfPCS12 = ICCXYZType.XYZToDouble(ricc.colorant[BLUE].y);
        double dfPCS20 = ICCXYZType.XYZToDouble(ricc.colorant[RED].z);
        double dfPCS21 = ICCXYZType.XYZToDouble(ricc.colorant[GREEN].z);
        double dfPCS22 = ICCXYZType.XYZToDouble(ricc.colorant[BLUE].z);

        double [] matrix = new double [9]; 
        matrix[M00] = maxValues[0] * (SRGB00 * dfPCS00 + SRGB01 * dfPCS10 + SRGB02 * dfPCS20);
        matrix[M01] = maxValues[0] * (SRGB00 * dfPCS01 + SRGB01 * dfPCS11 + SRGB02 * dfPCS21);
        matrix[M02] = maxValues[0] * (SRGB00 * dfPCS02 + SRGB01 * dfPCS12 + SRGB02 * dfPCS22);
        matrix[M10] = maxValues[1] * (SRGB10 * dfPCS00 + SRGB11 * dfPCS10 + SRGB12 * dfPCS20);
        matrix[M11] = maxValues[1] * (SRGB10 * dfPCS01 + SRGB11 * dfPCS11 + SRGB12 * dfPCS21);
        matrix[M12] = maxValues[1] * (SRGB10 * dfPCS02 + SRGB11 * dfPCS12 + SRGB12 * dfPCS22);
        matrix[M20] = maxValues[2] * (SRGB20 * dfPCS00 + SRGB21 * dfPCS10 + SRGB22 * dfPCS20);
        matrix[M21] = maxValues[2] * (SRGB20 * dfPCS01 + SRGB21 * dfPCS11 + SRGB22 * dfPCS21);
        matrix[M22] = maxValues[2] * (SRGB20 * dfPCS02 + SRGB21 * dfPCS12 + SRGB22 * dfPCS22);

        return matrix; }


    /**
     * Performs the transform.  Pass the input throught the LookUpTableFP, apply the
     * matrix to the output and finally pass the intermediate buffer through the
     * LookUpTable8.  This operation is designated (LFP*M*L8) * Data are already 
     * constructed.  Although the data for only one component is returned, the
     * transformation must be done for all components, because the matrix application
     * involves a linear combination of component input to produce the output.
     *   @param ncols number of columns in the input
     *   @param nrows number of rows in the input
     *   @param inb input data block
     *   @param outb output data block
     * @exception MatrixBasedTransformException
     */
    public void apply (DataBlkInt inb [], DataBlkInt outb [])
        throws MatrixBasedTransformException {
        int [][] in  = new int [3][], out = new int [3][]; // data references.

        int nrows = inb[0].h, ncols = inb[0].w;

        if ((fBuf == null) || (fBuf[0].length < ncols*nrows)) {
            fBuf = new float [3][ncols*nrows];}

        // for each component (rgb)
        for (int c=0; c<3; ++c) {

            // Reference the input and output samples.
            in[c]  = (int []) inb[c].getData(); 
            out[c] = (int []) outb[c].getData();
            
            // Assure a properly sized output buffer.
            if (out[c]==null || out[c].length < in[c].length) {
                out[c] = new int [in[c].length];
                outb[c].setData(out[c]); }

            // The first thing to do is to process the input into a standard form
            // and through the first input LUT, producing floating point output values
            standardizeMatrixLineThroughLut(inb[c], fBuf[c], dwMaxValue[c], fLut[c]); }

        // For each row and column
        float [] ra = fBuf[RED];
        float [] ga = fBuf[GREEN];
        float [] ba = fBuf[BLUE];

        int   [] ro = out [RED];
        int   [] go = out [GREEN];
        int   [] bo = out [BLUE];
        int   [] lut32 = lut.lut;

        double r, g, b;
        int val, index=0;
        for (int y = 0; y < inb[0].h; ++y) {
            int end = index+inb[0].w;
            while (index < end) {
                // Calculate the rgb pixel indices for this row / column
                r = ra[index];
                g = ga[index];
                b = ba[index];

                // Apply the matrix to the intermediate floating point data in order to index the 
                // final LUT.
                val = (int)(matrix[M00] * r + 
                            matrix[M01] * g + 
                            matrix[M02] * b + 
                            0.5);
                // Clip the calculated value if necessary..
                if      (val <  0)             ro[index] = lut32[0];
                else if (val >= lut32.length ) ro[index] = lut32[lut32.length-1];
                else                           ro[index] = lut32[val];
                        
                val = (int)(matrix[M10] * r + 
                            matrix[M11] * g + 
                            matrix[M12] * b + 
                            0.5);
                // Clip the calculated value if necessary..
                if      (val <  0)             go[index] = lut32[0];
                else if (val >= lut32.length ) go[index] = lut32[lut32.length-1];
                else                           go[index] = lut32[val];
                        
                val = (int)(matrix[M20] * r + 
                            matrix[M21] * g + 
                            matrix[M22] * b + 
                            0.5);
                // Clip the calculated value if necessary..
                if      (val <  0)             bo[index] = lut32[0];
                else if (val >= lut32.length ) bo[index] = lut32[lut32.length-1];
                else                           bo[index] = lut32[val];

                index++; }}}       

    /**
     * Performs the transform.  Pass the input throught the LookUpTableFP, apply the
     * matrix to the output and finally pass the intermediate buffer through the
     * LookUpTable8.  This operation is designated (LFP*M*L8) * Data are already 
     * constructed.  Although the data for only one component is returned, the
     * transformation must be done for all components, because the matrix application
     * involves a linear combination of component input to produce the output.
     *   @param ncols number of columns in the input
     *   @param nrows number of rows in the input
     *   @param inb input data block
     *   @param outb output data block
     * @exception MatrixBasedTransformException
     */
    public void apply (DataBlkFloat inb [], DataBlkFloat outb []) 
        throws MatrixBasedTransformException {

        float [][] in  = new float [3][], out = new float [3][]; // data references.

        int nrows = inb[0].h, ncols = inb[0].w;

        if ((fBuf == null) || (fBuf[0].length < ncols*nrows)) {
            fBuf = new float [3][ncols*nrows]; }

        // for each component (rgb)
        for (int c=0; c<3; ++c) {

            // Reference the input and output pixels.
            in[c]  = (float []) inb[c].getData(); 
            out[c] = (float []) outb[c].getData();
            
            // Assure a properly sized output buffer.
            if (out[c]==null || out[c].length < in[c].length) {
                out[c] = new float [in[c].length];
                outb[c].setData(out[c]); }

            // The first thing to do is to process the input into a standard form
            // and through the first input LUT, producing floating point output values
            standardizeMatrixLineThroughLut(inb[c], fBuf[c], dwMaxValue[c], fLut[c]); }

        int   [] lut32 = lut.lut;

        // For each row and column 
        int index=0, val;
        for (int y = 0; y < inb[0].h; ++y) {
            int end = index+inb[0].w;
            while (index < end) {
                // Calculate the rgb pixel indices for this row / column
                        
                // Apply the matrix to the intermediate floating point data inorder to index the 
                // final LUT.
                val = (int)(matrix[M00] * fBuf[RED]  [index] + 
                            matrix[M01] * fBuf[GREEN][index] + 
                            matrix[M02] * fBuf[BLUE] [index] + 
                            0.5);
                // Clip the calculated value if necessary..
                if      (val <  0)             out[0][index] = lut32[0];
                else if (val >= lut32.length ) out[0][index] = lut32[lut32.length-1];
                else                           out[0][index] = lut32[val];

                val = (int)(matrix[M10] * fBuf[RED]  [index] + 
                            matrix[M11] * fBuf[GREEN][index] + 
                            matrix[M12] * fBuf[BLUE] [index] + 
                            0.5);
                // Clip the calculated value if necessary..
                if      (val <  0)             out[1][index] = lut32[0];
                else if (val >= lut32.length ) out[1][index] = lut32[lut32.length-1];
                else                           out[1][index] = lut32[val];
                        
                val = (int)(matrix[M20] * fBuf[RED]  [index] + 
                            matrix[M21] * fBuf[GREEN][index] + 
                            matrix[M22] * fBuf[BLUE] [index] + 
                            0.5);
                // Clip the calculated value if necessary..
                if      (val <  0)             out[2][index] = lut32[0];
                else if (val >= lut32.length ) out[2][index] = lut32[lut32.length-1];
                else                           out[2][index] = lut32[val];
                        
                index++; }}
    }

    private static void standardizeMatrixLineThroughLut
        (
         DataBlkInt inb,            // input datablock
         float [] out,				// output data reference
         int dwInputMaxValue,		// Maximum value of the input for clipping  
         LookUpTableFP lut			// Inital input LUT
         )   {
        int wTemp, j=0;
        int [] in = (int []) inb.getData(); // input pixel reference
        float [] lutFP = lut.lut;
        for (int y = inb.uly; y < inb.uly+inb.h; ++y) {
            for (int x = inb.ulx; x < inb.ulx+inb.w; ++x) {
                int i = inb.offset+(y-inb.uly)*inb.scanw+(x-inb.ulx); // pixel index.
                if (in[i] > dwInputMaxValue) wTemp = dwInputMaxValue;
                else if (in[i] < 0) wTemp = 0;                       
                else wTemp = in[i];                                  
                out[j++] = lutFP[wTemp]; }}}      
     

    private static void standardizeMatrixLineThroughLut
        (
         DataBlkFloat inb,          // input datablock
         float [] out,				// output data reference
         float dwInputMaxValue,		// Maximum value of the input for clipping  
         LookUpTableFP lut			// Inital input LUT
         )   {
        int j=0;
        float wTemp;
        float [] in = (float []) inb.getData(); // input pixel reference
        float [] lutFP = lut.lut;

        for (int y = inb.uly; y < inb.uly+inb.h; ++y) {
            for (int x = inb.ulx; x < inb.ulx+inb.w; ++x) {
                int i = inb.offset+(y-inb.uly)*inb.scanw+(x-inb.ulx); // pixel index.
                if (in[i] > dwInputMaxValue) wTemp = dwInputMaxValue;
                else if (in[i] < 0) wTemp = 0;                       
                else wTemp = in[i];                                  
                out[j++] = lutFP[(int)wTemp]; }}}            

    /* end class MatrixBasedTransformTosRGB */ }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy