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