001: /*
002: * $RCSfile: ColorQuantizerDescriptor.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:31 $
010: * $State: Exp $
011: */
012: package javax.media.jai.operator;
013:
014: import java.awt.RenderingHints;
015: import java.awt.image.RenderedImage;
016: import java.awt.image.renderable.ParameterBlock;
017: import javax.media.jai.JAI;
018: import javax.media.jai.OperationDescriptorImpl;
019: import javax.media.jai.ParameterBlockJAI;
020: import javax.media.jai.RenderedOp;
021: import javax.media.jai.ROI;
022: import javax.media.jai.util.Range;
023: import javax.media.jai.registry.RenderedRegistryMode;
024:
025: /**
026: * This <code>OperationDescriptor</code> defines the "ColorQuantizer"
027: * operation.
028: *
029: * <p> This operation generates an optimal lookup table (LUT) based on
030: * the provided 3-band RGB source image by executing a color
031: * quantization algorithm. This LUT is stored in the property
032: * "JAI.LookupTable" that has a type of <code>LookupTableJAI</code>.
033: * Thus, it can be retrieved by means of <code>getProperty</code>.
034: * This LUT can be further utilized in other operations such as
035: * "errordiffusion" to convert the 3-band RGB image into a high-quality
036: * color-indexed image. The computation of the LUT can be deferred by
037: * defining a <code>DeferredProperty</code> from the property
038: * "JAI.LookupTable" and providing that as the parameter value for
039: * "errordiffusion". This operation also creates a color-indexed
040: * destination image based on the nearest distance classification (without
041: * dithering). However, the quality of this classification result may
042: * not be as good as the result of "errordiffusion".
043: *
044: * <p> The supported source image data type is implementation-dependent.
045: * For example, the Sun implementation will support only the byte type.
046: *
047: * <p> The data set used in the color quantization can be defined by
048: * the optional parameters <code>xPeriod</code>, <code>yPeriod</code>
049: * and <code>ROI</code>. If these parameters are provided, the pixels in
050: * the subsampled image (and in the ROI) will be used to compute the
051: * LUT.
052: *
053: * <p> Three built-in color quantization algorithms are supported by
054: * this operation: Paul Heckbert's median-cut algorithm, Anthony Dekker's
055: * NeuQuant algorithm, and the Oct-Tree color quantization algorithm of
056: * Gervautz and Purgathofer.
057: *
058: * <p> The median-cut color quantization computes the 3D color histogram
059: * first, then chooses and divides the largest color cube (in number of pixels)
060: * along the median, until the required number of clusters is obtained
061: * or all the cubes are not separable. The NeuQuant algorithm creates
062: * the cluster centers using Kohonen's self-organizing neural network.
063: * The Oct-Tree color quantization constructs an oct-tree of the
064: * color histogram, then repeatedly merges the offspring into the parent
065: * if they contain a number of pixels smaller than a threshold. With the
066: * equivalent parameters, the median-cut algorithm is the fastest, and the
067: * NeuQuant algorithm is the slowest. However, NeuQuant algorithm can
068: * still generate a good result with a relatively high subsample rate, which
069: * is useful for large images.
070: * In these three algorithms, the Oct-Tree algorithm is the most space
071: * consuming one. For further details of these algorithms,
072: * please refer to the following references:
073: * <table border=1>
074: * <tr>
075: * <th>Algorithm</th>
076: * <th>References</th>
077: * </tr>
078: * <tr>
079: * <td>Median-Cut</td>
080: * <td>Color Image Quantization for Frame Buffer
081: * Display, Paul Heckbert, SIGGRAPH proceedings, 1982, pp. 297-307
082: * </td></tr>
083: * <tr>
084: * <td>NeuQuant</td>
085: * <td>Kohonen Neural Networks for Optimal Colour Quantization,
086: * Anthony Dekker, In <i>Network: Computation in Neural Systems</i>,
087: * Volume 5, Institute of Physics Publishing, 1994, pp 351-367.
088: * </td>
089: * </tr>
090: * <tr>
091: * <td>Oct-Tree</td>
092: * <td><i>Interactive Computer Graphics: Functional, Procedural, and
093: * Device-Level Methods</i> by Peter Burger and Duncan Gillis,
094: * Addison-Wesley, 1989, pp 345.
095: * </td>
096: * </tr>
097: *</table>
098: *
099: * <p> The generated LUT may have fewer entries than expected. For
100: * example, the source image might not have as many colors as expected.
101: * In the oct-tree algorithm, all the offspring of a node are merged
102: * if they contain a number of pixels smaller than a threshold. This
103: * may result in slightly fewer colors than expected.
104: *
105: * <p> The learning procedure of the NeuQuant algorithm randomly goes
106: * through all the pixels in the training data set. To simplify and
107: * speed up the implementation, the bounding rectangle of the
108: * provided ROI may be used (by the implementation) to define the
109: * training data set instead of the ROI itself.
110: *
111: * <p><table border=1>
112: * <caption>Resource List</caption>
113: * <tr><th>Name</th> <th>Value</th></tr>
114: * <tr><td>GlobalName</td> <td>ColorQuantizer</td></tr>
115: * <tr><td>LocalName</td> <td>ColorQuantizer</td></tr>
116: * <tr><td>Vendor</td> <td>com.sun.media.jai</td></tr>
117: * <tr><td>Description</td> <td>Generates an optimal LUT by executing a
118: * color quantization algorithm, and a
119: * color-indexed image by the nearest distance
120: * classification.</td></tr>
121: * <tr><td>DocURL</td> <td>http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ColorQuantizerDescriptor.html</td></tr>
122: * <tr><td>Version</td> <td>1.1</td></tr>
123: * <tr><td>arg0Desc</td> <td>The color quantization algorithm name. One of
124: * ColorQuantizerDescriptor.MEDIANCUT,
125: * ColorQuantizerDescriptor.NEUQUANT, or
126: * ColorQuantizerDescriptor.OCTTREE</td></tr>
127: * <tr><td>arg1Desc</td> <td>The maximum color number, that is, the expected
128: * number of colors in the result image.</td></tr>
129: * <tr><td>arg2Desc</td> <td>This is an algorithm-dependent parameter. For
130: * the median-cut color quantization, it is the
131: * maximum size of the three-dimensional
132: * histogram.
133: * For the neuquant color quantization, it is the
134: * number of cycles. For the oct-tree color
135: * quantization, it is the maximum size of the
136: * oct-tree.</td></tr>
137: * <tr><td>arg3Desc</td> <td>The ROI in which the pixels are involved into
138: * the color quantization.</td></tr>
139: * <tr><td>arg4Desc</td> <td>The subsample rate in x direction.</td></tr>
140: * <tr><td>arg4Desc</td> <td>The subsample rate in y direction.</td></tr>
141: * </table></p>
142: *
143: * <p><table border=1>
144: * <caption>Parameter List</caption>
145: * <tr><th>Name</th> <th>Class Type</th>
146: * <th>Default Value</th></tr>
147: * <tr><td>quantizationAlgorithm</td>
148: * <td>javax.media.jai.operator.ColorQuantizerType</td>
149: * <td>ColorQuantizerDescriptor.MEDIANCUT</td>
150: * <tr><td>maxColorNum</td> <td>java.lang.Integer</td>
151: * <td>256</td>
152: * <tr><td>upperBound</td> <td>java.lang.Integer</td>
153: * <td>32768 for median-cut, 100 for neuquant,
154: * 65536 for oct-tree</td>
155: * <tr><td>roi</td> <td>javax.media.jai.ROI</td>
156: * <td>null</td>
157: * <tr><td>xPeriod</td> <td>java.lang.Integer</td>
158: * <td>1</td>
159: * <tr><td>yPeriod</td> <td>java.lang.Integer</td>
160: * <td>1</td>
161: * </table></p>
162: *
163: * @see javax.media.jai.ROI
164: * @see javax.media.jai.OperationDescriptor
165: *
166: * @since JAI 1.1.2
167: */
168: public class ColorQuantizerDescriptor extends OperationDescriptorImpl {
169: /** The predefined color quantization algorithms. */
170: /** The pre-defined median-cut color quantization algorithm. */
171: public static final ColorQuantizerType MEDIANCUT = new ColorQuantizerType(
172: "MEDIANCUT", 1);
173: /** The pre-defined NeuQuant color quantization algorithm. */
174: public static final ColorQuantizerType NEUQUANT = new ColorQuantizerType(
175: "NEUQUANT", 2);
176: /** The pre-defined Oct-Tree color quantization algorithm. */
177: public static final ColorQuantizerType OCTTREE = new ColorQuantizerType(
178: "OCTTREE", 3);
179:
180: /**
181: * The resource strings that provide the general documentation
182: * and specify the parameter list for this operation.
183: */
184: private static final String[][] resources = {
185: { "GlobalName", "ColorQuantizer" },
186: { "LocalName", "ColorQuantizer" },
187: { "Vendor", "com.sun.media.jai" },
188: { "Description",
189: JaiI18N.getString("ColorQuantizerDescriptor0") },
190: {
191: "DocURL",
192: "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ColorQuantizerDescriptor.html" },
193: { "Version", JaiI18N.getString("DescriptorVersion2") },
194: { "arg0Desc",
195: JaiI18N.getString("ColorQuantizerDescriptor1") },
196: { "arg1Desc",
197: JaiI18N.getString("ColorQuantizerDescriptor2") },
198: { "arg2Desc",
199: JaiI18N.getString("ColorQuantizerDescriptor3") },
200: { "arg3Desc",
201: JaiI18N.getString("ColorQuantizerDescriptor4") },
202: { "arg4Desc",
203: JaiI18N.getString("ColorQuantizerDescriptor5") },
204: { "arg5Desc",
205: JaiI18N.getString("ColorQuantizerDescriptor6") }, };
206:
207: /** The parameter name list for this operation. */
208: private static final String[] paramNames = {
209: "quantizationAlgorithm", "maxColorNum", "upperBound",
210: "roi", "xPeriod", "yPeriod" };
211:
212: /** The parameter class list for this operation. */
213: private static final Class[] paramClasses = {
214: javax.media.jai.operator.ColorQuantizerType.class,
215: java.lang.Integer.class, java.lang.Integer.class,
216: javax.media.jai.ROI.class, java.lang.Integer.class,
217: java.lang.Integer.class };
218:
219: /** The parameter default value list for this operation. */
220: private static final Object[] paramDefaults = { MEDIANCUT,
221: new Integer(256), null, null, new Integer(1),
222: new Integer(1) };
223:
224: private static final String[] supportedModes = { "rendered" };
225:
226: /** Constructor. */
227: public ColorQuantizerDescriptor() {
228: super (resources, supportedModes, 1, paramNames, paramClasses,
229: paramDefaults, null);
230:
231: }
232:
233: /**
234: * Returns the minimum legal value of a specified numeric parameter
235: * for this operation.
236: */
237: public Range getParamValueRange(int index) {
238: switch (index) {
239: case 1:
240: case 2:
241: case 4:
242: case 5:
243: return new Range(Integer.class, new Integer(1), null);
244: }
245: return null;
246: }
247:
248: /**
249: * Returns <code>true</code> if this operation is capable of handling
250: * the input parameters.
251: *
252: * <p> In addition to the default validations done in the super class,
253: * this method verifies that the provided quantization algorithm is one of
254: * the three predefined algorithms in this class.
255: *
256: * @throws IllegalArgumentException If <code>args</code> is <code>null</code>.
257: * @throws IllegalArgumentException If <code>msg</code> is <code>null</code>
258: * and the validation fails.
259: */
260: protected boolean validateParameters(String modeName,
261: ParameterBlock args, StringBuffer msg) {
262: if (args == null || msg == null) {
263: throw new IllegalArgumentException(JaiI18N
264: .getString("Generic0"));
265: }
266:
267: if (!super .validateParameters(modeName, args, msg))
268: return false;
269:
270: ColorQuantizerType algorithm = (ColorQuantizerType) args
271: .getObjectParameter(0);
272: if (algorithm != MEDIANCUT && algorithm != NEUQUANT
273: && algorithm != OCTTREE) {
274: msg.append(getName() + " "
275: + JaiI18N.getString("ColorQuantizerDescriptor7"));
276: return false;
277: }
278:
279: Integer secondOne = (Integer) args.getObjectParameter(2);
280: if (secondOne == null) {
281: int upperBound = 0;
282: if (algorithm.equals(MEDIANCUT))
283: upperBound = 32768;
284: else if (algorithm.equals(NEUQUANT)) // set the cycle for train to 100
285: upperBound = 100;
286: else if (algorithm.equals(OCTTREE)) // set the maximum tree size to 65536
287: upperBound = 65536;
288:
289: args.set(upperBound, 2);
290: }
291:
292: return true;
293: }
294:
295: /**
296: * Color quantization on the provided image.
297: *
298: * <p>Creates a <code>ParameterBlockJAI</code> from all
299: * supplied arguments except <code>hints</code> and invokes
300: * {@link JAI#create(String,ParameterBlock,RenderingHints)}.
301: *
302: * @see JAI
303: * @see ParameterBlockJAI
304: * @see RenderedOp
305: *
306: * @param source0 <code>RenderedImage</code> source 0.
307: * @param algorithm The algorithm to be chosen. May be <code>null</code>.
308: * @param maxColorNum The maximum color number. May be <code>null</code>.
309: * @param upperBound An algorithm-dependent parameter. See the parameter
310: * table above. May be <code>null</code>.
311: * @param roi The region of interest. May be <code>null</code>.
312: * @param xPeriod The X subsample rate. May be <code>null</code>.
313: * @param yPeriod The Y subsample rate. May be <code>null</code>.
314: * @param hints The <code>RenderingHints</code> to use.
315: * May be <code>null</code>.
316: * @return The <code>RenderedOp</code> destination.
317: * @throws IllegalArgumentException if <code>source0</code> is <code>null</code>.
318: */
319: public static RenderedOp create(RenderedImage source0,
320: ColorQuantizerType algorithm, Integer maxColorNum,
321: Integer upperBound, ROI roi, Integer xPeriod,
322: Integer yPeriod, RenderingHints hints) {
323: ParameterBlockJAI pb = new ParameterBlockJAI("ColorQuantizer",
324: RenderedRegistryMode.MODE_NAME);
325:
326: pb.setSource("source0", source0);
327:
328: pb.setParameter("quantizationAlgorithm", algorithm);
329: pb.setParameter("maxColorNum", maxColorNum);
330: pb.setParameter("upperBound", upperBound);
331: pb.setParameter("roi", roi);
332: pb.setParameter("xPeriod", xPeriod);
333: pb.setParameter("yPeriod", yPeriod);
334:
335: return JAI.create("ColorQuantizer", pb, hints);
336: }
337: }
|