package fr.unistra.pelican.util.data.distances;
import java.util.ArrayList;
import java.util.HashMap;
import fr.unistra.pelican.util.Keypoint;
import fr.unistra.pelican.util.data.Data;
import fr.unistra.pelican.util.data.KeypointArrayData;
/**
* Should probably be integrated to SURF ... 'will think about that later :/
* @author Régis Witz
*/
public class KeypointArraySURFDistance extends SURFDistance {
@Override
@SuppressWarnings("unchecked")
public double distance( Data d1, Data d2 ) {
ArrayList<Keypoint> values =
( ArrayList<Keypoint> ) ( ( KeypointArrayData ) d1 ).getValues();
ArrayList<Keypoint> values2 =
( ArrayList<Keypoint> ) ( ( KeypointArrayData ) d2 ).getValues();
int nbpoints = values.size();
int len1 = values.get(0).getDescLength();
int len2 = values2.get(0).getDescLength();
if( len1 != len2 ) {
System.err.println( "Incompatible keypoint descriptors lengths !" );
return 1;
}
int matchescount = 0; // total number of keypoints wich matched
int match;
for ( Keypoint key : values ) {
match = KeypointArraySURFDistance.match( key, values2 );
if ( match > -1 ) matchescount++; // found one !
}
double distance = 1;
if ( nbpoints > 0 ) distance = 1 - ( matchescount / nbpoints );
assert 0 <= distance && distance <= 1 :
this.getClass().getName() + " ¤[0;1] unverified : " + distance + ".";
return distance;
}
/** Matches <tt>this</tt> with a list of interest points <tt>keys</tt>. If one,
* returns the position in <tt>keys</tt> of the closest interest point.
* @param point Point to match with <tt>points</tt>.
* @param points Points to be matched with <tt>point</tt>.
* @return The position of the closest keypoint, or -1 if there is no
* point wich is close enough to <tt>this</tt>.
*/
public static int match( Keypoint point, ArrayList<Keypoint> points ) {
double mind = Double.MAX_VALUE, second = Double.MAX_VALUE;
double v1,v2;
int len, count = -1, match = -1;
Double[] desc = ( Double[] ) point.data.getValues();
Double[] desc2;
for ( Keypoint key : points ) {
count++;
desc2 = ( Double[] ) key.data.getValues();
// take advantage of Laplacian to speed up matching
if ( !desc2[1].equals( desc[1] ) ) continue;
len = key.getDescLength();
if ( len < 0 || len != point.getDescLength() ) continue;
// calculate the square distance between this.descriptor and key.descriptor
double d = 0.;
for ( int i = 3 ; i < len ; i++ ) {
v1 = desc2[i];
v2 = desc[i];
d += ( v1-v2 )*( v1-v2 );
}
if ( d < mind ) {
second = mind;
mind = d;
match = count;
} else if ( d < second ) second = d;
}
if ( mind < 0.5 * second ) return match;
return -1;
}
/** Match a <tt>KeypointArrayData</tt> with <tt>this</tt> and get the correspondances
* between the keypoints who matched.
* @param d1 A <tt>KeypointArrayData</tt> with wich <tt>d2</tt> must be compared.
* @param d2 A <tt>KeypointArrayData</tt> with wich <tt>d1</tt> must be compared.
* @return An {@link HashMap} : the keys are the matching points from <tt>this</tt> and
* the values are their echoes from <tt>data</tt>.
*/
@SuppressWarnings("unchecked")
public HashMap<Keypoint,Keypoint> getMatches( Data d1, Data d2 ) {
ArrayList<Keypoint> values =
( ArrayList<Keypoint> ) ( ( KeypointArrayData ) d1 ).getValues();
ArrayList<Keypoint> values2 =
( ArrayList<Keypoint> ) ( ( KeypointArrayData ) d2 ).getValues();
int nbkp1 = values.size();
int nbkp2 = values2.size();
if( nbkp1 == 0 || nbkp2 == 0 ) return null;
int kpsize = values.get(0).getDescLength();
int kpsize2 = values2.get(0).getDescLength();
if( kpsize != kpsize2 ) return null;
HashMap<Keypoint,Keypoint> map = new HashMap<Keypoint,Keypoint>();
int match;
for ( Keypoint key : values ) {
match = KeypointArraySURFDistance.match( key, values2 );
if ( match > -1 ) map.put( key, values2.get( match ) );
}
return map;
}
}