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