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}