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, 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, 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.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