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.ipd.extractor; 031 032import org.openimaj.feature.OrientedFeatureVector; 033import org.openimaj.image.FImage; 034import org.openimaj.image.feature.local.descriptor.gradient.GradientFeatureProvider; 035import org.openimaj.image.feature.local.descriptor.gradient.GradientFeatureProviderFactory; 036import org.openimaj.image.feature.local.detector.dog.extractor.AbstractDominantOrientationExtractor; 037import org.openimaj.image.feature.local.engine.ipd.InterestPointImageExtractorProperties; 038import org.openimaj.image.feature.local.extraction.FeatureVectorExtractor; 039import org.openimaj.image.feature.local.extraction.GradientScaleSpaceImageExtractorProperties; 040import org.openimaj.image.feature.local.extraction.ScaleSpaceImageExtractorProperties; 041import org.openimaj.image.feature.local.interest.InterestPointData; 042import org.openimaj.image.feature.local.interest.InterestPointDetector; 043import org.openimaj.image.processing.convolution.FImageGradients; 044 045/** 046 * <p> 047 * Class capable of extracting local descriptors from an interest point 048 * {@link InterestPointData} from an interest point detector 049 * {@link InterestPointDetector}. The actual feature extracted is determined by 050 * the {@link GradientFeatureProvider} that is provided by the 051 * {@link GradientFeatureProviderFactory} set during construction. 052 * </p> 053 * <p> 054 * The GradientFeatureExtractor first calculates the dominant orientation of the 055 * image patch described by the {@link InterestPointImageExtractorProperties} 056 * and then iterates over the pixels in an oriented square, centered on the 057 * interest point, passing the gradient and magnitude values of the respective 058 * pixel to the {@link GradientFeatureProvider}. 059 * </p> 060 * <p> 061 * The size of the sampling square is exactly equal to the patch in the 062 * properties, this is in turn controlled by the interest point's scale and 063 * possibly its shape. For some types of feature provider, this number might 064 * need to be set based on the internal settings of the provider. 065 * </p> 066 * 067 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 068 * 069 */ 070public class InterestPointGradientFeatureExtractor 071 implements 072 FeatureVectorExtractor<OrientedFeatureVector, InterestPointImageExtractorProperties<Float, FImage>> 073{ 074 private static final Float INVALID_PIXEL_VALUE = Float.NaN; 075 076 AbstractDominantOrientationExtractor dominantOrientationExtractor; 077 078 GradientFeatureProviderFactory factory; 079 080 private GradientScaleSpaceImageExtractorProperties<FImage> currentGradientProperties = new GradientScaleSpaceImageExtractorProperties<FImage>(); 081 082 /** 083 * @param dominantOrientationExtractor 084 * how dominant orientations are located 085 * @param factory 086 * object used to construct {@link GradientFeatureProvider} 087 * instances which in turn construction the actual features 088 */ 089 public InterestPointGradientFeatureExtractor(AbstractDominantOrientationExtractor dominantOrientationExtractor, 090 GradientFeatureProviderFactory factory) 091 { 092 this.dominantOrientationExtractor = dominantOrientationExtractor; 093 this.factory = factory; 094 } 095 096 /** 097 * Get the GradientScaleSpaceImageExtractorProperties for the given 098 * properties. The returned properties are the same as the input properties, 099 * but with the gradient images added. 100 * 101 * For efficiency, this method always returns the same cached 102 * GradientScaleSpaceImageExtractorProperties, and internally updates this 103 * as necessary. The gradient images are only recalculated when the input 104 * image from the input properties is different to the cached one. 105 * 106 * @param properties 107 * input properties 108 * @return cached GradientScaleSpaceImageExtractorProperties 109 */ 110 public GradientScaleSpaceImageExtractorProperties<FImage> getCurrentGradientProps( 111 ScaleSpaceImageExtractorProperties<FImage> properties) 112 { 113 if (properties.image != currentGradientProperties.image) { 114 currentGradientProperties.image = properties.image; 115 116 // only if the size of the image has changed do we need to reset the 117 // gradient and orientation images. 118 if (currentGradientProperties.orientation == null || 119 currentGradientProperties.orientation.height != currentGradientProperties.image.height || 120 currentGradientProperties.orientation.width != currentGradientProperties.image.width) 121 { 122 currentGradientProperties.orientation = new FImage(currentGradientProperties.image.width, 123 currentGradientProperties.image.height); 124 currentGradientProperties.magnitude = new FImage(currentGradientProperties.image.width, 125 currentGradientProperties.image.height); 126 } 127 128 FImageGradients.gradientMagnitudesAndOrientations(currentGradientProperties.image, 129 currentGradientProperties.magnitude, currentGradientProperties.orientation); 130 } 131 132 currentGradientProperties.x = properties.x; 133 currentGradientProperties.y = properties.y; 134 currentGradientProperties.scale = properties.scale; 135 136 return currentGradientProperties; 137 } 138 139 @Override 140 public OrientedFeatureVector[] extractFeature(InterestPointImageExtractorProperties<Float, FImage> properties) { 141 final GradientScaleSpaceImageExtractorProperties<FImage> gprops = getCurrentGradientProps(properties); 142 143 final float[] dominantOrientations = dominantOrientationExtractor.extractFeatureRaw(gprops); 144 145 final OrientedFeatureVector[] ret = new OrientedFeatureVector[dominantOrientations.length]; 146 147 for (int i = 0; i < dominantOrientations.length; i++) { 148 ret[i] = createFeature(properties, dominantOrientations[i]); 149 } 150 151 return ret; 152 } 153 154 /* 155 * Iterate over the pixels in a sampling patch provided in the properties 156 * instance and pass the information to a feature provider that will extract 157 * the relevant feature vector. 158 */ 159 protected OrientedFeatureVector createFeature(InterestPointImageExtractorProperties<Float, FImage> properties, 160 float orientation) 161 { 162 final GradientFeatureProvider provider = factory.newProvider(); 163 provider.setPatchOrientation(orientation); 164 165 // pass over all the pixels in the subimage, they are the sampling area 166 for (int y = 0; y < properties.featureWindowSize; y++) { 167 for (int x = 0; x < properties.featureWindowSize; x++) { 168 169 // check if the pixel is in the image bounds; if not ignore it 170 if (properties.image.pixels[y][x] != INVALID_PIXEL_VALUE) { 171 // calculate the actual position of the sample in the patch 172 // coordinate system 173 final float sx = (0.5f + x) / properties.featureWindowSize; 174 final float sy = (0.5f + y) / properties.featureWindowSize; 175 176 provider.addSample(sx, sy, 177 currentGradientProperties.magnitude.pixels[y][x], 178 currentGradientProperties.orientation.pixels[y][x] 179 ); 180 } 181 } 182 } 183 184 return provider.getFeatureVector(); 185 } 186}