1 /**
2 * Copyright (c) 2011, The University of Southampton and the individual contributors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * * Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * * Neither the name of the University of Southampton nor the names of its
16 * contributors may be used to endorse or promote products derived from this
17 * software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30 package org.openimaj.image.analysis.algorithm.histogram;
31
32 import org.openimaj.image.FImage;
33 import org.openimaj.image.analyser.ImageAnalyser;
34 import org.openimaj.math.geometry.shape.Rectangle;
35 import org.openimaj.math.statistics.distribution.Histogram;
36
37 /**
38 * This class implements a {@link WindowedHistogramExtractor} with the primary
39 * purpose of of producing efficient access to histograms of arbitrary windows
40 * of the image.
41 * <p>
42 * This class analyses an image and produces an 2D array of integers with a
43 * one-to-one correspondence with the image pixels. Each integer represents the
44 * bin of the histogram into which the corresponding pixel would fall.
45 *
46 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
47 */
48 public class BinnedWindowedExtractor implements ImageAnalyser<FImage>, WindowedHistogramExtractor {
49 protected int[][] binMap;
50 protected int nbins;
51 protected float min = 0;
52 protected float max = 1;
53
54 /**
55 * Construct with the given number of bins. The minimum expected value is
56 * assumed to be 0 and the maximum 1.
57 *
58 * @param nbins
59 * number of bins
60 */
61 public BinnedWindowedExtractor(int nbins) {
62 this.nbins = nbins;
63 }
64
65 /**
66 * Construct with the given number of bins, and range.
67 *
68 * @param nbins
69 * number of bins
70 * @param min
71 * minimum expected value
72 * @param max
73 * maximum expected value
74 */
75 public BinnedWindowedExtractor(int nbins, float min, float max) {
76 this.nbins = nbins;
77 this.min = min;
78 this.max = max;
79 }
80
81 /*
82 * (non-Javadoc)
83 *
84 * @see
85 * org.openimaj.image.analysis.algorithm.ImageHistogramAnalyser#getNumBins()
86 */
87 @Override
88 public int getNumBins() {
89 return nbins;
90 }
91
92 /**
93 * Set the number of bins. The new value will not take effect until
94 * {@link #analyseImage(FImage)} is called.
95 *
96 * @param nbins
97 * the number of bins to set
98 */
99 public void setNbins(int nbins) {
100 this.nbins = nbins;
101 }
102
103 /**
104 * Get the expected minimum value in the input image
105 *
106 * @return the expected minimum value
107 */
108 public float getMin() {
109 return min;
110 }
111
112 /**
113 * Set the expected minimum value. The new value will not take effect until
114 * {@link #analyseImage(FImage)} is called.
115 *
116 * @param min
117 * the minimum to set
118 */
119 public void setMin(float min) {
120 this.min = min;
121 }
122
123 /**
124 * Get the expected maximum value in the input image.
125 *
126 * @return the expected maximum value
127 */
128 public float getMax() {
129 return max;
130 }
131
132 /**
133 * Set the expected maximum value. The new value will not take effect until
134 * {@link #analyseImage(FImage)} is called.
135 *
136 * @param max
137 * the maximum to set
138 */
139 public void setMax(float max) {
140 this.max = max;
141 }
142
143 /**
144 * Computes the bin-map for this image.
145 */
146 @Override
147 public void analyseImage(FImage image) {
148 final int height = image.height;
149 final int width = image.width;
150
151 binMap = new int[height][width];
152
153 for (int y = 0; y < height; y++) {
154 for (int x = 0; x < width; x++) {
155 int bin = (int) (((image.pixels[y][x] - min) / (max - min)) * nbins);
156
157 if (bin > (nbins - 1))
158 bin = nbins - 1;
159
160 binMap[y][x] = bin;
161 }
162 }
163 }
164
165 /**
166 * Get the bin-map created in the last call to {@link #analyseImage(FImage)}
167 * .
168 *
169 * @return the bin map
170 */
171 public int[][] getBinMap() {
172 return binMap;
173 }
174
175 /*
176 * (non-Javadoc)
177 *
178 * @see
179 * org.openimaj.image.analysis.algorithm.ImageHistogramAnalyser#computeHistogram
180 * (org.openimaj.math.geometry.shape.Rectangle)
181 */
182 @Override
183 public Histogram computeHistogram(Rectangle roi) {
184 return computeHistogram((int) roi.x, (int) roi.y, (int) roi.width, (int) roi.height);
185 }
186
187 /*
188 * (non-Javadoc)
189 *
190 * @see
191 * org.openimaj.image.analysis.algorithm.ImageHistogramAnalyser#computeHistogram
192 * (int, int, int, int)
193 */
194 @Override
195 public Histogram computeHistogram(int x, int y, int w, int h) {
196 final Histogram hist = new Histogram(nbins);
197
198 computeHistogram(x, y, w, h, hist);
199
200 return hist;
201 }
202
203 /**
204 * Compute the histogram for the given window. The weight for each bin is
205 * taken from the given weights image.
206 *
207 * @param roi
208 * the window
209 * @param weights
210 * the weights image. Must be the same size as the analysed
211 * image.
212 * @return the histogram in the window of the last analysed image
213 */
214 public Histogram computeHistogram(Rectangle roi, FImage weights) {
215 return computeHistogram((int) roi.x, (int) roi.y, (int) roi.width, (int) roi.height, weights);
216 }
217
218 /**
219 * Compute the histogram for the given window. The weight for each bin is
220 * taken from the given weights image.
221 *
222 * @param x
223 * The x-coordinate of the top-left of the window
224 * @param y
225 * The y-coordinate of the top-left of the window
226 * @param w
227 * The width of the window
228 * @param h
229 * The height of the window
230 * @param weights
231 * the weights image. Must be the same size as the analysed
232 * image.
233 * @return the histogram in the window of the last analysed image
234 */
235 public Histogram computeHistogram(int x, int y, int w, int h, FImage weights) {
236 final Histogram hist = new Histogram(nbins);
237
238 final int starty = Math.max(0, y);
239 final int startx = Math.max(0, x);
240 final int stopy = Math.min(binMap.length, y + h);
241 final int stopx = Math.min(binMap[0].length, x + w);
242
243 for (int r = starty; r < stopy; r++) {
244 for (int c = startx; c < stopx; c++) {
245 hist.values[binMap[r][c]] += weights.pixels[r][c];
246 }
247 }
248
249 return hist;
250 }
251
252 /**
253 * Compute the histogram for the given window. The weight for each bin is
254 * taken from the given weights image, and is multiplied by the
255 * corresponding weight in the window image before accumulation. The size of
256 * the window is taken from the window weights image.
257 * <p>
258 * This method primarily allows you to compute a spatially weighted
259 * histogram. For example, the window weights image could be a 2D Gaussian,
260 * and thus the histogram would apply more weight on to the centre pixels.
261 *
262 * @param x
263 * The x-coordinate of the top-left of the window
264 * @param y
265 * The y-coordinate of the top-left of the window
266 * @param weights
267 * The weights image. Must be the same size as the analysed
268 * image.
269 * @param windowWeights
270 * The weights for each pixel in the window.
271 * @return the histogram in the window of the last analysed image
272 */
273 public Histogram computeHistogram(int x, int y, FImage weights, FImage windowWeights) {
274 final Histogram hist = new Histogram(nbins);
275
276 final int starty = Math.max(0, y);
277 final int startx = Math.max(0, x);
278 final int stopy = Math.min(binMap.length, y + windowWeights.height);
279 final int stopx = Math.min(binMap[0].length, x + windowWeights.width);
280
281 final int startwr = y < 0 ? -y : y;
282 final int startwc = x < 0 ? -x : x;
283
284 for (int r = starty, wr = startwr; r < stopy; r++, wr++) {
285 for (int c = startx, wc = startwc; c < stopx; c++, wc++) {
286 hist.values[binMap[r][c]] += (weights.pixels[r][c] * windowWeights.pixels[wr][wc]);
287 }
288 }
289
290 return hist;
291 }
292
293 @Override
294 public void computeHistogram(Rectangle roi, Histogram histogram) {
295 computeHistogram((int) roi.x, (int) roi.y, (int) roi.width, (int) roi.height, histogram);
296 }
297
298 @Override
299 public void computeHistogram(int x, int y, int w, int h, Histogram histogram) {
300 final int starty = Math.max(0, y);
301 final int startx = Math.max(0, x);
302 final int stopy = Math.min(binMap.length, y + h);
303 final int stopx = Math.min(binMap[0].length, x + w);
304
305 for (int r = starty; r < stopy; r++) {
306 for (int c = startx; c < stopx; c++) {
307 histogram.values[binMap[r][c]]++;
308 }
309 }
310 }
311 }