001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.image.analysis.algorithm.histogram; 031 032import org.openimaj.image.FImage; 033import org.openimaj.image.analyser.ImageAnalyser; 034import org.openimaj.math.geometry.shape.Rectangle; 035import org.openimaj.math.statistics.distribution.Histogram; 036 037/** 038 * This class implements a {@link WindowedHistogramExtractor} with the primary 039 * purpose of of producing efficient access to histograms of arbitrary windows 040 * of the image. 041 * <p> 042 * This class analyses an image and produces an 2D array of integers with a 043 * one-to-one correspondence with the image pixels. Each integer represents the 044 * bin of the histogram into which the corresponding pixel would fall. 045 * 046 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 047 */ 048public class BinnedWindowedExtractor implements ImageAnalyser<FImage>, WindowedHistogramExtractor { 049 protected int[][] binMap; 050 protected int nbins; 051 protected float min = 0; 052 protected float max = 1; 053 054 /** 055 * Construct with the given number of bins. The minimum expected value is 056 * assumed to be 0 and the maximum 1. 057 * 058 * @param nbins 059 * number of bins 060 */ 061 public BinnedWindowedExtractor(int nbins) { 062 this.nbins = nbins; 063 } 064 065 /** 066 * Construct with the given number of bins, and range. 067 * 068 * @param nbins 069 * number of bins 070 * @param min 071 * minimum expected value 072 * @param max 073 * maximum expected value 074 */ 075 public BinnedWindowedExtractor(int nbins, float min, float max) { 076 this.nbins = nbins; 077 this.min = min; 078 this.max = max; 079 } 080 081 /* 082 * (non-Javadoc) 083 * 084 * @see 085 * org.openimaj.image.analysis.algorithm.ImageHistogramAnalyser#getNumBins() 086 */ 087 @Override 088 public int getNumBins() { 089 return nbins; 090 } 091 092 /** 093 * Set the number of bins. The new value will not take effect until 094 * {@link #analyseImage(FImage)} is called. 095 * 096 * @param nbins 097 * the number of bins to set 098 */ 099 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}