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.math.statistics.distribution.Histogram;
34  
35  /**
36   * The {@link InterpolatedBinnedWindowedExtractor} is an extension to a
37   * {@link BinnedWindowedExtractor} that performs soft assignment to the
38   * histogram bins through linear interpolation. If a point being histogrammed
39   * lies directly between two bins, half of its weight will be added to both
40   * bins. If a point is directly in the centre of a bin, then its full weight
41   * will be added to both bins. All other cases use linear interpolation to
42   * distribute the weight across the two nearest bins.
43   * <p>
44   * Cyclic histograms are also supported (i.e. for angles).
45   * <p>
46   * For non cyclic histograms, the bin centres are at <code>min + binWidth/2 +
47   * n*binWidth</code> for <code>n=0..&lt;nbins</code>. Any point less than
48   * <code>binWidth/2</code> from the end of a non-cyclic histogram counts fully
49   * to the respective end bin.
50   * <p>
51   * For cyclic histograms, the bin centres are at <code>min + n*binWidth</code>
52   * for <code>n=0..&lt;nbins</code>.
53   * 
54   * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
55   */
56  public class InterpolatedBinnedWindowedExtractor extends BinnedWindowedExtractor {
57  	/**
58  	 * The weight to apply to the left bin (i.e. the one that was stored). The
59  	 * weight of the right bin is 1-this.
60  	 */
61  	float[][] weights;
62  
63  	/**
64  	 * Are the histograms cyclic?
65  	 */
66  	boolean wrap = false;
67  
68  	/**
69  	 * Construct with the given number of bins. The histogram is not cyclic. The
70  	 * minimum expected value is assumed to be 0 and the maximum 1.
71  	 * 
72  	 * @param nbins
73  	 *            number of bins
74  	 */
75  	public InterpolatedBinnedWindowedExtractor(int nbins) {
76  		super(nbins);
77  	}
78  
79  	/**
80  	 * Construct with the given number of bins. The histogram is optionally
81  	 * cyclic. The minimum expected value is assumed to be 0 and the maximum 1.
82  	 * 
83  	 * @param nbins
84  	 *            number of bins
85  	 * @param wrap
86  	 *            true if the histogram is cyclic; false otherwise
87  	 */
88  	public InterpolatedBinnedWindowedExtractor(int nbins, boolean wrap) {
89  		super(nbins);
90  		this.wrap = true;
91  	}
92  
93  	/**
94  	 * Construct with the given number of bins, and range. The histogram is not
95  	 * cyclic.
96  	 * 
97  	 * @param nbins
98  	 *            number of bins
99  	 * @param min
100 	 *            minimum expected value
101 	 * @param max
102 	 *            maximum expected value
103 	 */
104 	public InterpolatedBinnedWindowedExtractor(int nbins, float min, float max) {
105 		super(nbins, min, max);
106 	}
107 
108 	/**
109 	 * Construct with the given number of bins, and range. The histogram is
110 	 * optionally cyclic.
111 	 * 
112 	 * @param nbins
113 	 *            number of bins
114 	 * @param min
115 	 *            minimum expected value
116 	 * @param max
117 	 *            maximum expected value
118 	 * @param wrap
119 	 *            true if the histogram is cyclic; false otherwise
120 	 */
121 	public InterpolatedBinnedWindowedExtractor(int nbins, float min, float max, boolean wrap) {
122 		super(nbins, min, max);
123 		this.wrap = wrap;
124 	}
125 
126 	/**
127 	 * Computes the bin-map for this image.
128 	 */
129 	@Override
130 	public void analyseImage(FImage image) {
131 		final int height = image.height;
132 		final int width = image.width;
133 
134 		binMap = new int[height][width];
135 		weights = new float[height][width];
136 
137 		if (wrap) {
138 			for (int y = 0; y < height; y++) {
139 				for (int x = 0; x < width; x++) {
140 					final float val = ((image.pixels[y][x] - min) / (max - min)) * nbins;
141 					final int bin = (int) Math.floor(val);
142 					final float delta = val - bin;
143 
144 					final int lbin = bin % nbins;
145 					final float lweight = 1f - delta;
146 
147 					binMap[y][x] = lbin;
148 					weights[y][x] = lweight;
149 				}
150 			}
151 		} else {
152 			for (int y = 0; y < height; y++) {
153 				for (int x = 0; x < width; x++) {
154 					final float val = ((image.pixels[y][x] - min) / (max - min)) * nbins;
155 					final int bin = (int) Math.floor(val);
156 					final float delta = val - bin;
157 
158 					int lbin;
159 					float lweight;
160 					if (delta < 0.5) {
161 						// right bin
162 						lbin = bin - 1;
163 						lweight = 0.5f + (delta);
164 					} else {
165 						// left bin
166 						lbin = bin;
167 						lweight = 1.5f - (delta);
168 					}
169 
170 					if (lbin < 0) {
171 						lbin = 0;
172 						lweight = 1;
173 					} else if (bin >= nbins) {
174 						lbin = nbins - 1;
175 						lweight = 1;
176 					}
177 
178 					binMap[y][x] = lbin;
179 					weights[y][x] = lweight;
180 				}
181 			}
182 		}
183 	}
184 
185 	@Override
186 	public Histogram computeHistogram(int x, int y, int w, int h) {
187 		final Histogram hist = new Histogram(nbins);
188 
189 		final int starty = Math.max(0, y);
190 		final int startx = Math.max(0, x);
191 		final int stopy = Math.min(binMap.length, y + h);
192 		final int stopx = Math.min(binMap[0].length, x + w);
193 
194 		for (int r = starty; r < stopy; r++) {
195 			for (int c = startx; c < stopx; c++) {
196 				final int bin = binMap[r][c];
197 				hist.values[bin] += weights[r][c];
198 
199 				if (wrap && bin + 1 == nbins) {
200 					hist.values[0] += (1 - weights[r][c]);
201 				}
202 
203 				if (bin + 1 < nbins) {
204 					hist.values[bin + 1] += (1 - weights[r][c]);
205 				}
206 			}
207 		}
208 
209 		return hist;
210 	}
211 
212 	@Override
213 	public Histogram computeHistogram(int x, int y, int w, int h, FImage extWeights) {
214 		final Histogram hist = new Histogram(nbins);
215 
216 		final int starty = Math.max(0, y);
217 		final int startx = Math.max(0, x);
218 		final int stopy = Math.min(binMap.length, y + h);
219 		final int stopx = Math.min(binMap[0].length, x + w);
220 
221 		for (int r = starty; r < stopy; r++) {
222 			for (int c = startx; c < stopx; c++) {
223 				final int bin = binMap[r][c];
224 				hist.values[bin] += (extWeights.pixels[r][c] * weights[r][c]);
225 
226 				if (wrap && bin + 1 == nbins) {
227 					hist.values[0] += (extWeights.pixels[r][c] * (1 - weights[r][c]));
228 				}
229 
230 				if (bin + 1 < nbins) {
231 					hist.values[bin + 1] += (extWeights.pixels[r][c] * (1 - weights[r][c]));
232 				}
233 			}
234 		}
235 
236 		return hist;
237 	}
238 
239 	@Override
240 	public Histogram computeHistogram(int x, int y, FImage extWeights, FImage windowWeights)
241 	{
242 		final Histogram hist = new Histogram(nbins);
243 
244 		final int starty = Math.max(0, y);
245 		final int startx = Math.max(0, x);
246 		final int stopy = Math.min(binMap.length, y + windowWeights.height);
247 		final int stopx = Math.min(binMap[0].length, x + windowWeights.width);
248 
249 		final int startwr = y < 0 ? -y : y;
250 		final int startwc = x < 0 ? -x : x;
251 
252 		for (int r = starty, wr = startwr; r < stopy; r++, wr++) {
253 			for (int c = startx, wc = startwc; c < stopx; c++, wc++) {
254 				final int bin = binMap[r][c];
255 				hist.values[bin] += (extWeights.pixels[r][c] * weights[r][c] * windowWeights.pixels[wr][wc]);
256 
257 				if (wrap && bin + 1 == nbins) {
258 					hist.values[0] += (extWeights.pixels[r][c] * (1 - weights[r][c]) * windowWeights.pixels[wr][wc]);
259 				}
260 
261 				if (bin + 1 < nbins) {
262 					hist.values[bin + 1] += (extWeights.pixels[r][c] * (1 - weights[r][c]) * windowWeights.pixels[wr][wc]);
263 				}
264 			}
265 		}
266 
267 		return hist;
268 	}
269 
270 	/**
271 	 * Get the weights map
272 	 * 
273 	 * @return the weights map
274 	 */
275 	public float[][] getWeightsMap() {
276 		return weights;
277 	}
278 }