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.feature.local.detector.dog.extractor; 031 032import org.openimaj.feature.FloatFV; 033import org.openimaj.image.FImage; 034import org.openimaj.image.feature.local.extraction.FeatureVectorExtractor; 035import org.openimaj.image.feature.local.extraction.GradientScaleSpaceImageExtractorProperties; 036 037 038/** 039 * Extract an orientation histogram about an interest point. 040 * The window size is proportional to the scale of the interest point, 041 * and the samples are weighted by a Gaussian centered on the interest 042 * point with a variance proportional to the interest point scale. 043 * 044 * The histogram is also smoothed using the approach in the vlfeat 045 * implementation; multiple smoothings using a [1/3,1/3,1/3] kernel 046 * in a circular manner (i.e. it is assumed the histogram wraps around). 047 * 048 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 049 * 050 */ 051public class OrientationHistogramExtractor implements FeatureVectorExtractor<FloatFV, GradientScaleSpaceImageExtractorProperties<FImage>> { 052 /** 053 * Default number of orientation histogram bins; 054 * Lowe's IJCV paper (p.13) suggests 36 bins. 055 */ 056 public static int DEFAULT_NUM_BINS = 36; 057 058 /** 059 * Default value for weighting the scaling Gaussian 060 * relative to the keypoint scale. 061 * Lowe's IJCV paper (p.13) suggests 1.5. 062 */ 063 public static float DEFAULT_SCALING = 1.5f; 064 065 /** 066 * Default value for the number of iterations of the smoothing 067 * filter. The vlfeat SIFT implementation uses 6. 068 */ 069 public static int DEFAULT_SMOOTHING_ITERATIONS = 6; 070 071 /** 072 * Default value for the size of the sampling window relative 073 * to the sampling scale. Lowe's ICCV paper suggests 3; 074 */ 075 public static float DEFAULT_SAMPLING_SIZE = 3.0f; 076 077 protected int numBins; 078 protected float scaling; 079 protected int smoothingIterations; 080 protected float samplingSize; 081 082 /** 083 * Default constructor. 084 */ 085 public OrientationHistogramExtractor() { 086 this(DEFAULT_NUM_BINS, DEFAULT_SCALING, DEFAULT_SMOOTHING_ITERATIONS, DEFAULT_SAMPLING_SIZE); 087 } 088 089 /** 090 * Construct with the given parameter values. 091 * @param numBins number of orientation histogram bins 092 * @param scaling weighting for the scaling Gaussian relative to the keypoint scale. 093 * @param smoothingIterations the number of iterations of the smoothing filter 094 * @param samplingSize size of the sampling window relative to the sampling scale. 095 */ 096 public OrientationHistogramExtractor(int numBins, float scaling, int smoothingIterations, float samplingSize) { 097 this.numBins = numBins; 098 this.scaling = scaling; 099 this.smoothingIterations = smoothingIterations; 100 this.samplingSize = samplingSize; 101 } 102 103 /** 104 * Extract the orientation histogram given the properties. This method 105 * caches gradient and orientation maps as it's likely to be called 106 * multiple times (at different positions) for the same input image. 107 * 108 * @param props Properties describing the interest point in scale space. 109 * @return a FloatFV object representing the orientation histogram. 110 */ 111 @Override 112 public FloatFV[] extractFeature(GradientScaleSpaceImageExtractorProperties<FImage> props) { 113 return new FloatFV[] { new FloatFV(extractFeatureRaw(props)) }; 114 } 115 116 /** 117 * Extract the orientation histogram given the properties. This method 118 * caches gradient and orientation maps as it's likely to be called 119 * multiple times (at different positions) for the same input image. 120 * 121 * @param properties Properties describing the interest point in scale space. 122 * @return a float array representing the orientation histogram. 123 */ 124 public float[] extractFeatureRaw(GradientScaleSpaceImageExtractorProperties<FImage> properties) { 125 return createHistogram(properties.x, properties.y, properties.scale, properties.magnitude, properties.orientation); 126 } 127 128 /** 129 * Calculate the orientation histogram in a circular region about 130 * this interest point. The pixel contributions to the histogram 131 * are weighted by a Gaussian of variance sigma, which is proportional 132 * to the scale of the interest point. The radius of sampling region is 133 * proportional to sigma. 134 */ 135 float [] createHistogram(float fx, float fy, float scale, FImage magnitude, FImage orientation) { 136 float hist[] = new float[numBins]; 137 138 int ix = Math.round(fx); 139 int iy = Math.round(fy); 140 141 //sigma is calculated relative to the interest point scale 142 float sigma = scaling * scale; 143 //the radius is relative to sigma 144 int radius = (int) (sigma * samplingSize); 145 146 //don't loop outside the valid pixel area 147 int startx = Math.max(ix - radius, 1); 148 int stopx = Math.min(ix+radius, magnitude.width-2); 149 int starty = Math.max(iy - radius, 1); 150 int stopy = Math.min(iy+radius, magnitude.height-2); 151 152 float radiusSq = (radius + 0.5f) * (radius + 0.5f); //the square of the radius + half a pel 153 double sigmaSq2 = 2.0 * sigma * sigma; //2*sigma*sigma; for the Gaussian 154 155 //loop over the square containing the sampling circle 156 for (int y=starty; y<=stopy; y++) { 157 for (int x=startx; x<=stopx; x++) { 158 float distsq = (y - fy) * (y - fy) + (x - fx) * (x - fx); 159 160 if (distsq <= radiusSq) { 161 float weight = (float) Math.exp(-distsq / sigmaSq2); 162 163 float angle = orientation.pixels[y][x]; //angle is in range of -PI to PI. 164 165 //now find the right bin 166 int bin = (int) (numBins * (angle + Math.PI) / (0.00001 + (2.0 * Math.PI))); 167 hist[bin] += weight * magnitude.pixels[y][x]; 168 } 169 } 170 } 171 172 //smooth the histogram 173 for (int i=0; i<smoothingIterations; i++) 174 circularSmooth(hist); 175 176 return hist; 177 } 178 179 /** 180 * Smooth the values in a circular buffer with a (1/3)[1,1,1] kernel. 181 * @param buffer buffer to smooth 182 */ 183 protected void circularSmooth(float[] buffer) { 184 float prev = buffer[buffer.length - 1]; 185 186 for (int i = 0; i < buffer.length; i++) { 187 float temp = buffer[i]; 188 buffer[i] = (prev + buffer[i] + buffer[(i + 1 == buffer.length) ? 0 : i + 1]) / 3.0f; 189 prev = temp; 190 } 191 } 192}