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.gaussian;
31  
32  import org.openimaj.image.FImage;
33  import org.openimaj.image.Image;
34  import org.openimaj.image.analyser.ImageAnalyser;
35  import org.openimaj.image.analysis.pyramid.Pyramid;
36  import org.openimaj.image.processing.resize.ResizeProcessor;
37  import org.openimaj.image.processor.SinglebandImageProcessor;
38  
39  /**
40   * A Gaussian image pyramid consisting of a stack of octaves where the image
41   * halves its size. The pyramid is of the style described in Lowe's SIFT paper.
42   * 
43   * Octaves are processed by an OctaveProcessor as they are created if the
44   * processor is set in the options object.
45   * 
46   * The pyramid will only hold onto its octaves if either the keepOctaves option
47   * is set to true, or if a PyramidProcessor is set in the options. The
48   * PyramidProcessor will called after all the octaves are created.
49   * 
50   * Pyramids are Iterable for easy access to the octaves; however this will only
51   * work if the pyramid has already been populated with the octaves retained.
52   * 
53   * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
54   * 
55   * @param <I>
56   *            Type of underlying image
57   */
58  public class GaussianPyramid<I extends Image<?, I> & SinglebandImageProcessor.Processable<Float, FImage, I>>
59  		extends
60  		Pyramid<GaussianPyramidOptions<I>, GaussianOctave<I>, I>
61  		implements
62  		ImageAnalyser<I>, Iterable<GaussianOctave<I>>
63  {
64  	/**
65  	 * Construct a Pyramid with the given options.
66  	 * 
67  	 * @param options
68  	 *            the options
69  	 */
70  	public GaussianPyramid(GaussianPyramidOptions<I> options) {
71  		super(options);
72  	}
73  
74  	/*
75  	 * (non-Javadoc)
76  	 * 
77  	 * @see
78  	 * org.openimaj.image.processing.pyramid.AbstractPyramid#process(org.openimaj
79  	 * .image.Image)
80  	 */
81  	@Override
82  	public void process(I img) {
83  		if (img.getWidth() <= 1 || img.getHeight() <= 1)
84  			throw new IllegalArgumentException("Image is too small");
85  
86  		// the octave image size: 1 means same as input, 0.5 is twice as big as
87  		// input, 2 is half input, 4 is quarter input, etc
88  		float octaveSize = 1.0f;
89  
90  		// if doubleInitialImage is set, then the initial image should be scaled
91  		// to
92  		// twice its original size and the
93  		I image;
94  		if (options.doubleInitialImage) {
95  			image = ResizeProcessor.doubleSize(img);
96  			octaveSize *= 0.5;
97  		} else
98  			image = img.clone();
99  
100 		// Lowe's IJCV paper (P.10) suggests that if you double the size of the
101 		// initial image then it has a sigma of 1.0; if the image is not doubled
102 		// the sigma is 0.5
103 		final float currentSigma = (options.doubleInitialImage ? 1.0f : 0.5f);
104 		if (options.initialSigma > currentSigma) {
105 			// we now need to bring the starting image to a sigma of
106 			// initialSigma
107 			// in order to start building the pyramid (every octave starts at
108 			// initialSigma sigmas).
109 			final float sigma = (float) Math.sqrt(options.initialSigma * options.initialSigma - currentSigma
110 					* currentSigma);
111 			image.processInplace(this.options.createGaussianBlur(sigma));
112 		}
113 
114 		// the minimum size image in the pyramid must be bigger than
115 		// two pixels + whatever border is required by the options
116 		// (on both sides).
117 		final int minImageSize = 2 + (2 * options.getBorderPixels());
118 
119 		while (image.getHeight() > minImageSize && image.getWidth() > minImageSize) {
120 			// construct empty octave
121 			final GaussianOctave<I> currentOctave = new GaussianOctave<I>(this, octaveSize);
122 
123 			// populate the octave with images; once the octave
124 			// is complete any OctaveProcessor specified in the
125 			// options will be applied.
126 			currentOctave.process(image);
127 
128 			// get the image with 2*sigma from the octave and
129 			// half its size ready for the next octave
130 			image = ResizeProcessor.halfSize(currentOctave.getNextOctaveImage());
131 
132 			octaveSize *= 2.0; // the size of the octave increases by a factor
133 								// of two each iteration
134 
135 			// if the octaves array is not null we want to retain each octave.
136 			if (octaves != null)
137 				octaves.add(currentOctave);
138 		}
139 
140 		// if a PyramidProcessor was specified in the options it should
141 		// be applied now all the octaves are complete.
142 		if (options.getPyramidProcessor() != null) {
143 			options.getPyramidProcessor().process(this);
144 		}
145 	}
146 }