001: /*
002: * $RCSfile: ErodeDescriptor.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:35 $
010: * $State: Exp $
011: */
012: package javax.media.jai.operator;
013:
014: import com.sun.media.jai.util.AreaOpPropertyGenerator;
015: import java.awt.RenderingHints;
016: import java.awt.image.RenderedImage;
017: import javax.media.jai.JAI;
018: import javax.media.jai.KernelJAI;
019: import javax.media.jai.OperationDescriptorImpl;
020: import javax.media.jai.ParameterBlockJAI;
021: import javax.media.jai.PropertyGenerator;
022: import javax.media.jai.RenderedOp;
023: import javax.media.jai.registry.RenderedRegistryMode;
024:
025: /**
026: *
027: * An <code>OperationDescriptor</code> describing the "Erode" operation.
028: *
029: * <p> <b>Gray Scale Erosion</b>
030: * is a spatial operation that computes
031: * each output sample by subtracting elements of a kernel from the samples
032: * surrounding a particular source sample.
033: * The mathematical formulation for erosion operation is:
034: *
035: * <p> For a kernel K with a key position (xKey, yKey), the erosion
036: * of image I at (x,y) is given by:
037: * <pre>
038: * max{ f: f + K(xKey+i, yKey+j) <= I(x+i,y+j): all (i,j)}
039: *
040: * "all" possible (i,j) means that both I(x+i,y+j) and K(xKey+i, yKey+j)
041: * are in bounds. Otherwise, the value is set to 0.
042: * "f" represents all possible floats satisfying the restriction.
043: *
044: * </pre>
045: * <p> Intuitively, the kernel is like an unbrella and the key point
046: * is the handle. At every point, you try to push the umbrella up as high
047: * as possible but still underneath the image surface. The final height
048: * of the handle is the value after erosion. Thus if you want the image
049: * to erode from the upper right to bottom left, the following would do.
050: *
051: * <p><center>
052: * <table border=1>
053: * <tr align=center><td>0</td><td>0</td><td>X</td> </tr>
054: * <tr align=center><td>0</td><td>X</td><td>0</td> </tr>
055: * <tr align=center><td><b>X</b></td><td>0</td><td>0</td> </tr>
056: * </table></center>
057: *
058: * <p> Note that even if every entry of a kernel is zero,
059: * the erosion changes the image. Different key positions
060: * will also lead to different erosion results for such zero kernels.
061: *
062: * <p> Pseudo code for the erosion operation is as follows.
063: * Assuming the kernel K is of size M rows x N cols
064: * and the key position is (xKey, yKey).
065: *
066: * <pre>
067: *
068: * // erosion
069: * for every dst pixel location (x,y){
070: * tmp = infinity;
071: * for (i = -xKey; i < M - xKey; i++){
072: * for (j = -yKey; j < N - yKey; j++){
073: * if((x+i, y+j) are in bounds of src){
074: * tmp = min{tmp, src[x + i][y + j] - K[xKey + i][yKey + j]};
075: * }
076: * }
077: * }
078: * dst[x][y] = tmp;
079: * if (dst[x][y] == infinity)
080: * dst[x][y] = 0;
081: * }
082: * </pre>
083: *
084: * <p> The kernel cannot be bigger in any dimension than the image data.
085: *
086: * <p> <b>Binary Image Erosion</b>
087: * requires the kernel to be binary, that is, to have values 0 and 1
088: * for each kernel entry.
089: * Intuitively, binary erosion slides the kernel
090: * key position and place it at every point (x,y) in the src image.
091: * The dst value at this position is set to 1 if the entire kernel lies
092: * within the image bounds and the src image value is 1
093: * wherever the corresponding kernel value is 1."
094: * Otherwise, the value after erosion at (x,y) is set to 0.
095: * Erosion usually shrinks images, but it can fill holes
096: * with kernels like
097: * <pre> [1 0 1] </pre>
098: * and the key position at the center.
099: *
100: * <p> Pseudo code for the binary erosion operation is as follows.
101: *
102: * <pre>
103: * // erosion
104: * for every dst pixel location (x,y){
105: * dst[x][y] = 1;
106: * for (i = -xKey; i < M - xKey; i++){
107: * for (j = -yKey; j < N - yKey; j++){
108: * if((x+i,y+j) is out of bounds of src ||
109: * src(x+i, y+j)==0 && Key(xKey+i, yKey+j)==1){
110: * dst[x][y] = 0; break;
111: * }
112: * }
113: * }
114: * }
115: *
116: * The following can be used as references for the underlying
117: * connection between these two algorithms.
118: *
119: * <p> Reference: An Introduction to Nonlinear Image Processing,
120: * by Edward R. Bougherty and Jaakko Astola,
121: * Spie Optical Engineering Press, 1994.
122: *
123: * It should be noted that this operation automatically adds a
124: * value of <code>Boolean.TRUE</code> for the
125: * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> to the given
126: * <code>configuration</code> so that the operation is performed
127: * on the pixel values instead of being performed on the indices into
128: * the color map if the source(s) have an <code>IndexColorModel</code>.
129: * This addition will take place only if a value for the
130: * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> has not already been
131: * provided by the user. Note that the <code>configuration</code> Map
132: * is cloned before the new hint is added to it. The operation can be
133: * smart about the value of the <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code>
134: * <code>RenderingHints</code>, i.e. while the default value for the
135: * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> is
136: * <code>Boolean.TRUE</code>, in some cases the operator could set the
137: * default.
138: *
139: * <p><table border=1>
140: * <caption>Resource List</caption>
141: * <tr><th>Name</th> <th>Value</th></tr>
142: * <tr><td>GlobalName</td> <td>Erode</td></tr>
143: * <tr><td>LocalName</td> <td>Erode</td></tr>
144: * <tr><td>Vendor</td> <td>com.sun.media.jai</td></tr>
145: * <tr><td>Description</td> <td>Performs kernel based Erode on
146: * an image.</td></tr>
147: * <tr><td>DocURL</td> <td>http://java.sun.com/products/java-media/jai/forD
148: evelopers/jai-apidocs/javax/media/jai/operator/ErodeDescriptor.html</td
149: ></tr>
150: * <tr><td>Version</td> <td>1.1</td></tr>
151: * <tr><td>arg0Desc</td> <td>The erode kernel.</td></tr>
152: * </table></p>
153: *
154: * <p><table border=1>
155: * <caption>Parameter List</caption>
156: * <tr><th>Name</th> <th>Class Type</th>
157: * <th>Default Value</th></tr>
158: * <tr><td>kernel</td> <td>javax.media.jai.KernelJAI</td>
159: * <td>NO_PARAMETER_DEFAULT</td>
160: * </table></p>
161: *
162: * </pre>
163: *
164: *
165: * @see javax.media.jai.KernelJAI
166: *
167: * @since JAI 1.1
168: */
169:
170: public class ErodeDescriptor extends OperationDescriptorImpl {
171:
172: /**
173: * The resource strings that provide the general documentation and
174: * specify the parameter list for a Erode operation.
175: */
176: private static final String[][] resources = {
177: { "GlobalName", "Erode" },
178: { "LocalName", "Erode" },
179: { "Vendor", "com.sun.media.jai" },
180: { "Description", JaiI18N.getString("ErodeDescriptor0") },
181: {
182: "DocURL",
183: "http://java.sun.com/products/java-media/jai/forDevelopers/jaiapi/<br>javax.media.jai.operator.ErodeDescriptor.html" },
184: { "Version", JaiI18N.getString("DescriptorVersion") },
185: { "arg0Desc", JaiI18N.getString("ErodeDescriptor1") } };
186:
187: /** The parameter names for the Erode operation. */
188: private static final String[] paramNames = { "kernel" };
189:
190: /** The parameter class types for the Erode operation. */
191: private static final Class[] paramClasses = { javax.media.jai.KernelJAI.class };
192:
193: /** The parameter default values for the Erode operation. */
194: private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT };
195:
196: /** Constructor. */
197: public ErodeDescriptor() {
198: super (resources, 1, paramClasses, paramNames, paramDefaults);
199: }
200:
201: /**
202: * Returns an array of <code>PropertyGenerators</code> implementing
203: * property inheritance for the "Erode" operation.
204: *
205: * @return An array of property generators.
206: */
207: public PropertyGenerator[] getPropertyGenerators() {
208: PropertyGenerator[] pg = new PropertyGenerator[1];
209: pg[0] = new AreaOpPropertyGenerator();
210: return pg;
211: }
212:
213: /**
214: * Performs binary kernel based Erode operation on the image.
215: *
216: * <p>Creates a <code>ParameterBlockJAI</code> from all
217: * supplied arguments except <code>hints</code> and invokes
218: * {@link JAI#create(String,ParameterBlock,RenderingHints)}.
219: *
220: * @see JAI
221: * @see ParameterBlockJAI
222: * @see RenderedOp
223: *
224: * @param source0 <code>RenderedImage</code> source 0.
225: * @param kernel The binary convolution kernel.
226: * @param hints The <code>RenderingHints</code> to use.
227: * May be <code>null</code>.
228: * @return The <code>RenderedOp</code> destination.
229: * @throws IllegalArgumentException if <code>source0</code> is <code>null</code>.
230: * @throws IllegalArgumentException if <code>kernel</code> is <code>null</code>.
231: */
232: public static RenderedOp create(RenderedImage source0,
233: KernelJAI kernel, RenderingHints hints) {
234: ParameterBlockJAI pb = new ParameterBlockJAI("Erode",
235: RenderedRegistryMode.MODE_NAME);
236:
237: pb.setSource("source0", source0);
238:
239: pb.setParameter("kernel", kernel);
240:
241: return JAI.create("Erode", pb, hints);
242: }
243: }
|