001: /*
002: * $RCSfile: PiecewiseOpImage.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:40 $
010: * $State: Exp $
011: */
012: package com.sun.media.jai.opimage;
013:
014: import javax.media.jai.ColormapOpImage;
015: import java.awt.Rectangle;
016: import java.awt.image.DataBuffer;
017: import java.awt.image.Raster;
018: import java.awt.image.RenderedImage;
019: import java.awt.image.WritableRaster;
020: import javax.media.jai.Histogram;
021: import javax.media.jai.ImageLayout;
022: import javax.media.jai.LookupTableJAI;
023: import javax.media.jai.RasterAccessor;
024: import javax.media.jai.RasterFormatTag;
025: import java.util.Map;
026: import com.sun.media.jai.util.ImageUtil;
027:
028: /**
029: * An <code>OpImage</code> implementing the "Piecewise" operation.
030: *
031: * <p> The "Piecewise" operation maps the pixel values of an image using
032: * a piecewise linear function represented by a set of breakpoints for each
033: * band. The abscissa of each breakpoint is the source image gray level for
034: * the band in question and the ordinate is the destination image gray level
035: * to which it is mapped.
036: *
037: * @see javax.media.jai.operator.PiecewiseDescriptor
038: * @see PiecewiseCRIF
039: *
040: *
041: * @since EA4
042: */
043: final class PiecewiseOpImage extends ColormapOpImage {
044: /** The abscissas of the breakpoints. */
045: private float[][] abscissas;
046:
047: /** The slope at each abscissa. */
048: private float[][] slopes;
049:
050: /** The intercept at each abscissa. */
051: private float[][] intercepts;
052:
053: /** The minimum values of the output per band. */
054: private float[] minOrdinates;
055:
056: /** The maximum values of the output per band. */
057: private float[] maxOrdinates;
058:
059: /** Flag indicating byte data. */
060: private boolean isByteData = false;
061:
062: /** A lookup table for use in the case of byte data. */
063: private LookupTableJAI lut;
064:
065: /**
066: * Find the ordinate value for a given abscissa value.
067: *
068: * @param x The abscissa array.
069: * @param minValue The minimum source gray level in the breakpoint set.
070: * @param maxValue The maximum source gray level in the breakpoint set.
071: * @param a The array of piecewise slopes.
072: * @param b The array of piecewise ordinate intercepts.
073: * @param value The source gray level.
074: * @return The destination gray level.
075: */
076: private static float binarySearch(float[] x, float minValue,
077: float maxValue, float[] a, float[] b, float value) {
078: int highIndex = x.length - 1;
079:
080: if (value <= x[0]) {
081: return minValue;
082: } else if (value >= x[highIndex]) {
083: return maxValue;
084: }
085:
086: int lowIndex = 0;
087: int deltaIndex = highIndex - lowIndex;
088:
089: while (deltaIndex > 1) {
090: int meanIndex = lowIndex + deltaIndex / 2;
091: if (value >= x[meanIndex]) {
092: lowIndex = meanIndex;
093: } else {
094: highIndex = meanIndex;
095: }
096: deltaIndex = highIndex - lowIndex;
097: }
098:
099: return a[lowIndex] * value + b[lowIndex];
100: }
101:
102: /**
103: * Constructor.
104: *
105: * @param source The source image.
106: * @param layout The destination image layout.
107: * @param breakpoints The piecewise mapping stored by reference. The
108: * arrays breakpoints[b][0] and breakpoints[b][1] represent the abscissas
109: * and ordinates of the breakpoints, respectively, for band <i>b</i>. The
110: * number of sets of breakpoints must be one or equal to the number of
111: * image bands.
112: */
113: public PiecewiseOpImage(RenderedImage source, Map config,
114: ImageLayout layout, float[][][] breakpoints) {
115: super (source, layout, config, true);
116:
117: // Ensure that the number of sets of breakpoints is either unity
118: // or equal to the number of bands.
119: int numBands = sampleModel.getNumBands();
120:
121: // Initalize the instance variables.
122: initFields(numBands, breakpoints);
123:
124: // Set the byte data flag.
125: isByteData = sampleModel.getTransferType() == DataBuffer.TYPE_BYTE;
126:
127: // Perform byte-specific initialization.
128: if (isByteData) {
129: // Initialize the lookup table.
130: createLUT();
131:
132: // Clear the other instance variables for the garbage collector.
133: unsetFields();
134: }
135:
136: // Set flag to permit in-place operation.
137: permitInPlaceOperation();
138:
139: // Initialize the colormap if necessary.
140: initializeColormapOperation();
141: }
142:
143: /**
144: * Transform the colormap according to the rescaling parameters.
145: */
146: protected void transformColormap(byte[][] colormap) {
147:
148: byte byteTable[][] = lut.getByteData();
149:
150: for (int b = 0; b < 3; b++) {
151: byte[] map = colormap[b];
152: byte[] luTable = byteTable[b >= byteTable.length ? 0 : b];
153: int mapSize = map.length;
154:
155: for (int i = 0; i < mapSize; i++) {
156: map[i] = luTable[(map[i] & 0xFF)];
157: }
158: }
159: }
160:
161: /**
162: * Initialize various instance variables from the array of breakpoints.
163: * The principal derived values are the slope and ordinate-intercept of
164: * each piecewise segment.
165: *
166: * @param numBands The number of bands in the image.
167: * @param The breakpoints as breakpoints[numBands][0..1][numPoints].
168: */
169: private void initFields(int numBands, float[][][] breakpoints) {
170: abscissas = new float[numBands][];
171: slopes = new float[numBands][];
172: intercepts = new float[numBands][];
173: minOrdinates = new float[numBands];
174: maxOrdinates = new float[numBands];
175:
176: for (int band = 0; band < numBands; band++) {
177: abscissas[band] = breakpoints.length == 1 ? breakpoints[0][0]
178: : breakpoints[band][0];
179: int maxIndex = abscissas[band].length - 1;
180:
181: minOrdinates[band] = breakpoints.length == 1 ? breakpoints[0][1][0]
182: : breakpoints[band][1][0];
183: maxOrdinates[band] = breakpoints.length == 1 ? breakpoints[0][1][maxIndex]
184: : breakpoints[band][1][maxIndex];
185:
186: slopes[band] = new float[maxIndex];
187: intercepts[band] = new float[maxIndex];
188:
189: float[] x = abscissas[band];
190: float[] y = breakpoints.length == 1 ? breakpoints[0][1]
191: : breakpoints[band][1];
192: float[] a = slopes[band];
193: float[] b = intercepts[band];
194: for (int i1 = 0; i1 < maxIndex; i1++) {
195: int i2 = i1 + 1;
196: a[i1] = (y[i2] - y[i1]) / (x[i2] - x[i1]);
197: b[i1] = y[i1] - x[i1] * a[i1];
198: }
199: }
200: }
201:
202: /**
203: * Clear all instance fields which are ununsed references so the GC
204: * may clear them.
205: */
206: private void unsetFields() {
207: abscissas = null;
208: slopes = null;
209: intercepts = null;
210: minOrdinates = null;
211: maxOrdinates = null;
212: }
213:
214: /**
215: * Create a lookup table to be used in the case of byte data.
216: */
217: private void createLUT() {
218: // Allocate memory for the data array references.
219: int numBands = abscissas.length;
220: byte[][] data = new byte[numBands][];
221:
222: // Generate the data for each band.
223: for (int band = 0; band < numBands; band++) {
224: // Allocate memory for this band.
225: data[band] = new byte[256];
226:
227: // Cache the references to avoid extra indexing.
228: byte[] table = data[band];
229: float[] x = abscissas[band];
230: float[] a = slopes[band];
231: float[] b = intercepts[band];
232: float yL = minOrdinates[band];
233: float yH = maxOrdinates[band];
234:
235: // Initialize the lookup table data.
236: for (int value = 0; value < 256; value++) {
237: table[value] = ImageUtil.clampRoundByte(binarySearch(x,
238: yL, yH, a, b, value));
239: }
240: }
241:
242: // Construct the lookup table.
243: lut = new LookupTableJAI(data);
244: }
245:
246: /**
247: * Piecewises to the pixel values within a specified rectangle.
248: *
249: * @param sources Cobbled sources, guaranteed to provide all the
250: * source data necessary for computing the rectangle.
251: * @param dest The tile containing the rectangle to be computed.
252: * @param destRect The rectangle within the tile to be computed.
253: */
254: protected void computeRect(Raster[] sources, WritableRaster dest,
255: Rectangle destRect) {
256: // Retrieve format tags.
257: RasterFormatTag[] formatTags = getFormatTags();
258:
259: if (isByteData) {
260: computeRectByte(sources, dest, destRect);
261: } else {
262: RasterAccessor dst = new RasterAccessor(dest, destRect,
263: formatTags[1], getColorModel());
264: RasterAccessor src = new RasterAccessor(sources[0],
265: destRect, formatTags[0], getSource(0)
266: .getColorModel());
267:
268: switch (dst.getDataType()) {
269: case DataBuffer.TYPE_USHORT:
270: computeRectUShort(src, dst);
271: break;
272: case DataBuffer.TYPE_SHORT:
273: computeRectShort(src, dst);
274: break;
275: case DataBuffer.TYPE_INT:
276: computeRectInt(src, dst);
277: break;
278: case DataBuffer.TYPE_FLOAT:
279: computeRectFloat(src, dst);
280: break;
281: case DataBuffer.TYPE_DOUBLE:
282: computeRectDouble(src, dst);
283: break;
284: }
285:
286: dst.copyDataToRaster();
287: }
288: }
289:
290: private void computeRectByte(Raster[] sources, WritableRaster dest,
291: Rectangle destRect) {
292: lut.lookup(sources[0], dest, destRect);
293: }
294:
295: private void computeRectUShort(RasterAccessor src,
296: RasterAccessor dst) {
297: int dstWidth = dst.getWidth();
298: int dstHeight = dst.getHeight();
299: int dstBands = dst.getNumBands();
300:
301: int dstLineStride = dst.getScanlineStride();
302: int dstPixelStride = dst.getPixelStride();
303: int[] dstBandOffsets = dst.getBandOffsets();
304: short[][] dstData = dst.getShortDataArrays();
305:
306: int srcLineStride = src.getScanlineStride();
307: int srcPixelStride = src.getPixelStride();
308: int[] srcBandOffsets = src.getBandOffsets();
309: short[][] srcData = src.getShortDataArrays();
310:
311: for (int b = 0; b < dstBands; b++) {
312: short[] d = dstData[b];
313: short[] s = srcData[b];
314:
315: int dstLineOffset = dstBandOffsets[b];
316: int srcLineOffset = srcBandOffsets[b];
317:
318: // Cache the references to avoid extra indexing.
319: float[] x = abscissas[b];
320: float[] gain = slopes[b];
321: float[] bias = intercepts[b];
322: float yL = minOrdinates[b];
323: float yH = maxOrdinates[b];
324:
325: for (int h = 0; h < dstHeight; h++) {
326: int dstPixelOffset = dstLineOffset;
327: int srcPixelOffset = srcLineOffset;
328:
329: dstLineOffset += dstLineStride;
330: srcLineOffset += srcLineStride;
331:
332: for (int w = 0; w < dstWidth; w++) {
333: d[dstPixelOffset] = ImageUtil
334: .clampRoundUShort(binarySearch(x, yL, yH,
335: gain, bias,
336: s[srcPixelOffset] & 0xFFFF));
337:
338: dstPixelOffset += dstPixelStride;
339: srcPixelOffset += srcPixelStride;
340: }
341: }
342: }
343: }
344:
345: private void computeRectShort(RasterAccessor src, RasterAccessor dst) {
346: int dstWidth = dst.getWidth();
347: int dstHeight = dst.getHeight();
348: int dstBands = dst.getNumBands();
349:
350: int dstLineStride = dst.getScanlineStride();
351: int dstPixelStride = dst.getPixelStride();
352: int[] dstBandOffsets = dst.getBandOffsets();
353: short[][] dstData = dst.getShortDataArrays();
354:
355: int srcLineStride = src.getScanlineStride();
356: int srcPixelStride = src.getPixelStride();
357: int[] srcBandOffsets = src.getBandOffsets();
358: short[][] srcData = src.getShortDataArrays();
359:
360: for (int b = 0; b < dstBands; b++) {
361: short[] d = dstData[b];
362: short[] s = srcData[b];
363:
364: int dstLineOffset = dstBandOffsets[b];
365: int srcLineOffset = srcBandOffsets[b];
366:
367: // Cache the references to avoid extra indexing.
368: float[] x = abscissas[b];
369: float[] gain = slopes[b];
370: float[] bias = intercepts[b];
371: float yL = minOrdinates[b];
372: float yH = maxOrdinates[b];
373:
374: for (int h = 0; h < dstHeight; h++) {
375: int dstPixelOffset = dstLineOffset;
376: int srcPixelOffset = srcLineOffset;
377:
378: dstLineOffset += dstLineStride;
379: srcLineOffset += srcLineStride;
380:
381: for (int w = 0; w < dstWidth; w++) {
382: d[dstPixelOffset] = ImageUtil
383: .clampRoundShort(binarySearch(x, yL, yH,
384: gain, bias, s[srcPixelOffset]));
385:
386: dstPixelOffset += dstPixelStride;
387: srcPixelOffset += srcPixelStride;
388: }
389: }
390: }
391: }
392:
393: private void computeRectInt(RasterAccessor src, RasterAccessor dst) {
394: int dstWidth = dst.getWidth();
395: int dstHeight = dst.getHeight();
396: int dstBands = dst.getNumBands();
397:
398: int dstLineStride = dst.getScanlineStride();
399: int dstPixelStride = dst.getPixelStride();
400: int[] dstBandOffsets = dst.getBandOffsets();
401: int[][] dstData = dst.getIntDataArrays();
402:
403: int srcLineStride = src.getScanlineStride();
404: int srcPixelStride = src.getPixelStride();
405: int[] srcBandOffsets = src.getBandOffsets();
406: int[][] srcData = src.getIntDataArrays();
407:
408: for (int b = 0; b < dstBands; b++) {
409: int[] d = dstData[b];
410: int[] s = srcData[b];
411:
412: int dstLineOffset = dstBandOffsets[b];
413: int srcLineOffset = srcBandOffsets[b];
414:
415: // Cache the references to avoid extra indexing.
416: float[] x = abscissas[b];
417: float[] gain = slopes[b];
418: float[] bias = intercepts[b];
419: float yL = minOrdinates[b];
420: float yH = maxOrdinates[b];
421:
422: for (int h = 0; h < dstHeight; h++) {
423: int dstPixelOffset = dstLineOffset;
424: int srcPixelOffset = srcLineOffset;
425:
426: dstLineOffset += dstLineStride;
427: srcLineOffset += srcLineStride;
428:
429: for (int w = 0; w < dstWidth; w++) {
430: d[dstPixelOffset] = ImageUtil
431: .clampRoundInt(binarySearch(x, yL, yH,
432: gain, bias, s[srcPixelOffset]));
433:
434: dstPixelOffset += dstPixelStride;
435: srcPixelOffset += srcPixelStride;
436: }
437: }
438: }
439: }
440:
441: private void computeRectFloat(RasterAccessor src, RasterAccessor dst) {
442: int dstWidth = dst.getWidth();
443: int dstHeight = dst.getHeight();
444: int dstBands = dst.getNumBands();
445:
446: int dstLineStride = dst.getScanlineStride();
447: int dstPixelStride = dst.getPixelStride();
448: int[] dstBandOffsets = dst.getBandOffsets();
449: float[][] dstData = dst.getFloatDataArrays();
450:
451: int srcLineStride = src.getScanlineStride();
452: int srcPixelStride = src.getPixelStride();
453: int[] srcBandOffsets = src.getBandOffsets();
454: float[][] srcData = src.getFloatDataArrays();
455:
456: for (int b = 0; b < dstBands; b++) {
457: float[] d = dstData[b];
458: float[] s = srcData[b];
459:
460: int dstLineOffset = dstBandOffsets[b];
461: int srcLineOffset = srcBandOffsets[b];
462:
463: // Cache the references to avoid extra indexing.
464: float[] x = abscissas[b];
465: float[] gain = slopes[b];
466: float[] bias = intercepts[b];
467: float yL = minOrdinates[b];
468: float yH = maxOrdinates[b];
469:
470: for (int h = 0; h < dstHeight; h++) {
471: int dstPixelOffset = dstLineOffset;
472: int srcPixelOffset = srcLineOffset;
473:
474: dstLineOffset += dstLineStride;
475: srcLineOffset += srcLineStride;
476:
477: for (int w = 0; w < dstWidth; w++) {
478: d[dstPixelOffset] = binarySearch(x, yL, yH, gain,
479: bias, s[srcPixelOffset]);
480:
481: dstPixelOffset += dstPixelStride;
482: srcPixelOffset += srcPixelStride;
483: }
484: }
485: }
486: }
487:
488: private void computeRectDouble(RasterAccessor src,
489: RasterAccessor dst) {
490: int dstWidth = dst.getWidth();
491: int dstHeight = dst.getHeight();
492: int dstBands = dst.getNumBands();
493:
494: int dstLineStride = dst.getScanlineStride();
495: int dstPixelStride = dst.getPixelStride();
496: int[] dstBandOffsets = dst.getBandOffsets();
497: double[][] dstData = dst.getDoubleDataArrays();
498:
499: int srcLineStride = src.getScanlineStride();
500: int srcPixelStride = src.getPixelStride();
501: int[] srcBandOffsets = src.getBandOffsets();
502: double[][] srcData = src.getDoubleDataArrays();
503:
504: for (int b = 0; b < dstBands; b++) {
505: double[] d = dstData[b];
506: double[] s = srcData[b];
507:
508: int dstLineOffset = dstBandOffsets[b];
509: int srcLineOffset = srcBandOffsets[b];
510:
511: // Cache the references to avoid extra indexing.
512: float[] x = abscissas[b];
513: float[] gain = slopes[b];
514: float[] bias = intercepts[b];
515: float yL = minOrdinates[b];
516: float yH = maxOrdinates[b];
517:
518: for (int h = 0; h < dstHeight; h++) {
519: int dstPixelOffset = dstLineOffset;
520: int srcPixelOffset = srcLineOffset;
521:
522: dstLineOffset += dstLineStride;
523: srcLineOffset += srcLineStride;
524:
525: for (int w = 0; w < dstWidth; w++) {
526: d[dstPixelOffset] = binarySearch(x, yL, yH, gain,
527: bias, (float) s[srcPixelOffset]);
528:
529: dstPixelOffset += dstPixelStride;
530: srcPixelOffset += srcPixelStride;
531: }
532: }
533: }
534: }
535: }
|