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.engine.ipd; 031 032import java.util.List; 033 034import org.openimaj.feature.local.list.LocalFeatureList; 035import org.openimaj.image.FImage; 036import org.openimaj.image.analysis.pyramid.gaussian.GaussianPyramid; 037import org.openimaj.image.analysis.pyramid.gaussian.GaussianPyramidOptions; 038import org.openimaj.image.feature.local.descriptor.gradient.SIFTFeatureProvider; 039import org.openimaj.image.feature.local.detector.dog.extractor.DominantOrientationExtractor; 040import org.openimaj.image.feature.local.detector.ipd.collector.InterestPointFeatureCollector; 041import org.openimaj.image.feature.local.detector.ipd.extractor.InterestPointGradientFeatureExtractor; 042import org.openimaj.image.feature.local.detector.ipd.finder.OctaveInterestPointFinder; 043import org.openimaj.image.feature.local.interest.IPDSelectionMode; 044import org.openimaj.image.feature.local.interest.InterestPointData; 045import org.openimaj.image.feature.local.interest.MultiscaleInterestPointDetector; 046import org.openimaj.image.feature.local.keypoints.InterestPointKeypoint; 047 048/** 049 * Extract SIFT features as defined by David Lowe but located using interest 050 * point detectors. 051 * 052 * This Engine allows the control interest point detector used, whether scale 053 * simulation should be used and how interest point patches are extracted. 054 * 055 * @author Sina Samangooei (ss@ecs.soton.ac.uk) 056 * @param <T> 057 * The type of {@link InterestPointData} 058 */ 059public abstract class AbstractIPDSIFTEngine<T extends InterestPointData> { 060 061 private static final boolean DEFAULT_ACROSS_SCALES = false; 062 private static final IPDSelectionMode DEFAULT_SELECTION_MODE = new IPDSelectionMode.Threshold(2500f); 063 064 private FinderMode<T> finderMode = new FinderMode.Basic<T>(); 065 066 private MultiscaleInterestPointDetector<T> detector; 067 private boolean acrossScales = DEFAULT_ACROSS_SCALES; 068 private IPDSelectionMode selectionMode = DEFAULT_SELECTION_MODE; 069 070 /** 071 * set the selection mode number 072 * 073 * @param selectionMode 074 * the selection mode 075 */ 076 public void setSelectionMode(IPDSelectionMode selectionMode) { 077 this.selectionMode = selectionMode; 078 } 079 080 /** 081 * Initiate the engine with a given detector. 082 * 083 * @param detector 084 */ 085 public AbstractIPDSIFTEngine(MultiscaleInterestPointDetector<T> detector) { 086 this.detector = detector; 087 this.selectionMode = DEFAULT_SELECTION_MODE; 088 089 } 090 091 /** 092 * Find the interest points using the provided detector and extract a SIFT 093 * descriptor per point. 094 * 095 * @param image 096 * to extract features from 097 * @return extracted interest point features 098 */ 099 public LocalFeatureList<InterestPointKeypoint<T>> findFeatures(FImage image) { 100 final InterestPointFeatureCollector<T> collector = constructCollector(new InterestPointGradientFeatureExtractor( 101 new DominantOrientationExtractor(), new SIFTFeatureProvider())); 102 image = image.multiply(255f); 103 if (acrossScales) { 104 findAcrossScales(image, collector); 105 } 106 else { 107 findInSingleScale(image, collector); 108 } 109 return collector.getFeatures(); 110 111 } 112 113 /** 114 * Given an extractor, construct an {@link InterestPointFeatureCollector} 115 * 116 * @param extractor 117 * @return the collector 118 */ 119 public abstract InterestPointFeatureCollector<T> constructCollector(InterestPointGradientFeatureExtractor extractor); 120 121 private void findInSingleScale(FImage image, InterestPointFeatureCollector<T> collector) { 122 detector.findInterestPoints(image); 123 124 final List<T> points = this.selectionMode.selectPoints(this.detector); 125 for (final T point : points) { 126 collector.foundInterestPoint(image, point); 127 } 128 } 129 130 private void findAcrossScales(FImage image, InterestPointFeatureCollector<T> collector) { 131 final OctaveInterestPointFinder<T> finder = constructFinder(); 132 finder.setOctaveInterestPointListener(collector); 133 final GaussianPyramidOptions<FImage> options = new GaussianPyramidOptions<FImage>(); 134 options.setDoubleInitialImage(false); 135 options.setInitialSigma(1.0f); 136 options.setExtraScaleSteps(0); 137 options.setOctaveProcessor(finder); 138 final GaussianPyramid<FImage> pyr = new GaussianPyramid<FImage>(options); 139 pyr.process(image); 140 finder.finish(); 141 } 142 143 private OctaveInterestPointFinder<T> constructFinder() { 144 return getFinderMode().finder(this.detector, this.selectionMode); 145 } 146 147 /** 148 * @param acrossScales 149 */ 150 public void setAcrossScales(boolean acrossScales) { 151 this.acrossScales = acrossScales; 152 } 153 154 /** 155 * set the underlying finder 156 * 157 * @param finderMode 158 */ 159 public void setFinderMode(FinderMode<T> finderMode) { 160 this.finderMode = finderMode; 161 } 162 163 /** 164 * @return the finder used by {@link #findFeatures(FImage)} 165 */ 166 public FinderMode<T> getFinderMode() { 167 return finderMode; 168 } 169 170}