contrib/rpl/rgrl/rgrl_trans_homography2d.cxx
Go to the documentation of this file.
00001 #include "rgrl_trans_homography2d.h"
00002 //:
00003 // \file
00004 #include <vcl_cassert.h>
00005 
00006 #include <vnl/algo/vnl_svd.h>
00007 #include <vnl/vnl_inverse.h>
00008 #include <vnl/vnl_vector_fixed.h>
00009 #include <vnl/vnl_math.h>
00010 #include <vnl/vnl_double_2.h>
00011 #include <vnl/vnl_double_3.h>
00012 
00013 #include <rgrl/rgrl_util.h>
00014 
00015 rgrl_trans_homography2d::
00016 rgrl_trans_homography2d()
00017   : H_( 0.0 ),
00018     from_centre_( 0.0, 0.0 ),
00019     to_centre_( 0.0, 0.0 )
00020 {}
00021 
00022 rgrl_trans_homography2d::
00023 rgrl_trans_homography2d( vnl_matrix<double> const& H,
00024                         vnl_matrix<double> const& covar )
00025   : rgrl_transformation( covar ),
00026     H_( H ),
00027     from_centre_( 0.0, 0.0 ),
00028     to_centre_( 0.0, 0.0 )
00029 {
00030   assert( H.rows() == 3 );
00031   assert( H.cols() == 3 );
00032   assert( covar_.rows() == covar_.cols() );
00033   // assert( covar_.rows() == 3 );
00034 }
00035 
00036 rgrl_trans_homography2d::
00037 rgrl_trans_homography2d( vnl_matrix<double> const& H )
00038   : H_( H ),
00039     from_centre_( 0.0, 0.0 ),
00040     to_centre_( 0.0, 0.0 )
00041 {}
00042 
00043 rgrl_trans_homography2d::
00044 rgrl_trans_homography2d( vnl_matrix<double> const& H,
00045                         vnl_matrix<double> const& covar,
00046                         vnl_vector<double> const& from_centre,
00047                         vnl_vector<double> const& to_centre )
00048   : rgrl_transformation( covar ),
00049     from_centre_( from_centre ),
00050     to_centre_( to_centre )
00051 {
00052   assert( to_centre.size() == 2 );
00053 
00054   //Uncenter the H_ = to_matrix^-1 * H * from_matrix
00055   //
00056   //vnl_matrix<double> to_inv(3,3, vnl_matrix_identity);
00057   //to_inv(0,2) = to_centre[0];
00058   //to_inv(1,2) = to_centre[1];
00059   //
00060   //vnl_matrix<double> from_matrix(3,3, vnl_matrix_identity);
00061   //from_matrix(0,2) = -from_centre[0];
00062   //from_matrix(1,2) = -from_centre[1];
00063   //
00064   //H_ = to_inv * H * from_matrix;
00065   //from_centre_ = from_centre;
00066 
00067   H_ = H;
00068 }
00069 
00070 vnl_matrix_fixed<double, 3, 3>
00071 rgrl_trans_homography2d::
00072 H() const
00073 {
00074   return uncenter_H_matrix();
00075 }
00076 
00077 vnl_matrix_fixed<double, 3, 3>
00078 rgrl_trans_homography2d::
00079 uncenter_H_matrix( ) const
00080 {
00081   vnl_matrix_fixed<double, 3, 3> H;
00082 
00083   // uncenter To image
00084   vnl_matrix_fixed<double, 3, 3> to_inv;
00085   to_inv.set_identity();
00086   to_inv(0,2) = to_centre_[0];
00087   to_inv(1,2) = to_centre_[1];
00088 
00089   // uncenter From image
00090   vnl_matrix_fixed<double, 3, 3> from_matrix;
00091   from_matrix.set_identity();
00092   from_matrix(0,2) = -from_centre_[0];
00093   from_matrix(1,2) = -from_centre_[1];
00094 
00095   H = to_inv * H_ * from_matrix;
00096 
00097   return H;
00098 }
00099 
00100 vnl_matrix<double>
00101 rgrl_trans_homography2d::
00102 transfer_error_covar( vnl_vector<double> const& from_loc ) const
00103 {
00104   assert ( is_covar_set() );
00105   assert ( from_loc.size() ==2 );
00106 
00107   vnl_matrix_fixed<double, 2, 9 > jac;
00108   vnl_matrix_fixed<double, 3, 9 > jf(0.0); // homogeneous coordinate
00109   vnl_matrix_fixed<double, 2, 3 > jg(0.0); // inhomo, [u/w, v/w]^T
00110   vnl_double_3 from_homo( from_loc[0]-from_centre_[0],
00111                           from_loc[1]-from_centre_[1],
00112                           1.0 );
00113   // transform coordinate
00114   vnl_double_3 mapped_homo = H_ * from_homo;
00115 
00116   // homogeneous coordinate w.r.t homography parameters
00117   jf(0,0) = jf(1,3) = jf(2,6) = from_homo[0]; // x
00118   jf(0,1) = jf(1,4) = jf(2,7) = from_homo[1]; // y
00119   jf(0,2) = jf(1,5) = jf(2,8) = 1.0;
00120 
00121   // derivatives w.r.t division
00122   jg(0,0) = 1.0/mapped_homo[2];
00123   jg(0,2) = -mapped_homo[0]/vnl_math_sqr(mapped_homo[2]);
00124   jg(1,1) = 1.0/mapped_homo[2];
00125   jg(1,2) = -mapped_homo[1]/vnl_math_sqr(mapped_homo[2]);
00126 
00127   // since Jab_g(f(p)) = Jac_g * Jac_f
00128   jac = jg * jf;
00129 
00130   return jac * covar_ * jac.transpose();
00131 }
00132 
00133 //: Inverse map using pseudo-inverse of H_.
00134 void
00135 rgrl_trans_homography2d::
00136 inv_map( const vnl_vector<double>& to,
00137          vnl_vector<double>& from ) const
00138 {
00139   vnl_double_3 homo_to(to[0], to[1], 1.0);
00140 
00141   // apply inverse homography
00142   vnl_double_3 homo_from = vnl_inverse(uncenter_H_matrix()) * homo_to;
00143 
00144   from[0] = homo_from[0]/homo_from[2];
00145   from[1] = homo_from[1]/homo_from[2];
00146 
00147   return;
00148 }
00149 
00150 //:  Inverse map with an initial guess
00151 void
00152 rgrl_trans_homography2d::
00153 inv_map( const vnl_vector<double>& to,
00154          bool initialize_next,
00155          const vnl_vector<double>& to_delta,
00156          vnl_vector<double>& from,
00157          vnl_vector<double>& from_next_est) const
00158 {
00159   const double epsilon = 0.01;
00160   const double eps_squared = epsilon*epsilon;
00161   int t=0;
00162   const int max_t = 50;  //  Generally, only one or two iterations should be needed.
00163   assert (to.size() == from.size());
00164   vnl_vector<double> to_est = this->map_location(from);
00165   vnl_matrix<double> approx_A_inv;
00166   vnl_vector<double> homo_from_delta(3,1);
00167   vnl_vector<double> from_delta(2,1);
00168 
00169   while ( vnl_vector_ssd(to, to_est) > eps_squared && t<max_t )
00170   {
00171     ++t;
00172 
00173     // compute the inverse of the approximated affine from the jacobian
00174     vnl_matrix_fixed<double,2,3> J = homo_jacobian(from);
00175     vnl_svd<double> svd(J);
00176     approx_A_inv = svd.inverse();
00177 
00178     // Increase "from" by approx_A^-1*(to-to_est).  "homo_from_delta"
00179     // provides the correct direction, but its magnitude is
00180     // arbitrary. To get around the problem, we take (to -
00181     // to_est).two_norm() as an indication of the magnitude.
00182 
00183     homo_from_delta = approx_A_inv * (to - to_est);
00184     homo_from_delta /= homo_from_delta[2];
00185     from_delta[0] = homo_from_delta[0];
00186     from_delta[1] = homo_from_delta[1];
00187     from += from_delta.normalize()*(to - to_est).two_norm()*0.95;
00188     to_est = this->map_location(from);
00189   }
00190   if ( t > max_t )
00191     DebugMacro( 0, " rgrl_trans_homography2d::inv_map() -- no convergence\n");
00192 
00193   if ( initialize_next ) {
00194     if ( t == 0 ) { //approx_A_inv not yet calculated
00195       vnl_matrix_fixed<double,2,3> J = homo_jacobian(from);
00196       vnl_svd<double> svd(J);
00197       approx_A_inv = svd.inverse();
00198     }
00199     vnl_vector<double> homo_from_delta(3,1);
00200     homo_from_delta = approx_A_inv * to_delta;
00201     if (homo_from_delta[2] < 0)
00202       homo_from_delta *= -1;
00203     from_next_est[0] = from[0] + homo_from_delta[0]*to_delta.two_norm();
00204     from_next_est[1] = from[1] + homo_from_delta[1]*to_delta.two_norm();
00205   }
00206 }
00207 
00208 //: Return an inverse transformation of the uncentered transform
00209 rgrl_transformation_sptr
00210 rgrl_trans_homography2d::
00211 inverse_transform() const
00212 {
00213   vnl_matrix_fixed<double,3,3> H_inv = vnl_inverse(H_);
00214   rgrl_transformation_sptr result =
00215     new rgrl_trans_homography2d( H_inv, vnl_matrix<double>(), to_centre_, from_centre_ );
00216 
00217   const unsigned m = scaling_factors_.size();
00218   if ( m > 0 ) {
00219     vnl_vector<double> scaling( m );
00220     for ( unsigned int i=0; i<m; ++i )
00221       scaling[i] = 1.0 / scaling_factors_[i];
00222     result->set_scaling_factors( scaling );
00223   }
00224 
00225   return result;
00226 }
00227 
00228 //: Return the jacobian of the transform.
00229 vnl_matrix_fixed<double,2,3>
00230 rgrl_trans_homography2d::
00231 homo_jacobian( vnl_vector_fixed<double,2> const& from_loc ) const
00232 {
00233   // Let h_i be the i_th row of H_, and p be the homogeneous vector of from_loc
00234   // f_0(p) = h_0.p/h_2.p
00235   // f_1(p) = h_1.p/h_2.p
00236   // The jacobian is a 2x3 matrix with entries
00237   // [d(f_0)/dx   d(f_0)/dy   d(f_0)/dw;
00238   //  d(f_1)/dx   d(f_1)/dy   d(f_1)/dw]
00239   //
00240 
00241   vnl_vector_fixed<double,3> p(from_loc[0]-from_centre_[0], from_loc[1]-from_centre_[1], 1.0);
00242   vnl_vector_fixed<double,3> h_0 = H_.get_row(0);
00243   vnl_vector_fixed<double,3> h_1 = H_.get_row(1);
00244   vnl_vector_fixed<double,3> h_2 = H_.get_row(2);
00245   double inv_mapped_w = 1.0/dot_product(h_2, p);
00246   double mapped_x = dot_product(h_0, p)*inv_mapped_w;
00247   double mapped_y = dot_product(h_1, p)*inv_mapped_w;
00248 
00249   vnl_matrix_fixed<double,2,3> jacobian;
00250   jacobian.set_row(0, h_0-mapped_x*h_2 );
00251   jacobian.set_row(1, h_1-mapped_y*h_2 );
00252 
00253   return inv_mapped_w*jacobian;
00254 }
00255 
00256 //: Return the jacobian of the transform.
00257 void
00258 rgrl_trans_homography2d::
00259 jacobian_wrt_loc( vnl_matrix<double>& jacobian, vnl_vector<double> const& from_loc ) const
00260 {
00261   // The jacobian is a 2x2 matrix with entries
00262   // [d(f_0)/dx   d(f_0)/dy;
00263   //  d(f_1)/dx   d(f_1)/dy]
00264   //
00265   vnl_double_2 centered_from = from_loc;
00266   centered_from -= from_centre_;
00267 
00268   double mapped_w = H_(2,0)*centered_from[0] + H_(2,1)*centered_from[1] + H_(2,2);
00269 
00270   jacobian.set_size(2, 2);
00271   // w/ respect to x
00272   jacobian(0,0) = H_(0,0)*( H_(2,1)*centered_from[1]+H_(2,2) ) - H_(2,0)*( H_(0,1)*centered_from[1] + H_(0,2) );
00273   jacobian(1,0) = H_(1,0)*( H_(2,1)*centered_from[1]+H_(2,2) ) - H_(2,0)*( H_(1,1)*centered_from[1] + H_(1,2) );
00274   // w/ respect to y
00275   jacobian(0,1) = H_(0,1)*( H_(2,0)*centered_from[0]+H_(2,2) ) - H_(2,1)*( H_(0,0)*centered_from[0] + H_(0,2) );
00276   jacobian(1,1) = H_(1,1)*( H_(2,0)*centered_from[0]+H_(2,2) ) - H_(2,1)*( H_(1,0)*centered_from[0] + H_(1,2) );
00277 
00278   jacobian *= (1/(mapped_w*mapped_w));
00279 }
00280 
00281 // for output CENTERED transformation
00282 void
00283 rgrl_trans_homography2d::
00284 write(vcl_ostream& os ) const
00285 {
00286   //vnl_vector<double> origin(from_centre_.size(), 0.0);
00287 
00288   // tag
00289   os << "HOMOGRAPHY2D\n"
00290   // parameters
00291      << 2 << vcl_endl
00292      << H_ << from_centre_ << "  " << to_centre_ << vcl_endl;
00293 
00294   // parent
00295   rgrl_transformation::write( os );
00296 }
00297 
00298 // for input
00299 bool
00300 rgrl_trans_homography2d::
00301 read(vcl_istream& is )
00302 {
00303   int dim;
00304 
00305   // skip empty lines
00306   rgrl_util_skip_empty_lines( is );
00307 
00308   vcl_string str;
00309   vcl_getline( is, str );
00310 
00311   // The token should appear at the beginning of line
00312   if ( str.find( "HOMOGRAPHY2D" ) != 0 ) {
00313     WarningMacro( "The tag is not HOMOGRAPHY2D. reading is aborted.\n" );
00314     return false;
00315   }
00316 
00317   // input global xform
00318   dim=-1;
00319   is >> dim;
00320   if ( dim > 0 ) {
00321     is >> H_ >> from_centre_ >> to_centre_;
00322   }
00323 
00324   // parent
00325   return is.good() && rgrl_transformation::read( is );
00326 }
00327 
00328 void
00329 rgrl_trans_homography2d::
00330 map_loc( vnl_vector<double> const& from,
00331          vnl_vector<double>      & to  ) const
00332 {
00333   to.set_size(2);
00334   // convert "from" to homogeneous co-cord
00335   vnl_vector_fixed<double,3> h_from(from[0]-from_centre_[0], from[1]-from_centre_[1], 1);
00336   vnl_vector_fixed<double,3> h_to = H_*h_from;
00337   to[0] = h_to[0]/h_to[2] + to_centre_[0];
00338   to[1] = h_to[1]/h_to[2] + to_centre_[1];
00339 }
00340 
00341 void
00342 rgrl_trans_homography2d::
00343 map_dir( vnl_vector<double> const& from_loc,
00344          vnl_vector<double> const& from_dir,
00345          vnl_vector<double>      & to_dir  ) const
00346 {
00347   assert ( from_loc.size() == 2 );
00348   assert ( from_dir.size() == 2 );
00349   vnl_vector<double> to_loc_begin, to_loc_end;
00350   this->map_loc(from_loc, to_loc_begin);
00351   this->map_loc(from_loc+from_dir, to_loc_end);
00352 
00353   to_dir = to_loc_end - to_loc_begin;
00354   to_dir.normalize();
00355 }
00356 
00357 rgrl_transformation_sptr
00358 rgrl_trans_homography2d::
00359 scale_by( double scale ) const
00360 {
00361   vnl_matrix_fixed<double,3,3> new_H( H_ );
00362 
00363   // scale
00364   new_H(0,2) *= scale;
00365   new_H(1,2) *= scale;
00366 
00367   // move the scale on the fixed coordinate,
00368   // and divide the 3rd row by this scale
00369   new_H(2,0) /= scale;
00370   new_H(2,1) /= scale;
00371 
00372   // normalize
00373   new_H /= new_H.fro_norm();
00374 
00375   // centers
00376   vnl_vector_fixed<double,2> from = from_centre_ * scale;
00377   vnl_vector_fixed<double,2> to = to_centre_ * scale;
00378 
00379   rgrl_transformation_sptr xform
00380     =  new rgrl_trans_homography2d( new_H.as_ref(),
00381                                     vnl_matrix<double>(),
00382                                     from.as_ref(), to.as_ref() );
00383   xform->set_scaling_factors( this->scaling_factors() );
00384   return xform;
00385 }
00386 
00387 //: make a clone copy
00388 rgrl_transformation_sptr
00389 rgrl_trans_homography2d::
00390 clone() const
00391 {
00392   return new rgrl_trans_homography2d( *this );
00393 }