001: /*
002: * $RCSfile: MosaicDescriptor.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:40 $
010: * $State: Exp $
011: */package javax.media.jai.operator;
012:
013: import java.awt.RenderingHints;
014: import java.awt.Transparency;
015: import java.awt.image.RenderedImage;
016: import java.awt.image.renderable.ParameterBlock;
017: import javax.media.jai.ImageLayout;
018: import javax.media.jai.JAI;
019: import javax.media.jai.OperationDescriptorImpl;
020: import javax.media.jai.ParameterBlockJAI;
021: import javax.media.jai.PlanarImage;
022: import javax.media.jai.ROI;
023: import javax.media.jai.RenderedOp;
024: import javax.media.jai.registry.RenderedRegistryMode;
025:
026: /**
027: * An <code>OperationDescriptor</code> describing the "Mosaic" operation
028: * in the rendered mode.
029: *
030: * <p>
031: * The "Mosaic" operation creates a mosaic of two or more source images.
032: * This operation could be used for example to assemble a set of
033: * overlapping geospatially rectified images into a contiguous
034: * image. It could also be used to create a montage of photographs such
035: * as a panorama.
036: * </p>
037: *
038: * <p>
039: * All source images are assumed to have been geometrically mapped into
040: * a common coordinate space. The origin <code>(minX, minY)</code> of
041: * each image is therefore taken to represent the location of the respective
042: * image in the common coordinate system of the source images. This
043: * coordinate space will also be that of the destination image.
044: * </p>
045: *
046: * <p>
047: * All source images must have the same data type and sample size for all
048: * bands. The destination will have the same data type, sample size, and
049: * number of bands and color components as the sources.
050: * </p>
051: *
052: * <p>
053: * The destination layout may be specified by an {@link ImageLayout} hint
054: * provided via a {@link RenderingHints} supplied to the operation. If this
055: * hint contains a setting for the image bounds (origin and dimensions), it
056: * will be used even if it does not intersect the union of the bounds of
057: * the sources; otherwise the image bounds will be set to the union of all
058: * source image bounds. If the data type or sample size specified by the layout
059: * hint do not match those of the sources, then this portion of the hint will be
060: * ignored.
061: * </p>
062: *
063: * <p>
064: * It is permissible that the number of source images be initially zero. In
065: * this case a non-<code>null</code> <code>ImageLayout</code> must be
066: * supplied with valid width and height and a non-<code>null</code>
067: * <code>SampleModel</code>. The destination data type, sample size, number
068: * of bands, and image bounds will all be determined by the
069: * <code>ImageLayout</code>.
070: * </p>
071: *
072: * <p>
073: * If <code>sourceAlpha</code> is non-<code>null</code>, then any non-
074: * <code>null</code> elements of the array must be single-band images
075: * with the same data type and sample size as the sources.
076: * </p>
077: *
078: * <p>
079: * The source threshold array parameter has maximum dimensions as
080: * <code>double[NUM_SOURCES][NUM_BANDS]</code>. Default values of the
081: * thresholds actually used are defined as follows:
082: * <ul>
083: * <li>The default value of <code>sourceThreshold[0][0]</code> is
084: * <code>1.0</code>.</li>
085: * <li>If <code>sourceThreshold[i] != null</code> and
086: * <code>sourceThreshold[i].length < NUM_BANDS</code>, then set
087: * <code>sourceThreshold[i][j] = sourceThreshold[i][0]</code> for all
088: * <code>1 <= j < NUM_BANDS</code>.</li>
089: * <li>If <code>sourceThreshold[i] == null</code> then set
090: * <code>sourceThreshold[i] = sourceThreshold[0]</code>.</li>
091: * </ul>
092: * </p>
093: *
094: * <p>
095: * The background value array parameter has maximum dimensions as
096: * <code>double[NUM_BANDS]</code>. Default values of the
097: * background actually used are defined as follows:
098: * <ul>
099: * <li>The default value of <code>backgroundValues[0]</code> is
100: * <code>0.0</code>.</li>
101: * <li>If <code>backgroundValues.length < NUM_BANDS</code>, then set
102: * <code>backgroundValues[j] = backgroundValues[0]</code> for all
103: * <code>1 <= j < NUM_BANDS</code>.</li>
104: * </ul>
105: * The default behavior therefore is to set the background to zero.
106: * </p>
107: *
108: * <p>
109: * If a given destination position <tt>(x, y)</tt> is within the bounds
110: * of <tt>M</tt> source images, then the destination pixel value
111: * <tt>D(x, y)</tt> is computed using an algorithm selected on the
112: * basis of the <code>mosaicType</code> parameter value. If the destination
113: * position is not within any source image, then the destination pixel value
114: * is set to the specified background value.
115: * </p>
116: *
117: * <p>
118: * If the <code>mosaicType</code> parameter value is
119: * <code>MOSAIC_TYPE_BLEND</code>, then the destination pixel value
120: * is computed as:
121: * <pre>
122: * double[][][] s; // source pixel values
123: * double[][][] w; // derived source weight values
124: * double[][] d; // destination pixel values
125: *
126: * double weightSum = 0.0;
127: * for(int i = 0; i < M; i++) {
128: * weightSum += w[i][x][y];
129: * }
130: *
131: * if(weightSum != 0.0) {
132: * double sourceSum = 0.0;
133: * for(int i = 0; i < M; i++) {
134: * sourceSum += s[i][x][y]*w[i][x][y];
135: * }
136: * d[x][y] = sourceSum / weightSum;
137: * } else {
138: * d[x][y] = background;
139: * }
140: * </pre>
141: * where the index <tt>i</tt> is over the sources which contain
142: * <tt>(x, y)</tt>. The destination pixel value is therefore a
143: * blend of the source pixel values at the same position.
144: * </p>
145: *
146: * <p>
147: * If the <code>mosaicType</code> parameter value is
148: * <code>MOSAIC_TYPE_OVERLAY</code>, then the destination pixel value
149: * is computed as:
150: * <pre>
151: * d[x][y] = background;
152: * for(int i = 0; i < M; i++) {
153: * if(w[i][x][y] != 0.0) {
154: * d[x][y] = s[i][x][y];
155: * break;
156: * }
157: * }
158: * </pre>
159: * The destination pixel value is therefore the value of the first source
160: * pixel at the same position for which the derived weight value at the same
161: * position is non-zero.
162: * </p>
163: *
164: * <p>
165: * The derived weight values for the <tt>i</tt>th source are determined from
166: * the corresponding <code>sourceAlpha</code>, <code>sourceROI</code>, and
167: * <code>sourceThreshold</code> parameters as follows where for
168: * illustration purposes it is assumed that any alpha values range over
169: * <tt>[0.0, 1.0]</tt> with <tt>1.0</tt> being opaque:
170: * <pre>
171: * // Set flag indicating whether to interpret alpha values as bilevel.
172: * boolean isAlphaBitmask =
173: * !(mosaicType.equals(MOSAIC_TYPE_BLEND) &&
174: * sourceAlpha != null &&
175: * !(sourceAlpha.length < NUM_SOURCES));
176: * if(!isAlphaBitmask) {
177: * for(int i = 0; i < NUM_SOURCES; i++) {
178: * if(sourceAlpha[i] == null) {
179: * isAlphaBitmask = true;
180: * break;
181: * }
182: * }
183: * }
184: *
185: * // Derive source weights from the supplied parameters.
186: * w[i][x][y] = 0.0;
187: * if(sourceAlpha != null && sourceAlpha[i] != null) {
188: * w[i][x][y] = sourceAlpha[i][x][y];
189: * if(isAlphaBitmask && w[i][x][y] > 0.0) {
190: * w[i][x][y] = 1.0;
191: * }
192: * } else if(sourceROI != null && sourceROI[i] != null &&
193: * sourceROI[i].contains(x,y)) {
194: * w[i][x][y] = 1.0;
195: * } else if(s[i][x][y] >= sourceThreshold[i]) { // s[i][x][y] = source value
196: * w[i][x][y] = 1.0;
197: * }
198: * </pre>
199: * </p>
200: *
201: * <p>
202: * As illustrated above, the interpretation of the alpha values will vary
203: * depending on the values of the parameters supplied to the operation. If
204: * and only if <code>mosaicType</code> equals <code>MOSAIC_TYPE_BLEND</code>
205: * and an alpha mask is available for each source will the alpha values be
206: * treated as arbitrary values as for {@link Transparency#TRANSLUCENT}. In
207: * all other cases the alpha values will be treated as bilevel values
208: * as for {@link Transparency#BITMASK}.
209: * </p>
210: *
211: * <p>
212: * It should be remarked that the <code>MOSAIC_TYPE_BLEND</code> algorithm
213: * applied when the weights are treated as bilevel values is equivalent to
214: * averaging all non-transparent source pixels at a given position. This
215: * in effect intrinsically provides a third category of mosaicking. The
216: * available categories are summarized in the following table.
217: * <table border=1>
218: * <caption><b>Mosaic Categories</b></caption>
219: * <tr><th>Mosaic Type</th>
220: * <th>Transparency Type</th>
221: * <th>Category</th></tr>
222: * <tr><td><code>MOSAIC_TYPE_BLEND</code></td>
223: * <td><code>BITMASK</code></td>
224: * <td>Average</td></tr>
225: * <tr><td><code>MOSAIC_TYPE_BLEND</code></td>
226: * <td><code>TRANSLUCENT</code></td>
227: * <td>Alpha Blend</td></tr>
228: * <tr><td><code>MOSAIC_TYPE_OVERLAY</code></td>
229: * <td><code>BITMASK || TRANSLUCENT</code></td>
230: * <td>Superposition</td></tr>
231: * </table>
232: * </p>
233: *
234: * <p><table border=1>
235: * <caption><b>Resource List</b></caption>
236: * <tr><th>Name</th> <th>Value</th></tr>
237: * <tr><td>GlobalName</td> <td>Mosaic</td></tr>
238: * <tr><td>LocalName</td> <td>Mosaic</td></tr>
239: * <tr><td>Vendor</td> <td>com.sun.media.jai</td></tr>
240: * <tr><td>Description</td> <td>Creates a mosaic of two or more rendered images.</td></tr>
241: * <tr><td>DocURL</td> <td>http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MosaicDescriptor.html</td></tr>
242: * <tr><td>Version</td> <td>1.0</td></tr>
243: * <tr><td>arg0Desc</td> <td>Mosaicking type.</td></tr>
244: * <tr><td>arg1Desc</td> <td>Source alpha masks.</td></tr>
245: * <tr><td>arg2Desc</td> <td>Source region of interest masks.</td></tr>
246: * <tr><td>arg3Desc</td> <td>Source threshold values.</td></tr>
247: * <tr><td>arg4Desc</td> <td>Destination background value.</td></tr>
248: * </table></p>
249: *
250: * <p><table border=1>
251: * <caption><b>Parameter List</b></caption>
252: * <tr><th>Name</th> <th>Class Type</th>
253: * <th>Default Value</th></tr>
254: * <tr><td>mosaicType</td> <td>javax.media.jai.operator.MosaicType</td>
255: * <td>MOSAIC_TYPE_OVERLAY</td>
256: * <tr><td>sourceAlpha</td> <td>javax.media.jai.PlanarImage[]</td>
257: * <td>null</td>
258: * <tr><td>sourceROI</td> <td>javax.media.jai.ROI[]</td>
259: * <td>null</td>
260: * <tr><td>sourceThreshold</td> <td>double[][]</td>
261: * <td>double[][] {{1.0}}</td>
262: * <tr><td>backgroundValues</td> <td>double[]</td>
263: * <td>double[] {0.0}</td>
264: * </table></p>
265: *
266: * @since JAI 1.1.2
267: */
268: public class MosaicDescriptor extends OperationDescriptorImpl {
269:
270: /**
271: * Destination pixel equals alpha blend of source pixels.
272: */
273: public static final MosaicType MOSAIC_TYPE_BLEND = new MosaicType(
274: "MOSAIC_TYPE_BLEND", 1);
275:
276: /**
277: * Destination pixel equals first opaque source pixel.
278: */
279: public static final MosaicType MOSAIC_TYPE_OVERLAY = new MosaicType(
280: "MOSAIC_TYPE_OVERLAY", 0);
281:
282: /**
283: * The resource strings that provide the general documentation
284: * and specify the parameter list for this operation.
285: */
286: private static final String[][] resources = {
287: { "GlobalName", "Mosaic" },
288: { "LocalName", "Mosaic" },
289: { "Vendor", "com.sun.media.jai" },
290: { "Description", JaiI18N.getString("MosaicDescriptor0") },
291: {
292: "DocURL",
293: "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MosaicDescriptor.html" },
294: { "Version", JaiI18N.getString("DescriptorVersion") },
295: { "arg0Desc", JaiI18N.getString("MosaicDescriptor1") },
296: { "arg1Desc", JaiI18N.getString("MosaicDescriptor2") },
297: { "arg2Desc", JaiI18N.getString("MosaicDescriptor3") },
298: { "arg3Desc", JaiI18N.getString("MosaicDescriptor4") },
299: { "arg4Desc", JaiI18N.getString("MosaicDescriptor5") } };
300:
301: /** The parameter class list for this operation. */
302: private static final Class[] paramClasses = {
303: javax.media.jai.operator.MosaicType.class,
304: javax.media.jai.PlanarImage[].class,
305: javax.media.jai.ROI[].class, double[][].class,
306: double[].class };
307:
308: /** The parameter name list for this operation. */
309: private static final String[] paramNames = { "mosaicType",
310: "sourceAlpha", "sourceROI", "sourceThreshold",
311: "backgroundValues" };
312:
313: /** The parameter default value list for this operation. */
314: private static final Object[] paramDefaults = {
315: MOSAIC_TYPE_OVERLAY, null, null,
316: new double[][] { { 1.0 } }, new double[] { 0.0 } };
317:
318: /** Constructor. */
319: public MosaicDescriptor() {
320: super (resources,
321: new String[] { RenderedRegistryMode.MODE_NAME }, 0,
322: paramNames, paramClasses, paramDefaults, null);
323: }
324:
325: /**
326: * Creates a mosaic of two or more rendered images.
327: *
328: * <p>Creates a <code>ParameterBlockJAI</code> from all
329: * supplied arguments except <code>hints</code> and invokes
330: * {@link JAI#create(String,ParameterBlock,RenderingHints)}.
331: *
332: * @see JAI
333: * @see ParameterBlockJAI
334: * @see RenderedOp
335: *
336: * @param sources <code>RenderedImage</code> sources.
337: * @param mosaicType Mosaicking type.
338: * May be <code>null</code>.
339: * @param sourceAlpha
340: * May be <code>null</code>.
341: * @param sourceAlpha Source alpha masks.
342: * May be <code>null</code>.
343: * @param sourceROI Source region of interest masks.
344: * May be <code>null</code>.
345: * @param sourceThreshold Source threshold values.
346: * May be <code>null</code>.
347: * @param backgroundValues Destination background value.
348: * May be <code>null</code>.
349: * @param hints The <code>RenderingHints</code> to use.
350: * May be <code>null</code>.
351: * @return The <code>RenderedOp</code> destination.
352: * @throws IllegalArgumentException if any source is <code>null</code>.
353: */
354: public static RenderedOp create(RenderedImage[] sources,
355: MosaicType mosaicType, PlanarImage[] sourceAlpha,
356: ROI[] sourceROI, double[][] sourceThreshold,
357: double[] backgroundValues, RenderingHints hints) {
358: ParameterBlockJAI pb = new ParameterBlockJAI("Mosaic",
359: RenderedRegistryMode.MODE_NAME);
360:
361: int numSources = sources.length;
362: for (int i = 0; i < numSources; i++) {
363: pb.addSource(sources[i]);
364: }
365:
366: pb.setParameter("mosaicType", mosaicType);
367: pb.setParameter("sourceAlpha", sourceAlpha);
368: pb.setParameter("sourceROI", sourceROI);
369: pb.setParameter("sourceThreshold", sourceThreshold);
370: pb.setParameter("backgroundValues", backgroundValues);
371:
372: return JAI.create("Mosaic", pb, hints);
373: }
374: }
|