View Javadoc

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 }