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