contrib/rpl/rgrl/rgrl_est_homography2d.cxx
Go to the documentation of this file.
00001 #include "rgrl_est_homography2d.h"
00002 #include "rgrl_trans_homography2d.h"
00003 #include "rgrl_match_set.h"
00004 
00005 #include <vnl/algo/vnl_svd.h>
00006 #include <vnl/vnl_math.h>
00007 #include <vnl/vnl_double_3x3.h>
00008 #include <vnl/vnl_det.h>
00009 
00010 
00011 rgrl_est_homography2d::
00012 rgrl_est_homography2d( double condition_num_thrd )
00013   : condition_num_thrd_( condition_num_thrd )
00014 {
00015    rgrl_estimator::set_param_dof( 8 );
00016 }
00017 
00018 rgrl_transformation_sptr
00019 rgrl_est_homography2d::
00020 estimate( rgrl_set_of<rgrl_match_set_sptr> const& matches,
00021           rgrl_transformation const& /*cur_transform*/ ) const
00022 {
00023   // Normalization of the input data by s similarity transformation,
00024   // such that the new center is the origin, and the the average
00025   // distance from the origin is sqrt(2).
00026   //
00027   vcl_vector< vnl_vector<double> > norm_from_pts, norm_to_pts;
00028   vcl_vector< double > wgts;
00029   double from_scale, to_scale;
00030   vnl_vector<double> from_center(2), to_center(2);
00031   if ( !normalize( matches, norm_from_pts, norm_to_pts, wgts, from_scale, to_scale,
00032                    from_center, to_center ) )
00033     return 0;
00034 
00035   vnl_matrix< double > A( 2*norm_from_pts.size(), 9, 0.0 );
00036   for ( unsigned int i=0; i<norm_from_pts.size(); ++i ) {
00037     A( 2*i, 0 ) = A( 2*i+1, 3 ) = wgts[i] * norm_from_pts[i][0] * norm_to_pts[i][2];
00038     A( 2*i, 1 ) = A( 2*i+1, 4 ) = wgts[i] * norm_from_pts[i][1] * norm_to_pts[i][2];
00039     A( 2*i, 2 ) = A( 2*i+1, 5 ) = wgts[i] * norm_from_pts[i][2] * norm_to_pts[i][2];
00040     A( 2*i, 6 ) = wgts[i] * -1 * norm_from_pts[i][0] * norm_to_pts[i][0];
00041     A( 2*i, 7 ) = wgts[i] * -1 * norm_from_pts[i][1] * norm_to_pts[i][0];
00042     A( 2*i, 8 ) = wgts[i] * -1 * norm_from_pts[i][2] * norm_to_pts[i][0];
00043     A( 2*i+1, 6 ) = wgts[i] * -1 * norm_from_pts[i][0] * norm_to_pts[i][1];
00044     A( 2*i+1, 7 ) = wgts[i] * -1 * norm_from_pts[i][1] * norm_to_pts[i][1];
00045     A( 2*i+1, 8 ) = wgts[i] * -1 * norm_from_pts[i][2] * norm_to_pts[i][1];
00046   }
00047   vnl_svd<double> svd( A, 1.0e-8 );
00048 
00049   if ( svd.rank() < 8 ) {
00050     DebugMacro(1, "rank ("<<svd.rank()<<")  no solution." );
00051     return 0; // no solution
00052   }
00053   else {
00054     vnl_vector< double > nparams = svd.nullvector();
00055     vnl_matrix< double > normH( 3, 3 );
00056     for ( int r=0; r<3; ++r )
00057       for ( int c=0; c<3; ++c )
00058         normH( r, c ) = nparams( 3*r + c );
00059 
00060     // check rank of H
00061     vnl_double_3x3 tmpH(normH);
00062     if( vcl_abs(vnl_det(tmpH)) < 1e-8 ) 
00063       return 0;
00064 
00065     vnl_matrix<double> to_scale_matrix_inv(3,3,vnl_matrix_identity);
00066     vnl_matrix<double> from_scale_matrix(3,3,vnl_matrix_identity);
00067     to_scale_matrix_inv(0,0) = 1/to_scale;
00068     to_scale_matrix_inv(1,1) = 1/to_scale;
00069     from_scale_matrix(0,0) = from_scale;
00070     from_scale_matrix(1,1) = from_scale;
00071 
00072     // Denormalization by the scales only. Uncentering will be done
00073     // during the construction of the transformation.
00074     //
00075     vnl_matrix< double > H = to_scale_matrix_inv * normH * from_scale_matrix;
00076     vnl_matrix<double> covar(9,9,0.0);
00077     estimate_covariance( norm_from_pts, norm_to_pts, wgts,
00078                          from_scale, to_scale, covar);
00079 
00080     return new rgrl_trans_homography2d( H, covar, from_center, to_center );
00081   }
00082 }
00083 
00084 
00085 rgrl_transformation_sptr
00086 rgrl_est_homography2d::
00087 estimate( rgrl_match_set_sptr matches,
00088           rgrl_transformation const& cur_transform ) const
00089 {
00090   // use base class implementation
00091   return rgrl_estimator::estimate( matches, cur_transform );
00092 }
00093 
00094 const vcl_type_info&
00095 rgrl_est_homography2d::
00096 transformation_type() const
00097 {
00098   return rgrl_trans_homography2d::type_id();
00099 }
00100 
00101 bool
00102 rgrl_est_homography2d::
00103 normalize( rgrl_set_of<rgrl_match_set_sptr> const& matches,
00104            vcl_vector< vnl_vector<double> >& norm_froms,
00105            vcl_vector< vnl_vector<double> >& norm_tos,
00106            vcl_vector< double >& wgts,
00107            double& from_scale,
00108            double& to_scale,
00109            vnl_vector< double > & from_center,
00110            vnl_vector< double > & to_center ) const
00111 {
00112   // Iterators to go over the matches
00113   //
00114   typedef rgrl_match_set::const_from_iterator FIter;
00115   typedef FIter::to_iterator TIter;
00116 
00117   vnl_matrix<double> from_norm_matrix(3,3,0.0);
00118   vnl_matrix<double> to_norm_matrix(3,3,0.0);
00119   norm_froms.reserve( matches[0]->from_size() );
00120 
00121   // Compute the centers
00122   //
00123   from_center.fill( 0.0 );
00124   to_center.fill( 0.0 );
00125   vnl_vector<double> from_pt( 2 );
00126   vnl_vector<double> to_pt( 2 );
00127   double sum_wgt = 0;
00128 
00129   for ( unsigned ms=0; ms < matches.size(); ++ms ) {
00130     rgrl_match_set const& match_set = *matches[ms];
00131     for ( FIter fi = match_set.from_begin(); fi != match_set.from_end(); ++fi ){
00132       for ( TIter ti = fi.begin(); ti != fi.end(); ++ti ) {
00133         double const wgt = ti.cumulative_weight();
00134         from_pt = fi.from_feature()->location();
00135         from_center += from_pt * wgt;
00136         to_pt = ti.to_feature()->location();
00137         to_center += to_pt * wgt;
00138         sum_wgt += wgt;
00139       }
00140     }
00141   }
00142   // if the weight is too small or zero,
00143   // that means there is no good match
00144   if ( sum_wgt < 1e-13 ) {
00145     return false;
00146   }
00147 
00148   from_center /= sum_wgt;
00149   to_center /= sum_wgt;
00150 
00151   // Compute the average distance
00152   //
00153   double from_avg_distance = 0;
00154   double to_avg_distance = 0;
00155   for ( unsigned ms=0; ms < matches.size(); ++ms ) {
00156     rgrl_match_set const& match_set = *matches[ms];
00157     for ( FIter fi = match_set.from_begin(); fi != match_set.from_end(); ++fi ){
00158       for ( TIter ti = fi.begin(); ti != fi.end(); ++ti ) {
00159         double const wgt = ti.cumulative_weight();
00160         from_pt = fi.from_feature()->location();
00161         from_avg_distance +=
00162           wgt * vcl_sqrt( vnl_math_sqr( from_pt[0] - from_center[0] ) +
00163                           vnl_math_sqr( from_pt[1] - from_center[1] ) );
00164         to_pt = ti.to_feature()->location();
00165         to_avg_distance +=
00166           wgt * vcl_sqrt( vnl_math_sqr( to_pt[0] - to_center[0] ) +
00167                           vnl_math_sqr( to_pt[1] - to_center[1] ) );
00168       }
00169     }
00170   }
00171   from_avg_distance /= sum_wgt;
00172   to_avg_distance /= sum_wgt;
00173 
00174   // Put together the similarity transformation for normalization of
00175   // "from" points
00176   from_norm_matrix( 0, 0 ) = 1.0 / from_avg_distance;
00177   from_norm_matrix( 0, 2 ) = -from_center[0] / from_avg_distance;
00178   from_norm_matrix( 1, 1 ) = 1.0 / from_avg_distance;
00179   from_norm_matrix( 1, 2 ) = -from_center[1] / from_avg_distance;
00180   from_norm_matrix( 2, 2 ) = 1.0;
00181   from_scale = 1.0 / from_avg_distance;
00182 
00183   // Put together the similarity transformation for normalization of
00184   // "to" points
00185   to_norm_matrix( 0, 0 ) = 1.0 / to_avg_distance;
00186   to_norm_matrix( 0, 2 ) = -to_center[0] / to_avg_distance;
00187   to_norm_matrix( 1, 1 ) = 1.0 / to_avg_distance;
00188   to_norm_matrix( 1, 2 ) = -to_center[1] / to_avg_distance;
00189   to_norm_matrix( 2, 2 ) = 1.0;
00190   to_scale = 1.0 / to_avg_distance;
00191 
00192   // Now do the normalization
00193   vnl_vector<double> norm_from_pt(3,1.0);
00194   vnl_vector<double> norm_to_pt(3,1.0);
00195   for ( unsigned ms=0; ms < matches.size(); ++ms ) {
00196     rgrl_match_set const& match_set = *matches[ms];
00197     for ( FIter fi = match_set.from_begin(); fi != match_set.from_end(); ++fi ){
00198       norm_from_pt[0] = fi.from_feature()->location()[0];
00199       norm_from_pt[1] = fi.from_feature()->location()[1];
00200       for ( TIter ti = fi.begin(); ti != fi.end(); ++ti ) {
00201         norm_to_pt[0] = ti.to_feature()->location()[0];
00202         norm_to_pt[1] = ti.to_feature()->location()[1];
00203         norm_froms.push_back( from_norm_matrix * norm_from_pt );
00204         norm_tos.push_back( to_norm_matrix * norm_to_pt );
00205         wgts.push_back( ti.cumulative_weight() );
00206       }
00207     }
00208   }
00209 
00210   return true;
00211 }
00212 
00213 void
00214 rgrl_est_homography2d::
00215 estimate_covariance( const vcl_vector< vnl_vector<double> >& norm_froms,
00216                      const vcl_vector< vnl_vector<double> >& norm_tos,
00217                      const vcl_vector< double >& wgts,
00218                      double from_scale, // FIXME: unused
00219                      double to_scale,   // FIXME: unused
00220                      vnl_matrix<double>& covar ) const
00221 {
00222   vnl_matrix<double> JTJ(9,9, 0.0);
00223   vnl_matrix<double> J(2,9, 0.0);
00224 
00225   for (unsigned int i = 0; i<norm_froms.size(); i++) {
00226     J(0,0) = J(1,3) = norm_froms[i][0];
00227     J(0,1) = J(1,4) = norm_froms[i][1];
00228     J(0,2) = J(1,5) = norm_froms[i][2];
00229 
00230     J(0,6) = -norm_tos[i][0]*norm_froms[i][0];
00231     J(0,7) = -norm_tos[i][0]*norm_froms[i][1];
00232     J(0,8) = -norm_tos[i][0]*norm_froms[i][2];
00233 
00234     J(1,6) = -norm_tos[i][1]*norm_froms[i][0];
00235     J(1,7) = -norm_tos[i][1]*norm_froms[i][1];
00236     J(1,8) = -norm_tos[i][1]*norm_froms[i][2];
00237 
00238     JTJ += wgts[i]*J.transpose()*J;
00239   }
00240 
00241   vnl_svd<double> svd( J, 1.0e-8 );
00242   covar = svd.inverse();
00243 
00244   //Please note, the effect of the normalization on the point set is
00245   //not removed. This *might* be a problem.
00246 }