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.pyramid;
031
032import java.lang.reflect.Array;
033import java.util.Iterator;
034
035import org.openimaj.image.FImage;
036import org.openimaj.image.Image;
037import org.openimaj.image.analyser.ImageAnalyser;
038import org.openimaj.image.processing.convolution.FGaussianConvolve;
039import org.openimaj.image.processing.resize.BilinearInterpolation;
040import org.openimaj.image.processor.ImageProcessor;
041import org.openimaj.image.processor.Processor;
042import org.openimaj.image.processor.SinglebandImageProcessor;
043import org.openimaj.util.array.ArrayIterator;
044
045/**
046 * A simple image pyramid built as a stack of images. For convenience, when
047 * applied to an image as an {@link ImageProcessor}, the last level of the
048 * pyramid will be assigned to the input image.
049 * 
050 * {@link SimplePyramid}s also allow you to specify a processor that is applied
051 * between levels. For example, a Gaussian blur could be applied to make a
052 * Gaussian pyramid.
053 * 
054 * SimplePyramids are @link{Iterable}, so you can iterate over the levels.
055 * 
056 * @author Sina Samangooei (ss@ecs.soton.ac.uk)
057 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
058 * 
059 * @param <IMAGE>
060 *            the underlying image type
061 */
062public class SimplePyramid<IMAGE extends Image<?, IMAGE> & SinglebandImageProcessor.Processable<Float, FImage, IMAGE>>
063                implements
064                ImageAnalyser<IMAGE>,
065                ImageProcessor<IMAGE>,
066                Iterable<IMAGE>
067{
068        /**
069         * The images forming the pyramid
070         */
071        public IMAGE[] pyramid;
072
073        Processor<IMAGE> processor = null;
074
075        /**
076         * The factor by which each level changes in size. Numbers >1 imply
077         * shrinking between levels.
078         */
079        float power;
080
081        /**
082         * The number
083         */
084        int nlevels;
085
086        /**
087         * Construct a pyramid with the given scale factor. The number of levels is
088         * such that the lowest level of the pyramid is a minimum of 8 pixels on its
089         * shortest side.
090         * 
091         * @param power
092         *            scale factor between levels
093         */
094        public SimplePyramid(float power) {
095                this.power = power;
096                this.nlevels = -1;
097        }
098
099        /**
100         * Construct a pyramid with the given scale factor and number of levels. If
101         * the number of levels is zero or less, then the actual number of levels
102         * will be calculated dynamically so the shortest side of the bottom level
103         * has at least 8 pixels.
104         * 
105         * @param power
106         *            scale factor between levels
107         * @param nlevels
108         *            number of levels
109         */
110        public SimplePyramid(float power, int nlevels) {
111                this.power = power;
112                this.nlevels = nlevels;
113        }
114
115        /**
116         * Construct a pyramid with the given scale factor. The number of levels is
117         * such that the lowest level of the pyramid is a minimum of 8 pixels on its
118         * shortest side.
119         * 
120         * The processor will be applied before subsampling occurs.
121         * 
122         * @param power
123         *            scale factor between levels
124         * @param processor
125         *            a processor to apply before subsampling
126         */
127        public SimplePyramid(float power, Processor<IMAGE> processor) {
128                this.power = power;
129                this.nlevels = -1;
130                this.processor = processor;
131        }
132
133        /**
134         * Construct a pyramid with the given scale factor and number of levels. If
135         * the number of levels is zero or less, then the actual number of levels
136         * will be calculated dynamically so the shortest side of the bottom level
137         * has at least 8 pixels.
138         * 
139         * The processor will be applied before subsampling occurs.
140         * 
141         * @param power
142         *            scale factor between levels
143         * @param nlevels
144         *            number of levels
145         * @param processor
146         *            a processor to apply before subsampling
147         */
148        public SimplePyramid(float power, int nlevels, Processor<IMAGE> processor) {
149                this.power = power;
150                this.nlevels = nlevels;
151                this.processor = processor;
152        }
153
154        /**
155         * compute the number of levels such that the minimum size is at least 8.
156         * 
157         * @param size
158         *            size
159         * @return number of levels
160         */
161        protected int computeLevels(int size) {
162                int levels = 1;
163                while (true) {
164                        size /= power;
165
166                        if (size < 8)
167                                break;
168
169                        levels++;
170                }
171
172                return levels;
173        }
174
175        /*
176         * (non-Javadoc)
177         * 
178         * @see
179         * org.openimaj.image.analyser.ImageAnalyser#analyseImage(org.openimaj.image
180         * .Image)
181         */
182        @SuppressWarnings("unchecked")
183        @Override
184        public void analyseImage(IMAGE image) {
185                if (nlevels <= 0)
186                        nlevels = computeLevels(Math.min(image.getWidth(), image.getHeight()));
187
188                this.pyramid = (IMAGE[]) Array.newInstance(image.getClass(), nlevels);
189
190                pyramid[0] = image.clone();
191                for (int i = 1; i < nlevels; i++) {
192                        final int m = (int) Math.floor(pyramid[i - 1].getHeight() / power);
193                        final int n = (int) Math.floor(pyramid[i - 1].getWidth() / power);
194
195                        pyramid[i] = pyramid[i - 1].process(processor).process(new BilinearInterpolation(n, m, power));
196                }
197        }
198
199        /*
200         * (non-Javadoc)
201         * 
202         * @see
203         * org.openimaj.image.processor.ImageProcessor#processImage(org.openimaj
204         * .image.Image)
205         */
206        @Override
207        public void processImage(IMAGE image) {
208                analyseImage(image);
209                image.internalAssign(pyramid[nlevels - 1]);
210        }
211
212        @Override
213        public Iterator<IMAGE> iterator() {
214                return new ArrayIterator<IMAGE>(pyramid);
215        }
216
217        /**
218         * Convenience method to create a gaussian pyramid from an image. There is a
219         * fixed number of levels with powers of two between levels.
220         * 
221         * @param <T>
222         *            The type of image
223         * @param image
224         *            the image
225         * @param sigma
226         *            the amount of blurring
227         * @param nLevels
228         *            the number of levels
229         * @return the pyramid
230         */
231        public static <T extends Image<?, T> & SinglebandImageProcessor.Processable<Float, FImage, T>>
232                        SimplePyramid<T>
233                        createGaussianPyramid(T image, float sigma, int nLevels)
234        {
235                @SuppressWarnings("unchecked")
236                // work around compiler issue
237                final SimplePyramid<T> pyr = new SimplePyramid<T>(2f, nLevels, (Processor<T>) (new FGaussianConvolve(sigma)));
238
239                image.analyseWith(pyr);
240                return pyr;
241        }
242}