View Javadoc

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 }