001: /*
002: * $RCSfile: DilateOpImage.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:23 $
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 dilation on a source image.
036: *
037: * Dilation for grey scale images can be charaterized by "slide, add and max",
038: * while for binary images by "slide and set". As always, the kernel
039: * is expected to come with a key position.
040: *
041: * <p> <b> Grey scale dilation</b> is a spatial operation that computes
042: * each output sample by adding elements of a kernel to the samples
043: * surrounding a particular source sample and taking the maximum.
044: * A mathematical expression is:
045: *
046: * <p> For a kernel K with a key position (xKey,yKey), the dilation
047: * of image I at (x,y) is given by:
048: * <pre>
049: * max{ I(x-i, y-j) + K(xKey+i, yKey+j): some (i,j) restriction }
050: *
051: * where the (i,j) restriction means:
052: * all possible (i,j) so that both I(x-i,y-j) and K(xKey+i, yKey+j)
053: * are defined, that is, these indecies are in bounds.
054: *
055: * </pre>
056: * <p>Intuitively in 2D, the kernel is like
057: * an unbrella and the key point is the handle. When the handle moves
058: * all over the image surface, the upper outbounds of all the umbrella
059: * positions is the dilation. Thus if you want the image to dilate in
060: * the upper right direction, the following kernel would do with
061: * the bold face key position.
062: *
063: * <p><center>
064: * <table border=1>
065: * <tr align=center><td>0</td><td>0</td><td>50</td> </tr>
066: * <tr align=center><td>0</td><td>50</td><td>0</td> </tr>
067: * <tr align=center><td><b>0</b></td><td>0</td><td>0</td> </tr>
068: * </table></center>
069: *
070: * <p> Note also that zero kernel have effects on the dilation!
071: * That is because of the "max" in the add and max process. Thus
072: * a 3 x 1 zero kernel with the key persion at the bottom of the kernel
073: * dilates the image upwards.
074: *
075: * <p>
076: * After the kernel is rotated 180 degrees, Pseudo code for dilation operation
077: * is as follows. Of course, you should provide the kernel in its
078: * (unrotated) original form. Assuming the kernel K is of size M rows x N cols
079: * and the key position is (xKey, yKey).
080: *
081: * // dilation
082: * for every dst pixel location (x,y){
083: * dst[x][y] = -infinity;
084: * for (i = -xKey; i < M - xKey; i++){
085: * for (j = -yKey; j < N - yKey; j++){
086: * if((x+i, y+j) are in bounds of src &&
087: * (xKey+i, yKey+j) are in bounds of K){
088: * tmp = src[x + i][y + j]+ K[xKey + i][yKey + j];
089: * dst[x][y] = max{tmp, dst[x][y]};
090: * }
091: * }
092: * }
093: * }
094: * </pre>
095: *
096: * <p> Dilation, unlike convolution and most neighborhood operations,
097: * actually can grow the image region. But to conform with other
098: * image neighborhood operations, the border pixels are set to 0.
099: * For a 3 x 3 kernel with the key point at the center, there will
100: * be a pixel wide 0 stripe around the border.
101: *
102: * <p> The kernel cannot be bigger in any dimension than the image data.
103: *
104: * <p> <b>Binary Image Dilation</b>
105: * requires the kernel K to be binary.
106: * Intuitively, starting from dst image being a duplicate of src,
107: * binary dilation slides the kernel K to place the key position
108: * at every non-zero point (x,y) in src image and set dst positions
109: * under ones of K to 1.
110: *
111: * <p> After the kernel is rotated 180 degrees, the pseudo code for
112: * dilation operation is as follows. (Of course, you should provide
113: * the kernel in its original unrotated form.)
114: *
115: * <pre>
116: *
117: * // dilating
118: * for every dst pixel location (x,y){
119: * dst[x][y] = src[x][y];
120: * for (i = -xKey; i < M - xKey; i++){
121: * for (j = -yKey; j < N - yKey; j++){
122: * if(src[x+i,y+i]==1 && Key(xKey+i, yKey+j)==1){
123: * dst[x][y] = 1; break;
124: * }
125: * }
126: * }
127: * }
128: * </pre>
129:
130: * <p> Reference: An Introduction to Nonlinear Image Processing,
131: * by Edward R. Bougherty and Jaakko Astola,
132: * Spie Optical Engineering Press, 1994.
133: *
134: *
135: * @see KernelJAI
136: */
137: final class DilateOpImage extends AreaOpImage {
138:
139: /**
140: * The kernel with which to do the dilate operation.
141: */
142: protected KernelJAI kernel;
143:
144: /** Kernel variables. */
145: private int kw, kh, kx, ky;
146: private float[] kdata;
147:
148: /**
149: * Creates a DilateOpImage given a ParameterBlock containing the image
150: * source and pre-rotated dilation kernel. The image dimensions are
151: * derived
152: * from the source image. The tile grid layout, SampleModel, and
153: * ColorModel may optionally be specified by an ImageLayout
154: * object.
155: *
156: * @param source a RenderedImage.
157: * @param extender a BorderExtender, or null.
158: * @param layout an ImageLayout optionally containing the tile grid layout,
159: * SampleModel, and ColorModel, or null.
160: * @param kernel the pre-rotated dilation KernelJAI.
161: */
162: public DilateOpImage(RenderedImage source, BorderExtender extender,
163: Map config, ImageLayout layout, KernelJAI kernel) {
164: super (source, layout, config, true, extender, kernel
165: .getLeftPadding(), kernel.getRightPadding(), kernel
166: .getTopPadding(), kernel.getBottomPadding());
167:
168: this .kernel = kernel;
169: kw = kernel.getWidth();
170: kh = kernel.getHeight();
171: kx = kernel.getXOrigin();
172: ky = kernel.getYOrigin();
173:
174: kdata = kernel.getKernelData();
175: }
176:
177: /**
178: * Performs dilation on a specified rectangle. The sources are
179: * cobbled.
180: *
181: * @param sources an array of source Rasters, guaranteed to provide all
182: * necessary source data for computing the output.
183: * @param dest a WritableRaster tile containing the area to be computed.
184: * @param destRect the rectangle within dest to be processed.
185: */
186: protected void computeRect(Raster[] sources, WritableRaster dest,
187: Rectangle destRect) {
188: // Retrieve format tags.
189: RasterFormatTag[] formatTags = getFormatTags();
190:
191: Raster source = sources[0];
192: Rectangle srcRect = mapDestRect(destRect, 0);
193:
194: RasterAccessor srcAccessor = new RasterAccessor(source,
195: srcRect, formatTags[0], getSourceImage(0)
196: .getColorModel());
197: RasterAccessor dstAccessor = new RasterAccessor(dest, destRect,
198: formatTags[1], getColorModel());
199:
200: switch (dstAccessor.getDataType()) {
201: case DataBuffer.TYPE_BYTE:
202: byteLoop(srcAccessor, dstAccessor);
203: break;
204: case DataBuffer.TYPE_INT:
205: intLoop(srcAccessor, dstAccessor);
206: break;
207: case DataBuffer.TYPE_SHORT:
208: shortLoop(srcAccessor, dstAccessor);
209: break;
210: case DataBuffer.TYPE_USHORT:
211: ushortLoop(srcAccessor, dstAccessor);
212: break;
213: case DataBuffer.TYPE_FLOAT:
214: floatLoop(srcAccessor, dstAccessor);
215: break;
216: case DataBuffer.TYPE_DOUBLE:
217: doubleLoop(srcAccessor, dstAccessor);
218: break;
219:
220: default:
221: }
222:
223: // If the RasterAccessor object set up a temporary buffer for the
224: // op to write to, tell the RasterAccessor to write that data
225: // to the raster no that we're done with it.
226: if (dstAccessor.isDataCopy()) {
227: dstAccessor.clampDataArrays();
228: dstAccessor.copyDataToRaster();
229: }
230: }
231:
232: private void byteLoop(RasterAccessor src, RasterAccessor dst) {
233:
234: int dwidth = dst.getWidth();
235: int dheight = dst.getHeight();
236: int dnumBands = dst.getNumBands();
237:
238: int dstBandOffsets[] = dst.getBandOffsets();
239: int dstPixelStride = dst.getPixelStride();
240: int dstScanlineStride = dst.getScanlineStride();
241:
242: int srcBandOffsets[] = src.getBandOffsets();
243: int srcPixelStride = src.getPixelStride();
244: int srcScanlineStride = src.getScanlineStride();
245:
246: byte dstDataArrays[][] = dst.getByteDataArrays();
247: byte srcDataArrays[][] = src.getByteDataArrays();
248:
249: for (int k = 0; k < dnumBands; k++) {
250: byte dstData[] = dstDataArrays[k];
251: byte srcData[] = srcDataArrays[k];
252: int srcScanlineOffset = srcBandOffsets[k];
253: int dstScanlineOffset = dstBandOffsets[k];
254: for (int j = 0; j < dheight; j++) {
255: int srcPixelOffset = srcScanlineOffset;
256: int dstPixelOffset = dstScanlineOffset;
257:
258: for (int i = 0; i < dwidth; i++) {
259: int kernelVerticalOffset = 0;
260: int imageVerticalOffset = srcPixelOffset;
261: float f = Float.NEGATIVE_INFINITY;
262: for (int u = 0; u < kh; u++) {
263: int imageOffset = imageVerticalOffset;
264: for (int v = 0; v < kw; v++) {
265: float tmpIK = ((int) srcData[imageOffset] & 0xff)
266: + kdata[kernelVerticalOffset + v];
267: if (tmpIK > f) {
268: f = tmpIK;
269: }
270: imageOffset += srcPixelStride;
271: }
272: kernelVerticalOffset += kw;
273: imageVerticalOffset += srcScanlineStride;
274: }
275:
276: int val = (int) f;
277: if (val < 0) {
278: val = 0;
279: } else if (val > 255) {
280: val = 255;
281: }
282: dstData[dstPixelOffset] = (byte) val;
283: srcPixelOffset += srcPixelStride;
284: dstPixelOffset += dstPixelStride;
285: }
286: srcScanlineOffset += srcScanlineStride;
287: dstScanlineOffset += dstScanlineStride;
288: }
289: }
290: }
291:
292: private void shortLoop(RasterAccessor src, RasterAccessor dst) {
293:
294: int dwidth = dst.getWidth();
295: int dheight = dst.getHeight();
296: int dnumBands = dst.getNumBands();
297:
298: int dstBandOffsets[] = dst.getBandOffsets();
299: int dstPixelStride = dst.getPixelStride();
300: int dstScanlineStride = dst.getScanlineStride();
301:
302: int srcBandOffsets[] = src.getBandOffsets();
303: int srcPixelStride = src.getPixelStride();
304: int srcScanlineStride = src.getScanlineStride();
305:
306: short dstDataArrays[][] = dst.getShortDataArrays();
307: short srcDataArrays[][] = src.getShortDataArrays();
308:
309: for (int k = 0; k < dnumBands; k++) {
310: short dstData[] = dstDataArrays[k];
311: short srcData[] = srcDataArrays[k];
312: int srcScanlineOffset = srcBandOffsets[k];
313: int dstScanlineOffset = dstBandOffsets[k];
314: for (int j = 0; j < dheight; j++) {
315: int srcPixelOffset = srcScanlineOffset;
316: int dstPixelOffset = dstScanlineOffset;
317:
318: for (int i = 0; i < dwidth; i++) {
319: int kernelVerticalOffset = 0;
320: int imageVerticalOffset = srcPixelOffset;
321: float f = Float.NEGATIVE_INFINITY;
322: for (int u = 0; u < kh; u++) {
323: int imageOffset = imageVerticalOffset;
324: for (int v = 0; v < kw; v++) {
325: float tmpIK = srcData[imageOffset]
326: + kdata[kernelVerticalOffset + v];
327: if (tmpIK > f) {
328: f = tmpIK;
329: }
330: imageOffset += srcPixelStride;
331: }
332: kernelVerticalOffset += kw;
333: imageVerticalOffset += srcScanlineStride;
334: }
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.NEGATIVE_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:
396: int val = (int) f;
397: if (val < 0) {
398: val = 0;
399: } else if (val > 0xffff) {
400: val = 0xffff;
401: }
402: dstData[dstPixelOffset] = (short) val;
403: srcPixelOffset += srcPixelStride;
404: dstPixelOffset += dstPixelStride;
405: }
406: srcScanlineOffset += srcScanlineStride;
407: dstScanlineOffset += dstScanlineStride;
408: }
409: }
410: }
411:
412: private void intLoop(RasterAccessor src, RasterAccessor dst) {
413:
414: int dwidth = dst.getWidth();
415: int dheight = dst.getHeight();
416: int dnumBands = dst.getNumBands();
417:
418: int dstBandOffsets[] = dst.getBandOffsets();
419: int dstPixelStride = dst.getPixelStride();
420: int dstScanlineStride = dst.getScanlineStride();
421:
422: int srcBandOffsets[] = src.getBandOffsets();
423: int srcPixelStride = src.getPixelStride();
424: int srcScanlineStride = src.getScanlineStride();
425:
426: int dstDataArrays[][] = dst.getIntDataArrays();
427: int srcDataArrays[][] = src.getIntDataArrays();
428:
429: for (int k = 0; k < dnumBands; k++) {
430: int dstData[] = dstDataArrays[k];
431: int srcData[] = srcDataArrays[k];
432: int srcScanlineOffset = srcBandOffsets[k];
433: int dstScanlineOffset = dstBandOffsets[k];
434: for (int j = 0; j < dheight; j++) {
435: int srcPixelOffset = srcScanlineOffset;
436: int dstPixelOffset = dstScanlineOffset;
437:
438: for (int i = 0; i < dwidth; i++) {
439: int kernelVerticalOffset = 0;
440: int imageVerticalOffset = srcPixelOffset;
441: float f = Float.NEGATIVE_INFINITY;
442: for (int u = 0; u < kh; u++) {
443: int imageOffset = imageVerticalOffset;
444: for (int v = 0; v < kw; v++) {
445: float tmpIK = (int) srcData[imageOffset]
446: + kdata[kernelVerticalOffset + v];
447: if (tmpIK > f) {
448: f = tmpIK;
449: }
450: imageOffset += srcPixelStride;
451: }
452: kernelVerticalOffset += kw;
453: imageVerticalOffset += srcScanlineStride;
454: }
455:
456: dstData[dstPixelOffset] = (int) f;
457: srcPixelOffset += srcPixelStride;
458: dstPixelOffset += dstPixelStride;
459: }
460: srcScanlineOffset += srcScanlineStride;
461: dstScanlineOffset += dstScanlineStride;
462: }
463: }
464: }
465:
466: private void floatLoop(RasterAccessor src, RasterAccessor dst) {
467:
468: int dwidth = dst.getWidth();
469: int dheight = dst.getHeight();
470: int dnumBands = dst.getNumBands();
471:
472: int dstBandOffsets[] = dst.getBandOffsets();
473: int dstPixelStride = dst.getPixelStride();
474: int dstScanlineStride = dst.getScanlineStride();
475:
476: int srcBandOffsets[] = src.getBandOffsets();
477: int srcPixelStride = src.getPixelStride();
478: int srcScanlineStride = src.getScanlineStride();
479:
480: float dstDataArrays[][] = dst.getFloatDataArrays();
481: float srcDataArrays[][] = src.getFloatDataArrays();
482:
483: for (int k = 0; k < dnumBands; k++) {
484: float dstData[] = dstDataArrays[k];
485: float srcData[] = srcDataArrays[k];
486: int srcScanlineOffset = srcBandOffsets[k];
487: int dstScanlineOffset = dstBandOffsets[k];
488: for (int j = 0; j < dheight; j++) {
489: int srcPixelOffset = srcScanlineOffset;
490: int dstPixelOffset = dstScanlineOffset;
491:
492: for (int i = 0; i < dwidth; i++) {
493: int kernelVerticalOffset = 0;
494: int imageVerticalOffset = srcPixelOffset;
495: float f = Float.NEGATIVE_INFINITY;
496: for (int u = 0; u < kh; u++) {
497: int imageOffset = imageVerticalOffset;
498: for (int v = 0; v < kw; v++) {
499: float tmpIK = srcData[imageOffset]
500: + kdata[kernelVerticalOffset + v];
501: if (tmpIK > f) {
502: f = tmpIK;
503: }
504: imageOffset += srcPixelStride;
505: }
506: kernelVerticalOffset += kw;
507: imageVerticalOffset += srcScanlineStride;
508: }
509:
510: dstData[dstPixelOffset] = f;
511: srcPixelOffset += srcPixelStride;
512: dstPixelOffset += dstPixelStride;
513: }
514: srcScanlineOffset += srcScanlineStride;
515: dstScanlineOffset += dstScanlineStride;
516: }
517: }
518: }
519:
520: private void doubleLoop(RasterAccessor src, RasterAccessor dst) {
521:
522: int dwidth = dst.getWidth();
523: int dheight = dst.getHeight();
524: int dnumBands = dst.getNumBands();
525:
526: int dstBandOffsets[] = dst.getBandOffsets();
527: int dstPixelStride = dst.getPixelStride();
528: int dstScanlineStride = dst.getScanlineStride();
529:
530: int srcBandOffsets[] = src.getBandOffsets();
531: int srcPixelStride = src.getPixelStride();
532: int srcScanlineStride = src.getScanlineStride();
533:
534: double dstDataArrays[][] = dst.getDoubleDataArrays();
535: double srcDataArrays[][] = src.getDoubleDataArrays();
536:
537: for (int k = 0; k < dnumBands; k++) {
538: double dstData[] = dstDataArrays[k];
539: double srcData[] = srcDataArrays[k];
540: int srcScanlineOffset = srcBandOffsets[k];
541: int dstScanlineOffset = dstBandOffsets[k];
542: for (int j = 0; j < dheight; j++) {
543: int srcPixelOffset = srcScanlineOffset;
544: int dstPixelOffset = dstScanlineOffset;
545:
546: for (int i = 0; i < dwidth; i++) {
547: int kernelVerticalOffset = 0;
548: int imageVerticalOffset = srcPixelOffset;
549: double f = Double.NEGATIVE_INFINITY;
550: for (int u = 0; u < kh; u++) {
551: int imageOffset = imageVerticalOffset;
552: for (int v = 0; v < kw; v++) {
553: double tmpIK = srcData[imageOffset]
554: + kdata[kernelVerticalOffset + v];
555: if (tmpIK > f) {
556: f = tmpIK;
557: }
558: imageOffset += srcPixelStride;
559: }
560: kernelVerticalOffset += kw;
561: imageVerticalOffset += srcScanlineStride;
562: }
563:
564: dstData[dstPixelOffset] = f;
565: srcPixelOffset += srcPixelStride;
566: dstPixelOffset += dstPixelStride;
567: }
568: srcScanlineOffset += srcScanlineStride;
569: dstScanlineOffset += dstScanlineStride;
570: }
571: }
572: }
573: }
|