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.finder; 031 032import java.util.TreeSet; 033 034import org.openimaj.feature.local.list.LocalFeatureList; 035import org.openimaj.image.feature.local.interest.IPDSelectionMode; 036import org.openimaj.image.feature.local.interest.InterestPointData; 037import org.openimaj.image.feature.local.interest.InterestPointDetector; 038import org.openimaj.image.feature.local.keypoints.InterestPointKeypoint; 039import org.openimaj.math.geometry.shape.Ellipse; 040 041/** 042 * A characteristic octave interest point finder throws {@link InterestPointData} away if two instances are similar. 043 * Similarity is defined by the position, rotation and axis ratio of the two interest points. 044 * 045 * @author Sina Samangooei (ss@ecs.soton.ac.uk) 046 * 047 * @param <T> 048 */ 049public class CharacteristicOctaveInterestPointFinder<T extends InterestPointData> extends OctaveInterestPointFinder<T> { 050 051 private static final double DEFAULT_MAX_DISTANCE = 4; 052 private static final double DEFAULT_MAX_ROTATION = (Math.PI / 180.0) * 15.0; 053 private static final double DEFAULT_MAX_AXIS_RATIO = 0.1; 054 /** 055 * The maximum distance before two keypoints are considered "similar" 056 */ 057 public double maxDistance = DEFAULT_MAX_DISTANCE; 058 /** 059 * The maximum rotation difference before two keypoints are considered "similar" 060 */ 061 public double maxRotation = DEFAULT_MAX_ROTATION; 062 /** 063 * The maximum axis ratio difference before two keypoints are considered similar 064 */ 065 public double maxAxisRatio = DEFAULT_MAX_AXIS_RATIO; 066 067 /** 068 * construct this finder with the detector and selection mode 069 * @param detector 070 * @param selectionMode 071 */ 072 public CharacteristicOctaveInterestPointFinder(InterestPointDetector<T> detector, IPDSelectionMode selectionMode) { 073 super(detector, selectionMode); 074 } 075 076 @Override 077 public void finish() { 078 LocalFeatureList<InterestPointKeypoint<T>> locatedFeatures = this.listener.getFeatures(); 079 TreeSet<Integer> toRemove = new TreeSet<Integer>(); 080 for (int i = 0; i < locatedFeatures.size(); i++) { 081 InterestPointKeypoint<T> kp1 = locatedFeatures.get(i); 082 for (int j = i+1; j < locatedFeatures.size(); j++) { 083 InterestPointKeypoint<T> kp2 = locatedFeatures.get(j); 084 if(similarTo(kp1,kp2)){ 085 if(kp1.location.score >= kp2.location.score){ 086 toRemove.add(j); 087 } 088 else{ 089 toRemove.add(i); 090 } 091 } 092 } 093 } 094 int nRemove = 0; 095 for(int index : toRemove){ 096 locatedFeatures.remove(index - nRemove++); 097 } 098 } 099 100 private boolean similarTo(InterestPointKeypoint<T> kp1,InterestPointKeypoint<T> kp2) { 101 boolean similar = true; 102 // Similar position 103 similar = Math.sqrt(Math.pow(kp1.x -kp2.x,2) + Math.pow(kp1.y -kp2.y,2)) < maxDistance ; 104 if(!similar) return false; 105 Ellipse e1 = kp1.location.getEllipse(); 106 Ellipse e2 = kp2.location.getEllipse(); 107 // Ellipse with a similar rotation 108 similar = Math.abs(e1.getRotation() - e2.getRotation()) < maxRotation; 109 if(!similar) return false; 110 111 // Similar semi-major and semi-minor axis ratio 112 similar = Math.abs((e1.getMinor()/e1.getMajor()) - (e2.getMinor()/e2.getMajor())) < maxAxisRatio ; 113 if(!similar) return false; 114 115 return true; 116 } 117 118}