contrib/rpl/rgrl/rgrl_est_reduced_quad2d.cxx
Go to the documentation of this file.
00001 //:
00002 // \file
00003 // \author Charlene Tsai
00004 // \date   Sep 2003
00005 
00006 #include "rgrl_est_reduced_quad2d.h"
00007 
00008 #include <vcl_cassert.h>
00009 #include <vnl/algo/vnl_svd.h>
00010 #include <vnl/vnl_math.h>
00011 #include <vnl/vnl_matrix_fixed.h>
00012 #include <vnl/vnl_vector_fixed.h>
00013 #include "rgrl_trans_reduced_quad.h"
00014 #include "rgrl_match_set.h"
00015 
00016 rgrl_est_reduced_quad2d::
00017 rgrl_est_reduced_quad2d()
00018   : condition_num_thrd_( 0.0 )
00019 {}
00020 
00021 rgrl_est_reduced_quad2d::
00022 rgrl_est_reduced_quad2d( unsigned int dimension,
00023                          double condition_num_thrd )
00024   : condition_num_thrd_( condition_num_thrd )
00025 {
00026   assert (dimension == 2);
00027   // Derive the parameter_dof from the dimension
00028   //
00029   unsigned int param_dof = 3*dimension; //It is always for 2D
00030 
00031   // Pass the two variable to the parent class, where they're stored
00032   //
00033   rgrl_estimator::set_param_dof( param_dof );
00034 }
00035 
00036 rgrl_transformation_sptr
00037 rgrl_est_reduced_quad2d::
00038 estimate( rgrl_set_of<rgrl_match_set_sptr> const& matches,
00039           rgrl_transformation const& /*cur_transform*/ ) const
00040 {
00041   // Iterators to go over the matches
00042   //
00043   typedef rgrl_match_set::const_from_iterator FIter;
00044   typedef FIter::to_iterator TIter;
00045 
00046   // The dimensionality of the space we are working in. Find it by
00047   // looking at the dimension of one of the data points.
00048   //
00049   unsigned ms = 0;
00050   while ( ms < matches.size() &&
00051           matches[ms]->from_begin() == matches[ms]->from_end() )
00052     ++ms;
00053   if ( ms == matches.size() ) {
00054     DebugMacro( 0, "No data!\n" );
00055     return 0; // no data!
00056   }
00057   const unsigned int m = matches[ms]->from_begin().from_feature()->location().size();
00058   assert ( m==2 ); // only 2D reduced_quad2d estimation
00059 
00060   // ----------------------------
00061   // Create the constraint matrix. See "2D similarity transform with a
00062   // projector matrix" in notes.tex.
00063   //
00064   // The problem can be written as Xc=y. Then, the WLS solution
00065   // is c = inv(X^t W X)  X^t W y. See notes.tex.
00066   //
00067   // We use all the constraints from all the match sets to develop a
00068   // single linear system for the affine transformation.
00069   //
00070   vnl_matrix_fixed<double, 6, 6> XtWX;
00071   vnl_vector_fixed<double, 6> XtWy;
00072   XtWX.fill( 0.0 );
00073   XtWy.fill( 0.0 );
00074   vnl_matrix_fixed<double, 2, 6> D;
00075   D.fill( 0.0 );
00076 
00077   // Determine the weighted centres for the reduced_quad transformation. We
00078   // take the centres of all the points in all the match sets.
00079   //
00080   vnl_vector_fixed<double, 2> from_centre( 0.0, 0.0 );
00081   vnl_vector_fixed<double, 2> to_centre( 0.0, 0.0 );
00082   vnl_vector_fixed<double, 2> from_pt;
00083   vnl_vector_fixed<double, 2> to_pt;
00084   vnl_vector_fixed<double, 6> DtBq;
00085   vnl_matrix_fixed<double, 6, 2> DtB;
00086   double sum_wgt = 0.0;
00087   for ( unsigned ms=0; ms < matches.size(); ++ms ) {
00088     rgrl_match_set const& match_set = *matches[ms];
00089     for ( FIter fi = match_set.from_begin(); fi != match_set.from_end(); ++fi ) {
00090       for ( TIter ti = fi.begin(); ti != fi.end(); ++ti ) {
00091         double const wgt = ti.cumulative_weight();
00092         from_pt = fi.from_feature()->location();
00093         from_pt *= wgt;
00094         from_centre += from_pt;
00095         to_pt = ti.to_feature()->location();
00096         to_pt *= wgt;
00097         to_centre   += to_pt;
00098         sum_wgt += wgt;
00099       }
00100     }
00101   }
00102   // if the weight is too small or zero,
00103   // that means there is no good match
00104   if ( sum_wgt < 1e-13 ) {
00105     return 0;
00106   }
00107 
00108   from_centre /= sum_wgt;
00109   to_centre /= sum_wgt;
00110 
00111   if ( sum_wgt < 1e-16 ) return 0; //sum_wgt approaching 0
00112 
00113   // Since XtWX is symmetric, we only compute the upper triangle, and
00114   // copy it later into the lower triangle.
00115   unsigned count=0;
00116   for ( unsigned ms=0; ms < matches.size(); ++ms ) {
00117     rgrl_match_set const& match_set = *matches[ms];
00118     for ( FIter fi = match_set.from_begin(); fi != match_set.from_end(); ++fi ) {
00119       for ( TIter ti = fi.begin(); ti != fi.end(); ++ti ) {
00120         from_pt = fi.from_feature()->location();
00121         from_pt -= from_centre;
00122         to_pt = ti.to_feature()->location();
00123         to_pt -= to_centre;
00124         vnl_matrix<double> const& B = ti.to_feature()->error_projector();
00125         double const wgt = ti.cumulative_weight();
00126 
00127         assert( from_pt.size() == m );
00128         assert( to_pt.size() == m );
00129         assert( B.cols() == m && B.rows() == m );
00130         ++count;
00131 
00132         // For each constraint, add w*XtBX to XtWX
00133         //
00134         // X  = [px^2+py^2 0 px -py 1 0; 0 px^2+py^2 py px 0 1]
00135         D.fill( 0.0 );
00136         D(0,0) = D(1,1) = vnl_math_sqr(from_pt[0]) + vnl_math_sqr(from_pt[1]);
00137         D(0,2) = D(1,3) = from_pt[0];
00138         D(0,3) = -from_pt[1];
00139         D(1,2) = from_pt[1];
00140         D(0,4) = D(1,5) = 1;
00141 
00142         // store this product to save computation
00143         DtB = D.transpose() * B;
00144         XtWX += (DtB * D) * wgt;
00145 
00146         // add w*XtBq to XtWy
00147         DtBq = DtB * to_pt;
00148         for ( unsigned i = 0; i<6; ++i)
00149           XtWy[i] += wgt * DtBq[i];
00150       }
00151     }
00152   }
00153 
00154   // Find the scales and scale the matrices appropriate to normalize
00155   // them and increase the numerical stability.
00156   double factor0 = vnl_math_max(XtWX(4,4), XtWX(5,5));
00157   double factor1 = vnl_math_max(XtWX(2,2), XtWX(3,3));
00158   double factor2 = vnl_math_max(XtWX(1,1), XtWX(0,0));
00159   double scale0 = vcl_sqrt( (factor0 > 0 && factor2 > 0) ? factor2 / factor0 : 1);   // neither should be 0
00160   double scale1 = vcl_sqrt( (factor1 > 0 && factor2 > 0) ? factor2 / factor1 : 1 );
00161 
00162   vnl_vector_fixed<double, 6> s;
00163   s(0) = s(1) = 1; s(2) = s(3) = scale1;
00164   s(4) = s(5) = scale0;
00165 
00166   for ( int i=0; i<6; i++ ) {
00167     XtWy(i) *= s(i);
00168     for ( int j=0; j<6; j++ )
00169       XtWX(i,j) *= s(i) * s(j);
00170   }
00171 
00172   // ----------------------------
00173   // Compute the solution
00174 
00175   vnl_svd<double> svd( XtWX.as_ref() );
00176 
00177   // Due to floating point inaccuracies, some zero singular values may
00178   // look non-zero, so we correct for that.
00179   svd.zero_out_relative();
00180 
00181   if ( (unsigned)svd.rank() < 6) {
00182     DebugMacro(1, "rank ("<<svd.rank()<<") < "<<6<<"; no solution." );
00183     DebugMacro_abv(1, "(used " << count << " correspondences)\n" );
00184     return 0; // no solution
00185   }
00186   double cond_num = svd.well_condition();
00187   if ( condition_num_thrd_ > cond_num ) {
00188     DebugMacro(1, "Unstable xform with condition number = "<<cond_num<<'\n' );
00189     return 0; //no solution
00190   }
00191 
00192   // Compute the solution into XtWy
00193   //
00194   vnl_matrix_fixed<double, 6, 6> covar = svd.inverse();
00195   XtWy = covar * XtWy;
00196 
00197   // Eliminate the scale of XtWX
00198   //
00199   for ( int i=0; i<6; i++ ) {
00200     for ( int j=0; j<6; j++ )
00201       covar(i,j) *= s(i) * s(j);
00202   }
00203   for ( int i=0; i<6; i++ ) {
00204     XtWy(i) *= s(i);
00205   }
00206 
00207   // Copy the solution into the result variables, and construct a
00208   // transformation object.
00209 
00210   // Translation component
00211   vnl_vector<double> trans( 2 );
00212   trans[0] = XtWy[4];
00213   trans[1] = XtWy[5];
00214 
00215   // Affine component
00216   vnl_matrix<double> A( 2, 2 );
00217   A(0,0) =  A(1,1) = XtWy[2];
00218   A(0,1) =  -XtWy[3];
00219   A(1,0) = -A(0,1);
00220 
00221   // Quadratic component
00222   vnl_matrix<double> Q( 2, 3, 0.0 );
00223   Q(0,0) = Q(0,1) = XtWy[0];
00224   Q(1,0) = Q(1,1) = XtWy[1];
00225 
00226   return new rgrl_trans_reduced_quad( Q, A, trans, covar.as_ref(), from_centre.as_ref(), to_centre.as_ref() );
00227 }
00228 
00229 
00230 rgrl_transformation_sptr
00231 rgrl_est_reduced_quad2d::
00232 estimate( rgrl_match_set_sptr matches,
00233           rgrl_transformation const& cur_transform ) const
00234 {
00235   // use base class implementation
00236   return rgrl_estimator::estimate( matches, cur_transform );
00237 }
00238 
00239 const vcl_type_info&
00240 rgrl_est_reduced_quad2d::
00241 transformation_type() const
00242 {
00243   return rgrl_trans_reduced_quad::type_id();
00244 }