001: /*
002: * $RCSfile: MedianFilterOpImage.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:33 $
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.OpImage;
026: import javax.media.jai.RasterAccessor;
027: import javax.media.jai.RasterFormatTag;
028: import java.util.Map;
029: import javax.media.jai.operator.MedianFilterShape;
030:
031: // import com.sun.media.jai.test.OpImageTester;
032:
033: /**
034: * An abstract OpImage class that subclasses will use to perform
035: * MedianFiltering with specific masks.
036: *
037: *
038: */
039: abstract class MedianFilterOpImage extends AreaOpImage {
040:
041: protected MedianFilterShape maskType;
042: protected int maskSize;
043:
044: /**
045: * Creates a MedianFilterOpImage given an image source, an
046: * optional BorderExtender, a maskType and maskSize. The image
047: * dimensions are derived the source image. The tile grid layout,
048: * SampleModel, and ColorModel may optionally be specified by an
049: * ImageLayout object.
050: *
051: * @param source a RenderedImage.
052: * @param extender a BorderExtender, or null.
053: * @param layout an ImageLayout optionally containing the tile grid layout,
054: * SampleModel, and ColorModel, or null.
055: * @param maskType the filter mask type.
056: * @param maskSize the filter mask size.
057: */
058: public MedianFilterOpImage(RenderedImage source,
059: BorderExtender extender, Map config, ImageLayout layout,
060: MedianFilterShape maskType, int maskSize) {
061: super (source, layout, config, true, extender,
062: (maskSize - 1) / 2, (maskSize - 1) / 2, (maskSize / 2),
063: (maskSize / 2));
064: this .maskType = maskType;
065: this .maskSize = maskSize;
066: }
067:
068: /**
069: * Performs median filtering on a specified rectangle. The sources are
070: * cobbled.
071: *
072: * @param sources an array of source Rasters, guaranteed to provide all
073: * necessary source data for computing the output.
074: * @param dest a WritableRaster tile containing the area to be computed.
075: * @param destRect the rectangle within dest to be processed.
076: */
077: protected void computeRect(Raster[] sources, WritableRaster dest,
078: Rectangle destRect) {
079: // Retrieve format tags.
080: RasterFormatTag[] formatTags = getFormatTags();
081:
082: Raster source = sources[0];
083: Rectangle srcRect = mapDestRect(destRect, 0);
084:
085: RasterAccessor srcAccessor = new RasterAccessor(source,
086: srcRect, formatTags[0], getSource(0).getColorModel());
087: RasterAccessor dstAccessor = new RasterAccessor(dest, destRect,
088: formatTags[1], getColorModel());
089:
090: switch (dstAccessor.getDataType()) {
091: case DataBuffer.TYPE_BYTE:
092: byteLoop(srcAccessor, dstAccessor, maskSize);
093: break;
094: case DataBuffer.TYPE_SHORT:
095: shortLoop(srcAccessor, dstAccessor, maskSize);
096: break;
097: case DataBuffer.TYPE_USHORT:
098: ushortLoop(srcAccessor, dstAccessor, maskSize);
099: break;
100: case DataBuffer.TYPE_INT:
101: intLoop(srcAccessor, dstAccessor, maskSize);
102: break;
103: case DataBuffer.TYPE_FLOAT:
104: floatLoop(srcAccessor, dstAccessor, maskSize);
105: break;
106: case DataBuffer.TYPE_DOUBLE:
107: doubleLoop(srcAccessor, dstAccessor, maskSize);
108: break;
109: }
110:
111: // If the RasterAccessor object set up a temporary buffer for the
112: // op to write to, tell the RasterAccessor to write that data
113: // to the raster no that we're done with it.
114: if (dstAccessor.isDataCopy()) {
115: dstAccessor.clampDataArrays();
116: dstAccessor.copyDataToRaster();
117: }
118: }
119:
120: /** Performs median filtering using the subclass's mask on byte data */
121: protected abstract void byteLoop(RasterAccessor src,
122: RasterAccessor dst, int filterSize);
123:
124: /** Performs median filtering using the subclass's mask on short data */
125: protected abstract void shortLoop(RasterAccessor src,
126: RasterAccessor dst, int filterSize);
127:
128: /** Performs median filtering using the subclass's mask on ushort data */
129: protected abstract void ushortLoop(RasterAccessor src,
130: RasterAccessor dst, int filterSize);
131:
132: /** Performs median filtering using the subclass's mask on int data */
133: protected abstract void intLoop(RasterAccessor src,
134: RasterAccessor dst, int filterSize);
135:
136: /** Performs median filtering using the subclass's mask on float data */
137: protected abstract void floatLoop(RasterAccessor src,
138: RasterAccessor dst, int filterSize);
139:
140: /** Performs median filtering using the subclass's mask on double data */
141: protected abstract void doubleLoop(RasterAccessor src,
142: RasterAccessor dst, int filterSize);
143:
144: /** Returns the median of the input integer array */
145: protected int medianFilter(int data[]) {
146: if (data.length == 3) {
147: int a = data[0];
148: int b = data[1];
149: int c = data[2];
150: if (a < b) {
151: if (b < c) {
152: return b;
153: } else {
154: if (c > a) {
155: return c;
156: } else {
157: return a;
158: }
159: }
160: } else {
161: if (a < c) {
162: return a;
163: } else {
164: if (b < c) {
165: return c;
166: } else {
167: return b;
168: }
169: }
170: }
171: }
172: int left = 0;
173: int right = data.length - 1;
174: int target = data.length / 2;
175:
176: while (true) {
177: int oleft = left;
178: int oright = right;
179: int mid = data[(left + right) / 2];
180: do {
181: while (data[left] < mid) {
182: left++;
183: }
184: while (mid < data[right]) {
185: right--;
186: }
187: if (left <= right) {
188: int tmp = data[left];
189: data[left] = data[right];
190: data[right] = tmp;
191: left++;
192: right--;
193: }
194: } while (left <= right);
195: if (oleft < right && right >= target) {
196: left = oleft;
197: } else if (left < oright && left <= target) {
198: right = oright;
199: } else {
200: return data[target];
201: }
202: }
203: }
204:
205: /** Returns the median of the input float array */
206: protected float medianFilterFloat(float data[]) {
207: if (data.length == 3) {
208: float a = data[0];
209: float b = data[1];
210: float c = data[2];
211: if (a < b) {
212: if (b < c) {
213: return b;
214: } else {
215: if (c > a) {
216: return c;
217: } else {
218: return a;
219: }
220: }
221: } else {
222: if (a < c) {
223: return a;
224: } else {
225: if (b < c) {
226: return c;
227: } else {
228: return b;
229: }
230: }
231: }
232: }
233: int left = 0;
234: int right = data.length - 1;
235: int target = data.length / 2;
236:
237: while (true) {
238: int oleft = left;
239: int oright = right;
240: float mid = data[(left + right) / 2];
241: do {
242: while (data[left] < mid) {
243: left++;
244: }
245: while (mid < data[right]) {
246: right--;
247: }
248: if (left <= right) {
249: float tmp = data[left];
250: data[left] = data[right];
251: data[right] = tmp;
252: left++;
253: right--;
254: }
255: } while (left <= right);
256: if (oleft < right && right >= target) {
257: left = oleft;
258: } else if (left < oright && left <= target) {
259: right = oright;
260: } else {
261: return data[target];
262: }
263: }
264: }
265:
266: /** Returns the median of the input double array */
267: protected double medianFilterDouble(double data[]) {
268: if (data.length == 3) {
269: double a = data[0];
270: double b = data[1];
271: double c = data[2];
272: if (a < b) {
273: if (b < c) {
274: return b;
275: } else {
276: if (c > a) {
277: return c;
278: } else {
279: return a;
280: }
281: }
282: } else {
283: if (a < c) {
284: return a;
285: } else {
286: if (b < c) {
287: return c;
288: } else {
289: return b;
290: }
291: }
292: }
293: }
294: int left = 0;
295: int right = data.length - 1;
296: int target = data.length / 2;
297:
298: while (true) {
299: int oleft = left;
300: int oright = right;
301: double mid = data[(left + right) / 2];
302: do {
303: while (data[left] < mid) {
304: left++;
305: }
306: while (mid < data[right]) {
307: right--;
308: }
309: if (left <= right) {
310: double tmp = data[left];
311: data[left] = data[right];
312: data[right] = tmp;
313: left++;
314: right--;
315: }
316: } while (left <= right);
317: if (oleft < right && right >= target) {
318: left = oleft;
319: } else if (left < oright && left <= target) {
320: right = oright;
321: } else {
322: return data[target];
323: }
324: }
325: }
326:
327: // // Calls a method on OpImage that uses introspection, to make this
328: // // class, discover it's createTestImage() call, call it and then
329: // // benchmark the performance of the created OpImage chain.
330: // public static void main(String args[]) {
331: // String classname =
332: // "com.sun.media.jai.opimage.MedianFilterSquareOpImage";
333: // OpImageTester.performDiagnostics(classname,args);
334: // classname =
335: // "com.sun.media.jai.opimage.MedianFilterXOpImage";
336: // OpImageTester.performDiagnostics(classname,args);
337: // classname =
338: // "com.sun.media.jai.opimage.MedianFilterPlusOpImage";
339: // OpImageTester.performDiagnostics(classname,args);
340: // classname =
341: // "com.sun.media.jai.opimage.MedianFilterSeparableOpImage";
342: // OpImageTester.performDiagnostics(classname,args);
343: // }
344: }
|