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