1 /* 2 * $RCSfile: ErodeDescriptor.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:35 $ 10 * $State: Exp $ 11 */ 12 package com.lightcrafts.mediax.jai.operator; 13 import com.lightcrafts.media.jai.util.AreaOpPropertyGenerator; 14 import java.awt.RenderingHints; 15 import java.awt.image.RenderedImage; 16 import com.lightcrafts.mediax.jai.JAI; 17 import com.lightcrafts.mediax.jai.KernelJAI; 18 import com.lightcrafts.mediax.jai.OperationDescriptorImpl; 19 import com.lightcrafts.mediax.jai.ParameterBlockJAI; 20 import com.lightcrafts.mediax.jai.PropertyGenerator; 21 import com.lightcrafts.mediax.jai.RenderedOp; 22 import com.lightcrafts.mediax.jai.registry.RenderedRegistryMode; 23 24 /** 25 * 26 * An <code>OperationDescriptor</code> describing the "Erode" operation. 27 * 28 * <p> <b>Gray Scale Erosion</b> 29 * is a spatial operation that computes 30 * each output sample by subtracting elements of a kernel from the samples 31 * surrounding a particular source sample. 32 * The mathematical formulation for erosion operation is: 33 * 34 * <p> For a kernel K with a key position (xKey, yKey), the erosion 35 * of image I at (x,y) is given by: 36 * <pre> 37 * max{ f: f + K(xKey+i, yKey+j) <= I(x+i,y+j): all (i,j)} 38 * 39 * "all" possible (i,j) means that both I(x+i,y+j) and K(xKey+i, yKey+j) 40 * are in bounds. Otherwise, the value is set to 0. 41 * "f" represents all possible floats satisfying the restriction. 42 * 43 * </pre> 44 * <p> Intuitively, the kernel is like an unbrella and the key point 45 * is the handle. At every point, you try to push the umbrella up as high 46 * as possible but still underneath the image surface. The final height 47 * of the handle is the value after erosion. Thus if you want the image 48 * to erode from the upper right to bottom left, the following would do. 49 * 50 * <p><center> 51 * <table border=1> 52 * <tr align=center><td>0</td><td>0</td><td>X</td> </tr> 53 * <tr align=center><td>0</td><td>X</td><td>0</td> </tr> 54 * <tr align=center><td><b>X</b></td><td>0</td><td>0</td> </tr> 55 * </table></center> 56 * 57 * <p> Note that even if every entry of a kernel is zero, 58 * the erosion changes the image. Different key positions 59 * will also lead to different erosion results for such zero kernels. 60 * 61 * <p> Pseudo code for the erosion operation is as follows. 62 * Assuming the kernel K is of size M rows x N cols 63 * and the key position is (xKey, yKey). 64 * 65 * <pre> 66 * 67 * // erosion 68 * for every dst pixel location (x,y){ 69 * tmp = infinity; 70 * for (i = -xKey; i < M - xKey; i++){ 71 * for (j = -yKey; j < N - yKey; j++){ 72 * if((x+i, y+j) are in bounds of src){ 73 * tmp = min{tmp, src[x + i][y + j] - K[xKey + i][yKey + j]}; 74 * } 75 * } 76 * } 77 * dst[x][y] = tmp; 78 * if (dst[x][y] == infinity) 79 * dst[x][y] = 0; 80 * } 81 * </pre> 82 * 83 * <p> The kernel cannot be bigger in any dimension than the image data. 84 * 85 * <p> <b>Binary Image Erosion</b> 86 * requires the kernel to be binary, that is, to have values 0 and 1 87 * for each kernel entry. 88 * Intuitively, binary erosion slides the kernel 89 * key position and place it at every point (x,y) in the src image. 90 * The dst value at this position is set to 1 if the entire kernel lies 91 * within the image bounds and the src image value is 1 92 * wherever the corresponding kernel value is 1." 93 * Otherwise, the value after erosion at (x,y) is set to 0. 94 * Erosion usually shrinks images, but it can fill holes 95 * with kernels like 96 * <pre> [1 0 1] </pre> 97 * and the key position at the center. 98 * 99 * <p> Pseudo code for the binary erosion operation is as follows. 100 * 101 * <pre> 102 * // erosion 103 * for every dst pixel location (x,y){ 104 * dst[x][y] = 1; 105 * for (i = -xKey; i < M - xKey; i++){ 106 * for (j = -yKey; j < N - yKey; j++){ 107 * if((x+i,y+j) is out of bounds of src || 108 * src(x+i, y+j)==0 && Key(xKey+i, yKey+j)==1){ 109 * dst[x][y] = 0; break; 110 * } 111 * } 112 * } 113 * } 114 * 115 * The following can be used as references for the underlying 116 * connection between these two algorithms. 117 * 118 * <p> Reference: An Introduction to Nonlinear Image Processing, 119 * by Edward R. Bougherty and Jaakko Astola, 120 * Spie Optical Engineering Press, 1994. 121 * 122 * It should be noted that this operation automatically adds a 123 * value of <code>Boolean.TRUE</code> for the 124 * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> to the given 125 * <code>configuration</code> so that the operation is performed 126 * on the pixel values instead of being performed on the indices into 127 * the color map if the source(s) have an <code>IndexColorModel</code>. 128 * This addition will take place only if a value for the 129 * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> has not already been 130 * provided by the user. Note that the <code>configuration</code> Map 131 * is cloned before the new hint is added to it. The operation can be 132 * smart about the value of the <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> 133 * <code>RenderingHints</code>, i.e. while the default value for the 134 * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> is 135 * <code>Boolean.TRUE</code>, in some cases the operator could set the 136 * default. 137 * 138 * <p><table border=1> 139 * <caption>Resource List</caption> 140 * <tr><th>Name</th> <th>Value</th></tr> 141 * <tr><td>GlobalName</td> <td>Erode</td></tr> 142 * <tr><td>LocalName</td> <td>Erode</td></tr> 143 * <tr><td>Vendor</td> <td>com.lightcrafts.media.jai</td></tr> 144 * <tr><td>Description</td> <td>Performs kernel based Erode on 145 * an image.</td></tr> 146 * <tr><td>DocURL</td> <td>http://java.sun.com/products/java-media/jai/forD 147 evelopers/jai-apidocs/javax/media/jai/operator/ErodeDescriptor.html</td 148 ></tr> 149 * <tr><td>Version</td> <td>1.1</td></tr> 150 * <tr><td>arg0Desc</td> <td>The erode kernel.</td></tr> 151 * </table></p> 152 * 153 * <p><table border=1> 154 * <caption>Parameter List</caption> 155 * <tr><th>Name</th> <th>Class Type</th> 156 * <th>Default Value</th></tr> 157 * <tr><td>kernel</td> <td>com.lightcrafts.mediax.jai.KernelJAI</td> 158 * <td>NO_PARAMETER_DEFAULT</td> 159 * </table></p> 160 * 161 * </pre> 162 * 163 * 164 * @see com.lightcrafts.mediax.jai.KernelJAI 165 * 166 * @since JAI 1.1 167 */ 168 169 public class ErodeDescriptor extends OperationDescriptorImpl { 170 171 /** 172 * The resource strings that provide the general documentation and 173 * specify the parameter list for a Erode operation. 174 */ 175 private static final String[][] resources = { 176 {"GlobalName", "Erode"}, 177 {"LocalName", "Erode"}, 178 {"Vendor", "com.lightcrafts.media.jai"}, 179 {"Description", JaiI18N.getString("ErodeDescriptor0")}, 180 {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jaiapi/<br>com.lightcrafts.mediax.jai.operator.ErodeDescriptor.html"}, 181 {"Version", JaiI18N.getString("DescriptorVersion")}, 182 {"arg0Desc", JaiI18N.getString("ErodeDescriptor1")} 183 }; 184 185 /** The parameter names for the Erode operation. */ 186 private static final String[] paramNames = { 187 "kernel" 188 }; 189 190 /** The parameter class types for the Erode operation. */ 191 private static final Class[] paramClasses = { 192 com.lightcrafts.mediax.jai.KernelJAI.class 193 }; 194 195 /** The parameter default values for the Erode operation. */ 196 private static final Object[] paramDefaults = { 197 NO_PARAMETER_DEFAULT 198 }; 199 200 /** Constructor. */ ErodeDescriptor()201 public ErodeDescriptor() { 202 super(resources, 1, paramClasses, paramNames, paramDefaults); 203 } 204 205 /** 206 * Returns an array of <code>PropertyGenerators</code> implementing 207 * property inheritance for the "Erode" operation. 208 * 209 * @return An array of property generators. 210 */ getPropertyGenerators()211 public PropertyGenerator[] getPropertyGenerators() { 212 PropertyGenerator[] pg = new PropertyGenerator[1]; 213 pg[0] = new AreaOpPropertyGenerator(); 214 return pg; 215 } 216 217 218 /** 219 * Performs binary kernel based Erode operation on the image. 220 * 221 * <p>Creates a <code>ParameterBlockJAI</code> from all 222 * supplied arguments except <code>hints</code> and invokes 223 * {@link JAI#create(String,ParameterBlock,RenderingHints)}. 224 * 225 * @see JAI 226 * @see ParameterBlockJAI 227 * @see RenderedOp 228 * 229 * @param source0 <code>RenderedImage</code> source 0. 230 * @param kernel The binary convolution kernel. 231 * @param hints The <code>RenderingHints</code> to use. 232 * May be <code>null</code>. 233 * @return The <code>RenderedOp</code> destination. 234 * @throws IllegalArgumentException if <code>source0</code> is <code>null</code>. 235 * @throws IllegalArgumentException if <code>kernel</code> is <code>null</code>. 236 */ create(RenderedImage source0, KernelJAI kernel, RenderingHints hints)237 public static RenderedOp create(RenderedImage source0, 238 KernelJAI kernel, 239 RenderingHints hints) { 240 ParameterBlockJAI pb = 241 new ParameterBlockJAI("Erode", 242 RenderedRegistryMode.MODE_NAME); 243 244 pb.setSource("source0", source0); 245 246 pb.setParameter("kernel", kernel); 247 248 return JAI.create("Erode", pb, hints); 249 } 250 } 251