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}