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 }