1 /**
2 * Copyright (c) 2011, The University of Southampton and the individual contributors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * * Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * * Neither the name of the University of Southampton nor the names of its
16 * contributors may be used to endorse or promote products derived from this
17 * software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30 package org.openimaj.image.analysis.pyramid;
31
32 import java.lang.reflect.Array;
33 import java.util.Iterator;
34
35 import org.openimaj.image.FImage;
36 import org.openimaj.image.Image;
37 import org.openimaj.image.analyser.ImageAnalyser;
38 import org.openimaj.image.processing.convolution.FGaussianConvolve;
39 import org.openimaj.image.processing.resize.BilinearInterpolation;
40 import org.openimaj.image.processor.ImageProcessor;
41 import org.openimaj.image.processor.Processor;
42 import org.openimaj.image.processor.SinglebandImageProcessor;
43 import org.openimaj.util.array.ArrayIterator;
44
45 /**
46 * A simple image pyramid built as a stack of images. For convenience, when
47 * applied to an image as an {@link ImageProcessor}, the last level of the
48 * pyramid will be assigned to the input image.
49 *
50 * {@link SimplePyramid}s also allow you to specify a processor that is applied
51 * between levels. For example, a Gaussian blur could be applied to make a
52 * Gaussian pyramid.
53 *
54 * SimplePyramids are @link{Iterable}, so you can iterate over the levels.
55 *
56 * @author Sina Samangooei (ss@ecs.soton.ac.uk)
57 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
58 *
59 * @param <IMAGE>
60 * the underlying image type
61 */
62 public class SimplePyramid<IMAGE extends Image<?, IMAGE> & SinglebandImageProcessor.Processable<Float, FImage, IMAGE>>
63 implements
64 ImageAnalyser<IMAGE>,
65 ImageProcessor<IMAGE>,
66 Iterable<IMAGE>
67 {
68 /**
69 * The images forming the pyramid
70 */
71 public IMAGE[] pyramid;
72
73 Processor<IMAGE> processor = null;
74
75 /**
76 * The factor by which each level changes in size. Numbers >1 imply
77 * shrinking between levels.
78 */
79 float power;
80
81 /**
82 * The number
83 */
84 int nlevels;
85
86 /**
87 * Construct a pyramid with the given scale factor. The number of levels is
88 * such that the lowest level of the pyramid is a minimum of 8 pixels on its
89 * shortest side.
90 *
91 * @param power
92 * scale factor between levels
93 */
94 public SimplePyramid(float power) {
95 this.power = power;
96 this.nlevels = -1;
97 }
98
99 /**
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 }