001: /*
002: * $RCSfile: MlibHistogramOpImage.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:55:57 $
010: * $State: Exp $
011: */package com.sun.media.jai.mlib;
012:
013: import java.awt.Rectangle;
014: import java.awt.image.ComponentSampleModel;
015: import java.awt.image.Raster;
016: import java.awt.image.RenderedImage;
017: import javax.media.jai.Histogram;
018: import javax.media.jai.StatisticsOpImage;
019: import java.util.Iterator;
020: import java.util.TreeMap;
021: import com.sun.medialib.mlib.Image;
022: import com.sun.medialib.mlib.mediaLibImage;
023:
024: /**
025: * An <code>OpImage</code> implementing the "Histogram" operation as
026: * described in <code>javax.media.jai.operator.HistogramDescriptor</code>.
027: *
028: * @see javax.media.jai.Histogram
029: * @see javax.media.jai.operator.HistogramDescriptor
030: */
031: final class MlibHistogramOpImage extends StatisticsOpImage {
032:
033: /** Number of bins per band. */
034: private int[] numBins;
035:
036: /** The low value checked inclusive for each band. */
037: private double[] lowValueFP;
038:
039: /** The high value checked exclusive for each band. */
040: private double[] highValueFP;
041:
042: /** The low value checked inclusive for each band. */
043: private int[] lowValue;
044:
045: /** The high value checked exclusive for each band. */
046: private int[] highValue;
047:
048: /** The number of bands of the source image. */
049: private int numBands;
050:
051: private int[] bandIndexMap;
052:
053: private boolean reorderBands = false;
054:
055: /**
056: * Constructs an <code>MlibHistogramOpImage</code>.
057: *
058: * @param source The source image.
059: */
060: public MlibHistogramOpImage(RenderedImage source, int xPeriod,
061: int yPeriod, int[] numBins, double[] lowValueFP,
062: double[] highValueFP) {
063: super (source, null, // ROI
064: source.getMinX(), // xStart
065: source.getMinY(), // yStart
066: xPeriod, yPeriod);
067:
068: // Save the band count.
069: numBands = sampleModel.getNumBands();
070:
071: // Allocate memory to copy parameters.
072: this .numBins = new int[numBands];
073: this .lowValueFP = new double[numBands];
074: this .highValueFP = new double[numBands];
075:
076: // Copy parameters.
077: for (int b = 0; b < numBands; b++) {
078: this .numBins[b] = numBins.length == 1 ? numBins[0]
079: : numBins[b];
080: this .lowValueFP[b] = lowValueFP.length == 1 ? lowValueFP[0]
081: : lowValueFP[b];
082: this .highValueFP[b] = highValueFP.length == 1 ? highValueFP[0]
083: : highValueFP[b];
084: }
085:
086: // Convert low values to integers. ceil() is used because the
087: // pixel values are integral and the comparison is inclusive
088: // so a floor() might include unwanted values if the low
089: // value is floating point.
090: this .lowValue = new int[this .lowValueFP.length];
091: for (int i = 0; i < this .lowValueFP.length; i++) {
092: this .lowValue[i] = (int) Math.ceil(this .lowValueFP[i]);
093: }
094:
095: // Convert high values to integers. ceil() is used because the
096: // pixel values are integral and the comparison is exclusive
097: // so a floor might cause desired values to be excluded as
098: // only those through floor(high) - 1 would be included.
099: this .highValue = new int[this .highValueFP.length];
100: for (int i = 0; i < this .highValueFP.length; i++) {
101: this .highValue[i] = (int) Math.ceil(this .highValueFP[i]);
102: }
103:
104: // Set up the band re-index map if needed.
105: if (numBands > 1) {
106: ComponentSampleModel csm = (ComponentSampleModel) sampleModel;
107:
108: TreeMap indexMap = new TreeMap();
109:
110: // Determine whether there is more than one bank.
111: int[] indices = csm.getBankIndices();
112: boolean checkBanks = false;
113: for (int i = 1; i < numBands; i++) {
114: if (indices[i] != indices[i - 1]) {
115: checkBanks = true;
116: break;
117: }
118: }
119:
120: // Check the banks for ordering.
121: if (checkBanks) {
122: for (int i = 0; i < numBands; i++) {
123: indexMap.put(new Integer(indices[i]),
124: new Integer(i));
125: }
126:
127: bandIndexMap = new int[numBands];
128: Iterator bankIter = indexMap.keySet().iterator();
129: int k = 0;
130: while (bankIter.hasNext()) {
131: int idx = ((Integer) indexMap.get(bankIter.next()))
132: .intValue();
133: if (idx != k) {
134: reorderBands = true;
135: }
136: bandIndexMap[k++] = idx;
137: }
138: }
139:
140: // If band re-ordering not needed on basis of bank indices
141: // then check ordering of band offsets.
142: if (!reorderBands) {
143: indexMap.clear();
144:
145: if (bandIndexMap == null) {
146: bandIndexMap = new int[numBands];
147: }
148:
149: int[] offsets = csm.getBandOffsets();
150: for (int i = 0; i < numBands; i++) {
151: indexMap.put(new Integer(offsets[i]),
152: new Integer(i));
153: }
154:
155: Iterator offsetIter = indexMap.keySet().iterator();
156: int k = 0;
157: while (offsetIter.hasNext()) {
158: int idx = ((Integer) indexMap
159: .get(offsetIter.next())).intValue();
160: if (idx != k) {
161: reorderBands = true;
162: }
163: bandIndexMap[k++] = idx;
164: }
165: }
166: }
167: }
168:
169: protected String[] getStatisticsNames() {
170: String[] names = new String[1];
171: names[0] = "histogram";
172: return names;
173: }
174:
175: protected Object createStatistics(String name) {
176: if (name.equalsIgnoreCase("histogram")) {
177: return new Histogram(numBins, lowValueFP, highValueFP);
178: } else {
179: return java.awt.Image.UndefinedProperty;
180: }
181: }
182:
183: protected void accumulateStatistics(String name, Raster source,
184: Object stats) {
185: // Get the JAI histogram.
186: Histogram histogram = (Histogram) stats;
187: int numBands = histogram.getNumBands();
188: int[][] histJAI = histogram.getBins();
189:
190: // Get the tile bounds.
191: Rectangle tileRect = source.getBounds();
192:
193: // Get the tile bins.
194: int[][] histo;
195: if (!reorderBands && tileRect.equals(getBounds())) {
196: // Entire image: use the global histogram bins directly.
197: histo = histJAI;
198: } else {
199: // Sub-image: save results for this tile only.
200: histo = new int[numBands][];
201: for (int i = 0; i < numBands; i++) {
202: histo[i] = new int[histogram.getNumBins(i)];
203: }
204: }
205:
206: // Get the mlib image.
207: int formatTag = MediaLibAccessor
208: .findCompatibleTag(null, source);
209: MediaLibAccessor accessor = new MediaLibAccessor(source,
210: tileRect, formatTag);
211: mediaLibImage[] img = accessor.getMediaLibImages();
212:
213: // Determine the offset within the tile.
214: int offsetX = (xPeriod - ((tileRect.x - xStart) % xPeriod))
215: % xPeriod;
216: int offsetY = (yPeriod - ((tileRect.y - yStart) % yPeriod))
217: % yPeriod;
218:
219: if (histo == histJAI) {
220: synchronized (histogram) {
221: // Compute the histogram into the global array.
222: Image.Histogram2(histo, img[0], lowValue, highValue,
223: offsetX, offsetY, xPeriod, yPeriod);
224: }
225: } else {
226: // Compute the histogram into the local array.
227: Image.Histogram2(histo, img[0], lowValue, highValue,
228: offsetX, offsetY, xPeriod, yPeriod);
229:
230: // Accumulate values if not using the global histogram.
231: synchronized (histogram) {
232: for (int i = 0; i < numBands; i++) {
233: int numBins = histo[i].length;
234: int[] binsBandJAI = reorderBands ? histJAI[bandIndexMap[i]]
235: : histJAI[i];
236: int[] binsBand = histo[i];
237: for (int j = 0; j < numBins; j++) {
238: binsBandJAI[j] += binsBand[j];
239: }
240: }
241: }
242: }
243: }
244: }
|