00001 #include "rgrl_est_homo2d_lm.h"
00002
00003
00004 #include <rgrl/rgrl_est_homography2d.h>
00005 #include <rgrl/rgrl_trans_homography2d.h>
00006 #include <rgrl/rgrl_match_set.h>
00007 #include <rgrl/rgrl_internal_util.h>
00008
00009 #include <vnl/vnl_double_2.h>
00010 #include <vnl/vnl_double_3.h>
00011 #include <vnl/vnl_double_3x3.h>
00012 #include <vnl/vnl_matrix_fixed.h>
00013 #include <vnl/vnl_math.h>
00014 #include <vnl/vnl_det.h>
00015 #include <vnl/vnl_fastops.h>
00016 #include <vnl/vnl_transpose.h>
00017 #include <vnl/vnl_least_squares_function.h>
00018 #include <vnl/algo/vnl_orthogonal_complement.h>
00019 #include <vnl/algo/vnl_levenberg_marquardt.h>
00020 #include <vnl/algo/vnl_svd.h>
00021
00022 #include <vcl_cassert.h>
00023
00024 static
00025 inline
00026 void
00027 H2h( vnl_matrix_fixed<double,3,3> const& H, vnl_vector<double>& h )
00028 {
00029 for ( unsigned i=0; i<H.rows(); ++i )
00030 for ( unsigned j=0; j<H.cols(); ++j )
00031 h( i*3+j ) = H(i,j);
00032 }
00033
00034 static
00035 inline
00036 void
00037 h2H( vnl_vector<double> const& h, vnl_matrix_fixed<double,3,3>& H )
00038 {
00039 for ( unsigned i=0; i<3; ++i )
00040 for ( unsigned j=0; j<3; ++j )
00041 H(i,j) = h( i*3+j );
00042 }
00043
00044 inline
00045 void
00046 map_homo_point( vnl_double_3& mapped, vnl_vector<double> const& p, vnl_vector<double> const& loc )
00047 {
00048 mapped[0] = p[0]*loc[0] + p[1]*loc[1] + p[2];
00049 mapped[1] = p[3]*loc[0] + p[4]*loc[1] + p[5];
00050 mapped[2] = p[6]*loc[0] + p[7]*loc[1] + p[8];
00051 }
00052
00053 inline
00054 void
00055 map_inhomo_point( vnl_double_2& mapped, vnl_vector<double> const& p, vnl_vector<double> const& loc )
00056 {
00057 vnl_double_3 tmp;
00058 map_homo_point( tmp, p, loc );
00059 mapped[0] = tmp[0]/tmp[2];
00060 mapped[1] = tmp[1]/tmp[2];
00061 }
00062
00063 class rgrl_homo2d_func
00064 : public vnl_least_squares_function
00065 {
00066 public:
00067
00068 rgrl_homo2d_func( rgrl_set_of<rgrl_match_set_sptr> const& matches,
00069 int num_res, bool with_grad = true )
00070 : vnl_least_squares_function( 9, num_res, with_grad ? use_gradient : no_gradient ),
00071 matches_ptr_( &matches ),
00072 from_centre_(2, 0.0), to_centre_(2, 0.0)
00073 { }
00074
00075 void set_centres( vnl_vector<double> const& fc, vnl_vector<double> const& tc )
00076 {
00077 assert( fc.size() == 2 && tc.size() == 2 );
00078 from_centre_ = fc;
00079 to_centre_ = tc;
00080 }
00081
00082
00083 void f(vnl_vector<double> const& x, vnl_vector<double>& fx);
00084
00085
00086 void gradf(vnl_vector<double> const& x, vnl_matrix<double>& jacobian);
00087
00088 protected:
00089 typedef rgrl_match_set::const_from_iterator FIter;
00090 typedef FIter::to_iterator TIter;
00091
00092 rgrl_set_of<rgrl_match_set_sptr> const* matches_ptr_;
00093 vnl_double_2 from_centre_, to_centre_;
00094 };
00095
00096 void
00097 rgrl_homo2d_func::
00098 f(vnl_vector<double> const& x, vnl_vector<double>& fx)
00099 {
00100 vnl_double_2 mapped;
00101 vnl_matrix_fixed<double,2,2> error_proj_sqrt;
00102 unsigned int ind = 0;
00103 for ( unsigned ms = 0; ms<matches_ptr_->size(); ++ms )
00104 if ( (*matches_ptr_)[ms] != 0 ) {
00105 rgrl_match_set const& one_set = *((*matches_ptr_)[ms]);
00106 for ( FIter fi=one_set.from_begin(); fi!=one_set.from_end(); ++fi ) {
00107
00108 vnl_double_2 from = fi.from_feature()->location();
00109 from -= from_centre_;
00110 map_inhomo_point( mapped, x, from.as_ref() );
00111
00112 for ( TIter ti=fi.begin(); ti!=fi.end(); ++ti ) {
00113 vnl_double_2 to = ti.to_feature()->location();
00114 to -= to_centre_;
00115 error_proj_sqrt = ti.to_feature()->error_projector_sqrt();
00116 double const wgt = vcl_sqrt(ti.cumulative_weight());
00117 vnl_double_2 diff = error_proj_sqrt * (mapped - to);
00118
00119
00120 fx(ind) = wgt*diff[0];
00121 fx(ind+1) = wgt*diff[1];
00122 ind+=2;
00123 }
00124 }
00125 }
00126
00127
00128 assert( ind == get_number_of_residuals() );
00129 }
00130
00131 void
00132 rgrl_homo2d_func::
00133 gradf(vnl_vector<double> const& x, vnl_matrix<double>& jacobian)
00134 {
00135 assert( jacobian.rows() == get_number_of_residuals() && jacobian.cols() == 9 );
00136
00137 vnl_double_3 homo;
00138 vnl_matrix_fixed<double,2,2> error_proj_sqrt;
00139 vnl_matrix_fixed<double,2,9> base_jac, jac;
00140 vnl_matrix_fixed<double,3,9> jf(0.0);
00141 vnl_matrix_fixed<double,2,3> jg(0.0);
00142 unsigned int ind = 0;
00143 for ( unsigned ms = 0; ms<matches_ptr_->size(); ++ms )
00144 if ( (*matches_ptr_)[ms] ) {
00145 rgrl_match_set const& one_set = *((*matches_ptr_)[ms]);
00146 for ( FIter fi=one_set.from_begin(); fi!=one_set.from_end(); ++fi ) {
00147
00148 vnl_double_2 from = fi.from_feature()->location();
00149 from -= from_centre_;
00150 map_homo_point( homo, x, from.as_ref() );
00151
00152 jf(0,0) = jf(1,3) = jf(2,6) = from[0];
00153 jf(0,1) = jf(1,4) = jf(2,7) = from[1];
00154 jf(0,2) = jf(1,5) = jf(2,8) = 1.0;
00155
00156 jg(0,0) = 1.0/homo[2];
00157 jg(0,2) = -homo[0]/vnl_math_sqr(homo[2]);
00158 jg(1,1) = 1.0/homo[2];
00159 jg(1,2) = -homo[1]/vnl_math_sqr(homo[2]);
00160
00161 base_jac = jg * jf;
00162
00163 for ( TIter ti=fi.begin(); ti!=fi.end(); ++ti ) {
00164
00165 error_proj_sqrt = ti.to_feature()->error_projector_sqrt();
00166 double const wgt = vcl_sqrt(ti.cumulative_weight());
00167 jac = error_proj_sqrt * base_jac;
00168 jac *= wgt;
00169
00170
00171 for ( unsigned i=0; i<9; i++ ) {
00172 jacobian(ind, i) = jac(0, i);
00173 jacobian(ind+1, i) = jac(1, i);
00174 }
00175 ind+=2;
00176 }
00177 }
00178 }
00179 }
00180
00181
00182
00183
00184 rgrl_est_homo2d_lm::
00185 rgrl_est_homo2d_lm( bool with_grad )
00186 : with_grad_( with_grad )
00187 {
00188 rgrl_estimator::set_param_dof( 8 );
00189
00190
00191 rgrl_nonlinear_estimator::set_max_num_iter( 50 );
00192 rgrl_nonlinear_estimator::set_rel_thres( 1e-5 );
00193 }
00194
00195 rgrl_transformation_sptr
00196 rgrl_est_homo2d_lm::
00197 estimate( rgrl_set_of<rgrl_match_set_sptr> const& matches,
00198 rgrl_transformation const& cur_transform ) const
00199 {
00200
00201 vnl_matrix_fixed<double, 3, 3> init_H;
00202
00203 if ( !rgrl_internal_util_upgrade_to_homography2D( init_H, cur_transform ) ) {
00204
00205
00206 DebugMacro( 0, "Use normalized DLT to initialize" );
00207 rgrl_est_homography2d est_homo;
00208 rgrl_transformation_sptr tmp_trans= est_homo.estimate( matches, cur_transform );
00209 if ( !tmp_trans )
00210 return 0;
00211 rgrl_trans_homography2d const& trans = static_cast<rgrl_trans_homography2d const&>( *tmp_trans );
00212 init_H = trans.H();
00213 }
00214
00215
00216
00217
00218 vnl_vector<double> from_centre;
00219 vnl_vector<double> to_centre;
00220 if ( !rgrl_est_compute_weighted_centres( matches, from_centre, to_centre ) )
00221 return 0;
00222 DebugMacro( 3, "From center: " << from_centre
00223 <<" To center: " << to_centre << vcl_endl );
00224
00225
00226 {
00227
00228
00229 vnl_matrix<double> to_trans( 3, 3, vnl_matrix_identity );
00230 to_trans(0,2) = -to_centre[0];
00231 to_trans(1,2) = -to_centre[1];
00232
00233 vnl_matrix<double> from_inv( 3, 3, vnl_matrix_identity );
00234 from_inv(0,2) = from_centre[0];
00235 from_inv(1,2) = from_centre[1];
00236
00237 init_H = to_trans * init_H * from_inv;
00238 }
00239
00240 vnl_vector<double> p(9);
00241 H2h( init_H, p );
00242
00243
00244 const unsigned tot_num = rgrl_est_matches_residual_number(matches);
00245
00246
00247 rgrl_homo2d_func homo_func( matches,
00248 tot_num,
00249 with_grad_ );
00250 homo_func.set_centres( from_centre, to_centre );
00251
00252 vnl_levenberg_marquardt lm( homo_func );
00253
00254
00255
00256 lm.set_f_tolerance( relative_threshold_ );
00257 lm.set_max_function_evals( max_num_iterations_ );
00258
00259 bool ret;
00260 if ( with_grad_ )
00261 ret = lm.minimize_using_gradient(p);
00262 else
00263 ret = lm.minimize_without_gradient(p);
00264 if ( !ret ) {
00265 WarningMacro( "Levenberg-Marquardt failed" );
00266 return 0;
00267 }
00268
00269
00270
00271 p /= p.two_norm();
00272
00273 h2H( p, init_H );
00274 vnl_vector<double> centre(2,0.0);
00275
00276
00277 vnl_double_3x3 tmpH(init_H);
00278 if ( vcl_abs(vnl_det(tmpH)) < 1e-8 )
00279 return 0;
00280
00281
00282
00283
00284
00285
00286
00287
00288 vnl_matrix<double> jac(tot_num, 9), jtj(9, 9);
00289 homo_func.gradf( p, jac );
00290
00291
00292
00293
00294 vnl_fastops::AtA( jtj, jac );
00295
00296
00297
00298
00299 #if 1
00300 const vnl_matrix<double> compliment = vnl_orthogonal_complement( p );
00301
00302 vnl_svd<double> svd( vnl_transpose(compliment) * jtj *compliment, 1e-6 );
00303 if ( svd.rank() < 8 ) {
00304 WarningMacro( "The covariance of homography ranks less than 8! ");
00305 return 0;
00306 }
00307
00308 vnl_matrix<double>covar = compliment * svd.inverse() * compliment.transpose();
00309
00310 #else
00311 vnl_svd<double> svd( jtj, 1e-6 );
00312 DebugMacro(3, "SVD of JtJ: " << svd << vcl_endl);
00313
00314
00315 if ( svd.rank() < 8 ) {
00316 WarningMacro( "The covariance of homography ranks less than 8! ");
00317 return 0;
00318 }
00319
00320 vnl_matrix<double> covar ( svd.pinverse(8) );
00321 DebugMacro(3, "Covariance: " << covar << vcl_endl );
00322
00323
00324
00325
00326
00327
00328 double abs_dot = vcl_abs( dot_product( p, svd.nullvector() ) );
00329 if ( abs_dot < 0.9 ) {
00330 WarningMacro("The null vector of covariance matrix of homography is too DIFFERENT\n"
00331 << "compared to estimate of homography: dot_product=" << abs_dot << vcl_endl );
00332
00333 return 0;
00334 }
00335 #endif
00336
00337 DebugMacro(2, "null vector: " << svd.nullvector() << " estimate: " << p << vcl_endl );
00338
00339 return new rgrl_trans_homography2d( init_H.as_ref(), covar, from_centre, to_centre );
00340 }
00341
00342
00343 const vcl_type_info&
00344 rgrl_est_homo2d_lm::
00345 transformation_type() const
00346 {
00347 return rgrl_trans_homography2d::type_id();
00348 }