001: /*
002: * @(#) $Header: /cvs/jai-operators/src/main/ca/forklabs/media/jai/opimage/MedianCollectionOpImage.java,v 1.2 2007/07/05 18:24:15 forklabs Exp $
003: *
004: * Copyright (C) 2007 Forklabs Daniel Léonard
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License
008: * as published by the Free Software Foundation; either version 2
009: * of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: */
020:
021: package ca.forklabs.media.jai.opimage;
022:
023: import java.awt.Rectangle;
024: import java.awt.image.ColorModel;
025: import java.awt.image.DataBuffer;
026: import java.awt.image.Raster;
027: import java.awt.image.WritableRaster;
028: import java.util.Arrays;
029: import java.util.Map;
030: import javax.media.jai.CollectionImage;
031: import javax.media.jai.ImageLayout;
032: import javax.media.jai.OpImage;
033: import javax.media.jai.PlanarImage;
034: import javax.media.jai.PointOpImage;
035: import javax.media.jai.RasterAccessor;
036: import javax.media.jai.RasterFormatTag;
037: import ca.forklabs.media.jai.OpImageUtil;
038: import ca.forklabs.media.jai.operator.MedianCollectionDescriptor;
039:
040: /**
041: * Class {@link MedianCollectionOpImage} is an {@link OpImage} implementing the
042: * <em>MedianCollection</em> operation.
043: *
044: * @author <a href="mailto:forklabs at dev.java.net?subject=ca.forklabs.media.jai.opimage.MedianCollectionOpImage">Daniel Léonard</a>
045: * @version $Revision: 1.2 $
046: * @see MedianCollectionDescriptor
047: * @see MedianCollectionCRIF
048: */
049: public class MedianCollectionOpImage extends PointOpImage {
050:
051: //---------------------------
052: // Class variables
053: //---------------------------
054:
055: // exceptionnally, class variables are at the end of the file
056:
057: //---------------------------
058: // Constructor
059: //---------------------------
060:
061: /**
062: * Constructor.
063: * @param sources the collection of rendered images.
064: * @param config the rendering hints.
065: * @param layout the image layout.
066: */
067: @SuppressWarnings("unchecked")
068: public MedianCollectionOpImage(CollectionImage sources,
069: Map<?, ?> config, ImageLayout layout) {
070: super (OpImageUtil.vectorize(sources), layout, config, true);
071: }
072:
073: //---------------------------
074: // Instance methods
075: //---------------------------
076:
077: /**
078: * Gets the raster format tag for the image at the given index.
079: * @param index the index of the image.
080: * @return the raster format tag.
081: */
082: protected RasterFormatTag getFormatTag(int index) {
083: RasterFormatTag[] tags = this .getFormatTags();
084: RasterFormatTag tag = tags[index];
085: return tag;
086: }
087:
088: /**
089: * Gets the raster format tag for the given source image.
090: * @param index the index of the source image.
091: * @return the raster format tag.
092: */
093: protected RasterFormatTag getSourceFormatTag(int index) {
094: RasterFormatTag tag = this .getFormatTag(index);
095: return tag;
096: }
097:
098: /**
099: * Gets the raster format tag for the sink image.
100: * @return the raster format tag.
101: */
102: protected RasterFormatTag getSinkFormatTag() {
103: // the index of the sink image is the number of images,
104: // all the previous indices are for source images
105: int index = this .getNumSources();
106: RasterFormatTag tag = this .getFormatTag(index);
107: return tag;
108: }
109:
110: /**
111: * Builds a raster accessor for rasters associated with this operation.
112: * @param raster the raster the accessor is for.
113: * @param tag the format tag for the raster.
114: * @param bounds the bounds.
115: * @param color_model the color model.
116: * @return the raster accessor.
117: */
118: protected RasterAccessor buildRasterAccessor(Raster raster,
119: RasterFormatTag tag, Rectangle bounds,
120: ColorModel color_model) {
121: RasterAccessor accessor = new RasterAccessor(raster, bounds,
122: tag, color_model);
123: return accessor;
124: }
125:
126: /**
127: * Builds a raster accessor for the sink image.
128: * @param raster the raster of the sink image.
129: * @param bounds the bounds.
130: * @return the raster accessor.
131: */
132: protected RasterAccessor buildSinkRasterAccessor(
133: WritableRaster raster, Rectangle bounds) {
134: RasterFormatTag tag = this .getSinkFormatTag();
135: ColorModel color_model = this .getColorModel();
136: RasterAccessor sink_raster_accessor = this .buildRasterAccessor(
137: raster, tag, bounds, color_model);
138: return sink_raster_accessor;
139: }
140:
141: /**
142: * Builds the raster accessor for the source image at the given index.
143: * @param raster the source raster.
144: * @param bounds the bounds on the sink image.
145: * @param index the index of the source image.
146: * @return the raster accessor.
147: */
148: protected RasterAccessor buildSourceRasterAccessor(Raster raster,
149: Rectangle bounds, int index) {
150: Rectangle source_bounds = this .mapDestRect(bounds, index);
151: RasterFormatTag format_tag = this .getSourceFormatTag(index);
152: PlanarImage source = this .getSourceImage(index);
153: ColorModel color_model = source.getColorModel();
154: RasterAccessor accessor = this .buildRasterAccessor(raster,
155: format_tag, source_bounds, color_model);
156: return accessor;
157: }
158:
159: /**
160: * Builds all the source raster accessors.
161: * @param rasters the source rasters.
162: * @param bounds the bounds.
163: * @return the raster accessors.
164: */
165: protected RasterAccessor[] buildSourceRasterAccessors(
166: Raster[] rasters, Rectangle bounds) {
167: int len = this .getNumSources();
168: RasterAccessor[] accessors = new RasterAccessor[len];
169: for (int i = 0; i < len; i++) {
170: Raster raster = rasters[i];
171: RasterAccessor accessor = this .buildSourceRasterAccessor(
172: raster, bounds, i);
173: accessors[i] = accessor;
174: }
175: return accessors;
176: }
177:
178: /**
179: * Finds the median value.
180: * @param values the population to find the median in.
181: * @return the median.
182: * @exception ArithmeticException if the population size is <em>0</em>.
183: */
184: protected double computeMedian(double[] values) {
185: Arrays.sort(values);
186:
187: // there is are no checks to see if the array is
188: // null or of length 0, that would mean there are
189: // no source images, a much bigger problem
190: int len = values.length;
191: int middle = len / 2;
192: boolean is_even = (0 == (len % 2));
193:
194: double median;
195: if (is_even) {
196: double d1 = values[middle - 1];
197: double d2 = values[middle - 0];
198: median = (d1 + d2) / 2;
199: } else {
200: median = values[middle];
201: }
202:
203: return median;
204: }
205:
206: /**
207: * Builds the 3-dimensional source matrix.
208: * @param <A> the type of pixels.
209: * @param sources the source rasters.
210: * @param specialization the specialization for the type of pixels.
211: * @return the 3-dimensional source matrix.
212: */
213: protected <A> A[][] buildAllSourceData(RasterAccessor[] sources,
214: PixelSpecialization<A> specialization) {
215: int len = sources.length;
216: A[][] data = specialization.buildAllData(len);
217: for (int i = 0; i < len; i++) {
218: RasterAccessor source = sources[i];
219: data[i] = specialization.extractData(source);
220: }
221: return data;
222: }
223:
224: /**
225: * Resets the line offsets by setting them to their corresponding band
226: * offset.
227: * @param band the current band.
228: * @param band_offsets the band offsets.
229: * @param line_offsets the line offsets.
230: */
231: protected void resetLineOffsets(int band, int[][] band_offsets,
232: int[] line_offsets) {
233: for (int i = 0, len = line_offsets.length; i < len; i++) {
234: line_offsets[i] = band_offsets[i][band];
235: }
236: }
237:
238: /**
239: * Resets the pixel offsets by setting them to their corresponding line
240: * offset.
241: * @param line_offsets the line offsets.
242: * @param pixel_offsets the pixel offsets.
243: */
244: protected void resetPixelOffsets(int[] line_offsets,
245: int[] pixel_offsets) {
246: for (int i = 0, len = pixel_offsets.length; i < len; i++) {
247: pixel_offsets[i] = line_offsets[i];
248: }
249: }
250:
251: /**
252: * Increments the pixel offsets by their corresponding pixel strides.
253: * @param pixel_offsets the pixel offsets.
254: * @param pixel_strides the pixel strides.
255: */
256: protected void incrementPixelOffsets(int[] pixel_offsets,
257: int[] pixel_strides) {
258: for (int i = 0, len = pixel_offsets.length; i < len; i++) {
259: pixel_offsets[i] += pixel_strides[i];
260: }
261: }
262:
263: /**
264: * Increments the line offsets by their corresponding line strides.
265: * @param line_offsets the line offsets.
266: * @param line_strides the line strides.
267: */
268: protected void incrementLineOffsets(int[] line_offsets,
269: int[] line_strides) {
270: for (int i = 0, len = line_offsets.length; i < len; i++) {
271: line_offsets[i] += line_strides[i];
272: }
273: }
274:
275: /**
276: * Computes the median of the source images.
277: * @param <A> the type of pixels.
278: * @param sources the source rasters.
279: * @param sink the sink raster.
280: * @param specialization the specialization for the type of pixels.
281: */
282: @SuppressWarnings("hiding")
283: protected <A> void computeMedian(RasterAccessor[] sources,
284: RasterAccessor sink, PixelSpecialization<A> specialization) {
285: // If an image is broken into tiles, method computeRect() is called once for
286: // each tile, the rectangle bounds defining the tile.
287: //
288: // The band offset give the position of the first pixel (in a band), that is the
289: // pixel at position (0, 0) IN the tile - which can be anywhere in the image.
290: //
291: // The line stride gives the distance from a pixel on one line to the
292: // corresponding pixel on the next line. It can be seen as the width of the
293: // whole image (an NOT the width of the tile).
294: //
295: // The pixel stride gives the distance between to pixels on the same row.
296: //
297: // The formula to get to any pixel in the tile is as follow :
298: //
299: // pixel_offset = band_offset + (y * line_stride) + (x * pixel_stride)
300: //
301: // where 'x' and 'y' are the coordinate of the pixel IN the tile space (and not
302: // in the image space).
303: //
304: // The image data structure as represented as a matrix. All the pixels of one
305: // band are laid out in row major configuration in a single array (see the
306: // formula above) and all the bands are put together in another array, giving
307: // the matrix. Thus the first dimension of the array represents the band and
308: // the second represents the pixel. A pixel in band 'b' at position 'p' is
309: // accessed like that :
310: //
311: // data[b][p]
312: //
313: //
314: // Now, given that, here is how this code works. To calculate the median of any
315: // pixel, we need the value of each corresponding pixel in all the images. The
316: // inner loop (on 'i') is thus on the source images. The pixels are traversed
317: // top to bottom, left to right (loops on 'w' and on 'h'). Finally this process
318: // is repeated for each band (loop on 'b').
319: //
320: // To prevent the calculation of the pixel offset at each pixel with the formula
321: // above, and since at any given time we are interested with the same
322: // corresponding pixel in all images, source and sink alike, the pixel offset
323: // are calculated incrementally. In any given band, the offset of the first
324: // pixel, (0, 0) is 'band_offset'. This value, for each image, is kept in the
325: // line offset memory, the beginning of each new line being :
326: //
327: // 'band_offset + (h * line_stride)'
328: //
329: // or :
330: //
331: // 'band_offset += line_stride'
332: //
333: // for each new line after the first one. Using the 'line_offset', the position
334: // of each pixel is calculated by incrementing a copy of that offset by the
335: // 'pixel_stride'.
336:
337: int images = sources.length;
338:
339: // the values of each source image is stored at its corresponding index
340: // while the values of the sink image are stored at the last position, at
341: // index 'images'
342: int[] line_strides = new int[images + 1];
343: int[] pixel_strides = new int[images + 1];
344: int[][] band_offsets = new int[images + 1][];
345: for (int i = 0; i < images; i++) {
346: RasterAccessor source = sources[i];
347: line_strides[i] = source.getScanlineStride();
348: pixel_strides[i] = source.getPixelStride();
349: band_offsets[i] = source.getBandOffsets();
350: }
351: line_strides[images] = sink.getScanlineStride();
352: pixel_strides[images] = sink.getPixelStride();
353: band_offsets[images] = sink.getBandOffsets();
354:
355: int[] line_offsets = new int[images + 1];
356: int[] pixel_offsets = new int[images + 1];
357:
358: A[][] all_source_data = this .buildAllSourceData(sources,
359: specialization);
360: A[] sink_data = specialization.extractData(sink);
361: double[] values = new double[images];
362: int bands = sink.getNumBands();
363: int height = sink.getHeight();
364: int width = sink.getWidth();
365:
366: for (int b = 0; b < bands; b++) {
367: this .resetLineOffsets(b, band_offsets, line_offsets);
368: for (int h = 0; h < height; h++) {
369: this .resetPixelOffsets(line_offsets, pixel_offsets);
370: for (int w = 0; w < width; w++) {
371: for (int i = 0; i < images; i++) {
372: values[i] = specialization
373: .extractPixelValueAt(all_source_data,
374: i, b, pixel_offsets[i]);
375: }
376: double median = this .computeMedian(values);
377: specialization.setPixelValueAt(median, sink_data,
378: b, pixel_offsets[images]);
379: this .incrementPixelOffsets(pixel_offsets,
380: pixel_strides);
381: }
382: this .incrementLineOffsets(line_offsets, line_strides);
383: }
384: }
385: }
386:
387: /**
388: * Compute the median image for images with bytes as the data type.
389: * @param sources the source images.
390: * @param sink the sink image.
391: */
392: protected void computeMedianByte(RasterAccessor[] sources,
393: RasterAccessor sink) {
394: PixelSpecialization<byte[]> specialization = MedianCollectionOpImage.BYTE_SPECIALIZATION;
395: this .computeMedian(sources, sink, specialization);
396: }
397:
398: /**
399: * Compute the median image for images with shorts as the data type.
400: * @param sources the source images.
401: * @param sink the sink image.
402: */
403: protected void computeMedianShort(RasterAccessor[] sources,
404: RasterAccessor sink) {
405: PixelSpecialization<short[]> specialization = MedianCollectionOpImage.SHORT_SPECIALIZATION;
406: this .computeMedian(sources, sink, specialization);
407: }
408:
409: /**
410: * Compute the median image for images with integers as the data type.
411: * @param sources the source images.
412: * @param sink the sink image.
413: */
414: protected void computeMedianInt(RasterAccessor[] sources,
415: RasterAccessor sink) {
416: PixelSpecialization<int[]> specialization = MedianCollectionOpImage.INT_SPECIALIZATION;
417: this .computeMedian(sources, sink, specialization);
418: }
419:
420: /**
421: * Compute the median image for images with floats as the data type.
422: * @param sources the source images.
423: * @param sink the sink image.
424: */
425: protected void computeMedianFloat(RasterAccessor[] sources,
426: RasterAccessor sink) {
427: PixelSpecialization<float[]> specialization = MedianCollectionOpImage.FLOAT_SPECIALIZATION;
428: this .computeMedian(sources, sink, specialization);
429: }
430:
431: /**
432: * Compute the median image for images with doubles as the data type.
433: * @param sources the source images.
434: * @param sink the sink image.
435: */
436: protected void computeMedianDouble(RasterAccessor[] sources,
437: RasterAccessor sink) {
438: PixelSpecialization<double[]> specialization = MedianCollectionOpImage.DOUBLE_SPECIALIZATION;
439: this .computeMedian(sources, sink, specialization);
440: }
441:
442: /**
443: * Computes the median image.
444: * @param sources the source image.
445: * @param sink the median image.
446: */
447: protected void computeMedian(RasterAccessor[] sources,
448: RasterAccessor sink) {
449: int data_type = sink.getDataType();
450: switch (data_type) {
451: case DataBuffer.TYPE_BYTE:
452: this .computeMedianByte(sources, sink);
453: break;
454: case DataBuffer.TYPE_USHORT:
455: case DataBuffer.TYPE_SHORT:
456: this .computeMedianShort(sources, sink);
457: break;
458: case DataBuffer.TYPE_INT:
459: this .computeMedianInt(sources, sink);
460: break;
461: case DataBuffer.TYPE_FLOAT:
462: this .computeMedianFloat(sources, sink);
463: break;
464: case DataBuffer.TYPE_DOUBLE:
465: this .computeMedianDouble(sources, sink);
466: break;
467: default:
468: String message = this
469: .getUnknownDataTypeErrorMessage(data_type);
470: throw new IllegalStateException(message);
471: }
472: }
473:
474: /**
475: * Gets the error message telling that the raster data type is unknown.
476: * @param type the bad type.
477: * @return the formatted error message.
478: */
479: @SuppressWarnings("boxing")
480: protected String getUnknownDataTypeErrorMessage(int type) {
481: String key = Resources.UNKNOWN_DATA_TYPE;
482: String message = Resources.getLocalizedString(key, type);
483: return message;
484: }
485:
486: //---------------------------
487: // Overriden methods from javax.media.jai.OpImage
488: //---------------------------
489:
490: /**
491: * Calculates the median of the corresponding pixels of the source images
492: * within a specified rectangle.
493: * @param sources the cobbled sources.
494: * @param sink the raster for each calculation.
495: * @param bounds the region of interest.
496: */
497: @Override
498: protected void computeRect(Raster[] sources, WritableRaster sink,
499: Rectangle bounds) {
500: RasterAccessor[] source_accessors = this
501: .buildSourceRasterAccessors(sources, bounds);
502: RasterAccessor sink_accessor = this .buildSinkRasterAccessor(
503: sink, bounds);
504:
505: this .computeMedian(source_accessors, sink_accessor);
506:
507: if (sink_accessor.needsClamping()) {
508: sink_accessor.clampDataArrays();
509: }
510: sink_accessor.copyDataToRaster();
511: }
512:
513: //---------------------------
514: // Inner classes
515: //---------------------------
516:
517: /**
518: * Class {@code PixelSpecialization} specializes the median calculation
519: * algorithm on the array type.
520: * <p>
521: * It is impossible to use generics on primitive data types, but not on
522: * <em>arrays</em> of primitive data type. The median calculation algorithm,
523: * making extensive uses of multi-dimensional arrays, is a good candidate for
524: * a <em>template method</em> (especially since its inspiration is the core
525: * algorithm from <em>AddCollection</em>, algorithm duplicated for each
526: * numeric data type).
527: *
528: * @param <A> the array type (such as {@code byte[]} or {@code int[]}).
529: *
530: * @author <a href="mailto:daniel.leonard at umontreal.ca?subject=ca.umontreal.iro.image.arcticice.media.jai.operator.MedianCollectionOpImage$PixelSpecialization">Daniel Léonard</a>
531: * @version $Revision: 1.2 $
532: */
533: protected static abstract class PixelSpecialization<A> {
534:
535: /**
536: * Builds the 3-dimensional source matrix. The first dimension represents
537: * the different source images, the second dimension represents individual
538: * bands inside a single source image and the last dimension contains all
539: * the pixels.
540: * @param len the size of the first dimension, the number of source
541: * images.
542: * @return the source matrix.
543: */
544: public abstract A[][] buildAllData(int len);
545:
546: /**
547: * Extracts the 2-dimensional data matrix from the given image raster. The
548: * first dimension represents the individual bands inside the image and
549: * the last dimension contains all the pixels.
550: * @param accessor the raster accessor over the image raster.
551: * @return the image matrix.
552: */
553: public abstract A[] extractData(RasterAccessor accessor);
554:
555: /**
556: * Extracts the specified pixel, basically doing :
557: * <blockquote><code>
558: * double value = all_data[image][band][pixel];
559: * return value;
560: * </code></blockquote>
561: * @param all_data the 3-dimensional source matrix
562: * @param image the image position in the source matrix.
563: * @param band the band position in the source image.
564: * @param pixel the pixel position in the band.
565: * @return the pixel value.
566: */
567: public abstract double extractPixelValueAt(A[][] all_data,
568: int image, int band, int pixel);
569:
570: /**
571: * Changes the given pixel.
572: * @param value the new pixel value.
573: * @param data the image data.
574: * @param band the band position in the image.
575: * @param pixel the pixel position in the band.
576: */
577: public abstract void setPixelValueAt(double value, A[] data,
578: int band, int pixel);
579:
580: }
581:
582: //---------------------------
583: // Class variables
584: //---------------------------
585:
586: /** Specialization for data of type <em>byte</em>. */
587: protected static PixelSpecialization<byte[]> BYTE_SPECIALIZATION = new PixelSpecialization<byte[]>() {
588:
589: @Override
590: public byte[][][] buildAllData(int len) {
591: byte[][][] all_data = new byte[len][][];
592: return all_data;
593: }
594:
595: @Override
596: public byte[][] extractData(RasterAccessor raster_accessor) {
597: byte[][] data = raster_accessor.getByteDataArrays();
598: return data;
599: }
600:
601: @Override
602: public double extractPixelValueAt(byte[][][] all_data,
603: int image, int band, int pixel) {
604: double value = all_data[image][band][pixel];
605: return value;
606: }
607:
608: @Override
609: public void setPixelValueAt(double value, byte[][] sink_data,
610: int band, int pixel) {
611: sink_data[band][pixel] = (byte) value;
612: }
613:
614: };
615:
616: /** Specialization for data of type <em>short</em>. */
617: protected static PixelSpecialization<short[]> SHORT_SPECIALIZATION = new PixelSpecialization<short[]>() {
618:
619: @Override
620: public short[][][] buildAllData(int len) {
621: short[][][] all_data = new short[len][][];
622: return all_data;
623: }
624:
625: @Override
626: public short[][] extractData(RasterAccessor raster_accessor) {
627: short[][] data = raster_accessor.getShortDataArrays();
628: return data;
629: }
630:
631: @Override
632: public double extractPixelValueAt(short[][][] all_data,
633: int image, int band, int pixel) {
634: double value = all_data[image][band][pixel];
635: return value;
636: }
637:
638: @Override
639: public void setPixelValueAt(double value, short[][] sink_data,
640: int band, int pixel) {
641: sink_data[band][pixel] = (short) value;
642: }
643:
644: };
645:
646: /** Specialization for data of type <em>int</em>. */
647: protected static PixelSpecialization<int[]> INT_SPECIALIZATION = new PixelSpecialization<int[]>() {
648:
649: @Override
650: public int[][][] buildAllData(int len) {
651: int[][][] all_data = new int[len][][];
652: return all_data;
653: }
654:
655: @Override
656: public int[][] extractData(RasterAccessor raster_accessor) {
657: int[][] data = raster_accessor.getIntDataArrays();
658: return data;
659: }
660:
661: @Override
662: public double extractPixelValueAt(int[][][] all_data,
663: int image, int band, int pixel) {
664: double value = all_data[image][band][pixel];
665: return value;
666: }
667:
668: @Override
669: public void setPixelValueAt(double value, int[][] sink_data,
670: int band, int pixel) {
671: sink_data[band][pixel] = (int) value;
672: }
673:
674: };
675:
676: /** Specialization for data of type <em>float</em>. */
677: protected static PixelSpecialization<float[]> FLOAT_SPECIALIZATION = new PixelSpecialization<float[]>() {
678:
679: @Override
680: public float[][][] buildAllData(int len) {
681: float[][][] all_data = new float[len][][];
682: return all_data;
683: }
684:
685: @Override
686: public float[][] extractData(RasterAccessor raster_accessor) {
687: float[][] data = raster_accessor.getFloatDataArrays();
688: return data;
689: }
690:
691: @Override
692: public double extractPixelValueAt(float[][][] all_data,
693: int image, int band, int pixel) {
694: double value = all_data[image][band][pixel];
695: return value;
696: }
697:
698: @Override
699: public void setPixelValueAt(double value, float[][] sink_data,
700: int band, int pixel) {
701: sink_data[band][pixel] = (float) value;
702: }
703:
704: };
705:
706: /** Specialization for data of type <em>double</em>. */
707: protected static PixelSpecialization<double[]> DOUBLE_SPECIALIZATION = new PixelSpecialization<double[]>() {
708:
709: @Override
710: public double[][][] buildAllData(int len) {
711: double[][][] all_data = new double[len][][];
712: return all_data;
713: }
714:
715: @Override
716: public double[][] extractData(RasterAccessor raster_accessor) {
717: double[][] data = raster_accessor.getDoubleDataArrays();
718: return data;
719: }
720:
721: @Override
722: public double extractPixelValueAt(double[][][] all_data,
723: int image, int band, int pixel) {
724: double value = all_data[image][band][pixel];
725: return value;
726: }
727:
728: @Override
729: public void setPixelValueAt(double value, double[][] sink_data,
730: int band, int pixel) {
731: sink_data[band][pixel] = value;
732: }
733:
734: };
735:
736: }
737:
738: /*
739: * $Log: MedianCollectionOpImage.java,v $
740: * Revision 1.2 2007/07/05 18:24:15 forklabs
741: * Now uses CollectionImage instead of lists.
742: *
743: * Revision 1.1 2007/06/13 18:56:37 forklabs
744: * Operator mediancollection.
745: *
746: */
|