001: /*
002: * $RCSfile: BinarizeOpImage.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:15 $
010: * $State: Exp $
011: */
012: package com.sun.media.jai.opimage;
013:
014: import java.awt.Rectangle;
015: import java.awt.color.ColorSpace;
016: import java.awt.image.DataBuffer;
017: import java.awt.image.Raster;
018: import java.awt.image.RenderedImage;
019: import java.awt.image.SampleModel;
020: import java.awt.image.MultiPixelPackedSampleModel;
021: import java.awt.image.ColorModel;
022: import java.awt.image.PackedColorModel;
023: import java.awt.image.WritableRaster;
024: import java.util.Map;
025: import javax.media.jai.ImageLayout;
026: import javax.media.jai.OpImage;
027: import javax.media.jai.PlanarImage;
028: import javax.media.jai.PointOpImage;
029: import javax.media.jai.PixelAccessor;
030: import javax.media.jai.PackedImageData;
031: import javax.media.jai.UnpackedImageData;
032: import com.sun.media.jai.util.JDKWorkarounds;
033: import com.sun.media.jai.util.ImageUtil;
034:
035: /**
036: * An <code>OpImage</code> implementing the "Binarize" operation as
037: * described in <code>javax.media.jai.operator.BinarizeDescriptor</code>.
038: *
039: * <p>This <code>OpImage</code> maps all the pixels of an image
040: * whose value falls within a given range to a constant on a per-band basis.
041: * Each of the lower bound, upper bound, and constant arrays may have only
042: * one value in it. If that is the case, that value is used for all bands.
043: *
044: * @see javax.media.jai.operator.BinarizeDescriptor
045: * @see BinarizeCRIF
046: *
047: * @since version 1.1
048: */
049: final class BinarizeOpImage extends PointOpImage {
050:
051: /**
052: * Lookup table for ORing bytes of output.
053: */
054: private static byte[] byteTable = new byte[] { (byte) 0x80,
055: (byte) 0x40, (byte) 0x20, (byte) 0x10, (byte) 0x08,
056: (byte) 0x04, (byte) 0x02, (byte) 0x01, };
057:
058: /**
059: * bitsOn[j + (i<<3)]
060: * sets bits on from i to j
061: */
062: private static int[] bitsOn = null;
063:
064: /** The threshold. */
065: private double threshold;
066:
067: /**
068: * Constructor.
069: *
070: * @param source The source image.
071: * @param layout The destination image layout.
072: * @param threshold The threshold value for binarization.
073: */
074: public BinarizeOpImage(RenderedImage source, Map config,
075: ImageLayout layout, double threshold) {
076: super (source, layoutHelper(source, layout, config), config,
077: true);
078:
079: if (source.getSampleModel().getNumBands() != 1) {
080: throw new IllegalArgumentException(JaiI18N
081: .getString("BinarizeOpImage0"));
082: }
083:
084: this .threshold = threshold;
085: }
086:
087: // set the OpImage's SM to be MultiPixelPackedSampleModel
088: private static ImageLayout layoutHelper(RenderedImage source,
089: ImageLayout il, Map config) {
090:
091: ImageLayout layout = (il == null) ? new ImageLayout()
092: : (ImageLayout) il.clone();
093:
094: SampleModel sm = layout.getSampleModel(source);
095: if (!ImageUtil.isBinary(sm)) {
096: sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
097: layout.getTileWidth(source), layout
098: .getTileHeight(source), 1);
099: layout.setSampleModel(sm);
100: }
101:
102: ColorModel cm = layout.getColorModel(null);
103: if (cm == null
104: || !JDKWorkarounds.areCompatibleDataModels(sm, cm)) {
105: layout.setColorModel(ImageUtil.getCompatibleColorModel(sm,
106: config));
107: }
108:
109: return layout;
110: }
111:
112: /**
113: * Map the pixels inside a specified rectangle whose value is within a
114: * rang to a constant on a per-band basis.
115: *
116: * @param sources Cobbled sources, guaranteed to provide all the
117: * source data necessary for computing the rectangle.
118: * @param dest The tile containing the rectangle to be computed.
119: * @param destRect The rectangle within the tile to be computed.
120: */
121: protected void computeRect(Raster[] sources, 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
147: .getString("BinarizeOpImage1"));
148: }
149: }
150:
151: private void byteLoop(Raster source, 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(),
172: null);
173: PackedImageData pid = pa.getPackedPixels(dest, destRect, true,
174: false);
175: int offset = pid.offset;
176: PixelAccessor srcPa = new PixelAccessor(
177: source.getSampleModel(), null);
178:
179: UnpackedImageData srcImD = srcPa.getPixels(source, srcRect,
180: DataBuffer.TYPE_BYTE, false);
181: int srcOffset = srcImD.bandOffsets[0];
182: byte[] srcData = ((byte[][]) srcImD.data)[0];
183: int pixelStride = srcImD.pixelStride;
184:
185: int ind0 = pid.bitOffset;
186: for (int h = 0; h < destRect.height; h++) {
187: int indE = ind0 + destRect.width;
188: for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
189: if ((srcData[s] & 0xFF) >= thresholdI) {
190: pid.data[offset + (b >> 3)] |= byteTable[b % 8];
191: }
192: }
193: offset += pid.lineStride;
194: srcOffset += srcImD.lineStride;
195: }
196: pa.setPackedPixels(pid);
197: }
198:
199: // computation in short
200: private void shortLoop(Raster source, WritableRaster dest,
201: Rectangle destRect) {
202:
203: if (threshold <= Short.MIN_VALUE) {
204: // every bit is 1
205: setTo1(dest, destRect);
206: return;
207: } else if (threshold > Short.MAX_VALUE) {
208: //every bit is zeros;
209: return;
210: }
211:
212: short thresholdS = (short) (Math.ceil(threshold));
213: // computation can be done in integer
214: // even though threshold is of double type
215: // int thresholdI = (int)Math.ceil(this.threshold);
216: // or through a lookup table for byte case
217:
218: Rectangle srcRect = mapDestRect(destRect, 0); // should be identical to destRect
219:
220: PixelAccessor pa = new PixelAccessor(dest.getSampleModel(),
221: null);
222: PackedImageData pid = pa.getPackedPixels(dest, destRect, true,
223: false);
224: int offset = pid.offset;
225: PixelAccessor srcPa = new PixelAccessor(
226: source.getSampleModel(), null);
227:
228: UnpackedImageData srcImD = srcPa.getPixels(source, srcRect,
229: DataBuffer.TYPE_SHORT, false);
230: int srcOffset = srcImD.bandOffsets[0];
231: short[] srcData = ((short[][]) srcImD.data)[0];
232: int pixelStride = srcImD.pixelStride;
233:
234: int ind0 = pid.bitOffset;
235: for (int h = 0; h < destRect.height; h++) {
236: int indE = ind0 + destRect.width;
237: for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
238: if (srcData[s] >= thresholdS) {
239: pid.data[offset + (b >> 3)] |= byteTable[b % 8];
240: }
241: }
242: offset += pid.lineStride;
243: srcOffset += srcImD.lineStride;
244: }
245: pa.setPackedPixels(pid);
246: }
247:
248: // computation in short
249: private void ushortLoop(Raster source, WritableRaster dest,
250: Rectangle destRect) {
251:
252: if (threshold <= 0.0D) {
253: // every bit is 1
254: setTo1(dest, destRect);
255: return;
256: } else if (threshold > (double) (0xFFFF)) {
257: //every bit is zeros;
258: return;
259: }
260:
261: int thresholdI = (int) (Math.ceil(threshold));
262: // computation can be done in integer
263: // even though threshold is of double type
264: // int thresholdI = (int)Math.ceil(this.threshold);
265: // or through a lookup table for byte case
266:
267: Rectangle srcRect = mapDestRect(destRect, 0); // should be identical to destRect
268:
269: PixelAccessor pa = new PixelAccessor(dest.getSampleModel(),
270: null);
271: PackedImageData pid = pa.getPackedPixels(dest, destRect, true,
272: false);
273: int offset = pid.offset;
274: PixelAccessor srcPa = new PixelAccessor(
275: source.getSampleModel(), null);
276:
277: UnpackedImageData srcImD = srcPa.getPixels(source, srcRect,
278: DataBuffer.TYPE_USHORT, false);
279: int srcOffset = srcImD.bandOffsets[0];
280: short[] srcData = ((short[][]) srcImD.data)[0];
281: int pixelStride = srcImD.pixelStride;
282:
283: int ind0 = pid.bitOffset;
284: for (int h = 0; h < destRect.height; h++) {
285: int indE = ind0 + destRect.width;
286: for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
287: if ((srcData[s] & 0xFFFF) >= thresholdI) {
288: pid.data[offset + (b >> 3)] |= byteTable[b % 8];
289: }
290: }
291: offset += pid.lineStride;
292: srcOffset += srcImD.lineStride;
293: }
294: pa.setPackedPixels(pid);
295: }
296:
297: private void intLoop(Raster source, WritableRaster dest,
298: Rectangle destRect) {
299:
300: if (threshold <= Integer.MIN_VALUE) {
301: // every bit is 1
302: setTo1(dest, destRect);
303: return;
304: } else if (threshold > (double) Integer.MAX_VALUE) {
305: //every bit is zeros;
306: return;
307: }
308:
309: // computation can be done in integer
310: // even though threshold is of double type
311: int thresholdI = (int) Math.ceil(this .threshold);
312:
313: // computation can be done in integer
314: // even though threshold is of double type
315: // int thresholdI = (int)Math.ceil(this.threshold);
316:
317: Rectangle srcRect = mapDestRect(destRect, 0); // should be identical to destRect
318:
319: PixelAccessor pa = new PixelAccessor(dest.getSampleModel(),
320: null);
321: PackedImageData pid = pa.getPackedPixels(dest, destRect, true,
322: false);
323: int offset = pid.offset;
324: PixelAccessor srcPa = new PixelAccessor(
325: source.getSampleModel(), null);
326:
327: UnpackedImageData srcImD = srcPa.getPixels(source, srcRect,
328: DataBuffer.TYPE_INT, false);
329: int srcOffset = srcImD.bandOffsets[0];
330: int[] srcData = ((int[][]) srcImD.data)[0];
331: int pixelStride = srcImD.pixelStride;
332:
333: int ind0 = pid.bitOffset;
334: for (int h = 0; h < destRect.height; h++) {
335: int indE = ind0 + destRect.width;
336: for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
337: if (srcData[s] >= threshold) {
338: pid.data[offset + (b >> 3)] |= byteTable[b % 8];
339: }
340: }
341: offset += pid.lineStride;
342: srcOffset += srcImD.lineStride;
343: }
344: pa.setPackedPixels(pid);
345: }
346:
347: // computation in float
348: private void floatLoop(Raster source, WritableRaster dest,
349: Rectangle destRect) {
350:
351: Rectangle srcRect = mapDestRect(destRect, 0); // should be identical to destRect
352:
353: PixelAccessor pa = new PixelAccessor(dest.getSampleModel(),
354: null);
355: PackedImageData pid = pa.getPackedPixels(dest, destRect, true,
356: false);
357: int offset = pid.offset;
358: PixelAccessor srcPa = new PixelAccessor(
359: source.getSampleModel(), null);
360:
361: UnpackedImageData srcImD = srcPa.getPixels(source, srcRect,
362: DataBuffer.TYPE_FLOAT, false);
363: int srcOffset = srcImD.bandOffsets[0];
364: float[] srcData = ((float[][]) srcImD.data)[0];
365: int pixelStride = srcImD.pixelStride;
366:
367: int ind0 = pid.bitOffset;
368: for (int h = 0; h < destRect.height; h++) {
369: int indE = ind0 + destRect.width;
370: for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
371: if (srcData[s] > threshold) {
372: pid.data[offset + (b >> 3)] |= byteTable[b % 8];
373: }
374: }
375: offset += pid.lineStride;
376: srcOffset += srcImD.lineStride;
377: }
378: pa.setPackedPixels(pid);
379: }
380:
381: // computation in double
382: private void doubleLoop(Raster source, WritableRaster dest,
383: Rectangle destRect) {
384:
385: Rectangle srcRect = mapDestRect(destRect, 0); // should be identical to destRect
386:
387: PixelAccessor pa = new PixelAccessor(dest.getSampleModel(),
388: null);
389: PackedImageData pid = pa.getPackedPixels(dest, destRect, true,
390: false);
391: int offset = pid.offset;
392: PixelAccessor srcPa = new PixelAccessor(
393: source.getSampleModel(), null);
394:
395: UnpackedImageData srcImD = srcPa.getPixels(source, srcRect,
396: DataBuffer.TYPE_DOUBLE, false);
397: int srcOffset = srcImD.bandOffsets[0];
398: double[] srcData = ((double[][]) srcImD.data)[0];
399: int pixelStride = srcImD.pixelStride;
400:
401: int ind0 = pid.bitOffset;
402: for (int h = 0; h < destRect.height; h++) {
403: int indE = ind0 + destRect.width;
404: for (int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride) {
405: if (srcData[s] > threshold) {
406: pid.data[offset + (b >> 3)] |= byteTable[b % 8];
407: }
408: }
409: offset += pid.lineStride;
410: srcOffset += srcImD.lineStride;
411: }
412: pa.setPackedPixels(pid);
413: }
414:
415: // set all bits in a rectangular region to be 1
416: // need to be sure that paddings not changing
417: private void setTo1(Raster dest, Rectangle destRect) {
418: initBitsOn();
419: PixelAccessor pa = new PixelAccessor(dest.getSampleModel(),
420: null);
421: PackedImageData pid = pa.getPackedPixels(dest, destRect, true,
422: false);
423: int offset = pid.offset;
424:
425: for (int h = 0; h < destRect.height; h++) {
426: int ind0 = pid.bitOffset;
427: int indE = ind0 + destRect.width - 1;
428: if (indE < 8) {
429: // the entire row in data[offset]
430: pid.data[offset] = (byte) (pid.data[offset] | bitsOn[indE]); // (0<<3) + indE
431: } else {
432: //1st byte
433: pid.data[offset] = (byte) (pid.data[offset] | bitsOn[7]); // (0<<3) + 7
434: //middle bytes
435: for (int b = offset + 1; b <= offset + (indE - 7) / 8; b++) {
436: pid.data[b] = (byte) (0xff);
437: }
438: //last byte
439:
440: int remBits = indE % 8;
441: if (remBits % 8 != 7) {
442: indE = offset + indE / 8;
443: pid.data[indE] = (byte) (pid.data[indE] | bitsOn[remBits]); // (0<<3)+remBits
444: }
445: }
446: offset += pid.lineStride;
447: }
448: pa.setPackedPixels(pid);
449: }
450:
451: // setting bits i to j to 1;
452: // i <= j
453: private static synchronized void initBitsOn() {
454:
455: if (bitsOn != null)
456: return;
457:
458: bitsOn = new int[64];
459: for (int i = 0; i < 8; i++) {
460: for (int j = i; j < 8; j++) {
461: int bi = (0x00ff) >> i;
462: int bj = (0x00ff) << (7 - j);
463: bitsOn[j + (i << 3)] = bi & bj;
464: }
465: }
466: }
467: }
|