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..&lt;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..&lt;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}