contrib/rpl/rgrl/rgrl_est_affine.cxx
Go to the documentation of this file.
00001 #include "rgrl_est_affine.h"
00002 //:
00003 // \file
00004 // \author Amitha Perera
00005 // \date   Feb 2003
00006 
00007 #include <vcl_cassert.h>
00008 
00009 #include <vnl/algo/vnl_svd.h>
00010 #include <vnl/vnl_math.h>
00011 #include "rgrl_trans_affine.h"
00012 #include "rgrl_match_set.h"
00013 
00014 rgrl_est_affine::
00015 rgrl_est_affine( unsigned int dimension )
00016 {
00017   // Derive the parameter_dof from the dimension
00018   //
00019   unsigned int param_dof = (dimension == 2)? 6 : 12;
00020 
00021   // Pass the two variable to the parent class, where they're stored
00022   //
00023   rgrl_estimator::set_param_dof( param_dof );
00024 }
00025 
00026 
00027 rgrl_transformation_sptr
00028 rgrl_est_affine::
00029 estimate( rgrl_set_of<rgrl_match_set_sptr> const& matches,
00030           rgrl_transformation const& /*cur_transform*/ ) const
00031 {
00032   // Iterators to go over the matches
00033   //
00034   typedef rgrl_match_set::const_from_iterator FIter;
00035   typedef FIter::to_iterator TIter;
00036 
00037   // The dimensionality of the space we are working in. Find it by
00038   // looking at the dimension of one of the data points.
00039   //
00040   unsigned ms = 0;
00041   while ( ms < matches.size() &&
00042           matches[ms]->from_begin() == matches[ms]->from_end() )
00043     ++ms;
00044   if ( ms == matches.size() ) {
00045     DebugMacro(0, "No data!\n");
00046     return 0; // no data!
00047   }
00048   const unsigned int m = matches[ms]->from_begin().from_feature()->location().size();
00049   assert ( m>=1 );
00050 
00051   // ----------------------------
00052   // Create the constraint matrix. See "Affine transform with a
00053   // projector matrix" in notes.tex.
00054 
00055   // Holds \tilde{p} \tilde{p}^t
00056   //
00057   // This matrix will contain the equivalent of
00058   //   [  x^2  xy  xz  x
00059   //      xy   y^2 yz  y
00060   //      xz   yz  z^2 z
00061   //      x    y   z   1 ]
00062   // for this dimension.
00063   //
00064   vnl_matrix<double> ptp( m+1, m+1 );
00065 
00066   vnl_vector<double> Bq( m );
00067 
00068   // The affine problem can be written as Xp=y. Then, the WLS solution
00069   // is p = inv(X^t W X)  X^t W y. See notes.tex.
00070   //
00071   // We use all the constraints from all the match sets to develop a
00072   // single linear system for the affine transformation.
00073   //
00074   vnl_matrix<double> XtWX( m*(m+1), m*(m+1) );
00075   vnl_vector<double> XtWy( m*(m+1) );
00076   XtWX.fill( 0.0 );
00077   XtWy.fill( 0.0 );
00078 
00079   // Determine the weighted centres for the affine transformation. We
00080   // take the centres of all the points in all the match sets.
00081   //
00082   vnl_vector<double> from_centre( m, 0.0 );
00083   vnl_vector<double> to_centre( m, 0.0 );
00084   vnl_vector<double> from_pt( m );
00085   vnl_vector<double> to_pt( m );
00086   double sum_wgt = 0.0;
00087   unsigned count=0;  //for debugging
00088   for ( unsigned ms=0; ms < matches.size(); ++ms ) {
00089     rgrl_match_set const& match_set = *matches[ms];
00090     for ( FIter fi = match_set.from_begin(); fi != match_set.from_end(); ++fi ) {
00091       for ( TIter ti = fi.begin(); ti != fi.end(); ++ti ) {
00092         double const wgt = ti.cumulative_weight();
00093         from_pt = fi.from_feature()->location();
00094         from_pt *= wgt;
00095         from_centre += from_pt;
00096         to_pt = ti.to_feature()->location();
00097         to_pt *= wgt;
00098         to_centre   += to_pt;
00099         sum_wgt += wgt;
00100       }
00101     }
00102   }
00103   
00104   // if the weight is too small or zero,
00105   // that means there is no good match
00106   if( sum_wgt < 1e-13 ) {
00107     return 0;
00108   }
00109   
00110   from_centre /= sum_wgt;
00111   to_centre /= sum_wgt;
00112 
00113 
00114   // Since XtWX is symmetric, we only compute the upper triangle, and
00115   // copy it later into the lower triangle.
00116   //
00117   for ( unsigned ms=0; ms < matches.size(); ++ms ) {
00118     rgrl_match_set const& match_set = *matches[ms];
00119     for ( FIter fi = match_set.from_begin(); fi != match_set.from_end(); ++fi ) {
00120       for ( TIter ti = fi.begin(); ti != fi.end(); ++ti ) {
00121         from_pt = fi.from_feature()->location();
00122         from_pt -= from_centre;
00123         to_pt = ti.to_feature()->location();
00124         to_pt -= to_centre;
00125         vnl_matrix<double> const& B = ti.to_feature()->error_projector();
00126         double const wgt = ti.cumulative_weight();
00127 
00128         assert ( from_pt.size() == m );
00129         assert ( to_pt.size() == m );
00130         assert ( B.cols() == m && B.rows() == m );
00131         ++count;
00132 
00133         // Compute ptp.
00134         //
00135         for ( unsigned i=0; i < m; ++i ) {
00136           for ( unsigned j=0; j < m; ++j ) {
00137             ptp(i,j) = from_pt[i] * from_pt[j];
00138           }
00139           ptp(i,m) = from_pt[i];
00140           ptp(m,i) = from_pt[i];
00141         }
00142         ptp(m,m) = 1.0;
00143 
00144         // Construct the upper half of B \kron (p^t p) and add into XtWX
00145         //
00146         for ( unsigned i=0; i < m; ++i ) { // index into B
00147           for ( unsigned j=i; j < m; ++j ) {
00148             if ( B(i,j) != 0.0 ) {
00149               double wBij = wgt * B(i,j);
00150               unsigned off_r = i*(m+1); // offsets in XtWX
00151               unsigned off_c = j*(m+1);
00152               for ( unsigned r=0; r < m+1; ++r ) {  // index into ptp
00153                 for ( unsigned c=0; c < m+1; ++c ) {
00154                   XtWX(off_r+r,off_c+c) += wBij * ptp(r,c);
00155                 }
00156               }
00157             }
00158           }
00159         }
00160 
00161         // Construct Bq \kron p and copy into XtWy
00162         //
00163         Bq = B * to_pt;
00164         for ( unsigned i=0; i < m; ++i ) {
00165           double wBqi = wgt * Bq[i];
00166           unsigned off = i*(m+1); // offset in XtWy
00167           for ( unsigned j=0; j < m; ++j ) {
00168             XtWy[off+j] += wBqi * from_pt[j];
00169           }
00170           XtWy[off+m] += wBqi;
00171         }
00172       }
00173     }
00174   }
00175 
00176   // Complete XtWX by copying the upper triangle into the lower
00177   // triangle.
00178   //
00179   for ( unsigned i=0; i < m*(m+1); ++i ) {
00180     for ( unsigned j=0; j < i; ++j ) {
00181       XtWX(i,j) = XtWX(j,i);
00182     }
00183   }
00184 
00185   // Find the scales and scale the matrices appropriate to normalize
00186   // them and increase the numerical stability.
00187   //
00188   double factor0, factor1;
00189   vnl_vector<double> s(m*(m+1), 1);
00190   if ( m == 2) {
00191     factor0 = vnl_math_max(XtWX(2,2),XtWX(5,5));
00192     factor1 = vnl_math_max(vnl_math_max(XtWX(0,0), XtWX(1,1)),
00193                            vnl_math_max(XtWX(3,3), XtWX(4,4)));
00194     double scale = vcl_sqrt( (factor1 > 0 && factor0 > 0) ? factor1 / factor0 : 1 );   // neither should be 0
00195     s(2) = s(5) = scale;
00196   }
00197   else {
00198     factor0 = vnl_math_max( vnl_math_max( XtWX(3,3), XtWX(7,7) ),
00199                             XtWX(11, 11) );
00200     factor1 = vnl_math_max( vnl_math_max( vnl_math_max( vnl_math_max( XtWX(0,0), XtWX(1,1) ),
00201                                                         vnl_math_max( XtWX(2,2), XtWX(4,4) ) ),
00202                                           vnl_math_max( vnl_math_max( XtWX(5,5), XtWX(6,6) ),
00203                                                         vnl_math_max( XtWX(8,8), XtWX(9,9) ) ) ),
00204                             XtWX(10,10) );
00205     double scale = vcl_sqrt( (factor1 > 0 && factor0 > 0) ? factor1 / factor0 : 1 );   // neither should be 0
00206     s(3) = s(7) = s(11) = scale;
00207     DebugMacro(1, "rgrl_est_affine: scale factors = " << s << '\n' );
00208   }
00209 
00210   for ( unsigned i=0; i< m*(m+1); i++ ) {
00211     XtWy(i) *= s(i);
00212     for ( unsigned j=0; j< m*(m+1); j++ )
00213       XtWX(i,j) *= s(i) * s(j);
00214   }
00215 
00216   // ----------------------------
00217   // Compute the solution
00218 
00219   vnl_svd<double> svd( XtWX );
00220 
00221   // Due to floating point inaccuracies, some zero singular values may
00222   // look non-zero, so we correct for that.
00223   svd.zero_out_relative();
00224 
00225   // Use pseudo inverse
00226   if ( (unsigned)svd.rank() < (m+1)*m ) {
00227     DebugMacro(1, "rank ("<<svd.rank()<<") < "<<(m+1)*m<<"; no solution." );
00228     DebugMacro_abv(1, "(used " << count << " correspondences)\n" );
00229     DebugMacro_abv(1, "use pseudo inverse instead\n" );
00230     return 0;
00231   }
00232 
00233   // Compute the solution into XtWy
00234   //
00235   vnl_matrix<double> covar = svd.inverse();
00236   XtWy.pre_multiply( covar );
00237 
00238   // Eliminate the scale of XtWX
00239   //
00240   for ( unsigned i=0; i< m*(m+1); i++ ) {
00241     for ( unsigned j=0; j< m*(m+1); j++ )
00242       covar(i,j) *= s(i) * s(j);
00243   }
00244   for ( unsigned i=0; i< m*(m+1); i++ ) {
00245     XtWy(i) *= s(i);
00246   }
00247 
00248   // Copy the solution into the result variables, and construct a
00249   // transformation object.
00250 
00251   // Translation component
00252   vnl_vector<double> trans( m );
00253   for ( unsigned i=0; i < m; ++i ) {
00254     trans[i] = XtWy[ i*(m+1)+m ];
00255   }
00256 
00257   // Matrix component
00258   vnl_matrix<double> A( m, m );
00259   for ( unsigned i=0; i < m; ++i ) {
00260     for ( unsigned j=0; j < m; ++j ) {
00261       A(i,j) = XtWy[ i*(m+1)+j ];
00262     }
00263   }
00264 
00265   DebugMacro(1, "A =\n" << A << "T =\n" << trans-A*from_centre+to_centre << '\n' );
00266 
00267   return new rgrl_trans_affine( A, trans, covar, from_centre, to_centre );
00268 }
00269 
00270 
00271 rgrl_transformation_sptr
00272 rgrl_est_affine::
00273 estimate( rgrl_match_set_sptr matches,
00274           rgrl_transformation const& cur_transform ) const
00275 {
00276   // use base class implementation
00277   return rgrl_estimator::estimate( matches, cur_transform );
00278 }
00279 
00280 const vcl_type_info&
00281 rgrl_est_affine::
00282 transformation_type() const
00283 {
00284   return rgrl_trans_affine::type_id();
00285 }