001: /*
002: * $RCSfile: ConvolveOpImage.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:20 $
010: * $State: Exp $
011: */
012: package com.sun.media.jai.opimage;
013:
014: import java.awt.Rectangle;
015: import java.awt.image.DataBuffer;
016: import java.awt.image.SampleModel;
017: import java.awt.image.Raster;
018: import java.awt.image.RenderedImage;
019: import java.awt.image.WritableRaster;
020: import java.awt.image.renderable.ParameterBlock;
021: import java.awt.image.renderable.RenderedImageFactory;
022: import javax.media.jai.AreaOpImage;
023: import javax.media.jai.BorderExtender;
024: import javax.media.jai.ImageLayout;
025: import javax.media.jai.KernelJAI;
026: import javax.media.jai.OpImage;
027: import javax.media.jai.RasterAccessor;
028: import javax.media.jai.RasterFormatTag;
029: import java.util.Map;
030:
031: // import com.sun.media.jai.test.OpImageTester;
032:
033: /**
034: * An OpImage class to perform convolution on a source image.
035: *
036: * <p> This class implements a convolution operation. Convolution is a
037: * spatial operation that computes each output sample by multiplying
038: * elements of a kernel with the samples surrounding a particular
039: * source sample.
040: *
041: * <p> For each destination sample, the kernel is rotated 180 degrees
042: * and its "key element" is placed over the source pixel corresponding
043: * with the destination pixel. The kernel elements are multiplied
044: * with the source pixels under them, and the resulting products are
045: * summed together to produce the destination sample value.
046: *
047: * <p> Example code for the convolution operation on a single sample
048: * dst[x][y] is as follows. First your original kernel is rotated
049: * by 180 degrees, then the following -
050: * assuming the kernel is of size M rows x N columns
051: * and the rotated kernel's key element is at position (xKey, yKey):
052: *
053: * <pre>
054: * dst[x][y] = 0;
055: * for (int i = -xKey; i < M - xKey; i++) {
056: * for (int j = -yKey; j < N - yKey; j++) {
057: * dst[x][y] += src[x + i][y + j] * kernel[xKey + i][yKey + j];
058: * }
059: * }
060: * </pre>
061:
062: * <p> Convolution, or any neighborhood operation, leaves a band of
063: * pixels around the edges undefined, i.e., for a 3x3 kernel, only
064: * four kernel elements and four source pixels contribute to the
065: * destination pixel located at (0,0). Such pixels are not includined
066: * in the destination image. A BorderOpImage may be used to add an
067: * appropriate border to the source image in order to avoid shrinkage
068: * of the image boundaries.
069: *
070: * <p> The Kernel cannot be bigger in any dimension than the image data.
071: *
072: *
073: * @see KernelJAI
074: */
075: final class ConvolveOpImage extends AreaOpImage {
076:
077: /**
078: * The kernel with which to do the convolve operation.
079: */
080: protected KernelJAI kernel;
081:
082: /** Kernel variables. */
083: private int kw, kh, kx, ky;
084:
085: /**
086: * Creates a ConvolveOpImage given a ParameterBlock containing the image
087: * source and pre-rotated convolution kernel. The image dimensions are
088: * derived
089: * from the source image. The tile grid layout, SampleModel, and
090: * ColorModel may optionally be specified by an ImageLayout
091: * object.
092: *
093: * @param source a RenderedImage.
094: * @param extender a BorderExtender, or null.
095: * @param layout an ImageLayout optionally containing the tile grid layout,
096: * SampleModel, and ColorModel, or null.
097: * @param kernel the pre-rotated convolution KernelJAI.
098: */
099: public ConvolveOpImage(RenderedImage source,
100: BorderExtender extender, Map config, ImageLayout layout,
101: KernelJAI kernel) {
102: super (source, layout, config, true, extender, kernel
103: .getLeftPadding(), kernel.getRightPadding(), kernel
104: .getTopPadding(), kernel.getBottomPadding());
105:
106: this .kernel = kernel;
107: kw = kernel.getWidth();
108: kh = kernel.getHeight();
109: kx = kernel.getXOrigin();
110: ky = kernel.getYOrigin();
111: }
112:
113: /**
114: * Performs convolution on a specified rectangle. The sources are
115: * cobbled.
116: *
117: * @param sources an array of source Rasters, guaranteed to provide all
118: * necessary source data for computing the output.
119: * @param dest a WritableRaster tile containing the area to be computed.
120: * @param destRect the rectangle within dest to be processed.
121: */
122: protected void computeRect(Raster[] sources, WritableRaster dest,
123: Rectangle destRect) {
124: // Retrieve format tags.
125: RasterFormatTag[] formatTags = getFormatTags();
126:
127: Raster source = sources[0];
128: Rectangle srcRect = mapDestRect(destRect, 0);
129:
130: RasterAccessor srcAccessor = new RasterAccessor(source,
131: srcRect, formatTags[0], getSourceImage(0)
132: .getColorModel());
133: RasterAccessor dstAccessor = new RasterAccessor(dest, destRect,
134: formatTags[1], getColorModel());
135:
136: switch (dstAccessor.getDataType()) {
137: case DataBuffer.TYPE_BYTE:
138: byteLoop(srcAccessor, dstAccessor);
139: break;
140: case DataBuffer.TYPE_INT:
141: intLoop(srcAccessor, dstAccessor);
142: break;
143: case DataBuffer.TYPE_SHORT:
144: shortLoop(srcAccessor, dstAccessor);
145: break;
146: case DataBuffer.TYPE_USHORT:
147: ushortLoop(srcAccessor, dstAccessor);
148: break;
149: case DataBuffer.TYPE_FLOAT:
150: floatLoop(srcAccessor, dstAccessor);
151: break;
152: case DataBuffer.TYPE_DOUBLE:
153: doubleLoop(srcAccessor, dstAccessor);
154: break;
155:
156: default:
157: }
158:
159: // If the RasterAccessor object set up a temporary buffer for the
160: // op to write to, tell the RasterAccessor to write that data
161: // to the raster no that we're done with it.
162: if (dstAccessor.isDataCopy()) {
163: dstAccessor.clampDataArrays();
164: dstAccessor.copyDataToRaster();
165: }
166: }
167:
168: private void byteLoop(RasterAccessor src, RasterAccessor dst) {
169: int dwidth = dst.getWidth();
170: int dheight = dst.getHeight();
171: int dnumBands = dst.getNumBands();
172:
173: float[] kdata = kernel.getKernelData();
174: int kw = kernel.getWidth();
175: int kh = kernel.getHeight();
176:
177: byte dstDataArrays[][] = dst.getByteDataArrays();
178: int dstBandOffsets[] = dst.getBandOffsets();
179: int dstPixelStride = dst.getPixelStride();
180: int dstScanlineStride = dst.getScanlineStride();
181:
182: byte srcDataArrays[][] = src.getByteDataArrays();
183: int srcBandOffsets[] = src.getBandOffsets();
184: int srcPixelStride = src.getPixelStride();
185: int srcScanlineStride = src.getScanlineStride();
186:
187: for (int k = 0; k < dnumBands; k++) {
188: byte dstData[] = dstDataArrays[k];
189: byte srcData[] = srcDataArrays[k];
190: int srcScanlineOffset = srcBandOffsets[k];
191: int dstScanlineOffset = dstBandOffsets[k];
192: for (int j = 0; j < dheight; j++) {
193: int srcPixelOffset = srcScanlineOffset;
194: int dstPixelOffset = dstScanlineOffset;
195:
196: for (int i = 0; i < dwidth; i++) {
197: float f = 0.5F;
198: int kernelVerticalOffset = 0;
199: int imageVerticalOffset = srcPixelOffset;
200: for (int u = 0; u < kh; u++) {
201: int imageOffset = imageVerticalOffset;
202: for (int v = 0; v < kw; v++) {
203: f += ((int) srcData[imageOffset] & 0xff)
204: * kdata[kernelVerticalOffset + v];
205: imageOffset += srcPixelStride;
206: }
207: kernelVerticalOffset += kw;
208: imageVerticalOffset += srcScanlineStride;
209: }
210:
211: int val = (int) f;
212: if (val < 0) {
213: val = 0;
214: } else if (val > 255) {
215: val = 255;
216: }
217: dstData[dstPixelOffset] = (byte) val;
218: srcPixelOffset += srcPixelStride;
219: dstPixelOffset += dstPixelStride;
220: }
221: srcScanlineOffset += srcScanlineStride;
222: dstScanlineOffset += dstScanlineStride;
223: }
224: }
225: }
226:
227: private void shortLoop(RasterAccessor src, RasterAccessor dst) {
228: int dwidth = dst.getWidth();
229: int dheight = dst.getHeight();
230: int dnumBands = dst.getNumBands();
231:
232: float[] kdata = kernel.getKernelData();
233: int kw = kernel.getWidth();
234: int kh = kernel.getHeight();
235:
236: short dstDataArrays[][] = dst.getShortDataArrays();
237: int dstBandOffsets[] = dst.getBandOffsets();
238: int dstPixelStride = dst.getPixelStride();
239: int dstScanlineStride = dst.getScanlineStride();
240:
241: short srcDataArrays[][] = src.getShortDataArrays();
242: int srcBandOffsets[] = src.getBandOffsets();
243: int srcPixelStride = src.getPixelStride();
244: int srcScanlineStride = src.getScanlineStride();
245:
246: for (int k = 0; k < dnumBands; k++) {
247: short dstData[] = dstDataArrays[k];
248: short srcData[] = srcDataArrays[k];
249: int srcScanlineOffset = srcBandOffsets[k];
250: int dstScanlineOffset = dstBandOffsets[k];
251: for (int j = 0; j < dheight; j++) {
252: int srcPixelOffset = srcScanlineOffset;
253: int dstPixelOffset = dstScanlineOffset;
254:
255: for (int i = 0; i < dwidth; i++) {
256: float f = 0.5F;
257: int kernelVerticalOffset = 0;
258: int imageVerticalOffset = srcPixelOffset;
259: for (int u = 0; u < kh; u++) {
260: int imageOffset = imageVerticalOffset;
261: for (int v = 0; v < kw; v++) {
262: f += (srcData[imageOffset])
263: * kdata[kernelVerticalOffset + v];
264: imageOffset += srcPixelStride;
265: }
266: kernelVerticalOffset += kw;
267: imageVerticalOffset += srcScanlineStride;
268: }
269:
270: int val = (int) f;
271: if (val < Short.MIN_VALUE) {
272: val = Short.MIN_VALUE;
273: } else if (val > Short.MAX_VALUE) {
274: val = Short.MAX_VALUE;
275: }
276:
277: dstData[dstPixelOffset] = (short) val;
278: srcPixelOffset += srcPixelStride;
279: dstPixelOffset += dstPixelStride;
280: }
281: srcScanlineOffset += srcScanlineStride;
282: dstScanlineOffset += dstScanlineStride;
283: }
284: }
285: }
286:
287: private void ushortLoop(RasterAccessor src, RasterAccessor dst) {
288: int dwidth = dst.getWidth();
289: int dheight = dst.getHeight();
290: int dnumBands = dst.getNumBands();
291:
292: float[] kdata = kernel.getKernelData();
293: int kw = kernel.getWidth();
294: int kh = kernel.getHeight();
295:
296: short dstDataArrays[][] = dst.getShortDataArrays();
297: int dstBandOffsets[] = dst.getBandOffsets();
298: int dstPixelStride = dst.getPixelStride();
299: int dstScanlineStride = dst.getScanlineStride();
300:
301: short srcDataArrays[][] = src.getShortDataArrays();
302: int srcBandOffsets[] = src.getBandOffsets();
303: int srcPixelStride = src.getPixelStride();
304: int srcScanlineStride = src.getScanlineStride();
305:
306: for (int k = 0; k < dnumBands; k++) {
307: short dstData[] = dstDataArrays[k];
308: short srcData[] = srcDataArrays[k];
309: int srcScanlineOffset = srcBandOffsets[k];
310: int dstScanlineOffset = dstBandOffsets[k];
311: for (int j = 0; j < dheight; j++) {
312: int srcPixelOffset = srcScanlineOffset;
313: int dstPixelOffset = dstScanlineOffset;
314:
315: for (int i = 0; i < dwidth; i++) {
316: float f = 0.5F;
317: int kernelVerticalOffset = 0;
318: int imageVerticalOffset = srcPixelOffset;
319: for (int u = 0; u < kh; u++) {
320: int imageOffset = imageVerticalOffset;
321: for (int v = 0; v < kw; v++) {
322: f += (srcData[imageOffset] & 0xffff)
323: * kdata[kernelVerticalOffset + v];
324: imageOffset += srcPixelStride;
325: }
326: kernelVerticalOffset += kw;
327: imageVerticalOffset += srcScanlineStride;
328: }
329: int val = (int) f;
330: if (val < 0) {
331: val = 0;
332: } else if (val > 0xffff) {
333: val = 0xffff;
334: }
335:
336: dstData[dstPixelOffset] = (short) val;
337: srcPixelOffset += srcPixelStride;
338: dstPixelOffset += dstPixelStride;
339: }
340: srcScanlineOffset += srcScanlineStride;
341: dstScanlineOffset += dstScanlineStride;
342: }
343: }
344: }
345:
346: private void intLoop(RasterAccessor src, RasterAccessor dst) {
347: int dwidth = dst.getWidth();
348: int dheight = dst.getHeight();
349: int dnumBands = dst.getNumBands();
350:
351: float[] kdata = kernel.getKernelData();
352: int kw = kernel.getWidth();
353: int kh = kernel.getHeight();
354:
355: int dstDataArrays[][] = dst.getIntDataArrays();
356: int dstBandOffsets[] = dst.getBandOffsets();
357: int dstPixelStride = dst.getPixelStride();
358: int dstScanlineStride = dst.getScanlineStride();
359:
360: int srcDataArrays[][] = src.getIntDataArrays();
361: int srcBandOffsets[] = src.getBandOffsets();
362: int srcPixelStride = src.getPixelStride();
363: int srcScanlineStride = src.getScanlineStride();
364:
365: for (int k = 0; k < dnumBands; k++) {
366: int dstData[] = dstDataArrays[k];
367: int srcData[] = srcDataArrays[k];
368: int srcScanlineOffset = srcBandOffsets[k];
369: int dstScanlineOffset = dstBandOffsets[k];
370: for (int j = 0; j < dheight; j++) {
371: int srcPixelOffset = srcScanlineOffset;
372: int dstPixelOffset = dstScanlineOffset;
373:
374: for (int i = 0; i < dwidth; i++) {
375: float f = 0.5F;
376: int kernelVerticalOffset = 0;
377: int imageVerticalOffset = srcPixelOffset;
378: for (int u = 0; u < kh; u++) {
379: int imageOffset = imageVerticalOffset;
380: for (int v = 0; v < kw; v++) {
381: f += ((int) srcData[imageOffset])
382: * kdata[kernelVerticalOffset + v];
383: imageOffset += srcPixelStride;
384: }
385: kernelVerticalOffset += kw;
386: imageVerticalOffset += srcScanlineStride;
387: }
388:
389: dstData[dstPixelOffset] = (int) f;
390: srcPixelOffset += srcPixelStride;
391: dstPixelOffset += dstPixelStride;
392: }
393: srcScanlineOffset += srcScanlineStride;
394: dstScanlineOffset += dstScanlineStride;
395: }
396: }
397: }
398:
399: private void floatLoop(RasterAccessor src, RasterAccessor dst) {
400: int dwidth = dst.getWidth();
401: int dheight = dst.getHeight();
402: int dnumBands = dst.getNumBands();
403:
404: float[] kdata = kernel.getKernelData();
405: int kw = kernel.getWidth();
406: int kh = kernel.getHeight();
407:
408: float dstDataArrays[][] = dst.getFloatDataArrays();
409: int dstBandOffsets[] = dst.getBandOffsets();
410: int dstPixelStride = dst.getPixelStride();
411: int dstScanlineStride = dst.getScanlineStride();
412:
413: float srcDataArrays[][] = src.getFloatDataArrays();
414: int srcBandOffsets[] = src.getBandOffsets();
415: int srcPixelStride = src.getPixelStride();
416: int srcScanlineStride = src.getScanlineStride();
417:
418: for (int k = 0; k < dnumBands; k++) {
419: float dstData[] = dstDataArrays[k];
420: float srcData[] = srcDataArrays[k];
421: int srcScanlineOffset = srcBandOffsets[k];
422: int dstScanlineOffset = dstBandOffsets[k];
423: for (int j = 0; j < dheight; j++) {
424: int srcPixelOffset = srcScanlineOffset;
425: int dstPixelOffset = dstScanlineOffset;
426:
427: for (int i = 0; i < dwidth; i++) {
428: float f = 0.0F;
429: int kernelVerticalOffset = 0;
430: int imageVerticalOffset = srcPixelOffset;
431: for (int u = 0; u < kh; u++) {
432: int imageOffset = imageVerticalOffset;
433: for (int v = 0; v < kw; v++) {
434: f += (srcData[imageOffset])
435: * kdata[kernelVerticalOffset + v];
436: imageOffset += srcPixelStride;
437: }
438: kernelVerticalOffset += kw;
439: imageVerticalOffset += srcScanlineStride;
440: }
441:
442: dstData[dstPixelOffset] = f;
443: srcPixelOffset += srcPixelStride;
444: dstPixelOffset += dstPixelStride;
445: }
446: srcScanlineOffset += srcScanlineStride;
447: dstScanlineOffset += dstScanlineStride;
448: }
449: }
450: }
451:
452: private void doubleLoop(RasterAccessor src, RasterAccessor dst) {
453: int dwidth = dst.getWidth();
454: int dheight = dst.getHeight();
455: int dnumBands = dst.getNumBands();
456:
457: float[] kdata = kernel.getKernelData();
458: int kw = kernel.getWidth();
459: int kh = kernel.getHeight();
460:
461: double dstDataArrays[][] = dst.getDoubleDataArrays();
462: int dstBandOffsets[] = dst.getBandOffsets();
463: int dstPixelStride = dst.getPixelStride();
464: int dstScanlineStride = dst.getScanlineStride();
465:
466: double srcDataArrays[][] = src.getDoubleDataArrays();
467: int srcBandOffsets[] = src.getBandOffsets();
468: int srcPixelStride = src.getPixelStride();
469: int srcScanlineStride = src.getScanlineStride();
470:
471: for (int k = 0; k < dnumBands; k++) {
472: double dstData[] = dstDataArrays[k];
473: double srcData[] = srcDataArrays[k];
474: int srcScanlineOffset = srcBandOffsets[k];
475: int dstScanlineOffset = dstBandOffsets[k];
476: for (int j = 0; j < dheight; j++) {
477: int srcPixelOffset = srcScanlineOffset;
478: int dstPixelOffset = dstScanlineOffset;
479:
480: for (int i = 0; i < dwidth; i++) {
481: double f = 0.5;
482: int kernelVerticalOffset = 0;
483: int imageVerticalOffset = srcPixelOffset;
484: for (int u = 0; u < kh; u++) {
485: int imageOffset = imageVerticalOffset;
486: for (int v = 0; v < kw; v++) {
487: f += (srcData[imageOffset])
488: * kdata[kernelVerticalOffset + v];
489: imageOffset += srcPixelStride;
490: }
491: kernelVerticalOffset += kw;
492: imageVerticalOffset += srcScanlineStride;
493: }
494:
495: dstData[dstPixelOffset] = f;
496: srcPixelOffset += srcPixelStride;
497: dstPixelOffset += dstPixelStride;
498: }
499: srcScanlineOffset += srcScanlineStride;
500: dstScanlineOffset += dstScanlineStride;
501: }
502: }
503: }
504:
505: // public static OpImage createTestImage(OpImageTester oit) {
506: // float data[] = {0.05f,0.10f,0.05f,
507: // 0.10f,0.40f,0.10f,
508: // 0.05f,0.10f,0.05f};
509: // KernelJAI kJAI = new KernelJAI(3,3,1,1,data);
510: // return new ConvolveOpImage(oit.getSource(), null, null,
511: // new ImageLayout(oit.getSource()),
512: // kJAI);
513: // }
514:
515: // public static void main(String args[]) {
516: // String classname = "com.sun.media.jai.opimage.ConvolveOpImage";
517: // OpImageTester.performDiagnostics(classname,args);
518: // }
519: }
|