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.math.statistics.distribution.Histogram; 034 035/** 036 * The {@link InterpolatedBinnedWindowedExtractor} is an extension to a 037 * {@link BinnedWindowedExtractor} that performs soft assignment to the 038 * histogram bins through linear interpolation. If a point being histogrammed 039 * lies directly between two bins, half of its weight will be added to both 040 * bins. If a point is directly in the centre of a bin, then its full weight 041 * will be added to both bins. All other cases use linear interpolation to 042 * distribute the weight across the two nearest bins. 043 * <p> 044 * Cyclic histograms are also supported (i.e. for angles). 045 * <p> 046 * For non cyclic histograms, the bin centres are at <code>min + binWidth/2 + 047 * n*binWidth</code> for <code>n=0..<nbins</code>. Any point less than 048 * <code>binWidth/2</code> from the end of a non-cyclic histogram counts fully 049 * to the respective end bin. 050 * <p> 051 * For cyclic histograms, the bin centres are at <code>min + n*binWidth</code> 052 * for <code>n=0..<nbins</code>. 053 * 054 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 055 */ 056public class InterpolatedBinnedWindowedExtractor extends BinnedWindowedExtractor { 057 /** 058 * The weight to apply to the left bin (i.e. the one that was stored). The 059 * weight of the right bin is 1-this. 060 */ 061 float[][] weights; 062 063 /** 064 * Are the histograms cyclic? 065 */ 066 boolean wrap = false; 067 068 /** 069 * Construct with the given number of bins. The histogram is not cyclic. The 070 * minimum expected value is assumed to be 0 and the maximum 1. 071 * 072 * @param nbins 073 * number of bins 074 */ 075 public InterpolatedBinnedWindowedExtractor(int nbins) { 076 super(nbins); 077 } 078 079 /** 080 * Construct with the given number of bins. The histogram is optionally 081 * cyclic. The minimum expected value is assumed to be 0 and the maximum 1. 082 * 083 * @param nbins 084 * number of bins 085 * @param wrap 086 * true if the histogram is cyclic; false otherwise 087 */ 088 public InterpolatedBinnedWindowedExtractor(int nbins, boolean wrap) { 089 super(nbins); 090 this.wrap = true; 091 } 092 093 /** 094 * Construct with the given number of bins, and range. The histogram is not 095 * cyclic. 096 * 097 * @param nbins 098 * number of bins 099 * @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}