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.analysis.algorithm.SummedAreaTable;
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 implementation is based on a stack of {@link SummedAreaTable}s, with one
043 * {@link SummedAreaTable} per histogram bin. Obviously this is quite memory
044 * intensive, so should probably only be used with small numbers of bins.
045 * However, the advantage over a {@link BinnedWindowedExtractor} is that the
046 * histogram extraction is an O(1) operation, and it is thus very quick for
047 * evaluating many windows.
048 *
049 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
050 */
051public class SATWindowedExtractor implements WindowedHistogramExtractor {
052        protected final SummedAreaTable[] sats;
053        protected final int nbins;
054
055        /**
056         * Protected constructor for subclasses to use if they don't wan't to
057         * compute the SATs at construction time
058         *
059         * @param nbins
060         *            the number of histogram bins.
061         */
062        protected SATWindowedExtractor(int nbins) {
063                this.nbins = nbins;
064                this.sats = new SummedAreaTable[nbins];
065        }
066
067        /**
068         * Construct with the given spatial histogram magnitude maps. Each image
069         * represents a bin of the histogram, and each element is the histogram
070         * weight at the respective spatial location for the corresponding bin
071         * (usually the images will be very sparse).
072         *
073         * @param magnitudeMaps
074         *            array of images, one per bin, with each pixel set to the
075         *            histogram magnitude at that bin
076         */
077        public SATWindowedExtractor(FImage[] magnitudeMaps) {
078                this.nbins = magnitudeMaps.length;
079
080                sats = new SummedAreaTable[nbins];
081                computeSATs(magnitudeMaps);
082        }
083
084        protected void computeSATs(FImage[] magnitudeMaps) {
085                for (int i = 0; i < nbins; i++) {
086                        sats[i] = new SummedAreaTable(magnitudeMaps[i]);
087                }
088        }
089
090        /*
091         * (non-Javadoc)
092         * 
093         * @see
094         * org.openimaj.image.analysis.algorithm.ImageHistogramAnalyser#getNumBins()
095         */
096        @Override
097        public int getNumBins() {
098                return nbins;
099        }
100
101        /*
102         * (non-Javadoc)
103         * 
104         * @see
105         * org.openimaj.image.analysis.algorithm.ImageHistogramAnalyser#computeHistogram
106         * (org.openimaj.math.geometry.shape.Rectangle)
107         */
108        @Override
109        public Histogram computeHistogram(Rectangle roi) {
110                return computeHistogram((int) roi.x, (int) roi.y, (int) roi.width, (int) roi.height);
111        }
112
113        /*
114         * (non-Javadoc)
115         * 
116         * @see
117         * org.openimaj.image.analysis.algorithm.ImageHistogramAnalyser#computeHistogram
118         * (int, int, int, int)
119         */
120        @Override
121        public Histogram computeHistogram(final int x, final int y, final int w, final int h) {
122                final Histogram hist = new Histogram(nbins);
123                final int x2 = x + w;
124                final int y2 = y + h;
125
126                for (int i = 0; i < nbins; i++) {
127                        final float val = sats[i].calculateArea(x, y, x2, y2);
128                        // rounding errors in the SAT can lead to small values that should
129                        // actually be zero
130                        hist.values[i] = val < 1e-4 ? 0 : val;
131
132                }
133
134                return hist;
135        }
136
137        @Override
138        public void computeHistogram(final int x, final int y, final int w, final int h, final Histogram hist) {
139                final int x2 = x + w;
140                final int y2 = y + h;
141                final double[] values = hist.values;
142
143                for (int i = 0; i < values.length; i++) {
144                        final float val = sats[i].calculateArea(x, y, x2, y2);
145                        values[i] = Math.max(0, val); // rounding errors in the SAT
146                        // might lead to small -ve's
147                }
148        }
149
150        @Override
151        public void computeHistogram(Rectangle roi, Histogram histogram) {
152                computeHistogram((int) roi.x, (int) roi.y, (int) roi.width, (int) roi.height, histogram);
153        }
154}