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.pixel.statistics;
031
032import org.openimaj.feature.FeatureVectorProvider;
033import org.openimaj.image.MBFImage;
034import org.openimaj.math.statistics.distribution.MultidimensionalHistogram;
035
036
037/**
038 * An array of multidimensional histograms calculated from image pixels
039 * (assumes image is in 0-1 range)
040 * 
041 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
042 *
043 */
044public class BlockHistogramModel extends AbstractPixelStatisticsModel implements FeatureVectorProvider<MultidimensionalHistogram> {
045        private static final long serialVersionUID = 1L;
046        
047        /**
048         * The histogram data 
049         */
050        public MultidimensionalHistogram [][] histograms;
051        
052        int blocks_x;
053        int blocks_y;
054        int [] dims;
055        
056        /**
057         * Construct with the given parameters
058         * @param blocks_x the number of blocks in the x-direction
059         * @param blocks_y the number of blocks in the y-direction
060         * @param nbins the number of bins in each dimension for the histograms
061         */
062        public BlockHistogramModel(int blocks_x, int blocks_y, int... nbins) {
063                super(nbins.length);
064                this.dims = nbins;
065                this.blocks_x = blocks_x;
066                this.blocks_y = blocks_y;
067                this.histograms = new MultidimensionalHistogram[blocks_y][blocks_x];
068                
069                for (int y=0; y<blocks_y; y++)
070                        for (int x=0; x<blocks_x; x++)
071                                histograms[y][x] = new MultidimensionalHistogram(dims);
072        }
073
074        /**
075         * @return a flattened version of the histogram array formed by 
076         * concatenating the histograms of all the blocks in scan order.
077         */
078        public MultidimensionalHistogram toSingleHistogram() {
079                int [] newdims = new int[dims.length + 2];
080                
081                for (int i=0; i<dims.length; i++)
082                        newdims[i] = dims[i];
083                newdims[dims.length] = blocks_x;
084                newdims[dims.length+1] = blocks_y;
085                
086                MultidimensionalHistogram h = new MultidimensionalHistogram(newdims);
087                
088                for (int y=0; y<blocks_y; y++) {
089                        for (int x=0; x<blocks_x; x++) {
090                                int blkid = x + y*blocks_x;
091                                for (int i=0; i<histograms[y][x].values.length; i++) {
092                                        h.values[i + blkid*histograms[y][x].values.length] = histograms[y][x].values[i];
093                                }
094                        }
095                }
096                
097                return h;
098        }
099        
100        protected void reset(MultidimensionalHistogram histogram) {
101                for (int i=0; i<histogram.values.length; i++)
102                        histogram.values[i] = 0;
103        }
104        
105        @Override
106        public void estimateModel(MBFImage... images) {
107                //reset histograms
108                for (int y=0; y<blocks_y; y++)
109                        for (int x=0; x<blocks_x; x++)
110                                reset(histograms[y][x]);
111                
112                //accumulate
113                for (MBFImage img : images) {
114                        for (int y=0; y<blocks_y; y++) {
115                                for (int x=0; x<blocks_x; x++) {
116                                        accum(img, x, y);
117                                }
118                        }
119                }
120                
121                //normalise
122                for (int y=0; y<blocks_y; y++)
123                        for (int x=0; x<blocks_x; x++)
124                                histograms[y][x].normalise();
125        }
126        
127        protected void accum(MBFImage im, int bx, int by) {
128                assert (im.numBands() == ndims);
129
130                MultidimensionalHistogram histogram = histograms[by][bx];
131                int height = im.getHeight();
132                int width = im.getWidth();
133                
134                int cols_per_block = width / blocks_x;          
135                int startx = bx*cols_per_block;
136                int stopx = (1+bx)*cols_per_block;
137
138                int rows_per_block = height / blocks_y;
139                int starty = by*rows_per_block;
140                int stopy = (1+by)*rows_per_block;
141                
142                if (stopx >= width) stopx = width;
143                if (stopy >= height) stopy = height;
144                
145                for (int y=starty; y<stopy; y++) {
146                        for (int x=startx; x<stopx; x++) {
147                                int [] bins = new int[ndims];
148                                
149                                for (int i=0; i<ndims; i++) {
150                                        bins[i] = (int)(im.getBand(i).pixels[y][x] * (histogram.nbins[i]));
151                                        if (bins[i] >= histogram.nbins[i]) bins[i] = histogram.nbins[i] - 1;
152                                }
153                                
154                                int bin = 0;
155                                for (int i=0; i<ndims; i++) {
156                                        int f = 1;
157                                        for (int j=0; j<i; j++)
158                                                f *= histogram.nbins[j];
159                                        
160                                        bin += f * bins[i];
161                                }
162                                
163                                histogram.values[bin]++;
164                        }
165                }
166        }
167        
168        @Override
169        public String toString() {
170                String s = "LocalHistogram[\n";
171                
172                for (int y=0; y<blocks_y; y++)
173                        for (int x=0; x<blocks_x; x++)
174                                s += "\t(" + x + ", " + y + ") = " + histograms[y][x].toString() + "\n";
175                                
176                s += "]\n";
177                return s;
178        }
179        
180        @Override
181        public BlockHistogramModel clone() {
182                BlockHistogramModel model = new BlockHistogramModel(blocks_x, blocks_x, dims);
183                model.histograms = new MultidimensionalHistogram[blocks_y][blocks_x];
184                
185                for (int y=0; y<blocks_y; y++)
186                        for (int x=0; x<blocks_x; x++)
187                                model.histograms[y][x] = histograms[y][x].clone();
188                
189                return model;
190        }
191
192        @Override
193        public MultidimensionalHistogram getFeatureVector() {
194                return toSingleHistogram();
195        }
196}