contrib/rpl/rgrl/rgrl_est_rigid.cxx
Go to the documentation of this file.
00001 //:
00002 // \file
00003 // \author Tomasz Malisiewicz
00004 // \date   March 2004
00005 
00006 #include "rgrl_est_rigid.h"
00007 
00008 #include <vcl_cassert.h>
00009 #include <vnl/algo/vnl_svd.h>
00010 #include <vnl/algo/vnl_determinant.h>
00011 #if 0
00012 #include <vnl/algo/vnl_matrix_inverse.h>
00013 #endif
00014 #include "rgrl_trans_rigid.h"
00015 #include "rgrl_match_set.h"
00016 
00017 rgrl_est_rigid::
00018 rgrl_est_rigid( unsigned int dimension )
00019 {
00020   // only allow 2d and 3d estimation
00021   assert(dimension == 2 || dimension == 3);
00022 
00023   unsigned int param_dof = (dimension == 3)?6 : 3;
00024 
00025   // Pass the two variable to the parent class, where they're stored
00026   //
00027   rgrl_estimator::set_param_dof( param_dof );
00028 }
00029 
00030 rgrl_transformation_sptr
00031 rgrl_est_rigid::
00032 estimate( rgrl_set_of<rgrl_match_set_sptr> const& matches,
00033           rgrl_transformation const& cur_trans_in ) const
00034 {
00035   // Get the current rgrl_trans_rigid instance.
00036   const rgrl_trans_rigid* cur_trans =
00037     dynamic_cast<const rgrl_trans_rigid*>(&cur_trans_in);
00038   assert(cur_trans);
00039 
00040   // so we want to have some sort of state, but we don't really want to un-const-ize this method,
00041   // so we implement a quick hack by using const_cast to change the value of stats
00042   vcl_vector<vcl_vector<double> >* pp =
00043     const_cast<vcl_vector<vcl_vector<double> >* >(&stats);
00044 
00045   // reset the stats before this new run
00046   pp->clear();
00047   vcl_vector<double> cur_stat;
00048 
00049   // Make a copy of the current transform that we can modify.
00050   rgrl_transformation_sptr current_trans =
00051     new rgrl_trans_rigid(cur_trans->R(), cur_trans->t());
00052 
00053   // Iterators to go over the matches
00054   //
00055   typedef rgrl_match_set::const_from_iterator FIter;
00056   typedef FIter::to_iterator TIter;
00057 
00058   // The dimensionality of the space we are working in. Find it by
00059   // looking at the dimension of one of the data points.
00060   //
00061   unsigned ms = 0;
00062   while ( ms < matches.size() &&
00063           matches[ms]->from_begin() == matches[ms]->from_end() )
00064     ++ms;
00065   if ( ms == matches.size() ) {
00066     DebugMacro( 0, "No data!\n" );
00067     return 0; // no data!
00068   }
00069   const unsigned int m = matches[ms]->from_begin().from_feature()->location().size();
00070   assert ( m==3 ); // currently only 3D estimation is implemented
00071 
00072   int numiterations = 1;
00073   do
00074   {
00075     cur_stat.clear();
00076 
00077     // This problem can be written as Xp=y. Then, the Weighted Least Squares solution
00078     // is p = inv(X^t W X)  X^t W y.
00079     //
00080     // We use all the constraints from all the match sets to develop a
00081     // single linear system for the rigid transformation.
00082     //
00083     vnl_matrix<double> XtWX( 6, 6 );
00084     vnl_vector<double> XtWy( 6 );
00085     XtWX.fill( 0.0 );
00086     XtWy.fill( 0.0 );
00087 
00088     unsigned count=0;
00089 
00090     vnl_vector<double> from_pt( m );
00091     vnl_vector<double> to_pt( m );
00092     for (unsigned int ms=0; ms < matches.size(); ++ms )
00093     {
00094       rgrl_match_set const& match_set = *matches[ms];
00095       for (FIter fi = match_set.from_begin(); fi != match_set.from_end(); ++fi )
00096       {
00097         for (TIter ti = fi.begin(); ti != fi.end(); ++ti )
00098         {
00099           from_pt = fi.from_feature()->location();
00100           to_pt = ti.to_feature()->location();
00101           vnl_matrix<double> const& B = ti.to_feature()->error_projector();
00102 
00103           double const wgt = ti.cumulative_weight();
00104 
00105           assert( from_pt.size() == m );
00106           assert( to_pt.size() == m );
00107           assert( B.cols() == m && B.rows() == m );
00108           ++count;
00109 
00110           // form XX matrix and RR vector
00111           vnl_matrix<double> XX(3,6,0.0);
00112           vnl_vector<double> RR(3,0.0);
00113 
00114           vnl_vector<double> g(current_trans->map_location(from_pt));
00115           vnl_vector<double> f(to_pt);
00116 
00117           XX(0,0)=0;      XX(0,1)=g[2];   XX(0,2)=-g[1];  XX(0,3)=1;
00118           XX(1,0)=-g[2];  XX(1,1)=0;      XX(1,2)=g[0];   XX(1,4)=1;
00119           XX(2,0)=g[1];   XX(2,1)=-g[0];  XX(2,2)=0;      XX(2,5)=1;
00120 
00121           RR=f-g;
00122 
00123           XtWX+=XX.transpose()*B*XX*wgt;
00124           XtWy+=XX.transpose()*B*RR*wgt;
00125 
00126           //There are some differences in here from Amitha's implementation; however the math is equivalent since
00127           //the error projector is idempotent(B^2=B) and symmetric(B^T=B) (is this always true?)
00128         }
00129       }
00130     }
00131 
00132 
00133     // ----------------------------
00134     // Compute the solution
00135 
00136     vnl_svd<double> svd( XtWX );
00137 
00138     // Due to floating point inaccuracies, some zero singular values may
00139     // look non-zero, so we correct for that.
00140     svd.zero_out_relative();
00141 
00142     // Exit if not full rank
00143     if ( (unsigned)svd.rank() < m ) {
00144       DebugMacro(1, "rank ("<<svd.rank()<<") < "<<m<<"; no solution." );
00145       DebugMacro_abv(1, "(used " << count << " correspondences)\n" );
00146       return 0; // no solution
00147     }
00148 
00149     // Compute the solution into XtWy
00150     //
00151     vnl_matrix<double> covar = svd.inverse();
00152     XtWy.pre_multiply( covar );
00153 
00154     // Copy the solution into the result variables, and construct a
00155     // transformation object.
00156 
00157     // Translation component
00158     vnl_vector<double> trans( m );
00159     trans[0]=XtWy[3];
00160     trans[1]=XtWy[4];
00161     trans[2]=XtWy[5];
00162 
00163     //vcl_cerr<<"Estimated parameter vector in raw form is\n"<<XtWy<<vcl_endl;
00164 
00165     // Matrix component
00166     vnl_matrix<double> R( m, m );
00167     R(0,0)=1.0;      R(0,1)=-XtWy[2]; R(0,2)=XtWy[1];
00168     R(1,0)=XtWy[2];  R(1,1)=1.0;      R(1,2)=-XtWy[0];
00169     R(2,0)=-XtWy[1]; R(2,1)=XtWy[0];  R(2,2)=1.0;
00170 
00171     // Force the estimated "rotation" matrix to be a rotation matrix.
00172     //
00173 
00174     cur_stat.push_back(vnl_determinant(R));
00175     DebugMacro_abv(2, "about to orthonormalize with determinant "<<vnl_determinant(R)<<vcl_endl);
00176     vnl_svd<double> svdR( R );
00177 
00178     // Set singular values to unity
00179     double scale = 1;
00180     svdR.W(0) = scale;
00181     svdR.W(1) = scale;
00182     svdR.W(2) = scale;
00183     R = svdR.recompose();
00184 
00185     // The new estimate is incremental over the old one.
00186     rgrl_trans_rigid* old_sim = dynamic_cast<rgrl_trans_rigid* >( current_trans.as_pointer() );
00187     double fro_norm2 = trans.magnitude();
00188     trans += R * old_sim->t();
00189 
00190     vnl_matrix<double> I(3,3,vnl_matrix_identity);
00191     I -= R;
00192 
00193     double fro_norm1 = I.frobenius_norm();
00194 
00195 #ifdef DEBUG
00196     vcl_cerr<<"fro_norm of deltaT,deltatrans is "<<fro_norm1<<','<<fro_norm2<<vcl_endl;
00197 #endif
00198 
00199     cur_stat.push_back(fro_norm1);
00200     cur_stat.push_back(fro_norm2);
00201 
00202     // The new rotation is the old rotation followed by our
00203     // estimated incremental rotation.
00204     //
00205     R *= old_sim->R();
00206 
00207     current_trans = new rgrl_trans_rigid(R,trans,covar);
00208 
00209     // push back statistics before quitting
00210     pp->push_back(cur_stat);
00211 
00212     // this is the termination criterion: it is based on the fro_norm of the current iteration's
00213     // estimate of the rotation and the translation.  Iterations stop when new rotations and new translations
00214     // become negligible
00215     //if (fro_norm1<1e-4 && fro_norm2<1e-4)
00216     if (fro_norm1<1e-2 && fro_norm2<1e-1)
00217     {
00218       //std::cerr<<"----------- done rgrl_est_rigid w/ "<<numiterations<<" iterations----"<<std::endl;
00219       break;
00220     }
00221     //else
00222     //{
00223       //std::cerr<<"forbenius norms are: "<<fro_norm1<<" and "<<fro_norm2<<std::endl;
00224     //}
00225 
00226     numiterations++;
00227   } while (true);
00228 
00229   // at this point we should calculate the covariance matrix
00230   vnl_matrix<double> covar(3,3,vnl_matrix_identity);
00231 
00232   // TJM: turn this off for now since it is fairly computationally intensive
00233   //determine_covariance(matches,current_trans);
00234 
00235   return current_trans;
00236 
00237   //return new rgrl_trans_rigid(R,t,covar);
00238 }
00239 
00240 //: Determine the covariance matrix of this rigid xform given the matches
00241 void rgrl_est_rigid::determine_covariance( rgrl_set_of<rgrl_match_set_sptr> const& matches,
00242                                            rgrl_transformation_sptr current_trans) const
00243 {
00244   // first, we have to extract the angles from our linearized rotation matrix
00245   double alpha,theta,phi;
00246 
00247   rgrl_trans_rigid* tttt = dynamic_cast<rgrl_trans_rigid*>(current_trans.ptr());
00248 
00249   assert(tttt);
00250   tttt->determine_angles(phi,alpha,theta);
00251   //determine_angles(tttt->R(),phi,alpha,theta);
00252   vnl_vector<double> t = tttt->t();
00253 
00254 
00255   vnl_matrix<double> Rphi(3,3,0.0);
00256   vnl_matrix<double> Ralpha(3,3,0.0);
00257   vnl_matrix<double> Rtheta(3,3,0.0);
00258   vnl_matrix<double> Rphid(3,3,0.0);
00259   vnl_matrix<double> Ralphad(3,3,0.0);
00260   vnl_matrix<double> Rthetad(3,3,0.0);
00261 
00262   vnl_matrix<double> Rphidd(3,3,0.0);
00263   vnl_matrix<double> Ralphadd(3,3,0.0);
00264   vnl_matrix<double> Rthetadd(3,3,0.0);
00265 
00266 
00267   Rtheta(0,0) = vcl_cos(theta);
00268   Rtheta(0,1) = -vcl_sin(theta);
00269   Rtheta(1,0) = vcl_sin(theta);
00270   Rtheta(1,1) = vcl_cos(theta);
00271   Rtheta(2,2) = 1;
00272 
00273   Ralpha(0,0) = vcl_cos(alpha);
00274   Ralpha(0,2) = vcl_sin(alpha);
00275   Ralpha(1,1) = 1;
00276   Ralpha(2,0) = -vcl_sin(alpha);
00277   Ralpha(2,2) = vcl_cos(alpha);
00278 
00279   Rphi(0,0) = 1;
00280   Rphi(1,1) = vcl_cos(phi);
00281   Rphi(1,2) = -vcl_sin(phi);
00282   Rphi(2,1) = vcl_sin(phi);
00283   Rphi(2,2) = vcl_cos(phi);
00284 
00285   // derivative matrices now
00286   Rthetad(0,0) = -vcl_sin(theta);
00287   Rthetad(0,1) = -vcl_cos(theta);
00288   Rthetad(1,0) = vcl_cos(theta);
00289   Rthetad(1,1) = -vcl_sin(theta);
00290 
00291   Ralphad(0,0) = -vcl_sin(alpha);
00292   Ralphad(0,2) = vcl_cos(alpha);
00293   Ralphad(2,0) = -vcl_cos(alpha);
00294   Ralphad(2,2) = -vcl_sin(alpha);
00295 
00296   Rphid(1,1) = -vcl_sin(phi);
00297   Rphid(1,2) = -vcl_cos(phi);
00298   Rphid(2,1) = vcl_cos(phi);
00299   Rphid(2,2) = -vcl_sin(phi);
00300 
00301   //second derivative matrices
00302   Rthetadd(0,0) = -vcl_cos(theta);
00303   Rthetadd(0,1) = vcl_sin(theta);
00304   Rthetadd(1,0) = -vcl_sin(theta);
00305   Rthetadd(1,1) = -vcl_cos(theta);
00306 
00307   Ralphadd(0,0) = -vcl_cos(alpha);
00308   Ralphadd(0,2) = -vcl_sin(alpha);
00309   Ralphadd(2,0) = vcl_sin(alpha);
00310   Ralphadd(2,2) = -vcl_cos(alpha);
00311 
00312   Rphidd(1,1) = -vcl_cos(phi);
00313   Rphidd(1,2) = vcl_sin(phi);
00314   Rphidd(2,1) = -vcl_sin(phi);
00315   Rphidd(2,2) = -vcl_cos(phi);
00316 
00317 
00318   // now the entire rotation matrices
00319   vnl_matrix<double> R = Rphi * Ralpha * Rtheta;
00320   vnl_matrix<double> dRdphi  = Rphid * Ralpha  * Rtheta;
00321   vnl_matrix<double> dRdalpha = Rphi  * Ralphad * Rtheta;
00322   vnl_matrix<double> dRdtheta = Rphi  * Ralpha  * Rthetad;
00323 
00324   vnl_matrix<double> d2Rdphidtheta   = Rphid * Ralpha  * Rthetad;
00325   vnl_matrix<double> d2Rdphidalpha   = Rphid * Ralphad * Rtheta;
00326   vnl_matrix<double> d2Rdthetadalpha = Rphi  * Ralphad * Rthetad;
00327 
00328 
00329   vnl_matrix<double> d2Rdphi2   = Rphidd * Ralpha   * Rtheta;
00330   vnl_matrix<double> d2Rdalpha2 = Rphi   * Ralphadd * Rtheta;
00331   vnl_matrix<double> d2Rdtheta2 = Rphi   * Ralpha   * Rthetadd;
00332 
00333 
00334   // we now calculate the hessian, then invert it to get our approximate covariance
00335 
00336   vnl_matrix<double> Htt(3,3,0.0);
00337   vnl_matrix<double> Hoo(3,3,0.0);
00338   vnl_matrix<double> Hot(3,3,0.0);
00339 
00340   const unsigned m = 3;
00341 
00342   vnl_vector<double> from_pt( m );
00343   vnl_vector<double> to_pt( m );
00344 
00345     typedef rgrl_match_set::const_from_iterator FIter;
00346   typedef FIter::to_iterator TIter;
00347 
00348 
00349   for (unsigned int ms=0; ms < matches.size(); ++ms )
00350   {
00351     rgrl_match_set const& match_set = *matches[ms];
00352     for (FIter fi = match_set.from_begin(); fi != match_set.from_end(); ++fi )
00353     {
00354       for (TIter ti = fi.begin(); ti != fi.end(); ++ti )
00355       {
00356         from_pt = fi.from_feature()->location();
00357         to_pt = ti.to_feature()->location();
00358         vnl_matrix<double> const& B = ti.to_feature()->error_projector();
00359         double const wgt = ti.cumulative_weight();
00360 
00361         Htt = Htt + wgt*2*B;
00362         Hot.set_row(0,Hot.get_row(0) + wgt*2*(B*dRdtheta*from_pt));
00363         Hot.set_row(1,Hot.get_row(1) + wgt*2*(B*dRdalpha*from_pt));
00364         Hot.set_row(2,Hot.get_row(2) + wgt*2*(B*dRdphi*from_pt));
00365 
00366 
00367         Hoo(0,0) = Hoo(0,0) + wgt*2* (
00368           inner_product ((t - to_pt), B * d2Rdtheta2 * from_pt) +
00369           inner_product (from_pt , ( dRdtheta.transpose() * B * dRdtheta + R.transpose()*B*d2Rdtheta2) * from_pt ));
00370 
00371         Hoo(1,1) = Hoo(1,1) + wgt*2* (
00372           inner_product ((t - to_pt), B * d2Rdalpha2 * from_pt) +
00373           inner_product (from_pt , ( dRdalpha.transpose() * B * dRdalpha + R.transpose()*B*d2Rdalpha2) * from_pt ));
00374 
00375         Hoo(2,2) = Hoo(2,2) + wgt*2* (
00376           inner_product ((t - to_pt), B * d2Rdphi2 * from_pt) +
00377           inner_product (from_pt , ( dRdphi.transpose() * B * dRdphi+ R.transpose()*B*d2Rdphi2) * from_pt ));
00378 
00379         Hoo(0,1) = Hoo(0,1) + wgt*2* (
00380           inner_product ((t - to_pt), B * d2Rdthetadalpha * from_pt) +
00381           inner_product (from_pt , ( dRdtheta.transpose() * B * dRdalpha + R.transpose()*B*d2Rdthetadalpha) * from_pt ));
00382 
00383 
00384         Hoo(0,2) = Hoo(0,2) + wgt*2* (
00385           inner_product ((t - to_pt), B * d2Rdphidtheta * from_pt) +
00386           inner_product (from_pt , ( dRdtheta.transpose() * B * dRdphi + R.transpose()*B*d2Rdphidtheta) * from_pt ));
00387 
00388         Hoo(1,2) = Hoo(1,2) + wgt*2* (
00389           inner_product ((t - to_pt), B * d2Rdphidalpha * from_pt) +
00390           inner_product (from_pt , ( dRdalpha.transpose() * B * dRdphi + R.transpose()*B*d2Rdphidalpha) * from_pt ));
00391       }
00392     }
00393   }
00394 
00395   Hoo(1,0) = Hoo(0,1);
00396   Hoo(2,0) = Hoo(0,2);
00397   Hoo(2,1) = Hoo(1,2);
00398 
00399   vnl_matrix<double> hessian(6,6,0.0);
00400 
00401   //std::cout<<"hoo is\n"<<Hoo<<std::endl;
00402   //std::cout<<"hot is\n"<<Hot<<std::endl;
00403   //std::cout<<"htt is\n"<<Htt<<std::endl;
00404   hessian.update(Hoo,0,0);
00405   hessian.update(Htt,3,3);
00406   hessian.update(Hot,0,3);
00407   hessian.update(Hot.transpose(),3,0);
00408 
00409 #if 0
00410   vnl_matrix<double> covar = vnl_matrix_inverse<double>(hessian);
00411 #endif
00412 
00413   vnl_svd<double> svd(hessian);
00414   //svd.zero_out_absolute(10e-8);
00415   vnl_matrix<double> covar = svd.inverse();
00416 
00417   rgrl_trans_rigid* rigid = dynamic_cast<rgrl_trans_rigid*>(current_trans.ptr());
00418 
00419   current_trans = new rgrl_trans_rigid(rigid->R(),rigid->t(),covar);
00420 }
00421 
00422 
00423 rgrl_transformation_sptr
00424 rgrl_est_rigid::
00425 estimate( rgrl_match_set_sptr matches,
00426           rgrl_transformation const& cur_transform ) const
00427 {
00428   // use base class implementation
00429   return rgrl_estimator::estimate( matches, cur_transform );
00430 }
00431 
00432 const vcl_type_info&
00433 rgrl_est_rigid::
00434 transformation_type() const
00435 {
00436   return rgrl_trans_rigid::type_id();
00437 }