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