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