contrib/mul/mbl/mbl_correspond_points.cxx
Go to the documentation of this file.
00001 // This is mul/mbl/mbl_correspond_points.cxx
00002 #include "mbl_correspond_points.h"
00003 //:
00004 // \file
00005 // \brief Shapiro & Brady's point correspondence algorithm
00006 // \author Tim Cootes
00007 
00008 #include <vcl_cmath.h>
00009 #include <vgl/vgl_distance.h>
00010 #include <vnl/vnl_math.h>
00011 #include <vnl/algo/vnl_symmetric_eigensystem.h>
00012 #include <vcl_algorithm.h>
00013 
00014 //=======================================================================
00015 // Dflt ctor
00016 //=======================================================================
00017 
00018 mbl_correspond_points::mbl_correspond_points()
00019 {
00020 }
00021 
00022 //: Return index of row in H2 most similar to row i of H1
00023 unsigned mbl_correspond_points::closest_row(const vnl_matrix<double>& H1,
00024                                             const vnl_matrix<double>& H2,
00025                                             unsigned i1)
00026 {
00027   unsigned nc = vcl_min(H1.cols(),H2.cols());
00028   unsigned best_i = 0;
00029   double best_d2 = -1.0;
00030   const double* h1 = &H1(i1,0);
00031   for (unsigned i=0;i<H2.rows();++i)
00032   {
00033     const double* h2 = &H2(i,0);
00034     double sum=0.0;
00035     for (unsigned j=0;j<nc;++j)
00036     {
00037       double d = h1[j]-h2[j];
00038       sum += d*d;
00039     }
00040     if (best_d2 < 0 || sum<best_d2)
00041     {
00042       best_d2=sum;
00043       best_i = i;
00044     }
00045   }
00046 
00047   return best_i;
00048 }
00049 
00050 //: Ensure each column vector points in the same way
00051 void mbl_correspond_points::fix_eigenvectors(vnl_matrix<double>& P)
00052 {
00053   for (unsigned j=0;j<P.cols();++j)
00054   {
00055     vnl_vector<double> p = P.get_column(j);
00056     if (p.sum()<0) { p*=-1.0; P.set_column(j,p); }
00057   }
00058 }
00059 
00060 //: Find best correspondence between points1 and points2
00061 //  On exit, matches[i] gives index of points2 which
00062 //  corresponds to points1[i].
00063 //  \param sigma Scaling factor defining kernel width
00064 void mbl_correspond_points::correspond(const vcl_vector<vgl_point_2d<double> >& points1,
00065                                        const vcl_vector<vgl_point_2d<double> >& points2,
00066                                        vcl_vector<unsigned>& matches, double sigma)
00067 {
00068   unsigned n1 = points1.size();
00069   unsigned n2 = points2.size();
00070 
00071   vnl_matrix<double> H1(n1,n1),H2(n2,n2);
00072   proximity_by_tanh(points1,H1,sigma);
00073   proximity_by_tanh(points2,H2,sigma);
00074 
00075 
00076   // Compute eigen structure of each proximity
00077   vnl_matrix<double> P1(n1,n1),P2(n2,n2);
00078   evals1_.set_size(n1);
00079   evals2_.set_size(n2);
00080   vnl_symmetric_eigensystem_compute(H1,P1,evals1_);
00081   vnl_symmetric_eigensystem_compute(H2,P2,evals2_);
00082 
00083   // Arrange that values/vectors ordered with largest first
00084   P1.fliplr(); evals1_.flip();
00085   P2.fliplr(); evals2_.flip();
00086 
00087   // Directions of eigenvectors are ambiguous.
00088   // Ensure they're all facing the same way.
00089   fix_eigenvectors(P1);
00090   fix_eigenvectors(P2);
00091 
00092   // Select best matches based on row correspondence
00093   // Note that there may be a many to one correspondence here.
00094   matches.resize(n1);
00095   for (unsigned i=0;i<n1;++i)
00096     matches[i] = closest_row(P1,P2,i);
00097 }
00098 
00099 //: Construct distance matrix using cosh kernel
00100 //  On exit, D(i,j) = tanh(pi*d_ij/sigma) * 2/(pi*d_ij)
00101 //  where d_ij is the distance between points i and j
00102 void mbl_correspond_points::proximity_by_tanh(const vcl_vector<vgl_point_2d<double> >& points,
00103                                               vnl_matrix<double>& H, double sigma)
00104 {
00105   const unsigned n = points.size();
00106   const vgl_point_2d<double> *p = &points[0];
00107   H.set_size(n,n);
00108 
00109   const double k1 = 2.0/vnl_math::pi;
00110   const double k2 = vnl_math::pi/sigma;
00111 
00112   for (unsigned i=0;i<n;++i)
00113   {
00114     H(i,i)=0.0;
00115     for (unsigned j=i+1;j<n;++j)
00116     {
00117       double d = vgl_distance(p[i],p[j]);
00118       H(i,j) = H(j,i) = k1*vcl_tanh(k2*d)/d;
00119     }
00120   }
00121 }