core/vgl/algo/vgl_h_matrix_2d_compute_linear.cxx
Go to the documentation of this file.
00001 // This is core/vgl/algo/vgl_h_matrix_2d_compute_linear.cxx
00002 #include "vgl_h_matrix_2d_compute_linear.h"
00003 //:
00004 // \file
00005 
00006 #include <vcl_iostream.h>
00007 #include <vcl_cmath.h>
00008 #include <vcl_cassert.h>
00009 #include <vnl/vnl_inverse.h>
00010 #include <vnl/vnl_transpose.h>
00011 #include <vnl/algo/vnl_svd.h>
00012 #include <vgl/algo/vgl_norm_trans_2d.h>
00013 
00014 //: Construct a vgl_h_matrix_2d_compute_linear object.
00015 // The allow_ideal_points flag is described below.
00016 vgl_h_matrix_2d_compute_linear::vgl_h_matrix_2d_compute_linear(bool allow_ideal_points):
00017   allow_ideal_points_(allow_ideal_points)
00018 {
00019 }
00020 
00021 
00022 // Should provide:
00023 //   Points-only method
00024 //   Lines-only
00025 //   Points&lines
00026 //
00027 // FSM - this is now done by vgl_h_matrix_2d_compute_design.
00028 
00029 const int TM_UNKNOWNS_COUNT = 9;
00030 const double DEGENERACY_THRESHOLD = 0.00001;  // FSM. see below.
00031 
00032 //-----------------------------------------------------------------------------
00033 //
00034 //: Compute a plane-plane projectivity using linear least squares.
00035 // Returns false if the calculation fails or there are fewer than four point
00036 // matches in the list.  The algorithm finds the nullvector of the $2 n \times 9$ design
00037 // matrix:
00038 // \f[
00039 // \left(\begin{array}{ccccccccc}
00040 // 0 & 0 & 0 &        x_1 z_1' & y_1 z_1' & z_1 z_1' & -x_1 y_1' & -y_1 y_1' & -z_1 y_1' \cr
00041 // x_1 z_1' & y_1 z_1' & z_1 z_1' & 0 & 0 & 0 & -x_1 x_1' & -y_1 x_1' & -z_1 x_1' \cr
00042 // \multicolumn{9}{c}{\cdots} \cr
00043 // 0 & 0 & 0 &        x_n z_n' & y_n z_n' & z_n z_n' & -x_n y_n' & -y_n y_n' & -z_n y_n'\cr
00044 // x_n z_n' & y_n z_n' & z_n z_n' & 0 & 0 & 0 & -x_n x_n' & -y_n x_n' & -z_n x_n'
00045 // \end{array}\right)
00046 // \f]
00047 // If \t allow_ideal_points was set at construction, the $3 \times 9$ version which
00048 // allows for ideal points is used.
00049 bool vgl_h_matrix_2d_compute_linear::
00050 solve_linear_problem(int equ_count,
00051                      vcl_vector<vgl_homg_point_2d<double> > const& p1,
00052                      vcl_vector<vgl_homg_point_2d<double> > const& p2,
00053                      vgl_h_matrix_2d<double>& H)
00054 {
00055   //transform the point sets and fill the design matrix
00056   vnl_matrix<double> D(equ_count, TM_UNKNOWNS_COUNT);
00057   int n = p1.size();
00058   int row = 0;
00059   for (int i = 0; i < n; i++) {
00060     D(row, 0) =  p1[i].x() * p2[i].w();
00061     D(row, 1) =  p1[i].y() * p2[i].w();
00062     D(row, 2) =  p1[i].w() * p2[i].w();
00063     D(row, 3) = 0;
00064     D(row, 4) = 0;
00065     D(row, 5) = 0;
00066     D(row, 6) = -p1[i].x() * p2[i].x();
00067     D(row, 7) = -p1[i].y() * p2[i].x();
00068     D(row, 8) = -p1[i].w() * p2[i].x();
00069     ++row;
00070 
00071     D(row, 0) = 0;
00072     D(row, 1) = 0;
00073     D(row, 2) = 0;
00074     D(row, 3) =  p1[i].x() * p2[i].w();
00075     D(row, 4) =  p1[i].y() * p2[i].w();
00076     D(row, 5) =  p1[i].w() * p2[i].w();
00077     D(row, 6) = -p1[i].x() * p2[i].y();
00078     D(row, 7) = -p1[i].y() * p2[i].y();
00079     D(row, 8) = -p1[i].w() * p2[i].y();
00080     ++row;
00081 
00082     if (allow_ideal_points_) {
00083       D(row, 0) =  p1[i].x() * p2[i].y();
00084       D(row, 1) =  p1[i].y() * p2[i].y();
00085       D(row, 2) =  p1[i].w() * p2[i].y();
00086       D(row, 3) = -p1[i].x() * p2[i].x();
00087       D(row, 4) = -p1[i].y() * p2[i].x();
00088       D(row, 5) = -p1[i].w() * p2[i].x();
00089       D(row, 6) = 0;
00090       D(row, 7) = 0;
00091       D(row, 8) = 0;
00092       ++row;
00093     }
00094   }
00095 
00096   D.normalize_rows();
00097   vnl_svd<double> svd(D);
00098 
00099   //
00100   // FSM added :
00101   //
00102   if (svd.W(7)<DEGENERACY_THRESHOLD*svd.W(8)) {
00103     vcl_cerr << "vgl_h_matrix_2d_compute_linear : design matrix has rank < 8\n"
00104              << "vgl_h_matrix_2d_compute_linear : probably due to degenerate point configuration\n";
00105     return false;
00106   }
00107   // form the matrix from the nullvector
00108   H.set(svd.nullvector().data_block());
00109   return true;
00110 }
00111 
00112 bool vgl_h_matrix_2d_compute_linear::
00113 compute_p(vcl_vector<vgl_homg_point_2d<double> > const& points1,
00114           vcl_vector<vgl_homg_point_2d<double> > const& points2,
00115           vgl_h_matrix_2d<double>& H)
00116 {
00117   //number of points must be the same
00118   assert(points1.size() == points2.size());
00119   int n = points1.size();
00120 
00121   int equ_count = n * (allow_ideal_points_ ? 3 : 2);
00122   if (n * 2 < TM_UNKNOWNS_COUNT - 1) {
00123     vcl_cerr << "vgl_h_matrix_2d_compute_linear: Need at least 4 matches.\n";
00124     if (n == 0) vcl_cerr << "Could be vcl_vector setlength idiosyncrasies!\n";
00125     return false;
00126   }
00127   //compute the normalizing transforms
00128   vgl_norm_trans_2d<double> tr1, tr2;
00129   if (!tr1.compute_from_points(points1))
00130     return false;
00131   if (!tr2.compute_from_points(points2))
00132     return false;
00133   vcl_vector<vgl_homg_point_2d<double> > tpoints1, tpoints2;
00134   for (int i = 0; i<n; i++)
00135   {
00136     tpoints1.push_back(tr1(points1[i]));
00137     tpoints2.push_back(tr2(points2[i]));
00138   }
00139   vgl_h_matrix_2d<double> hh;
00140   if (!solve_linear_problem(equ_count, tpoints1, tpoints2, hh))
00141     return false;
00142   //
00143   // Next, hh has to be transformed back to the coordinate system of
00144   // the original point sets, i.e.,
00145   //  p1' = tr1 p1 , p2' = tr2 p2
00146   // hh was determined from the transform relation
00147   //  p2' = hh p1', thus
00148   // (tr2 p2) = hh (tr1 p1)
00149   //  p2 = (tr2^-1 hh tr1) p1 = H p1
00150   vgl_h_matrix_2d<double> tr2_inv = tr2.get_inverse();
00151   H = tr2_inv*hh*tr1;
00152   return true;
00153 }
00154 
00155 bool vgl_h_matrix_2d_compute_linear::
00156 compute_l(vcl_vector<vgl_homg_line_2d<double> > const& lines1,
00157           vcl_vector<vgl_homg_line_2d<double> > const& lines2,
00158           vgl_h_matrix_2d<double>& H)
00159 {
00160   //number of lines must be the same
00161   assert(lines1.size() == lines2.size());
00162   int n = lines1.size();
00163   int equ_count = 2*n;
00164   //compute the normalizing transforms. By convention, these are point
00165   //transformations.
00166   vgl_norm_trans_2d<double> tr1, tr2;
00167   if (!tr1.compute_from_lines(lines1))
00168     return false;
00169   if (!tr2.compute_from_lines(lines2))
00170     return false;
00171   vcl_vector<vgl_homg_point_2d<double> > tlines1, tlines2;
00172   for (vcl_vector<vgl_homg_line_2d<double> >::const_iterator
00173        lit = lines1.begin(); lit != lines1.end(); lit++)
00174   {
00175     // transform the lines according to the normalizing transform
00176     vgl_homg_line_2d<double> l = tr1(*lit);
00177     // convert the line to a point to use the same linear code
00178     vgl_homg_point_2d<double> p(l.a(), l.b(), l.c());
00179     tlines1.push_back(p);
00180   }
00181   for (vcl_vector<vgl_homg_line_2d<double> >::const_iterator
00182        lit = lines2.begin(); lit != lines2.end(); lit++)
00183   {
00184     // transform the lines according to the normalizing transform
00185     vgl_homg_line_2d<double> l = tr2(*lit);
00186     // convert the line to a point to use the same linear code
00187     vgl_homg_point_2d<double> p(l.a(), l.b(), l.c());
00188     tlines2.push_back(p);
00189   }
00190 
00191   vgl_h_matrix_2d<double> hl,hp,tr2inv;
00192   if (!solve_linear_problem(equ_count, tlines1, tlines2, hl))
00193     return false;
00194   // The result is a transform on lines so we need to convert it to
00195   // a point transform, i.e., hp = hl^-t.
00196   vnl_matrix_fixed<double, 3, 3> const &  Ml = hl.get_matrix();
00197   vnl_matrix_fixed<double, 3, 3> Mp = vnl_inverse_transpose(Ml);
00198   hp.set(Mp);
00199   //
00200   // Next, hp has to be transformed back to the coordinate system of
00201   // the original lines, i.e.,
00202   //  l1' = tr1 l1 , l2' = tr2 l2
00203   // hp was determined from the transform relation
00204   //  l2' = hh l1', thus
00205   // (tr2 l2) = hh (tr1 l1)
00206   //  p2 = (tr2^-1 hh tr1) p1 = H p1
00207   tr2inv = tr2.get_inverse();
00208   H = tr2inv*hp*tr1;
00209   return true;
00210 }
00211 
00212 bool vgl_h_matrix_2d_compute_linear::
00213 compute_pl(vcl_vector<vgl_homg_point_2d<double> > const& points1,
00214            vcl_vector<vgl_homg_point_2d<double> > const& points2,
00215            vcl_vector<vgl_homg_line_2d<double> > const& lines1,
00216            vcl_vector<vgl_homg_line_2d<double> > const& lines2,
00217            vgl_h_matrix_2d<double>& H)
00218 {
00219   //number of points must be the same
00220   assert(points1.size() == points2.size());
00221   int np = points1.size();
00222   //number of lines must be the same
00223   assert(lines1.size() == lines2.size());
00224   int nl = lines1.size();
00225 
00226   int equ_count = np * (allow_ideal_points_ ? 3 : 2) + 2*nl;
00227   if ((np+nl)*2+1 < TM_UNKNOWNS_COUNT)
00228   {
00229     vcl_cerr << "vgl_h_matrix_2d_compute_linear: Need at least 4 matches.\n";
00230     if (np+nl == 0) vcl_cerr << "Could be vcl_vector setlength idiosyncrasies!\n";
00231     return false;
00232   }
00233   //compute the normalizing transforms
00234   vgl_norm_trans_2d<double> tr1, tr2;
00235   if (!tr1.compute_from_points_and_lines(points1,lines1))
00236     return false;
00237   if (!tr2.compute_from_points_and_lines(points2,lines2))
00238     return false;
00239   vcl_vector<vgl_homg_point_2d<double> > tpoints1, tpoints2;
00240   for (int i = 0; i<np; i++)
00241   {
00242     tpoints1.push_back(tr1(points1[i]));
00243     tpoints2.push_back(tr2(points2[i]));
00244   }
00245   for (int i = 0; i<nl; i++)
00246   {
00247     double a=lines1[i].a(), b=lines1[i].b(), c=lines1[i].c(), d=vcl_sqrt(a*a+b*b);
00248     tpoints1.push_back(tr1(vgl_homg_point_2d<double>(-a*c,-b*c,d)));
00249     a=lines2[i].a(), b=lines2[i].b(), c=lines2[i].c(), d = vcl_sqrt(a*a+b*b);
00250     tpoints2.push_back(tr2(vgl_homg_point_2d<double>(-a*c,-b*c,d)));
00251   }
00252   vgl_h_matrix_2d<double> hh;
00253   if (!solve_linear_problem(equ_count, tpoints1, tpoints2, hh))
00254     return false;
00255 
00256   vgl_h_matrix_2d<double> tr2_inv = tr2.get_inverse();
00257   H = tr2_inv*hh*tr1;
00258   return true;
00259 }
00260 
00261 //--------------------------------------------------------
00262 //:
00263 //  The solution equations should be weighted by the length of
00264 //  the corresponding line matches.  This weighting is given by w.
00265 //
00266 //  The two equations resulting from l1i<->l2i should be
00267 //  weighted by wi.  Form a m x m diagonal matrix W with elements from w,
00268 //  with m = 2*Nc, where Nc=l1.size()=l2.size() is the number of
00269 //  corresponding line pairs.  The weighted least squares problem is
00270 //  expressed as:
00271 //
00272 //               (D^tWD)x = Mx = 0
00273 //
00274 //  where D is the design matrix and x is the 9 element vector of unknown
00275 //  homography matrix elements. This problem can be solved using SVD as in the
00276 //  case of unweighted least squares.
00277 //
00278 bool vgl_h_matrix_2d_compute_linear::
00279 solve_weighted_least_squares(vcl_vector<vgl_homg_line_2d<double> > const& l1,
00280                              vcl_vector<vgl_homg_line_2d<double> > const& l2,
00281                              vcl_vector<double> const& w,
00282                              vgl_h_matrix_2d<double>& H)
00283 {
00284   int Nc = l1.size();
00285   // Note the w has size Nc so we need to form a 2*Nc vector with
00286   // repeated values
00287   vnl_vector<double> two_w(2*Nc);
00288   int j =0;
00289   for (int i = 0; i<Nc; i++, j+=2)
00290   {
00291     two_w[j]=w[i];
00292     two_w[j+1]=w[i];
00293   }
00294   vnl_diag_matrix<double> W(two_w);
00295 
00296   //Form the design matrix, D
00297   vnl_matrix<double> D(2*Nc, TM_UNKNOWNS_COUNT);
00298   vnl_matrix<double> M(TM_UNKNOWNS_COUNT, TM_UNKNOWNS_COUNT);
00299 
00300   int row = 0;
00301   for (int i = 0; i < Nc; i++)
00302   {
00303     D(row, 0) =  l1[i].a() * l2[i].c();
00304     D(row, 1) =  l1[i].b() * l2[i].c();
00305     D(row, 2) =  l1[i].c() * l2[i].c();
00306     D(row, 3) = 0;
00307     D(row, 4) = 0;
00308     D(row, 5) = 0;
00309     D(row, 6) = -l1[i].a() * l2[i].a();
00310     D(row, 7) = -l1[i].b() * l2[i].a();
00311     D(row, 8) = -l1[i].c() * l2[i].a();
00312     ++row;
00313 
00314     D(row, 0) = 0;
00315     D(row, 1) = 0;
00316     D(row, 2) = 0;
00317     D(row, 3) =  l1[i].a() * l2[i].c();
00318     D(row, 4) =  l1[i].b() * l2[i].c();
00319     D(row, 5) =  l1[i].c() * l2[i].c();
00320     D(row, 6) = -l1[i].a() * l2[i].b();
00321     D(row, 7) = -l1[i].b() * l2[i].b();
00322     D(row, 8) = -l1[i].c() * l2[i].b();
00323     ++row;
00324   }
00325   M = vnl_transpose(D)*W*D;
00326   D.normalize_rows();
00327   vnl_svd<double> svd(D);
00328 
00329   //
00330   // FSM added :
00331   //
00332   if (svd.W(7)<DEGENERACY_THRESHOLD*svd.W(8)) {
00333     vcl_cerr << "vgl_h_matrix_2d_compute_linear : design matrix has rank < 8\n"
00334              << "vgl_h_matrix_2d_compute_linear : probably due to degenerate point configuration\n";
00335     return false;
00336   }
00337   // form the matrix from the nullvector
00338   H.set(svd.nullvector().data_block());
00339   return true;
00340 }
00341 
00342 bool vgl_h_matrix_2d_compute_linear::
00343 compute_l(vcl_vector<vgl_homg_line_2d<double> > const& lines1,
00344           vcl_vector<vgl_homg_line_2d<double> > const& lines2,
00345           vcl_vector<double> const & weights,
00346           vgl_h_matrix_2d<double>& H)
00347 {
00348   //number of lines must be the same
00349   assert(lines1.size() == lines2.size());
00350 //int n = lines1.size();
00351   //compute the normalizing transforms. By convention, these are point
00352   //transformations.
00353   vgl_norm_trans_2d<double> tr1, tr2;
00354   if (!tr1.compute_from_lines(lines1))
00355     return false;
00356   if (!tr2.compute_from_lines(lines2))
00357     return false;
00358   vcl_vector<vgl_homg_line_2d<double> > tlines1, tlines2;
00359   for (vcl_vector<vgl_homg_line_2d<double> >::const_iterator
00360        lit = lines1.begin(); lit != lines1.end(); lit++)
00361   {
00362     // transform the lines according to the normalizing transform
00363     vgl_homg_line_2d<double> l = tr1(*lit);
00364     tlines1.push_back(l);
00365   }
00366   for (vcl_vector<vgl_homg_line_2d<double> >::const_iterator
00367        lit = lines2.begin(); lit != lines2.end(); lit++)
00368   {
00369     // transform the lines according to the normalizing transform
00370     vgl_homg_line_2d<double> l = tr2(*lit);
00371     tlines2.push_back(l);
00372   }
00373 
00374   vgl_h_matrix_2d<double> hl,hp,tr2inv;
00375   if (!solve_weighted_least_squares(tlines1, tlines2, weights, hl))
00376     return false;
00377   // The result is a transform on lines so we need to convert it to
00378   // a point transform, i.e., hp = hl^-t.
00379   vnl_matrix_fixed<double, 3, 3> const &  Ml = hl.get_matrix();
00380   // make sure Ml can be inverted
00381   if( vnl_det(Ml) == 0.0 )
00382       return false;     
00383   vnl_matrix_fixed<double, 3, 3> Mp = vnl_inverse_transpose(Ml);
00384   hp.set(Mp);
00385   //
00386   // Next, hp has to be transformed back to the coordinate system of
00387   // the original lines, i.e.,
00388   //  l1' = tr1 l1 , l2' = tr2 l2
00389   // hp was determined from the transform relation
00390   //  l2' = hh l1', thus
00391   // (tr2 l2) = hh (tr1 l1)
00392   //  p2 = (tr2^-1 hh tr1) p1 = H p1
00393   tr2inv = tr2.get_inverse();
00394   H = tr2inv*hp*tr1;
00395   return true;
00396 }