1 /* Copyright (C) 2005-2011 Fabio Riccardi */
2 
3 /*
4  * $RCSfile: ErodeOpImage.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:56:24 $
12  * $State: Exp $
13  */
14 package com.lightcrafts.jai.opimage;
15 
16 import java.awt.Rectangle;
17 import java.awt.image.DataBuffer;
18 import java.awt.image.Raster;
19 import java.awt.image.RenderedImage;
20 import java.awt.image.WritableRaster;
21 import com.lightcrafts.mediax.jai.AreaOpImage;
22 import com.lightcrafts.mediax.jai.BorderExtender;
23 import com.lightcrafts.mediax.jai.ImageLayout;
24 import com.lightcrafts.mediax.jai.KernelJAI;
25 import com.lightcrafts.mediax.jai.RasterAccessor;
26 import com.lightcrafts.mediax.jai.RasterFormatTag;
27 import java.util.Map;
28 // import com.lightcrafts.media.jai.test.OpImageTester;
29 
30 /**
31  *
32  * An OpImage class to perform erosion on a source image.
33  *
34  * <p> This class implements an erosion operation.
35  *
36  * <p> <b>Grey Scale Erosion</b>
37  * is a spatial operation that computes
38  * each output sample by subtract elements of a kernel to the samples
39  * surrounding a particular source sample with some care.
40  * A mathematical expression is:
41  *
42  * <p> For a kernel K with a key position (xKey, yKey), the erosion
43  * of image I at (x,y) is given by:
44  * <pre>
45  *     max{a:  a + K(xKey+i, yKey+j) <= I(x+i,y+j): all (i,j) }
46  *
47  *      all possible (i,j) means that both I(x+i,y+j) and K(xKey+i, yKey+j)
48  *      are in bounds. Otherwise, the value is set to 0.
49  *
50  * </pre>
51  * <p> Intuitively, the kernel is like an unbrella and the key point
52  * is the handle. At every point, you try to push the umbrella up as high
53  * as possible but still underneath the image surface. The final height
54  * of the handle is the value after erosion. Thus if you want the image
55  * to erode from the upper right to bottom left, the following would do.
56  *
57  * <p><center>
58  * <table border=1>
59  * <tr align=center><td>0</td><td>0</td><td>X</td> </tr>
60  * <tr align=center><td>0</td><td>X</td><td>0</td> </tr>
61  * <tr align=center><td><b>X</b></td><td>0</td><td>0</td> </tr>
62  * </table></center>
63  *
64  * <p> Note that zero kernel erosion has effects on the image, the
65  * location of the key position and size of kernel all matter.
66  *
67  * <p> Pseudo code for the erosion operation is as follows.
68  * Assuming the kernel K is of size M rows x N cols
69  * and the key position is (xKey, yKey).
70  *
71  * <pre>
72  *
73  * // erosion
74  * for every dst pixel location (x,y){
75  *    tmp = infinity;
76  *    for (i = -xKey; i < M - xKey; i++){
77  *       for (j = -yKey; j < N - yKey; j++){
78  *          if((x+i, y+j) are in bounds of src){
79  *             tmp = min{tmp, src[x + i][y + j] - K[xKey + i][yKey + j]};
80  *          }
81  *       }
82  *    }
83  *    dst[x][y] = tmp;
84  *    if (dst[x][y] == infinity)
85  *        dst[x][y] = 0;
86  * }
87  * </pre>
88  *
89  * <p> The kernel cannot be bigger in any dimension than the image data.
90  *
91  * <p> <b>Binary Image Erosion</b>
92  * requires the kernel to be binary as well.
93  * Intuitively, binary erosion slides the kernel
94  * key position and place it at every point (x,y) in the src image.
95  * The dst value at this position is set to 1 if all the kernel
96  * are fully supported by the src image, and the src image value is 1
97  * whenever the kernel has value 1.
98  * Otherwise, the value after erosion at (x,y) is set to 0.
99  * Erosion usually shrinks images, but it can fill holes
100  * with kernels like
101  * <pre> [1 <b>0</b> 1] </pre>
102  * and the key position at the center.
103  *
104  * <p> Pseudo code for the erosion operation is as follows.
105  *
106  * <pre>
107  * // erosion
108  * for every dst pixel location (x,y){
109  *    dst[x][y] = 1;
110  *    for (i = -xKey; i < M - xKey; i++){
111  *       for (j = -yKey; j < N - yKey; j++){
112  *         if((x+i,y+j) is out of bounds of src ||
113  *             src(x+i, y+j)==0 && Key(xKey+i, yKey+j)==1){
114  *            dst[x][y] = 0; break;
115  *          }
116  *       }
117  *    }
118  * }
119  * </pre>
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  *
126  * @see KernelJAI
127  */
128 final class LCErodeOpImage extends AreaOpImage {
129 
130     /**
131      * The kernel with which to do the erode operation.
132      */
133     protected KernelJAI kernel;
134 
135     /** Kernel variables. */
136     private int kw, kh, kx, ky;
137     private float[] kdata;
138     private int[] integerKdata;
139 
140     /**
141      * Creates a ErodeOpImage given a ParameterBlock containing the image
142      * source and pre-rotated erosion kernel.  The image dimensions are
143      * derived
144      * from the source image.  The tile grid layout, SampleModel, and
145      * ColorModel may optionally be specified by an ImageLayout
146      * object.
147      *
148      * @param source a RenderedImage.
149      * @param extender a BorderExtender, or null.
150      * @param layout an ImageLayout optionally containing the tile grid layout,
151      *        SampleModel, and ColorModel, or null.
152      * @param kernel the pre-rotated erosion KernelJAI.
153      */
LCErodeOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, KernelJAI kernel)154     public LCErodeOpImage(RenderedImage source,
155                            BorderExtender extender,
156                            Map config,
157                            ImageLayout layout,
158                            KernelJAI kernel) {
159 	super(source,
160               layout,
161               config,
162               true,
163               extender,
164               kernel.getLeftPadding(),
165               kernel.getRightPadding(),
166               kernel.getTopPadding(),
167               kernel.getBottomPadding());
168 
169 	this.kernel = kernel;
170 	kw = kernel.getWidth();
171 	kh = kernel.getHeight();
172 	kx = kernel.getXOrigin();
173 	ky = kernel.getYOrigin();
174 
175         kdata = kernel.getKernelData();
176         integerKdata = new int[kdata.length];
177         for (int i = 0; i < kdata.length; i++)
178             integerKdata[i] = kdata[i] == 0 ? 0 : 1;
179     }
180 
181     /**
182      * Performs erosion on a specified rectangle. The sources are
183      * cobbled.
184      *
185      * @param sources an array of source Rasters, guaranteed to provide all
186      *                necessary source data for computing the output.
187      * @param dest a WritableRaster tile containing the area to be computed.
188      * @param destRect the rectangle within dest to be processed.
189      */
computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect)190     protected void computeRect(Raster[] sources,
191                                WritableRaster dest,
192                                Rectangle destRect) {
193         // Retrieve format tags.
194         RasterFormatTag[] formatTags = getFormatTags();
195 
196         Raster source = sources[0];
197         Rectangle srcRect = mapDestRect(destRect, 0);
198 
199 
200         RasterAccessor srcAccessor =
201             new RasterAccessor(source, srcRect,
202                                formatTags[0], getSourceImage(0).getColorModel());
203         RasterAccessor dstAccessor =
204             new RasterAccessor(dest, destRect,
205                                formatTags[1], getColorModel());
206 
207         switch (dstAccessor.getDataType()) {
208         case DataBuffer.TYPE_BYTE:
209             byteLoop(srcAccessor, dstAccessor);
210             break;
211         case DataBuffer.TYPE_INT:
212             intLoop(srcAccessor, dstAccessor);
213             break;
214         case DataBuffer.TYPE_SHORT:
215             shortLoop(srcAccessor, dstAccessor);
216             break;
217         case DataBuffer.TYPE_USHORT:
218             ushortLoop(srcAccessor, dstAccessor);
219             break;
220         case DataBuffer.TYPE_FLOAT:
221             floatLoop(srcAccessor, dstAccessor);
222             break;
223         case DataBuffer.TYPE_DOUBLE:
224             doubleLoop(srcAccessor, dstAccessor);
225             break;
226 
227         default:
228         }
229 
230         // If the RasterAccessor object set up a temporary buffer for the
231         // op to write to, tell the RasterAccessor to write that data
232         // to the raster no that we're done with it.
233         if (dstAccessor.isDataCopy()) {
234             dstAccessor.clampDataArrays();
235             dstAccessor.copyDataToRaster();
236         }
237     }
byteLoop(RasterAccessor src, RasterAccessor dst)238     private void byteLoop(RasterAccessor src, RasterAccessor dst) {
239 
240         int dwidth    = dst.getWidth();
241         int dheight   = dst.getHeight();
242         int dnumBands = dst.getNumBands();
243 
244         int dstBandOffsets[]  = dst.getBandOffsets();
245         int dstPixelStride    = dst.getPixelStride();
246         int dstScanlineStride = dst.getScanlineStride();
247 
248         int srcBandOffsets[]  = src.getBandOffsets();
249         int srcPixelStride    = src.getPixelStride();
250         int srcScanlineStride = src.getScanlineStride();
251 
252         byte dstDataArrays[][] = dst.getByteDataArrays();
253         byte srcDataArrays[][] = src.getByteDataArrays();
254 
255         for (int k = 0; k < dnumBands; k++) {
256             byte dstData[] = dstDataArrays[k];
257             byte srcData[] = srcDataArrays[k];
258             int srcScanlineOffset = srcBandOffsets[k];
259             int dstScanlineOffset = dstBandOffsets[k];
260             for (int j = 0; j < dheight; j++) {
261                 int srcPixelOffset = srcScanlineOffset;
262                 int dstPixelOffset = dstScanlineOffset;
263 
264                 for (int i = 0; i < dwidth; i++) {
265                     int kernelVerticalOffset = 0;
266                     int imageVerticalOffset = srcPixelOffset;
267 
268                     int val = (int) srcData[imageVerticalOffset + srcScanlineStride * ky + srcPixelStride * kx] & 0xff;
269 
270                     label: if (val > 0) {
271                         for (int u = 0; u < kh; u++) {
272                             int imageOffset = imageVerticalOffset;
273                             for (int v = 0; v < kw; v++) {
274                                 if (((int) srcData[imageOffset] & 0xff) == 0 && integerKdata[kernelVerticalOffset + v] != 0) {
275                                     val = 0;
276                                     break label;
277                                 }
278                                 imageOffset += srcPixelStride;
279                             }
280                             kernelVerticalOffset += kw;
281                             imageVerticalOffset += srcScanlineStride;
282                         }
283                     }
284 
285                     dstData[dstPixelOffset] = (byte) val;
286                     srcPixelOffset += srcPixelStride;
287                     dstPixelOffset += dstPixelStride;
288                 }
289                 srcScanlineOffset += srcScanlineStride;
290                 dstScanlineOffset += dstScanlineStride;
291             }
292         }
293     }
294 
295 
shortLoop(RasterAccessor src, RasterAccessor dst)296     private void shortLoop(RasterAccessor src, RasterAccessor dst) {
297 
298         int dwidth    = dst.getWidth();
299         int dheight   = dst.getHeight();
300         int dnumBands = dst.getNumBands();
301 
302         int dstBandOffsets[]  = dst.getBandOffsets();
303         int dstPixelStride    = dst.getPixelStride();
304         int dstScanlineStride = dst.getScanlineStride();
305 
306         int srcBandOffsets[]  = src.getBandOffsets();
307         int srcPixelStride    = src.getPixelStride();
308         int srcScanlineStride = src.getScanlineStride();
309 
310         short dstDataArrays[][] = dst.getShortDataArrays();
311         short srcDataArrays[][] = src.getShortDataArrays();
312 
313         for (int k = 0; k < dnumBands; k++)  {
314             short dstData[] = dstDataArrays[k];
315             short srcData[] = srcDataArrays[k];
316             int srcScanlineOffset = srcBandOffsets[k];
317             int dstScanlineOffset = dstBandOffsets[k];
318             for (int j = 0; j < dheight; j++)  {
319                 int srcPixelOffset = srcScanlineOffset;
320                 int dstPixelOffset = dstScanlineOffset;
321 
322                 for (int i = 0; i < dwidth; i++)  {
323                     int kernelVerticalOffset = 0;
324                     int imageVerticalOffset = srcPixelOffset;
325 		    float f = Float.POSITIVE_INFINITY;
326 		      for (int u = 0; u < kh; u++)  {
327                         int imageOffset = imageVerticalOffset;
328                         for (int v = 0; v < kw; v++)  {
329 			    float tmpIK = srcData[imageOffset] -
330 			      kdata[kernelVerticalOffset + v];
331 			    if(tmpIK < f){
332 			      f = tmpIK;
333 			    }
334 			    imageOffset += srcPixelStride;
335                         }
336                         kernelVerticalOffset += kw;
337                         imageVerticalOffset += srcScanlineStride;
338 		      }
339 		      if (Float.isInfinite(f)){
340 			 f = 0.0F;
341 		      }
342                     int val  = (int)f;
343                     if (val < Short.MIN_VALUE)  {
344                         val = Short.MIN_VALUE;
345                     } else if (val > Short.MAX_VALUE)  {
346                         val = Short.MAX_VALUE;
347                     }
348                     dstData[dstPixelOffset] = (short)val;
349                     srcPixelOffset += srcPixelStride;
350                     dstPixelOffset += dstPixelStride;
351                 }
352                 srcScanlineOffset += srcScanlineStride;
353                 dstScanlineOffset += dstScanlineStride;
354             }
355         }
356     }
357 
358 
ushortLoop(RasterAccessor src, RasterAccessor dst)359     private void ushortLoop(RasterAccessor src, RasterAccessor dst) {
360 
361         int dwidth    = dst.getWidth();
362         int dheight   = dst.getHeight();
363         int dnumBands = dst.getNumBands();
364 
365         int dstBandOffsets[]  = dst.getBandOffsets();
366         int dstPixelStride    = dst.getPixelStride();
367         int dstScanlineStride = dst.getScanlineStride();
368 
369         int srcBandOffsets[]  = src.getBandOffsets();
370         int srcPixelStride    = src.getPixelStride();
371         int srcScanlineStride = src.getScanlineStride();
372 
373         short dstDataArrays[][] = dst.getShortDataArrays();
374         short srcDataArrays[][] = src.getShortDataArrays();
375 
376         for (int k = 0; k < dnumBands; k++)  {
377             short dstData[] = dstDataArrays[k];
378             short srcData[] = srcDataArrays[k];
379             int srcScanlineOffset = srcBandOffsets[k];
380             int dstScanlineOffset = dstBandOffsets[k];
381             for (int j = 0; j < dheight; j++)  {
382                 int srcPixelOffset = srcScanlineOffset;
383                 int dstPixelOffset = dstScanlineOffset;
384 
385                 for (int i = 0; i < dwidth; i++)  {
386                     int kernelVerticalOffset = 0;
387                     int imageVerticalOffset = srcPixelOffset;
388 		    float f = Float.POSITIVE_INFINITY;
389 		      for (int u = 0; u < kh; u++)  {
390                         int imageOffset = imageVerticalOffset;
391                         for (int v = 0; v < kw; v++)  {
392 			    float tmpIK = (srcData[imageOffset] &  0xffff) -
393 			      kdata[kernelVerticalOffset + v];
394 			    if(tmpIK < f){
395 			      f = tmpIK;
396 			    }
397 			    imageOffset += srcPixelStride;
398                         }
399                         kernelVerticalOffset += kw;
400                         imageVerticalOffset += srcScanlineStride;
401 		      }
402 		      if (Float.isInfinite(f)){
403 			 f = 0.0F;
404 		      }
405                     int val  = (int)f;
406                     if (val < 0)  {
407                         val = 0;
408                     } else if (val > 0xffff)  {
409                         val = 0xffff;
410                     }
411                     dstData[dstPixelOffset] = (short)val;
412                     srcPixelOffset += srcPixelStride;
413                     dstPixelOffset += dstPixelStride;
414                 }
415                 srcScanlineOffset += srcScanlineStride;
416                 dstScanlineOffset += dstScanlineStride;
417             }
418         }
419     }
420 
intLoop(RasterAccessor src, RasterAccessor dst)421     private void intLoop(RasterAccessor src, RasterAccessor dst) {
422 
423         int dwidth    = dst.getWidth();
424         int dheight   = dst.getHeight();
425         int dnumBands = dst.getNumBands();
426 
427         int dstBandOffsets[]  = dst.getBandOffsets();
428         int dstPixelStride    = dst.getPixelStride();
429         int dstScanlineStride = dst.getScanlineStride();
430 
431         int srcBandOffsets[]  = src.getBandOffsets();
432         int srcPixelStride    = src.getPixelStride();
433         int srcScanlineStride = src.getScanlineStride();
434 
435         int dstDataArrays[][] = dst.getIntDataArrays();
436         int srcDataArrays[][] = src.getIntDataArrays();
437 
438         for (int k = 0; k < dnumBands; k++)  {
439             int dstData[] = dstDataArrays[k];
440             int srcData[] = srcDataArrays[k];
441             int srcScanlineOffset = srcBandOffsets[k];
442             int dstScanlineOffset = dstBandOffsets[k];
443             for (int j = 0; j < dheight; j++)  {
444                 int srcPixelOffset = srcScanlineOffset;
445                 int dstPixelOffset = dstScanlineOffset;
446 
447                 for (int i = 0; i < dwidth; i++)  {
448                     int kernelVerticalOffset = 0;
449                     int imageVerticalOffset = srcPixelOffset;
450 		    float f = Float.POSITIVE_INFINITY;
451 		      for (int u = 0; u < kh; u++)  {
452                         int imageOffset = imageVerticalOffset;
453                         for (int v = 0; v < kw; v++)  {
454 			    float tmpIK = (int)srcData[imageOffset] -
455 			      kdata[kernelVerticalOffset + v];
456 			    if(tmpIK < f){
457 			      f = tmpIK;
458 			    }
459 			    imageOffset += srcPixelStride;
460                         }
461                         kernelVerticalOffset += kw;
462                         imageVerticalOffset += srcScanlineStride;
463 		      }
464                     if (Float.isInfinite(f)){
465 		      f = 0.0F;
466 		    }
467                     dstData[dstPixelOffset] = (int)f;
468                     srcPixelOffset += srcPixelStride;
469                     dstPixelOffset += dstPixelStride;
470                 }
471                 srcScanlineOffset += srcScanlineStride;
472                 dstScanlineOffset += dstScanlineStride;
473             }
474         }
475     }
476 
floatLoop(RasterAccessor src, RasterAccessor dst)477     private void floatLoop(RasterAccessor src, RasterAccessor dst) {
478 
479         int dwidth    = dst.getWidth();
480         int dheight   = dst.getHeight();
481         int dnumBands = dst.getNumBands();
482 
483         int dstBandOffsets[]  = dst.getBandOffsets();
484         int dstPixelStride    = dst.getPixelStride();
485         int dstScanlineStride = dst.getScanlineStride();
486 
487         int srcBandOffsets[]  = src.getBandOffsets();
488         int srcPixelStride    = src.getPixelStride();
489         int srcScanlineStride = src.getScanlineStride();
490 
491         float dstDataArrays[][] = dst.getFloatDataArrays();
492         float srcDataArrays[][] = src.getFloatDataArrays();
493 
494         for (int k = 0; k < dnumBands; k++)  {
495             float dstData[] = dstDataArrays[k];
496             float srcData[] = srcDataArrays[k];
497             int srcScanlineOffset = srcBandOffsets[k];
498             int dstScanlineOffset = dstBandOffsets[k];
499             for (int j = 0; j < dheight; j++)  {
500                 int srcPixelOffset = srcScanlineOffset;
501                 int dstPixelOffset = dstScanlineOffset;
502 
503                 for (int i = 0; i < dwidth; i++)  {
504                     int kernelVerticalOffset = 0;
505                     int imageVerticalOffset = srcPixelOffset;
506 		    float f = Float.POSITIVE_INFINITY;
507 		      for (int u = 0; u < kh; u++)  {
508                         int imageOffset = imageVerticalOffset;
509                         for (int v = 0; v < kw; v++)  {
510 			    float tmpIK = srcData[imageOffset] -
511 			      kdata[kernelVerticalOffset + v];
512 			    if(tmpIK < f){
513 			      f = tmpIK;
514 			    }
515 			    imageOffset += srcPixelStride;
516                         }
517                         kernelVerticalOffset += kw;
518                         imageVerticalOffset += srcScanlineStride;
519 		      }
520 		      if (Float.isInfinite(f)){
521      			 f = 0.0F;
522 		      }
523                     dstData[dstPixelOffset] = f;
524                     srcPixelOffset += srcPixelStride;
525                     dstPixelOffset += dstPixelStride;
526                 }
527                 srcScanlineOffset += srcScanlineStride;
528                 dstScanlineOffset += dstScanlineStride;
529             }
530         }
531     }
532 
533 
doubleLoop(RasterAccessor src, RasterAccessor dst)534     private void doubleLoop(RasterAccessor src, RasterAccessor dst) {
535 
536         int dwidth    = dst.getWidth();
537         int dheight   = dst.getHeight();
538         int dnumBands = dst.getNumBands();
539 
540         int dstBandOffsets[]  = dst.getBandOffsets();
541         int dstPixelStride    = dst.getPixelStride();
542         int dstScanlineStride = dst.getScanlineStride();
543 
544         int srcBandOffsets[]  = src.getBandOffsets();
545         int srcPixelStride    = src.getPixelStride();
546         int srcScanlineStride = src.getScanlineStride();
547 
548         double dstDataArrays[][] = dst.getDoubleDataArrays();
549         double srcDataArrays[][] = src.getDoubleDataArrays();
550 
551         for (int k = 0; k < dnumBands; k++)  {
552             double dstData[] = dstDataArrays[k];
553             double srcData[] = srcDataArrays[k];
554             int srcScanlineOffset = srcBandOffsets[k];
555             int dstScanlineOffset = dstBandOffsets[k];
556             for (int j = 0; j < dheight; j++)  {
557                 int srcPixelOffset = srcScanlineOffset;
558                 int dstPixelOffset = dstScanlineOffset;
559 
560                 for (int i = 0; i < dwidth; i++)  {
561                     int kernelVerticalOffset = 0;
562                     int imageVerticalOffset = srcPixelOffset;
563 		    double f = Double.POSITIVE_INFINITY;
564 		      for (int u = 0; u < kh; u++)  {
565                         int imageOffset = imageVerticalOffset;
566                         for (int v = 0; v < kw; v++)  {
567 			    double tmpIK = srcData[imageOffset] -
568 			      kdata[kernelVerticalOffset + v];
569 			    if(tmpIK < f){
570 			      f = tmpIK;
571 			    }
572 			    imageOffset += srcPixelStride;
573                         }
574                         kernelVerticalOffset += kw;
575                         imageVerticalOffset += srcScanlineStride;
576 		      }
577 
578 		      if (Double.isInfinite(f)){
579 			f = 0.0D;
580 		      }
581                     dstData[dstPixelOffset] = f;
582                     srcPixelOffset += srcPixelStride;
583                     dstPixelOffset += dstPixelStride;
584                 }
585                 srcScanlineOffset += srcScanlineStride;
586                 dstScanlineOffset += dstScanlineStride;
587             }
588         }
589     }
590 }
591