001: /*
002: * $RCSfile: MatchCDFDescriptor.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.1 $
009: * $Date: 2005/02/11 04:57:38 $
010: * $State: Exp $
011: */
012: package javax.media.jai.operator;
013:
014: import java.awt.Image;
015: import java.awt.RenderingHints;
016: import java.awt.image.RenderedImage;
017: import java.awt.image.renderable.ParameterBlock;
018: import java.awt.image.renderable.RenderableImage;
019: import javax.media.jai.Histogram;
020: import javax.media.jai.JAI;
021: import javax.media.jai.OperationDescriptorImpl;
022: import javax.media.jai.ParameterBlockJAI;
023: import javax.media.jai.RenderableOp;
024: import javax.media.jai.RenderedOp;
025: import javax.media.jai.registry.RenderableRegistryMode;
026: import javax.media.jai.registry.RenderedRegistryMode;
027:
028: /**
029: * An <code>OperationDescriptor</code> describing the "MatchCDF" operation.
030: *
031: * <p> The "MatchCDF" operation performs a piecewise linear mapping of the
032: * pixel values of an image such that the Cumulative Distribution Function
033: * (CDF) of the destination image matches as closely as possible a specified
034: * Cumulative Distribution Function. The desired CDF is described by an
035: * array of the form <pre>float CDF[numBands][numBins[b]]</pre> where
036: * <pre>numBins[b]</pre> denotes the number of bins in the histogram of the
037: * source image for band <i>b</i>. Each element in the array
038: * <pre>CDF[b]</pre> must be non-negative, the array must represent a non-
039: * decreasing sequence, and the last element of the array must be 1.0F.
040: * The source image must have a <code>Histogram</code> object available via
041: * its <code>getProperty()</code> method.
042: *
043: * <p><table border=1>
044: * <caption>Resource List</caption>
045: * <tr><th>Name</th> <th>Value</th></tr>
046: * <tr><td>GlobalName</td> <td>MatchCDF</td></tr>
047: * <tr><td>LocalName</td> <td>MatchCDF</td></tr>
048: * <tr><td>Vendor</td> <td>com.sun.media.jai</td></tr>
049: * <tr><td>Description</td> <td>Matches pixel values to a supplied CDF.</td></tr>
050: * <tr><td>DocURL</td> <td>http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MatchCDFDescriptor.html</td></tr>
051: * <tr><td>Version</td> <td>1.0</td></tr>
052: * <tr><td>arg0Desc</td> <td>The desired Cumulative Distribution Function.</td></tr>
053: * </table></p>
054: *
055: * <p><table border=1>
056: * <caption>Parameter List</caption>
057: * <tr><th>Name</th> <th>Class Type</th>
058: * <th>Default Value</th></tr>
059: * <tr><td>CDF</td> <td>float[][]</td>
060: * <td>CDF for histogram equalization</td>
061: * </table></p>
062: *
063: * @see java.awt.image.DataBuffer
064: * @see javax.media.jai.ImageLayout
065: * @see javax.media.jai.OperationDescriptor
066: */
067: public class MatchCDFDescriptor extends OperationDescriptorImpl {
068: /**
069: * The resource strings that provide the general documentation
070: * and specify the parameter list for this operation.
071: */
072: private static final String[][] resources = {
073: { "GlobalName", "MatchCDF" },
074: { "LocalName", "MatchCDF" },
075: { "Vendor", "com.sun.media.jai" },
076: { "Description", JaiI18N.getString("MatchCDFDescriptor0") },
077: {
078: "DocURL",
079: "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MatchCDFDescriptor.html" },
080: { "Version", JaiI18N.getString("DescriptorVersion") },
081: { "arg0Desc",
082: "The desired Cumulative Distribution Function." }, };
083:
084: /** The parameter class list for this operation. */
085: private static final Class[] paramClasses = { float[][].class };
086:
087: /** The parameter name list for this operation. */
088: private static final String[] paramNames = { "CDF" };
089:
090: /** The parameter default value list for this operation. */
091: private static final Object[] paramDefaults = { null };
092:
093: private static final String[] supportedModes = { "rendered",
094: "renderable" };
095:
096: /** Constructor. */
097: public MatchCDFDescriptor() {
098: super (resources, supportedModes, 1, paramNames, paramClasses,
099: paramDefaults, null);
100: }
101:
102: /**
103: * Validates the input sources and parameter.
104: *
105: * <p> In addition to the standard checks performed by the
106: * superclass method, this method checks that the source image
107: * contains a "histogram" property and that the "CDF" array
108: * is appropriate for it.
109: */
110: public boolean validateArguments(String modeName,
111: ParameterBlock args, StringBuffer msg) {
112: if (!super .validateArguments(modeName, args, msg)) {
113: return false;
114: }
115:
116: if (!modeName.equalsIgnoreCase("rendered"))
117: return true;
118:
119: // Get the source and the CDF array.
120: RenderedImage src = args.getRenderedSource(0);
121:
122: float[][] CDF = (float[][]) args.getObjectParameter(0);
123:
124: // Ensure that the Histogram is available and that the CDF array
125: // is appropriate for it.
126: Object prop = src.getProperty("histogram");
127: if (prop == null || prop.equals(Image.UndefinedProperty)) {
128: // Property is null or undefined.
129: msg.append(getName() + " "
130: + JaiI18N.getString("MatchCDFDescriptor1"));
131: return false;
132: } else if (!(prop instanceof Histogram)) {
133: // Property is not a Histogram.
134: msg.append(getName() + " "
135: + JaiI18N.getString("MatchCDFDescriptor2"));
136: return false;
137: } else {
138: Histogram hist = (Histogram) prop;
139: int numBands = hist.getNumBands();
140:
141: if (CDF == null) {
142: int[] numBins = hist.getNumBins();
143: CDF = new float[numBands][];
144:
145: for (int b = 0; b < numBands; b++) {
146: CDF[b] = new float[numBins[b]];
147: for (int i = 0; i < numBins[b]; i++)
148: CDF[b][i] = (i + 1) / numBins[b];
149: }
150: }
151:
152: if (CDF.length != numBands) {
153: // CDF length does not match Histogram.
154: msg.append(getName() + " "
155: + JaiI18N.getString("MatchCDFDescriptor3"));
156: return false;
157: }
158:
159: for (int b = 0; b < numBands; b++) {
160: if (CDF[b].length != hist.getNumBins(b)) {
161: // Check that CDF length for this band matches Histogram.
162: msg.append(getName() + " "
163: + JaiI18N.getString("MatchCDFDescriptor4"));
164: return false;
165: }
166: }
167:
168: for (int b = 0; b < numBands; b++) {
169: float[] CDFband = CDF[b];
170: int length = CDFband.length;
171:
172: if (CDFband[length - 1] != 1.0) {
173: // Last CDF array element value is not 1.0.
174: msg.append(getName() + " "
175: + JaiI18N.getString("MatchCDFDescriptor7"));
176: return false;
177: }
178:
179: for (int i = 0; i < length; i++) {
180: if (CDFband[i] < 0.0F) {
181: // Negative CDF value.
182: msg
183: .append(getName()
184: + " "
185: + JaiI18N
186: .getString("MatchCDFDescriptor5"));
187: return false;
188: } else if (i != 0) {
189: if (CDFband[i] < CDFband[i - 1]) {
190: // Decreasing sequence.
191: msg
192: .append(getName()
193: + " "
194: + JaiI18N
195: .getString("MatchCDFDescriptor6"));
196: return false;
197: }
198: }
199: }
200: }
201:
202: return true;
203: }
204: }
205:
206: /**
207: * Matches pixel values to a supplied CDF.
208: *
209: * <p>Creates a <code>ParameterBlockJAI</code> from all
210: * supplied arguments except <code>hints</code> and invokes
211: * {@link JAI#create(String,ParameterBlock,RenderingHints)}.
212: *
213: * @see JAI
214: * @see ParameterBlockJAI
215: * @see RenderedOp
216: *
217: * @param source0 <code>RenderedImage</code> source 0.
218: * @param CDF The desired Cumulative Distribution Function.
219: * May be <code>null</code>.
220: * @param hints The <code>RenderingHints</code> to use.
221: * May be <code>null</code>.
222: * @return The <code>RenderedOp</code> destination.
223: * @throws IllegalArgumentException if <code>source0</code> is <code>null</code>.
224: */
225: public static RenderedOp create(RenderedImage source0,
226: float[][] CDF, RenderingHints hints) {
227: ParameterBlockJAI pb = new ParameterBlockJAI("MatchCDF",
228: RenderedRegistryMode.MODE_NAME);
229:
230: pb.setSource("source0", source0);
231:
232: pb.setParameter("CDF", CDF);
233:
234: return JAI.create("MatchCDF", pb, hints);
235: }
236:
237: /**
238: * Matches pixel values to a supplied CDF.
239: *
240: * <p>Creates a <code>ParameterBlockJAI</code> from all
241: * supplied arguments except <code>hints</code> and invokes
242: * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}.
243: *
244: * @see JAI
245: * @see ParameterBlockJAI
246: * @see RenderableOp
247: *
248: * @param source0 <code>RenderableImage</code> source 0.
249: * @param CDF The desired Cumulative Distribution Function.
250: * May be <code>null</code>.
251: * @param hints The <code>RenderingHints</code> to use.
252: * May be <code>null</code>.
253: * @return The <code>RenderableOp</code> destination.
254: * @throws IllegalArgumentException if <code>source0</code> is <code>null</code>.
255: */
256: public static RenderableOp createRenderable(
257: RenderableImage source0, float[][] CDF, RenderingHints hints) {
258: ParameterBlockJAI pb = new ParameterBlockJAI("MatchCDF",
259: RenderableRegistryMode.MODE_NAME);
260:
261: pb.setSource("source0", source0);
262:
263: pb.setParameter("CDF", CDF);
264:
265: return JAI.createRenderable("MatchCDF", pb, hints);
266: }
267: }
|