1 /*
2  * $RCSfile: MatchCDFCRIF.java,v $
3  *
4  * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
5  *
6  * Use is subject to license terms.
7  *
8  * $Revision: 1.1 $
9  * $Date: 2005/02/11 04:56:31 $
10  * $State: Exp $
11  */
12 package com.lightcrafts.media.jai.opimage;
13 import java.awt.RenderingHints;
14 import java.awt.image.RenderedImage;
15 import java.awt.image.renderable.ParameterBlock;
16 import com.lightcrafts.mediax.jai.CRIFImpl;
17 import com.lightcrafts.mediax.jai.Histogram;
18 import com.lightcrafts.mediax.jai.ImageLayout;
19 
20 /**
21  * A <code>CRIF</code> supporting the "MatchCDF" operation in the rendered
22  * and renderable image layers.
23  *
24  * @see com.lightcrafts.mediax.jai.operator.MatchCDFDescriptor
25  * @see com.lightcrafts.mediax.jai.operator.PiecewiseDescriptor
26  * @see PiecewiseOpImage
27  *
28  *
29  * @since EA4
30  */
31 public class MatchCDFCRIF extends CRIFImpl {
32     /*
33      * Creates a piecewise mapping from an image with cumulative distribution
34      * function of pixel values CDFin to another given by CDFout.
35      */
createHistogramMap(float[] CDFin, float[] CDFout, double lowValue, double binWidth, int numBins, float[] abscissa, float[] ordinate)36     private static void createHistogramMap(float[] CDFin, float[] CDFout,
37                                            double lowValue, double binWidth,
38                                            int numBins,
39                                            float[] abscissa,
40                                            float[] ordinate) {
41         // Initialize the first abscissa and the output CDF index.
42         double x = lowValue;
43         int j = 0;
44         int jMax = numBins - 1;
45 
46         // Generate one breakpoint for each bin.
47         for(int i = 0; i < numBins; i++) {
48             // Find the first output bin such that the output CDF is
49             // greater than or equal to the input CDF at the current bin.
50             float w = CDFin[i];
51             while(CDFout[j] < w && j < jMax) j++;
52 
53             // Set the breakpoint values.
54             abscissa[i] = (float)x;
55             ordinate[i] = (float)(j*binWidth);
56 
57             // Increment the abscissa.
58             x += binWidth;
59         }
60     }
61 
62     /**
63      * Create a set of breakpoints which will map the input histogram to
64      * an output histogram with the specified cumulative distribution function.
65      *
66      * @param histIn The input histogram.
67      * @param CDFOut The output CDF.
68      * @return A piecewise transform which will modify the input histogram
69      * to match the output CDF.
70      */
createSpecificationMap(Histogram histIn, float[][] CDFOut)71     private static float[][][] createSpecificationMap(Histogram histIn,
72                                                       float[][] CDFOut) {
73         // Allocate the overall breakpoint array.
74         int numBands = histIn.getNumBands();
75         float[][][] bp = new float[numBands][][];
76 
77         // Calculate a different set of breakpoints for each band.
78         float[] CDFin = null;
79         for(int band = 0; band < numBands; band++) {
80             // Allocate memory for the breakpoints for this band.
81             int numBins = histIn.getNumBins(band);
82             bp[band] = new float[2][];
83             bp[band][0] = new float[numBins];
84             bp[band][1] = new float[numBins];
85 
86             // Calculate the total count over all bins of this band.
87             int[] binsIn = histIn.getBins(band);
88             long binTotalIn = binsIn[0];
89             for(int i = 1; i < numBins; i++) {
90                 binTotalIn += binsIn[i];
91             }
92 
93             // Allocate memory for the CDF for this band only if needed.
94             if(CDFin == null || CDFin.length < numBins) {
95                 CDFin = new float[numBins];
96             }
97 
98             // Calculate the Cumulative Distribution Function (CDF) for the
99             // input histogram for this band.
100             CDFin[0] = (float)binsIn[0]/binTotalIn;
101             for(int i = 1; i < numBins; i++) {
102                 CDFin[i] = CDFin[i-1] + (float)binsIn[i]/binTotalIn;
103             }
104 
105             // Calculate the mapping function.
106             double binWidth =
107                 (histIn.getHighValue(band) - histIn.getLowValue(band))/numBins;
108             createHistogramMap(CDFin,
109                                CDFOut.length > 1 ? CDFOut[band] : CDFOut[0],
110                                histIn.getLowValue(band),
111                                binWidth, numBins, bp[band][0], bp[band][1]);
112         }
113 
114         return bp;
115     }
116 
117     /** Constructor. */
MatchCDFCRIF()118     public MatchCDFCRIF() {
119         super("matchcdf");
120     }
121 
122     /**
123      * Creates a new instance of <code>PiecewiseOpImage</code> in the
124      * rendered layer.
125      *
126      * @param args   The source image and the breakpoints.
127      * @param hints  Optionally contains destination image layout.
128      */
create(ParameterBlock args, RenderingHints renderHints)129     public RenderedImage create(ParameterBlock args,
130                                 RenderingHints renderHints) {
131         // Get ImageLayout from renderHints if any.
132         ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints);
133 
134 
135         // Derive breakpoints from the histogram and specified CDF.
136 	RenderedImage src = args.getRenderedSource(0);
137 	Histogram hist = (Histogram)src.getProperty("histogram");
138 	float[][] CDF = (float[][])args.getObjectParameter(0);
139 	float[][][] bp = createSpecificationMap(hist, CDF);
140 
141 	return new PiecewiseOpImage(src, renderHints, layout, bp);
142     }
143 }
144