1 /* 2 * $RCSfile: BinarizeOpImage.java,v $ 3 * 4 * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. 5 * 6 * Use is subject to license terms. 7 * 8 * $Revision: 1.1 $ 9 * $Date: 2005/02/11 04:56:15 $ 10 * $State: Exp $ 11 */ 12 package com.lightcrafts.media.jai.opimage; 13 import java.awt.Rectangle; 14 import java.awt.image.DataBuffer; 15 import java.awt.image.Raster; 16 import java.awt.image.RenderedImage; 17 import java.awt.image.SampleModel; 18 import java.awt.image.MultiPixelPackedSampleModel; 19 import java.awt.image.ColorModel; 20 import java.awt.image.WritableRaster; 21 import java.util.Map; 22 import com.lightcrafts.mediax.jai.ImageLayout; 23 import com.lightcrafts.mediax.jai.PointOpImage; 24 import com.lightcrafts.mediax.jai.PixelAccessor; 25 import com.lightcrafts.mediax.jai.PackedImageData; 26 import com.lightcrafts.mediax.jai.UnpackedImageData; 27 import com.lightcrafts.media.jai.util.JDKWorkarounds; 28 import com.lightcrafts.media.jai.util.ImageUtil; 29 30 /** 31 * An <code>OpImage</code> implementing the "Binarize" operation as 32 * described in <code>com.lightcrafts.mediax.jai.operator.BinarizeDescriptor</code>. 33 * 34 * <p>This <code>OpImage</code> maps all the pixels of an image 35 * whose value falls within a given range to a constant on a per-band basis. 36 * Each of the lower bound, upper bound, and constant arrays may have only 37 * one value in it. If that is the case, that value is used for all bands. 38 * 39 * @see com.lightcrafts.mediax.jai.operator.BinarizeDescriptor 40 * @see BinarizeCRIF 41 * 42 * @since version 1.1 43 */ 44 final class BinarizeOpImage extends PointOpImage { 45 46 /** 47 * Lookup table for ORing bytes of output. 48 */ 49 private static byte[] byteTable = new byte[] { 50 (byte)0x80, (byte)0x40, (byte)0x20, (byte)0x10, 51 (byte)0x08, (byte)0x04, (byte)0x02, (byte)0x01, 52 }; 53 54 /** 55 * bitsOn[j + (i<<3)] 56 * sets bits on from i to j 57 */ 58 private static int[] bitsOn = null; 59 60 /** The threshold. */ 61 private double threshold; 62 63 /** 64 * Constructor. 65 * 66 * @param source The source image. 67 * @param layout The destination image layout. 68 * @param threshold The threshold value for binarization. 69 */ BinarizeOpImage(RenderedImage source, Map config, ImageLayout layout, double threshold)70 public BinarizeOpImage(RenderedImage source, 71 Map config, 72 ImageLayout layout, 73 double threshold) { 74 super(source, layoutHelper(source, layout, config), config, true); 75 76 if(source.getSampleModel().getNumBands() != 1) { 77 throw new IllegalArgumentException(JaiI18N.getString("BinarizeOpImage0")); 78 } 79 80 this.threshold = threshold; 81 } 82 83 // set the OpImage's SM to be MultiPixelPackedSampleModel layoutHelper(RenderedImage source, ImageLayout il, Map config)84 private static ImageLayout layoutHelper(RenderedImage source, 85 ImageLayout il, 86 Map config) { 87 88 ImageLayout layout = (il == null) ? 89 new ImageLayout() : (ImageLayout)il.clone(); 90 91 SampleModel sm = layout.getSampleModel(source); 92 if(!ImageUtil.isBinary(sm)) { 93 sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, 94 layout.getTileWidth(source), 95 layout.getTileHeight(source), 96 1); 97 layout.setSampleModel(sm); 98 } 99 100 ColorModel cm = layout.getColorModel(null); 101 if(cm == null || 102 !JDKWorkarounds.areCompatibleDataModels(sm, cm)) { 103 layout.setColorModel(ImageUtil.getCompatibleColorModel(sm, 104 config)); 105 } 106 107 return layout; 108 } 109 110 111 /** 112 * Map the pixels inside a specified rectangle whose value is within a 113 * rang to a constant on a per-band basis. 114 * 115 * @param sources Cobbled sources, guaranteed to provide all the 116 * source data necessary for computing the rectangle. 117 * @param dest The tile containing the rectangle to be computed. 118 * @param destRect The rectangle within the tile to be computed. 119 */ computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect)120 protected void computeRect(Raster[] sources, 121 WritableRaster dest, 122 Rectangle destRect) { 123 switch (sources[0].getSampleModel().getDataType()) { 124 case DataBuffer.TYPE_BYTE: 125 byteLoop(sources[0], dest, destRect); 126 break; 127 128 case DataBuffer.TYPE_SHORT: 129 shortLoop(sources[0], dest, destRect); 130 break; 131 case DataBuffer.TYPE_USHORT: 132 ushortLoop(sources[0], dest, destRect); 133 break; 134 case DataBuffer.TYPE_INT: 135 intLoop(sources[0], dest, destRect); 136 break; 137 138 case DataBuffer.TYPE_FLOAT: 139 floatLoop(sources[0], dest, destRect); 140 break; 141 case DataBuffer.TYPE_DOUBLE: 142 doubleLoop(sources[0], dest, destRect); 143 break; 144 145 default: 146 throw new RuntimeException(JaiI18N.getString("BinarizeOpImage1")); 147 } 148 } 149 byteLoop(Raster source, WritableRaster dest, Rectangle destRect)150 private void byteLoop(Raster source, 151 WritableRaster dest, 152 Rectangle destRect){ 153 154 if(threshold <= 0.0D){ 155 // every bit is 1 156 setTo1(dest, destRect); 157 return; 158 }else if (threshold > 255.0D){ 159 //every bit is zeros; 160 return; 161 } 162 163 short thresholdI = (short)Math.ceil(threshold); 164 // computation can be done in integer 165 // even though threshold is of double type 166 // int thresholdI = (int)Math.ceil(this.threshold); 167 // or through a lookup table for byte case 168 169 Rectangle srcRect = mapDestRect(destRect,0); // should be identical to destRect 170 171 PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null); 172 PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false); 173 int offset = pid.offset; 174 PixelAccessor srcPa = new PixelAccessor(source.getSampleModel(), null); 175 176 UnpackedImageData srcImD = srcPa.getPixels(source, srcRect, DataBuffer.TYPE_BYTE, false); 177 int srcOffset = srcImD.bandOffsets[0]; 178 byte[] srcData = ((byte[][])srcImD.data)[0]; 179 int pixelStride= srcImD.pixelStride; 180 181 int ind0 = pid.bitOffset; 182 for(int h = 0; h < destRect.height; h++){ 183 int indE = ind0 + destRect.width; 184 for(int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride){ 185 if((srcData[s]&0xFF) >= thresholdI) { 186 pid.data[offset + (b >> 3)] |= byteTable[b%8]; 187 } 188 } 189 offset += pid.lineStride; 190 srcOffset += srcImD.lineStride; 191 } 192 pa.setPackedPixels(pid); 193 } 194 195 196 // computation in short shortLoop(Raster source, WritableRaster dest, Rectangle destRect)197 private void shortLoop(Raster source, 198 WritableRaster dest, 199 Rectangle destRect){ 200 201 if(threshold <= Short.MIN_VALUE){ 202 // every bit is 1 203 setTo1(dest, destRect); 204 return; 205 }else if (threshold > Short.MAX_VALUE){ 206 //every bit is zeros; 207 return; 208 } 209 210 short thresholdS = (short)( Math.ceil(threshold)); 211 // computation can be done in integer 212 // even though threshold is of double type 213 // int thresholdI = (int)Math.ceil(this.threshold); 214 // or through a lookup table for byte case 215 216 Rectangle srcRect = mapDestRect(destRect,0); // should be identical to destRect 217 218 PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null); 219 PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false); 220 int offset = pid.offset; 221 PixelAccessor srcPa = new PixelAccessor(source.getSampleModel(), null); 222 223 UnpackedImageData srcImD = srcPa.getPixels(source, srcRect, DataBuffer.TYPE_SHORT, false); 224 int srcOffset = srcImD.bandOffsets[0]; 225 short[] srcData = ((short[][])srcImD.data)[0]; 226 int pixelStride= srcImD.pixelStride; 227 228 int ind0 = pid.bitOffset; 229 for(int h = 0; h < destRect.height; h++){ 230 int indE = ind0 + destRect.width; 231 for(int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride){ 232 if(srcData[s] >= thresholdS) { 233 pid.data[offset + (b >> 3)] |= byteTable[b%8]; 234 } 235 } 236 offset += pid.lineStride; 237 srcOffset += srcImD.lineStride; 238 } 239 pa.setPackedPixels(pid); 240 } 241 242 243 // computation in short ushortLoop(Raster source, WritableRaster dest, Rectangle destRect)244 private void ushortLoop(Raster source, 245 WritableRaster dest, 246 Rectangle destRect){ 247 248 if(threshold <= 0.0D){ 249 // every bit is 1 250 setTo1(dest, destRect); 251 return; 252 }else if (threshold > (double)(0xFFFF)){ 253 //every bit is zeros; 254 return; 255 } 256 257 int thresholdI = (int)( Math.ceil(threshold)); 258 // computation can be done in integer 259 // even though threshold is of double type 260 // int thresholdI = (int)Math.ceil(this.threshold); 261 // or through a lookup table for byte case 262 263 Rectangle srcRect = mapDestRect(destRect,0); // should be identical to destRect 264 265 PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null); 266 PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false); 267 int offset = pid.offset; 268 PixelAccessor srcPa = new PixelAccessor(source.getSampleModel(), null); 269 270 UnpackedImageData srcImD = srcPa.getPixels(source, srcRect, DataBuffer.TYPE_USHORT, false); 271 int srcOffset = srcImD.bandOffsets[0]; 272 short[] srcData = ((short[][])srcImD.data)[0]; 273 int pixelStride= srcImD.pixelStride; 274 275 int ind0 = pid.bitOffset; 276 for(int h = 0; h < destRect.height; h++){ 277 int indE = ind0 + destRect.width; 278 for(int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride){ 279 if((srcData[s]&0xFFFF) >= thresholdI) { 280 pid.data[offset + (b >> 3)] |= byteTable[b%8]; 281 } 282 } 283 offset += pid.lineStride; 284 srcOffset += srcImD.lineStride; 285 } 286 pa.setPackedPixels(pid); 287 } 288 289 290 intLoop(Raster source, WritableRaster dest, Rectangle destRect)291 private void intLoop(Raster source, 292 WritableRaster dest, 293 Rectangle destRect){ 294 295 if(threshold <= Integer.MIN_VALUE){ 296 // every bit is 1 297 setTo1(dest, destRect); 298 return; 299 }else if (threshold > (double)Integer.MAX_VALUE){ 300 //every bit is zeros; 301 return; 302 } 303 304 // computation can be done in integer 305 // even though threshold is of double type 306 int thresholdI = (int)Math.ceil(this.threshold); 307 308 // computation can be done in integer 309 // even though threshold is of double type 310 // int thresholdI = (int)Math.ceil(this.threshold); 311 312 Rectangle srcRect = mapDestRect(destRect,0); // should be identical to destRect 313 314 PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null); 315 PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false); 316 int offset = pid.offset; 317 PixelAccessor srcPa = new PixelAccessor(source.getSampleModel(), null); 318 319 UnpackedImageData srcImD = srcPa.getPixels(source, srcRect, DataBuffer.TYPE_INT, false); 320 int srcOffset = srcImD.bandOffsets[0]; 321 int[] srcData = ((int[][])srcImD.data)[0]; 322 int pixelStride= srcImD.pixelStride; 323 324 int ind0 = pid.bitOffset; 325 for(int h = 0; h < destRect.height; h++){ 326 int indE = ind0 + destRect.width; 327 for(int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride){ 328 if(srcData[s] >= threshold) { 329 pid.data[offset + (b >> 3)] |= byteTable[b%8]; 330 } 331 } 332 offset += pid.lineStride; 333 srcOffset += srcImD.lineStride; 334 } 335 pa.setPackedPixels(pid); 336 } 337 338 339 // computation in float floatLoop(Raster source, WritableRaster dest, Rectangle destRect)340 private void floatLoop(Raster source, 341 WritableRaster dest, 342 Rectangle destRect){ 343 344 Rectangle srcRect = mapDestRect(destRect,0); // should be identical to destRect 345 346 PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null); 347 PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false); 348 int offset = pid.offset; 349 PixelAccessor srcPa = new PixelAccessor(source.getSampleModel(), null); 350 351 UnpackedImageData srcImD = srcPa.getPixels(source, srcRect, DataBuffer.TYPE_FLOAT, false); 352 int srcOffset = srcImD.bandOffsets[0]; 353 float[] srcData = ((float[][])srcImD.data)[0]; 354 int pixelStride= srcImD.pixelStride; 355 356 int ind0 = pid.bitOffset; 357 for(int h = 0; h < destRect.height; h++){ 358 int indE = ind0 + destRect.width; 359 for(int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride){ 360 if (srcData[s]>threshold) { 361 pid.data[offset + (b >> 3)] |= byteTable[b%8]; 362 } 363 } 364 offset += pid.lineStride; 365 srcOffset += srcImD.lineStride; 366 } 367 pa.setPackedPixels(pid); 368 } 369 370 // computation in double doubleLoop(Raster source, WritableRaster dest, Rectangle destRect)371 private void doubleLoop(Raster source, 372 WritableRaster dest, 373 Rectangle destRect){ 374 375 Rectangle srcRect = mapDestRect(destRect,0); // should be identical to destRect 376 377 PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null); 378 PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false); 379 int offset = pid.offset; 380 PixelAccessor srcPa = new PixelAccessor(source.getSampleModel(), null); 381 382 UnpackedImageData srcImD = srcPa.getPixels(source, srcRect, DataBuffer.TYPE_DOUBLE, false); 383 int srcOffset = srcImD.bandOffsets[0]; 384 double[] srcData = ((double[][])srcImD.data)[0]; 385 int pixelStride= srcImD.pixelStride; 386 387 int ind0 = pid.bitOffset; 388 for(int h = 0; h < destRect.height; h++){ 389 int indE = ind0 + destRect.width; 390 for(int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride){ 391 if (srcData[s]>threshold) { 392 pid.data[offset + (b >> 3)] |= byteTable[b%8]; 393 } 394 } 395 offset += pid.lineStride; 396 srcOffset += srcImD.lineStride; 397 } 398 pa.setPackedPixels(pid); 399 } 400 401 // set all bits in a rectangular region to be 1 402 // need to be sure that paddings not changing setTo1(Raster dest, Rectangle destRect)403 private void setTo1(Raster dest, Rectangle destRect){ 404 initBitsOn(); 405 PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null); 406 PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false); 407 int offset = pid.offset; 408 409 for(int h = 0; h < destRect.height; h++){ 410 int ind0 = pid.bitOffset; 411 int indE = ind0 + destRect.width - 1; 412 if (indE < 8){ 413 // the entire row in data[offset] 414 pid.data[offset] = (byte)(pid.data[offset] | bitsOn[indE]); // (0<<3) + indE 415 }else{ 416 //1st byte 417 pid.data[offset] = (byte)(pid.data[offset] | bitsOn[7]); // (0<<3) + 7 418 //middle bytes 419 for(int b = offset + 1; b <= offset + (indE-7)/8; b++){ 420 pid.data[b] = (byte)(0xff); 421 } 422 //last byte 423 424 int remBits = indE % 8; 425 if(remBits % 8 != 7){ 426 indE = offset + indE/8; 427 pid.data[indE] = (byte)(pid.data[indE] | bitsOn[remBits]); // (0<<3)+remBits 428 } 429 } 430 offset += pid.lineStride; 431 } 432 pa.setPackedPixels(pid); 433 } 434 435 // setting bits i to j to 1; 436 // i <= j initBitsOn()437 private static synchronized void initBitsOn() { 438 439 if(bitsOn != null) 440 return; 441 442 bitsOn = new int[64]; 443 for(int i = 0; i < 8; i++){ 444 for(int j = i; j< 8; j++){ 445 int bi = (0x00ff) >> i; 446 int bj = (0x00ff) << (7-j); 447 bitsOn[j + (i<<3)] = bi & bj; 448 } 449 } 450 } 451 } 452