001: /*
002: * $RCSfile: ErodeOpImage.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:56:24 $
010: * $State: Exp $
011: */
012: package com.sun.media.jai.opimage;
013:
014: import java.awt.Rectangle;
015: import java.awt.image.DataBuffer;
016: import java.awt.image.SampleModel;
017: import java.awt.image.Raster;
018: import java.awt.image.RenderedImage;
019: import java.awt.image.WritableRaster;
020: import java.awt.image.renderable.ParameterBlock;
021: import java.awt.image.renderable.RenderedImageFactory;
022: import javax.media.jai.AreaOpImage;
023: import javax.media.jai.BorderExtender;
024: import javax.media.jai.ImageLayout;
025: import javax.media.jai.KernelJAI;
026: import javax.media.jai.OpImage;
027: import javax.media.jai.RasterAccessor;
028: import javax.media.jai.RasterFormatTag;
029: import java.util.Map;
030:
031: // import com.sun.media.jai.test.OpImageTester;
032:
033: /**
034: *
035: * An OpImage class to perform erosion on a source image.
036: *
037: * <p> This class implements an erosion operation.
038: *
039: * <p> <b>Grey Scale Erosion</b>
040: * is a spatial operation that computes
041: * each output sample by subtract elements of a kernel to the samples
042: * surrounding a particular source sample with some care.
043: * A mathematical expression is:
044: *
045: * <p> For a kernel K with a key position (xKey, yKey), the erosion
046: * of image I at (x,y) is given by:
047: * <pre>
048: * max{a: a + K(xKey+i, yKey+j) <= I(x+i,y+j): all (i,j) }
049: *
050: * all possible (i,j) means that both I(x+i,y+j) and K(xKey+i, yKey+j)
051: * are in bounds. Otherwise, the value is set to 0.
052: *
053: * </pre>
054: * <p> Intuitively, the kernel is like an unbrella and the key point
055: * is the handle. At every point, you try to push the umbrella up as high
056: * as possible but still underneath the image surface. The final height
057: * of the handle is the value after erosion. Thus if you want the image
058: * to erode from the upper right to bottom left, the following would do.
059: *
060: * <p><center>
061: * <table border=1>
062: * <tr align=center><td>0</td><td>0</td><td>X</td> </tr>
063: * <tr align=center><td>0</td><td>X</td><td>0</td> </tr>
064: * <tr align=center><td><b>X</b></td><td>0</td><td>0</td> </tr>
065: * </table></center>
066: *
067: * <p> Note that zero kernel erosion has effects on the image, the
068: * location of the key position and size of kernel all matter.
069: *
070: * <p> Pseudo code for the erosion operation is as follows.
071: * Assuming the kernel K is of size M rows x N cols
072: * and the key position is (xKey, yKey).
073: *
074: * <pre>
075: *
076: * // erosion
077: * for every dst pixel location (x,y){
078: * tmp = infinity;
079: * for (i = -xKey; i < M - xKey; i++){
080: * for (j = -yKey; j < N - yKey; j++){
081: * if((x+i, y+j) are in bounds of src){
082: * tmp = min{tmp, src[x + i][y + j] - K[xKey + i][yKey + j]};
083: * }
084: * }
085: * }
086: * dst[x][y] = tmp;
087: * if (dst[x][y] == infinity)
088: * dst[x][y] = 0;
089: * }
090: * </pre>
091: *
092: * <p> The kernel cannot be bigger in any dimension than the image data.
093: *
094: * <p> <b>Binary Image Erosion</b>
095: * requires the kernel to be binary as well.
096: * Intuitively, binary erosion slides the kernel
097: * key position and place it at every point (x,y) in the src image.
098: * The dst value at this position is set to 1 if all the kernel
099: * are fully supported by the src image, and the src image value is 1
100: * whenever the kernel has value 1.
101: * Otherwise, the value after erosion at (x,y) is set to 0.
102: * Erosion usually shrinks images, but it can fill holes
103: * with kernels like
104: * <pre> [1 <b>0</b> 1] </pre>
105: * and the key position at the center.
106: *
107: * <p> Pseudo code for the erosion operation is as follows.
108: *
109: * <pre>
110: * // erosion
111: * for every dst pixel location (x,y){
112: * dst[x][y] = 1;
113: * for (i = -xKey; i < M - xKey; i++){
114: * for (j = -yKey; j < N - yKey; j++){
115: * if((x+i,y+j) is out of bounds of src ||
116: * src(x+i, y+j)==0 && Key(xKey+i, yKey+j)==1){
117: * dst[x][y] = 0; break;
118: * }
119: * }
120: * }
121: * }
122: * </pre>
123: *
124: * <p> Reference: An Introduction to Nonlinear Image Processing,
125: * by Edward R. Bougherty and Jaakko Astola,
126: * Spie Optical Engineering Press, 1994.
127: *
128: *
129: * @see KernelJAI
130: */
131: final class ErodeOpImage extends AreaOpImage {
132:
133: /**
134: * The kernel with which to do the erode operation.
135: */
136: protected KernelJAI kernel;
137:
138: /** Kernel variables. */
139: private int kw, kh, kx, ky;
140: private float[] kdata;
141:
142: /**
143: * Creates a ErodeOpImage given a ParameterBlock containing the image
144: * source and pre-rotated erosion kernel. The image dimensions are
145: * derived
146: * from the source image. The tile grid layout, SampleModel, and
147: * ColorModel may optionally be specified by an ImageLayout
148: * object.
149: *
150: * @param source a RenderedImage.
151: * @param extender a BorderExtender, or null.
152: * @param layout an ImageLayout optionally containing the tile grid layout,
153: * SampleModel, and ColorModel, or null.
154: * @param kernel the pre-rotated erosion KernelJAI.
155: */
156: public ErodeOpImage(RenderedImage source, BorderExtender extender,
157: Map config, ImageLayout layout, KernelJAI kernel) {
158: super (source, layout, config, true, extender, kernel
159: .getLeftPadding(), kernel.getRightPadding(), kernel
160: .getTopPadding(), kernel.getBottomPadding());
161:
162: this .kernel = kernel;
163: kw = kernel.getWidth();
164: kh = kernel.getHeight();
165: kx = kernel.getXOrigin();
166: ky = kernel.getYOrigin();
167:
168: kdata = kernel.getKernelData();
169: }
170:
171: /**
172: * Performs erosion on a specified rectangle. The sources are
173: * cobbled.
174: *
175: * @param sources an array of source Rasters, guaranteed to provide all
176: * necessary source data for computing the output.
177: * @param dest a WritableRaster tile containing the area to be computed.
178: * @param destRect the rectangle within dest to be processed.
179: */
180: protected void computeRect(Raster[] sources, WritableRaster dest,
181: Rectangle destRect) {
182: // Retrieve format tags.
183: RasterFormatTag[] formatTags = getFormatTags();
184:
185: Raster source = sources[0];
186: Rectangle srcRect = mapDestRect(destRect, 0);
187:
188: RasterAccessor srcAccessor = new RasterAccessor(source,
189: srcRect, formatTags[0], getSourceImage(0)
190: .getColorModel());
191: RasterAccessor dstAccessor = new RasterAccessor(dest, destRect,
192: formatTags[1], getColorModel());
193:
194: switch (dstAccessor.getDataType()) {
195: case DataBuffer.TYPE_BYTE:
196: byteLoop(srcAccessor, dstAccessor);
197: break;
198: case DataBuffer.TYPE_INT:
199: intLoop(srcAccessor, dstAccessor);
200: break;
201: case DataBuffer.TYPE_SHORT:
202: shortLoop(srcAccessor, dstAccessor);
203: break;
204: case DataBuffer.TYPE_USHORT:
205: ushortLoop(srcAccessor, dstAccessor);
206: break;
207: case DataBuffer.TYPE_FLOAT:
208: floatLoop(srcAccessor, dstAccessor);
209: break;
210: case DataBuffer.TYPE_DOUBLE:
211: doubleLoop(srcAccessor, dstAccessor);
212: break;
213:
214: default:
215: }
216:
217: // If the RasterAccessor object set up a temporary buffer for the
218: // op to write to, tell the RasterAccessor to write that data
219: // to the raster no that we're done with it.
220: if (dstAccessor.isDataCopy()) {
221: dstAccessor.clampDataArrays();
222: dstAccessor.copyDataToRaster();
223: }
224: }
225:
226: private void byteLoop(RasterAccessor src, RasterAccessor dst) {
227:
228: int dwidth = dst.getWidth();
229: int dheight = dst.getHeight();
230: int dnumBands = dst.getNumBands();
231:
232: int dstBandOffsets[] = dst.getBandOffsets();
233: int dstPixelStride = dst.getPixelStride();
234: int dstScanlineStride = dst.getScanlineStride();
235:
236: int srcBandOffsets[] = src.getBandOffsets();
237: int srcPixelStride = src.getPixelStride();
238: int srcScanlineStride = src.getScanlineStride();
239:
240: byte dstDataArrays[][] = dst.getByteDataArrays();
241: byte srcDataArrays[][] = src.getByteDataArrays();
242:
243: for (int k = 0; k < dnumBands; k++) {
244: byte dstData[] = dstDataArrays[k];
245: byte srcData[] = srcDataArrays[k];
246: int srcScanlineOffset = srcBandOffsets[k];
247: int dstScanlineOffset = dstBandOffsets[k];
248: for (int j = 0; j < dheight; j++) {
249: int srcPixelOffset = srcScanlineOffset;
250: int dstPixelOffset = dstScanlineOffset;
251:
252: for (int i = 0; i < dwidth; i++) {
253: int kernelVerticalOffset = 0;
254: int imageVerticalOffset = srcPixelOffset;
255: float f = Float.POSITIVE_INFINITY;
256: for (int u = 0; u < kh; u++) {
257: int imageOffset = imageVerticalOffset;
258: for (int v = 0; v < kw; v++) {
259: float tmpIK = ((int) srcData[imageOffset] & 0xff)
260: - kdata[kernelVerticalOffset + v];
261: if (tmpIK < f) {
262: f = tmpIK;
263: }
264: imageOffset += srcPixelStride;
265: }
266: kernelVerticalOffset += kw;
267: imageVerticalOffset += srcScanlineStride;
268: }
269:
270: if (Float.isInfinite(f)) {
271: f = 0;
272: }
273: int val = (int) f;
274:
275: if (val < 0) {
276: val = 0;
277: } else if (val > 255) {
278: val = 255;
279: }
280: dstData[dstPixelOffset] = (byte) val;
281: srcPixelOffset += srcPixelStride;
282: dstPixelOffset += dstPixelStride;
283: }
284: srcScanlineOffset += srcScanlineStride;
285: dstScanlineOffset += dstScanlineStride;
286: }
287: }
288: }
289:
290: private void shortLoop(RasterAccessor src, RasterAccessor dst) {
291:
292: int dwidth = dst.getWidth();
293: int dheight = dst.getHeight();
294: int dnumBands = dst.getNumBands();
295:
296: int dstBandOffsets[] = dst.getBandOffsets();
297: int dstPixelStride = dst.getPixelStride();
298: int dstScanlineStride = dst.getScanlineStride();
299:
300: int srcBandOffsets[] = src.getBandOffsets();
301: int srcPixelStride = src.getPixelStride();
302: int srcScanlineStride = src.getScanlineStride();
303:
304: short dstDataArrays[][] = dst.getShortDataArrays();
305: short srcDataArrays[][] = src.getShortDataArrays();
306:
307: for (int k = 0; k < dnumBands; k++) {
308: short dstData[] = dstDataArrays[k];
309: short srcData[] = srcDataArrays[k];
310: int srcScanlineOffset = srcBandOffsets[k];
311: int dstScanlineOffset = dstBandOffsets[k];
312: for (int j = 0; j < dheight; j++) {
313: int srcPixelOffset = srcScanlineOffset;
314: int dstPixelOffset = dstScanlineOffset;
315:
316: for (int i = 0; i < dwidth; i++) {
317: int kernelVerticalOffset = 0;
318: int imageVerticalOffset = srcPixelOffset;
319: float f = Float.POSITIVE_INFINITY;
320: for (int u = 0; u < kh; u++) {
321: int imageOffset = imageVerticalOffset;
322: for (int v = 0; v < kw; v++) {
323: float tmpIK = srcData[imageOffset]
324: - kdata[kernelVerticalOffset + v];
325: if (tmpIK < f) {
326: f = tmpIK;
327: }
328: imageOffset += srcPixelStride;
329: }
330: kernelVerticalOffset += kw;
331: imageVerticalOffset += srcScanlineStride;
332: }
333: if (Float.isInfinite(f)) {
334: f = 0.0F;
335: }
336: int val = (int) f;
337: if (val < Short.MIN_VALUE) {
338: val = Short.MIN_VALUE;
339: } else if (val > Short.MAX_VALUE) {
340: val = Short.MAX_VALUE;
341: }
342: dstData[dstPixelOffset] = (short) val;
343: srcPixelOffset += srcPixelStride;
344: dstPixelOffset += dstPixelStride;
345: }
346: srcScanlineOffset += srcScanlineStride;
347: dstScanlineOffset += dstScanlineStride;
348: }
349: }
350: }
351:
352: private void ushortLoop(RasterAccessor src, RasterAccessor dst) {
353:
354: int dwidth = dst.getWidth();
355: int dheight = dst.getHeight();
356: int dnumBands = dst.getNumBands();
357:
358: int dstBandOffsets[] = dst.getBandOffsets();
359: int dstPixelStride = dst.getPixelStride();
360: int dstScanlineStride = dst.getScanlineStride();
361:
362: int srcBandOffsets[] = src.getBandOffsets();
363: int srcPixelStride = src.getPixelStride();
364: int srcScanlineStride = src.getScanlineStride();
365:
366: short dstDataArrays[][] = dst.getShortDataArrays();
367: short srcDataArrays[][] = src.getShortDataArrays();
368:
369: for (int k = 0; k < dnumBands; k++) {
370: short dstData[] = dstDataArrays[k];
371: short srcData[] = srcDataArrays[k];
372: int srcScanlineOffset = srcBandOffsets[k];
373: int dstScanlineOffset = dstBandOffsets[k];
374: for (int j = 0; j < dheight; j++) {
375: int srcPixelOffset = srcScanlineOffset;
376: int dstPixelOffset = dstScanlineOffset;
377:
378: for (int i = 0; i < dwidth; i++) {
379: int kernelVerticalOffset = 0;
380: int imageVerticalOffset = srcPixelOffset;
381: float f = Float.POSITIVE_INFINITY;
382: for (int u = 0; u < kh; u++) {
383: int imageOffset = imageVerticalOffset;
384: for (int v = 0; v < kw; v++) {
385: float tmpIK = (srcData[imageOffset] & 0xffff)
386: - kdata[kernelVerticalOffset + v];
387: if (tmpIK < f) {
388: f = tmpIK;
389: }
390: imageOffset += srcPixelStride;
391: }
392: kernelVerticalOffset += kw;
393: imageVerticalOffset += srcScanlineStride;
394: }
395: if (Float.isInfinite(f)) {
396: f = 0.0F;
397: }
398: int val = (int) f;
399: if (val < 0) {
400: val = 0;
401: } else if (val > 0xffff) {
402: val = 0xffff;
403: }
404: dstData[dstPixelOffset] = (short) val;
405: srcPixelOffset += srcPixelStride;
406: dstPixelOffset += dstPixelStride;
407: }
408: srcScanlineOffset += srcScanlineStride;
409: dstScanlineOffset += dstScanlineStride;
410: }
411: }
412: }
413:
414: private void intLoop(RasterAccessor src, RasterAccessor dst) {
415:
416: int dwidth = dst.getWidth();
417: int dheight = dst.getHeight();
418: int dnumBands = dst.getNumBands();
419:
420: int dstBandOffsets[] = dst.getBandOffsets();
421: int dstPixelStride = dst.getPixelStride();
422: int dstScanlineStride = dst.getScanlineStride();
423:
424: int srcBandOffsets[] = src.getBandOffsets();
425: int srcPixelStride = src.getPixelStride();
426: int srcScanlineStride = src.getScanlineStride();
427:
428: int dstDataArrays[][] = dst.getIntDataArrays();
429: int srcDataArrays[][] = src.getIntDataArrays();
430:
431: for (int k = 0; k < dnumBands; k++) {
432: int dstData[] = dstDataArrays[k];
433: int srcData[] = srcDataArrays[k];
434: int srcScanlineOffset = srcBandOffsets[k];
435: int dstScanlineOffset = dstBandOffsets[k];
436: for (int j = 0; j < dheight; j++) {
437: int srcPixelOffset = srcScanlineOffset;
438: int dstPixelOffset = dstScanlineOffset;
439:
440: for (int i = 0; i < dwidth; i++) {
441: int kernelVerticalOffset = 0;
442: int imageVerticalOffset = srcPixelOffset;
443: float f = Float.POSITIVE_INFINITY;
444: for (int u = 0; u < kh; u++) {
445: int imageOffset = imageVerticalOffset;
446: for (int v = 0; v < kw; v++) {
447: float tmpIK = (int) srcData[imageOffset]
448: - kdata[kernelVerticalOffset + v];
449: if (tmpIK < f) {
450: f = tmpIK;
451: }
452: imageOffset += srcPixelStride;
453: }
454: kernelVerticalOffset += kw;
455: imageVerticalOffset += srcScanlineStride;
456: }
457: if (Float.isInfinite(f)) {
458: f = 0.0F;
459: }
460: dstData[dstPixelOffset] = (int) f;
461: srcPixelOffset += srcPixelStride;
462: dstPixelOffset += dstPixelStride;
463: }
464: srcScanlineOffset += srcScanlineStride;
465: dstScanlineOffset += dstScanlineStride;
466: }
467: }
468: }
469:
470: private void floatLoop(RasterAccessor src, RasterAccessor dst) {
471:
472: int dwidth = dst.getWidth();
473: int dheight = dst.getHeight();
474: int dnumBands = dst.getNumBands();
475:
476: int dstBandOffsets[] = dst.getBandOffsets();
477: int dstPixelStride = dst.getPixelStride();
478: int dstScanlineStride = dst.getScanlineStride();
479:
480: int srcBandOffsets[] = src.getBandOffsets();
481: int srcPixelStride = src.getPixelStride();
482: int srcScanlineStride = src.getScanlineStride();
483:
484: float dstDataArrays[][] = dst.getFloatDataArrays();
485: float srcDataArrays[][] = src.getFloatDataArrays();
486:
487: for (int k = 0; k < dnumBands; k++) {
488: float dstData[] = dstDataArrays[k];
489: float srcData[] = srcDataArrays[k];
490: int srcScanlineOffset = srcBandOffsets[k];
491: int dstScanlineOffset = dstBandOffsets[k];
492: for (int j = 0; j < dheight; j++) {
493: int srcPixelOffset = srcScanlineOffset;
494: int dstPixelOffset = dstScanlineOffset;
495:
496: for (int i = 0; i < dwidth; i++) {
497: int kernelVerticalOffset = 0;
498: int imageVerticalOffset = srcPixelOffset;
499: float f = Float.POSITIVE_INFINITY;
500: for (int u = 0; u < kh; u++) {
501: int imageOffset = imageVerticalOffset;
502: for (int v = 0; v < kw; v++) {
503: float tmpIK = srcData[imageOffset]
504: - kdata[kernelVerticalOffset + v];
505: if (tmpIK < f) {
506: f = tmpIK;
507: }
508: imageOffset += srcPixelStride;
509: }
510: kernelVerticalOffset += kw;
511: imageVerticalOffset += srcScanlineStride;
512: }
513: if (Float.isInfinite(f)) {
514: f = 0.0F;
515: }
516: dstData[dstPixelOffset] = f;
517: srcPixelOffset += srcPixelStride;
518: dstPixelOffset += dstPixelStride;
519: }
520: srcScanlineOffset += srcScanlineStride;
521: dstScanlineOffset += dstScanlineStride;
522: }
523: }
524: }
525:
526: private void doubleLoop(RasterAccessor src, RasterAccessor dst) {
527:
528: int dwidth = dst.getWidth();
529: int dheight = dst.getHeight();
530: int dnumBands = dst.getNumBands();
531:
532: int dstBandOffsets[] = dst.getBandOffsets();
533: int dstPixelStride = dst.getPixelStride();
534: int dstScanlineStride = dst.getScanlineStride();
535:
536: int srcBandOffsets[] = src.getBandOffsets();
537: int srcPixelStride = src.getPixelStride();
538: int srcScanlineStride = src.getScanlineStride();
539:
540: double dstDataArrays[][] = dst.getDoubleDataArrays();
541: double srcDataArrays[][] = src.getDoubleDataArrays();
542:
543: for (int k = 0; k < dnumBands; k++) {
544: double dstData[] = dstDataArrays[k];
545: double srcData[] = srcDataArrays[k];
546: int srcScanlineOffset = srcBandOffsets[k];
547: int dstScanlineOffset = dstBandOffsets[k];
548: for (int j = 0; j < dheight; j++) {
549: int srcPixelOffset = srcScanlineOffset;
550: int dstPixelOffset = dstScanlineOffset;
551:
552: for (int i = 0; i < dwidth; i++) {
553: int kernelVerticalOffset = 0;
554: int imageVerticalOffset = srcPixelOffset;
555: double f = Double.POSITIVE_INFINITY;
556: for (int u = 0; u < kh; u++) {
557: int imageOffset = imageVerticalOffset;
558: for (int v = 0; v < kw; v++) {
559: double tmpIK = srcData[imageOffset]
560: - kdata[kernelVerticalOffset + v];
561: if (tmpIK < f) {
562: f = tmpIK;
563: }
564: imageOffset += srcPixelStride;
565: }
566: kernelVerticalOffset += kw;
567: imageVerticalOffset += srcScanlineStride;
568: }
569:
570: if (Double.isInfinite(f)) {
571: f = 0.0D;
572: }
573: dstData[dstPixelOffset] = f;
574: srcPixelOffset += srcPixelStride;
575: dstPixelOffset += dstPixelStride;
576: }
577: srcScanlineOffset += srcScanlineStride;
578: dstScanlineOffset += dstScanlineStride;
579: }
580: }
581: }
582: }
|