contrib/rpl/rgrl/rgrl_est_similarity2d.cxx
Go to the documentation of this file.
00001 //:
00002 // \file
00003 // \author Charlene Tsai
00004 // \date   Sep 2003
00005 
00006 #include "rgrl_est_similarity2d.h"
00007 
00008 #include <vcl_cassert.h>
00009 #include <vnl/algo/vnl_svd.h>
00010 #include <vnl/vnl_math.h>
00011 #include <vnl/vnl_matrix_fixed.h>
00012 #include <vnl/vnl_vector_fixed.h>
00013 #include "rgrl_trans_similarity.h"
00014 #include "rgrl_match_set.h"
00015 
00016 rgrl_est_similarity2d::
00017 rgrl_est_similarity2d( unsigned int dimension )
00018 {
00019   assert (dimension == 2);
00020   // Derive the parameter_dof from the dimension
00021   //
00022   unsigned int param_dof = 2*dimension; //It is always for 2D
00023 
00024   // Pass the two variable to the parent class, where they're stored
00025   //
00026   rgrl_estimator::set_param_dof( param_dof );
00027 }
00028 
00029 rgrl_transformation_sptr
00030 rgrl_est_similarity2d::
00031 estimate( rgrl_set_of<rgrl_match_set_sptr> const& matches,
00032           rgrl_transformation const& /*cur_transform*/ ) const
00033 {
00034   // Iterators to go over the matches
00035   //
00036   typedef rgrl_match_set::const_from_iterator FIter;
00037   typedef FIter::to_iterator TIter;
00038 
00039   // The dimensionality of the space we are working in. Find it by
00040   // looking at the dimension of one of the data points.
00041   //
00042   unsigned ms = 0;
00043   while ( ms < matches.size() &&
00044           matches[ms]->from_begin() == matches[ms]->from_end() )
00045     ++ms;
00046   if ( ms == matches.size() ) {
00047     DebugMacro( 0, "No data!\n" );
00048     return 0; // no data!
00049   }
00050   const unsigned int m = matches[ms]->from_begin().from_feature()->location().size();
00051   assert ( m==2 ); // only 2D similarity2d estimation
00052 
00053   // ----------------------------
00054   // Create the constraint matrix. See "2D similarity transform with a
00055   // projector matrix" in notes.tex.
00056   //
00057   // The similarity problem can be written as Xc=y. Then, the WLS solution
00058   // is c = inv(X^t W X)  X^t W y. See notes.tex.
00059   //
00060   // We use all the constraints from all the match sets to develop a
00061   // single linear system for the affine transformation.
00062   //
00063   vnl_matrix_fixed<double, 4, 4> XtWX;
00064   vnl_vector_fixed<double, 4> XtWy;
00065   XtWX.fill( 0.0 );
00066   XtWy.fill( 0.0 );
00067 
00068   // Determine the weighted centres for the similarity transformation. We
00069   // take the centres of all the points in all the match sets.
00070   //
00071   vnl_vector_fixed<double, 2> from_centre( 0.0, 0.0 );
00072   vnl_vector_fixed<double, 2> to_centre( 0.0, 0.0 );
00073   vnl_vector_fixed<double, 2> from_pt;
00074   vnl_vector_fixed<double, 2> to_pt;
00075   vnl_vector_fixed<double, 4> DtBq;
00076   vnl_matrix_fixed<double, 2, 4> D; // holds [px -py 1 0; py px 0 1]
00077   vnl_matrix_fixed<double, 4, 2> Dt; // holds [px -py 1 0; py px 0 1]^T
00078   vnl_matrix_fixed<double, 4, 2> DtB;      // holds product of D^T * B
00079   double sum_wgt = 0.0;
00080   D.fill( 0.0 );
00081   Dt.fill( 0.0 );
00082   for ( unsigned ms=0; ms < matches.size(); ++ms ) {
00083     rgrl_match_set const& match_set = *matches[ms];
00084     for ( FIter fi = match_set.from_begin(); fi != match_set.from_end(); ++fi ) {
00085       for ( TIter ti = fi.begin(); ti != fi.end(); ++ti ) {
00086         double const wgt = ti.cumulative_weight();
00087         from_pt = fi.from_feature()->location();
00088         from_pt *= wgt;
00089         from_centre += from_pt;
00090         to_pt = ti.to_feature()->location();
00091         to_pt *= wgt;
00092         to_centre   += to_pt;
00093         sum_wgt += wgt;
00094       }
00095     }
00096   }
00097   // if the weight is too small or zero,
00098   // that means there is no good match
00099   if ( sum_wgt < 1e-13 ) {
00100     return 0;
00101   }
00102 
00103   from_centre /= sum_wgt;
00104   to_centre /= sum_wgt;
00105 
00106   // Compute XtWX is symmetric.
00107   //
00108   unsigned count=0;  //for debugging
00109   for ( unsigned ms=0; ms < matches.size(); ++ms ) {
00110     rgrl_match_set const& match_set = *matches[ms];
00111     for ( FIter fi = match_set.from_begin(); fi != match_set.from_end(); ++fi ) {
00112       for ( TIter ti = fi.begin(); ti != fi.end(); ++ti ) {
00113         from_pt = fi.from_feature()->location();
00114         from_pt -= from_centre;
00115         to_pt = ti.to_feature()->location();
00116         to_pt -= to_centre;
00117         vnl_matrix<double> const& B = ti.to_feature()->error_projector();
00118         double const wgt = ti.cumulative_weight();
00119 
00120         assert( from_pt.size() == m );
00121         assert( to_pt.size() == m );
00122         assert( B.cols() == m && B.rows() == m );
00123         ++count;
00124 
00125         // For each constraint, add w*DtBD to XtWX
00126         D(0,0) = from_pt[0]; D(0,1) = -from_pt[1]; D(0,2) = 1;
00127         D(1,0) = from_pt[1]; D(1,1) =  from_pt[0]; D(1,3) = 1;
00128         // transpose D explicitly for efficiency
00129         Dt(0,0) = from_pt[0]; Dt(1,0) = -from_pt[1]; Dt(2,0) = 1;
00130         Dt(0,1) = from_pt[1]; Dt(1,1) =  from_pt[0]; Dt(3,1) = 1;
00131 
00132         DtB = Dt * B;
00133         XtWX += (DtB * D) * wgt;
00134 
00135         // add w*DtBq to XtWy
00136         // DtBq = to_pt.pre_multiply( DtB );
00137         DtBq = DtB * to_pt;
00138         for ( unsigned i = 0; i<4; ++i)
00139           XtWy[i] += wgt * DtBq[i];
00140       }
00141     }
00142   }
00143 
00144   // Find the scales and scale the matrices appropriate to normalize
00145   // them and increase the numerical stability.
00146   double factor0 = vnl_math_max(XtWX(2,2),XtWX(3,3));
00147   double factor1 = vnl_math_max(XtWX(1,1),XtWX(0,0));
00148   double scale = vcl_sqrt( (factor1 > 0 && factor0 > 0) ? factor1 / factor0 : 1 );   // neither should be 0
00149 
00150   vnl_vector_fixed<double, 4> s;
00151   s(2) = s(3) = scale; s(0) = s(1) = 1;
00152   for ( int i=0; i<4; i++ ) {
00153     XtWy(i) *= s(i);
00154     for ( int j=0; j<4; j++ )
00155       XtWX(i,j) *= s(i) * s(j);
00156   }
00157 
00158 
00159   // ----------------------------
00160   // Compute the solution
00161 
00162   vnl_svd<double> svd( XtWX.as_ref() );
00163 
00164   // Due to floating point inaccuracies, some zero singular values may
00165   // look non-zero, so we correct for that.
00166   svd.zero_out_relative();
00167 
00168   if ( (unsigned)svd.rank() < 4) {
00169     DebugMacro(1, "rank ("<<svd.rank()<<") < 4; no solution." );
00170     DebugMacro_abv(1, "(used " << count << " correspondences)\n" );
00171     return 0; // no solution
00172   }
00173 
00174   // Compute the solution into XtWy
00175   //
00176   vnl_matrix_fixed<double, 4, 4> covar = svd.inverse();
00177   XtWy = covar * XtWy;
00178 
00179   // Eliminate the scale of XtWX
00180   //
00181   for ( int i=0; i<4; i++ ) {
00182     for ( int j=0; j<4; j++ )
00183       covar(i,j) *= s(i) * s(j);
00184   }
00185   for ( int i=0; i<4; i++ ) {
00186     XtWy(i) *= s(i);
00187   }
00188 
00189   // Copy the solution into the result variables, and construct a
00190   // transformation object.
00191 
00192   // Translation component
00193   vnl_vector<double> trans( m );
00194   trans[0] = XtWy[2];
00195   trans[1] = XtWy[3];
00196 
00197   // Matrix component
00198   vnl_matrix<double> A( m, m );
00199   A(0,0) =  A(1,1) = XtWy[0];
00200   A(0,1) =  -XtWy[1];
00201   A(1,0) = -A(0,1);
00202 
00203   return new rgrl_trans_similarity( A, trans, covar.as_ref(), from_centre.as_ref(), to_centre.as_ref() );
00204 }
00205 
00206 
00207 rgrl_transformation_sptr
00208 rgrl_est_similarity2d::
00209 estimate( rgrl_match_set_sptr matches,
00210           rgrl_transformation const& cur_transform ) const
00211 {
00212   // use base class implementation
00213   return rgrl_estimator::estimate( matches, cur_transform );
00214 }
00215 
00216 const vcl_type_info&
00217 rgrl_est_similarity2d::
00218 transformation_type() const
00219 {
00220   return rgrl_trans_similarity::type_id();
00221 }