1 /*
2  * $RCSfile: MosaicDescriptor.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:57:40 $
10  * $State: Exp $
11  */package com.lightcrafts.mediax.jai.operator;
12 
13 import java.awt.RenderingHints;
14 import java.awt.Transparency;
15 import java.awt.image.RenderedImage;
16 import java.awt.image.renderable.ParameterBlock;
17 import com.lightcrafts.mediax.jai.ImageLayout;
18 import com.lightcrafts.mediax.jai.JAI;
19 import com.lightcrafts.mediax.jai.OperationDescriptorImpl;
20 import com.lightcrafts.mediax.jai.ParameterBlockJAI;
21 import com.lightcrafts.mediax.jai.PlanarImage;
22 import com.lightcrafts.mediax.jai.ROI;
23 import com.lightcrafts.mediax.jai.RenderedOp;
24 import com.lightcrafts.mediax.jai.registry.RenderedRegistryMode;
25 
26 /**
27  * An <code>OperationDescriptor</code> describing the "Mosaic" operation
28  * in the rendered mode.
29  *
30  * <p>
31  * The "Mosaic" operation creates a mosaic of two or more source images.
32  * This operation could be used for example to assemble a set of
33  * overlapping geospatially rectified images into a contiguous
34  * image. It could also be used to create a montage of photographs such
35  * as a panorama.
36  * </p>
37  *
38  * <p>
39  * All source images are assumed to have been geometrically mapped into
40  * a common coordinate space. The origin <code>(minX,&nbsp;minY)</code> of
41  * each image is therefore taken to represent the location of the respective
42  * image in the common coordinate system of the source images. This
43  * coordinate space will also be that of the destination image.
44  * </p>
45  *
46  * <p>
47  * All source images must have the same data type and sample size for all
48  * bands. The destination will have the same data type, sample size, and
49  * number of bands and color components as the sources.
50  * </p>
51  *
52  * <p>
53  * The destination layout may be specified by an {@link ImageLayout} hint
54  * provided via a {@link RenderingHints} supplied to the operation. If this
55  * hint contains a setting for the image bounds (origin and dimensions), it
56  * will be used even if it does not intersect the union of the bounds of
57  * the sources; otherwise the image bounds will be set to the union of all
58  * source image bounds. If the data type or sample size specified by the layout
59  * hint do not match those of the sources, then this portion of the hint will be
60  * ignored.
61  * </p>
62  *
63  * <p>
64  * It is permissible that the number of source images be initially zero. In
65  * this case a non-<code>null</code> <code>ImageLayout</code> must be
66  * supplied with valid width and height and a non-<code>null</code>
67  * <code>SampleModel</code>. The destination data type, sample size, number
68  * of bands, and image bounds will all be determined by the
69  * <code>ImageLayout</code>.
70  * </p>
71  *
72  * <p>
73  * If <code>sourceAlpha</code> is non-<code>null</code>, then any non-
74  * <code>null</code> elements of the array must be single-band images
75  * with the same data type and sample size as the sources.
76  * </p>
77  *
78  * <p>
79  * The source threshold array parameter has maximum dimensions as
80  * <code>double[NUM_SOURCES][NUM_BANDS]</code>. Default values of the
81  * thresholds actually used are defined as follows:
82  * <ul>
83  * <li>The default value of <code>sourceThreshold[0][0]</code> is
84  * <code>1.0</code>.</li>
85  * <li>If <code>sourceThreshold[i] != null</code> and
86  * <code>sourceThreshold[i].length < NUM_BANDS</code>, then set
87  * <code>sourceThreshold[i][j] = sourceThreshold[i][0]</code> for all
88  * <code>1 <= j < NUM_BANDS</code>.</li>
89  * <li>If <code>sourceThreshold[i] == null</code> then set
90  * <code>sourceThreshold[i] = sourceThreshold[0]</code>.</li>
91  * </ul>
92  * </p>
93  *
94  * <p>
95  * The background value array parameter has maximum dimensions as
96  * <code>double[NUM_BANDS]</code>. Default values of the
97  * background actually used are defined as follows:
98  * <ul>
99  * <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,&nbsp;y)</tt> is within the bounds
110  * of <tt>M</tt> source images, then the destination pixel value
111  * <tt>D(x,&nbsp;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,&nbsp;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,&nbsp;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.lightcrafts.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>com.lightcrafts.mediax.jai.operator.MosaicType</td>
255  *                               <td>MOSAIC_TYPE_OVERLAY</td>
256  * <tr><td>sourceAlpha</td>      <td>com.lightcrafts.mediax.jai.PlanarImage[]</td>
257  *                               <td>null</td>
258  * <tr><td>sourceROI</td>        <td>com.lightcrafts.mediax.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 =
274         new MosaicType("MOSAIC_TYPE_BLEND", 1);
275 
276     /**
277      * Destination pixel equals first opaque source pixel.
278      */
279     public static final MosaicType MOSAIC_TYPE_OVERLAY =
280         new MosaicType("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.lightcrafts.media.jai"},
290         {"Description", JaiI18N.getString("MosaicDescriptor0")},
291         {"DocURL",      "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MosaicDescriptor.html"},
292         {"Version",     JaiI18N.getString("DescriptorVersion")},
293         {"arg0Desc",    JaiI18N.getString("MosaicDescriptor1")},
294         {"arg1Desc",    JaiI18N.getString("MosaicDescriptor2")},
295         {"arg2Desc",    JaiI18N.getString("MosaicDescriptor3")},
296         {"arg3Desc",    JaiI18N.getString("MosaicDescriptor4")},
297         {"arg4Desc",    JaiI18N.getString("MosaicDescriptor5")}
298     };
299 
300     /** The parameter class list for this operation. */
301     private static final Class[] paramClasses = {
302         com.lightcrafts.mediax.jai.operator.MosaicType.class,
303         com.lightcrafts.mediax.jai.PlanarImage[].class,
304         com.lightcrafts.mediax.jai.ROI[].class,
305         double[][].class,
306         double[].class
307     };
308 
309     /** The parameter name list for this operation. */
310     private static final String[] paramNames = {
311         "mosaicType",
312         "sourceAlpha",
313         "sourceROI",
314         "sourceThreshold",
315         "backgroundValues"
316     };
317 
318     /** The parameter default value list for this operation. */
319     private static final Object[] paramDefaults = {
320         MOSAIC_TYPE_OVERLAY,
321         null,
322         null,
323         new double[][] {{1.0}},
324         new double[] {0.0}
325     };
326 
327     /** Constructor. */
MosaicDescriptor()328     public MosaicDescriptor() {
329         super(resources,
330               new String[] {RenderedRegistryMode.MODE_NAME},
331               0,
332               paramNames,
333               paramClasses,
334               paramDefaults,
335               null);
336     }
337 
338     /**
339      * Creates a mosaic of two or more rendered images.
340      *
341      * <p>Creates a <code>ParameterBlockJAI</code> from all
342      * supplied arguments except <code>hints</code> and invokes
343      * {@link JAI#create(String,ParameterBlock,RenderingHints)}.
344      *
345      * @see JAI
346      * @see ParameterBlockJAI
347      * @see RenderedOp
348      *
349      * @param sources <code>RenderedImage</code> sources.
350      * @param mosaicType Mosaicking type.
351      * May be <code>null</code>.
352      * @param sourceAlpha
353      * May be <code>null</code>.
354      * @param sourceAlpha Source alpha masks.
355      * May be <code>null</code>.
356      * @param sourceROI Source region of interest masks.
357      * May be <code>null</code>.
358      * @param sourceThreshold Source threshold values.
359      * May be <code>null</code>.
360      * @param backgroundValues Destination background value.
361      * May be <code>null</code>.
362      * @param hints The <code>RenderingHints</code> to use.
363      * May be <code>null</code>.
364      * @return The <code>RenderedOp</code> destination.
365      * @throws IllegalArgumentException if any source is <code>null</code>.
366      */
create(RenderedImage[] sources, MosaicType mosaicType, PlanarImage[] sourceAlpha, ROI[] sourceROI, double[][] sourceThreshold, double[] backgroundValues, RenderingHints hints)367     public static RenderedOp create(RenderedImage[] sources,
368                                     MosaicType mosaicType,
369                                     PlanarImage[] sourceAlpha,
370                                     ROI[] sourceROI,
371                                     double[][] sourceThreshold,
372                                     double[] backgroundValues,
373                                     RenderingHints hints)  {
374         ParameterBlockJAI pb =
375             new ParameterBlockJAI("Mosaic",
376                                   RenderedRegistryMode.MODE_NAME);
377 
378         int numSources = sources.length;
379         for(int i = 0; i < numSources; i++) {
380             pb.addSource(sources[i]);
381         }
382 
383         pb.setParameter("mosaicType", mosaicType);
384         pb.setParameter("sourceAlpha", sourceAlpha);
385         pb.setParameter("sourceROI", sourceROI);
386         pb.setParameter("sourceThreshold", sourceThreshold);
387         pb.setParameter("backgroundValues", backgroundValues);
388 
389         return JAI.create("Mosaic", pb, hints);
390     }
391 }
392