contrib/oxl/mvl/FMatrixComputeNonLinear.cxx
Go to the documentation of this file.
00001 // This is oxl/mvl/FMatrixComputeNonLinear.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 
00008 #include "FMatrixComputeNonLinear.h"
00009 
00010 #include <vcl_iostream.h>
00011 
00012 #include <vnl/vnl_matrix.h>
00013 #include <vnl/algo/vnl_levenberg_marquardt.h>
00014 #include <vnl/vnl_double_2.h>
00015 #include <vnl/vnl_double_3.h>
00016 #include <vgl/vgl_homg_line_2d.h>
00017 #include <mvl/HomgNorm2D.h>
00018 #include <mvl/HomgPoint2D.h>
00019 #include <mvl/HomgLine2D.h>
00020 #include <mvl/HomgInterestPointSet.h>
00021 #include <mvl/FMatrix.h>
00022 #include <mvl/FManifoldProject.h>
00023 #include <mvl/FMatrixCompute7Point.h>
00024 
00025 
00026 // Seven parameters for a minimal parametrization of the F matrix
00027 const int FMatrixComputeNonLinear_nparams = 7;
00028 
00029 //-----------------------------------------------------------------------------
00030 //: Constructor
00031 //
00032 FMatrixComputeNonLinear::FMatrixComputeNonLinear(PairMatchSetCorner* matches)
00033  : vnl_least_squares_function(FMatrixComputeNonLinear_nparams, matches->compute_match_count(), no_gradient),
00034    data_size_(matches->compute_match_count()),
00035    matches_(*matches),
00036    one_(true)
00037 {
00038   // Copy matching points from matchset.
00039   // Set up some initial variables
00040   HomgInterestPointSet const* points1 = matches_.get_corners1();
00041   HomgInterestPointSet const* points2 = matches_.get_corners2();
00042   vcl_vector<HomgPoint2D> dead1, dead2;
00043   vcl_vector<int> point1_int, point2_int;
00044   matches_.extract_matches(dead1, point1_int, dead2, point2_int);
00045   data_size_ = matches_.count();
00046   points1_.resize(points1->size());
00047   points2_.resize(points2->size());
00048 
00049   // Get the actual image points
00050   for (int a = 0; a < data_size_; a++) {
00051     vnl_double_2 temp1;
00052     temp1 = points1->get_2d(point1_int[a]);
00053     points1_[a] = vgl_homg_point_2d<double>(temp1[0], temp1[1], 1.0);
00054   }
00055 
00056   for (int a = 0; a < data_size_; a++) {
00057     vnl_double_2 temp2;
00058     temp2 = points2->get_2d(point2_int[a]);
00059     points2_[a] = vgl_homg_point_2d<double>(temp2[0], temp2[1], 1.0);
00060   }
00061 }
00062 
00063 //-----------------------------------------------------------------------------
00064 //: Compute the F Matrix by augmenting a 7 point basis
00065 
00066 bool FMatrixComputeNonLinear::compute_basis(FMatrix* F, vcl_vector<int> basis)
00067 {
00068   one_ = false;
00069   vcl_vector<vgl_homg_point_2d<double> > basis1(7), basis2(7);
00070   for (int i = 0; i < 7; i++) {
00071     int other = matches_.get_match_12(basis[i]);
00072     if (other == -1)
00073       vcl_cerr << "The basis index doesn't include a match for " << i << ".\n";
00074     else {
00075       vnl_double_2 p1 = matches_.get_corners1()->get_2d(basis[i]);
00076       vnl_double_2 p2 = matches_.get_corners2()->get_2d(other);
00077         basis1[i] = vgl_homg_point_2d<double>(p1[0], p1[1], 1.0);
00078         basis2[i] = vgl_homg_point_2d<double>(p2[0], p2[1], 1.0);
00079     }
00080   }
00081   basis1_ = basis1;
00082   basis2_ = basis2;
00083   return compute(F);
00084 }
00085 
00086 
00087 // General compute method, will alternate between either of the
00088 // selected options
00089 bool FMatrixComputeNonLinear::compute(FMatrix* F)
00090 {
00091   FMatrix F_final;
00092   // fm_fmatrix_nagmin
00093   vcl_cerr << "FMatrixComputeNonLinear: matches = "<< data_size_ <<", using "<< FMatrixComputeNonLinear_nparams <<" parameters\n";
00094   double so_far = 1e+8;
00095   FMatrix norm_F = *F;
00096   if (one_)
00097   {
00098     for (p_ = 0; p_ < 3; p_++)
00099     {
00100       for (q_ = 0; q_ < 3; q_++)
00101       {
00102         for (r_ = 0; r_ < 4; r_++)
00103         {
00104           FMatrix norm_F = *F;
00105           int r1 = 0, c1 = 0, r2 = 0, c2 = 0;
00106           get_plan(r1, c1, r2, c2);
00107           vnl_matrix<double> mat;
00108           norm_F.get(&mat);
00109 
00110           switch (r_) {
00111             case 0 :
00112               mat /= mat.get(r1, c1);
00113               break;
00114             case 1 :
00115               mat /= mat.get(r1, c2);
00116               break;
00117             case 2 :
00118               mat /= mat.get(r2, c1);
00119               break;
00120             case 3 :
00121               mat /= mat.get(r2, c2);
00122               break;
00123             default: // this cannot happen
00124               break;
00125           }
00126 
00127           vnl_vector<double> f_params(FMatrixComputeNonLinear_nparams);
00128           fmatrix_to_params(FMatrix(mat), f_params);
00129           //FMatrix res = params_to_fmatrix(f_params);
00130           vnl_levenberg_marquardt lm(*this);
00131 
00132           // Adjusting these parameters will alter the
00133           // convergence properties of the minimisation
00134           lm.set_max_function_evals(200);
00135           lm.set_f_tolerance(1e-6);
00136           lm.set_x_tolerance(1e-6);
00137 //        lm.set_epsilon_function(1e-6);
00138           lm.minimize(f_params);
00139 
00140           if (lm.get_end_error() < so_far) {
00141             so_far = lm.get_end_error();
00142             vcl_cerr << "so_far : " << so_far << vcl_endl;
00143             norm_F = params_to_fmatrix(f_params);
00144             F_final = norm_F;
00145             vgl_homg_point_2d<double> e1, e2;
00146             F_final.get_epipoles(e1, e2);
00147             vcl_cerr << "Epipole locations 1 : " << e1 << " 2 : " << e2 << '\n';
00148           }
00149         }
00150       }
00151     }
00152   }
00153   else
00154   {
00155     F_orig_ = norm_F;
00156     vnl_vector<double> f_params(FMatrixComputeNonLinear_nparams, 0.0);
00157     FMatrix res = params_to_fmatrix(f_params);
00158     vnl_double_3x3 mat1 = norm_F.get_matrix();
00159     vnl_double_3x3 mat2 = res.get_matrix();
00160     mat1 /= mat1(2, 2);
00161     mat2 /= mat2(2, 2);
00162 
00163     vnl_levenberg_marquardt lm(*this);
00164 
00165     // Adjusting these parameters will alter the
00166     // convergence properties of the minimisation
00167     lm.set_max_function_evals(100);
00168     lm.set_f_tolerance(1e-4);
00169     lm.set_x_tolerance(1e-3);
00170     lm.set_epsilon_function(1e-4);
00171     lm.minimize(f_params);
00172 
00173     if (lm.get_end_error() < so_far) {
00174       so_far = lm.get_end_error();
00175       vcl_cerr << "so_far : " << so_far << vcl_endl;
00176       for (int l = 0; l < 7; l++)
00177         vcl_cerr << f_params(l) << vcl_endl;
00178       norm_F = params_to_fmatrix(f_params);
00179       F_final = norm_F;
00180       lm.diagnose_outcome();
00181       vgl_homg_point_2d<double> e1, e2;
00182       F_final.get_epipoles(e1, e2);
00183       vcl_cerr << "Epipole locations 1 : " << e1 << " 2 : " << e2 << '\n';
00184     }
00185   }
00186   *F = F_final;
00187   return true;
00188 }
00189 
00190 //-----------------------------------------------------------------------------
00191 //: The virtual function from vnl_levenberg_marquardt which returns the RMS epipolar error and a vector of residuals.
00192 void FMatrixComputeNonLinear::f(const vnl_vector<double>& f_params, vnl_vector<double>& fx)
00193 {
00194   FMatrix F = params_to_fmatrix(f_params);
00195 
00196   fx = calculate_residuals(&F);
00197 }
00198 
00199 // Convert an F Matrix to the parameters to be minimised
00200 void FMatrixComputeNonLinear::fmatrix_to_params(const FMatrix& F, vnl_vector<double>& params)
00201 {
00202   // Leaving d as 1.0 on all the rotations
00203   int c1= 0, r1 = 0, c2 = 0, r2 = 0;
00204   get_plan(r1, c1, r2, c2);
00205   double b = 0.0, a = 0.0, c = 0.0;
00206   switch (r_) {
00207     case 0 :
00208       a = F.get(r1, c2);
00209       b = -F.get(r2, c1);
00210       c = -F.get(r2, c2);
00211       break;
00212     case 1 :
00213       a = F.get(r1, c1);
00214       b = -F.get(r2 ,c1);
00215       c = -F.get(r2, c2);
00216       break;
00217     case 2 :
00218       a = F.get(r1, c1);
00219       b = F.get(r1, c2);
00220       c = -F.get(r2, c2);
00221       break;
00222     case 3 :
00223       a = F.get(r1, c1);
00224       b = F.get(r1, c2);
00225       c = -F.get(r2, c1);
00226       break;
00227     default: // this cannot happen
00228       break;
00229   }
00230   vgl_homg_point_2d<double> one, two;
00231   vnl_double_2 e1, e2;
00232   F.get_epipoles(one, two);
00233   vnl_double_3 e1h(one.x(), one.y(), one.w());
00234   vnl_double_3 e2h(two.x(), two.y(), two.w());
00235   e1h = e1h.normalize();
00236   e2h = e2h.normalize();
00237 
00238   switch (p_) {
00239     case 0 :
00240       e1 = vnl_double_2(e1h[1]/e1h[0], e1h[2]/e1h[0]);
00241       break;
00242     case 1 :
00243       e1 = vnl_double_2(e1h[0]/e1h[1], e1h[2]/e1h[1]);
00244       break;
00245     case 2 :
00246       e1 = vnl_double_2(e1h[0]/e1h[2], e1h[1]/e1h[2]);
00247       break;
00248     default: // this cannot happen
00249       break;
00250   }
00251   switch (q_) {
00252     case 0 :
00253       e2 = vnl_double_2(e2h[1]/e2h[0], e2h[2]/e2h[0]);
00254       break;
00255     case 1 :
00256       e2 = vnl_double_2(e2h[0]/e2h[1], e2h[2]/e2h[1]);
00257       break;
00258     case 2 :
00259       e2 = vnl_double_2(e2h[0]/e2h[2], e2h[1]/e2h[2]);
00260       break;
00261     default: // this cannot happen
00262       break;
00263   }
00264   params.put(0, a);
00265   params.put(1, b);
00266   params.put(2, c);
00267   params.put(3, e1[0]);
00268   params.put(4, e1[1]);
00269   params.put(5, e2[0]);
00270   params.put(6, e2[1]);
00271 }
00272 
00273 
00274 // Convert the parameters to an F Matrix
00275 FMatrix FMatrixComputeNonLinear::params_to_fmatrix(const vnl_vector<double>& params)
00276 {
00277   FMatrix ret;
00278 
00279   if (one_)
00280   {
00281     vnl_double_3x3 ref = ret.get_matrix();
00282     // Again the d is moved about through the different parametrizations
00283     int c1= 0, r1 = 0, c2 = 0, r2 = 0;
00284     get_plan(r1, c1, r2, c2);
00285     double a = params.get(0);
00286     double b = params.get(1);
00287     double c = params.get(2);
00288     double x = -params.get(3);
00289     double y = -params.get(4);
00290     double xd = -params.get(5);
00291     double yd = -params.get(6);
00292     double d;
00293     switch (r_)
00294     {
00295       case 0 :
00296         d = 1.0;
00297         ref.put(r1, c2, a);
00298         ref.put(r2, c1, -b);
00299         ref.put(r2, c2, -c);
00300         ref.put(r1, c1, d);
00301         ref.put(r1, p_, d*x + a*y);
00302         ref.put(r2, p_, -b*x - c*y);
00303         ref.put(q_, c1, d*xd - b*yd);
00304         ref.put(q_, c2, a*xd - c*yd);
00305         ref.put(q_, p_, xd*(d*x + a*y) - yd*(b*x + c*y));
00306         break;
00307       case 1 :
00308         d = 1.0;
00309         ref.put(r1, c1, a);
00310         ref.put(r2, c1, -b);
00311         ref.put(r2, c2, -c);
00312         ref.put(r1, c2, d);
00313         ref.put(r1, p_, a*x + d*y);
00314         ref.put(r2, p_, -b*x - c*y);
00315         ref.put(q_, c1, a*xd - b*yd);
00316         ref.put(q_, c2, d*xd - c*yd);
00317         ref.put(q_, p_, xd*(a*x + d*y) - yd*(b*x + c*y));
00318         break;
00319       case 2 :
00320         d = -1.0;
00321         ref.put(r1, c1, a);
00322         ref.put(r1, c2, b);
00323         ref.put(r2, c2, -c);
00324         ref.put(r2, c1, -d);
00325         ref.put(r1, p_, a*x + b*y);
00326         ref.put(r2, p_, -d*x - c*y);
00327         ref.put(q_, c1, a*xd - d*yd);
00328         ref.put(q_, c2, b*xd - c*yd);
00329         ref.put(q_, p_, xd*(a*x + b*y) - yd*(d*x + c*y));
00330         break;
00331       case 3 :
00332         d = -1.0;
00333         ref.put(r1, c1, a);
00334         ref.put(r1, c2, b);
00335         ref.put(r2, c1, -c);
00336         ref.put(r2, c2, -d);
00337         ref.put(r1, p_, a*x + b*y);
00338         ref.put(r2, p_, -c*x - d*y);
00339         ref.put(q_, c1, a*xd - c*yd);
00340         ref.put(q_, c2, b*xd - d*yd);
00341         ref.put(q_, p_, xd*(a*x + b*y) - yd*(c*x + d*y));
00342         break;
00343       default: // this cannot happen
00344         break;
00345     }
00346     ret.set(ref);
00347     return ret;
00348   }
00349   else
00350   {
00351     vcl_vector<vgl_homg_point_2d<double> > new_points1(7);
00352     vgl_homg_point_2d<double> e1, e2;
00353     F_orig_.get_epipoles(e1, e2);
00354     double e1nx = e1.x()/e1.w(), e1ny = e1.y()/e1.w();
00355 //  double e2nx = e2.x()/e2.w(), e2ny = e2.y()/e2.w();
00356     for (int i = 0; i < 7; i++) {
00357       double t1x = basis1_[i].x()/basis1_[i].w(), t1y = basis1_[i].y()/basis1_[i].w();
00358 //    double t2x = basis2_[i].x()/basis2_[i].w(), t2y = basis2_[i].y()/basis2_[i].w();
00359       double grads1 = -(e1ny - t1y)/(e1nx - t1x);
00360 //    double grads2 = -(e2ny - t2y)/(e2nx - t2x);
00361       new_points1[i] = vgl_homg_point_2d<double>(params[i]/grads1 + t1x, params[i]*grads1 + t1y, 1.0);
00362 //    new_points2[i] = vgl_homg_point_2d<double>(params[i]/grads2 + t2x, params[i]*grads2 + t2y, 1.0);
00363     }
00364     FMatrixCompute7Point computor(true, true);
00365     vcl_vector<FMatrix*> ref;
00366     if (!computor.compute(new_points1, basis2_, ref))
00367       vcl_cerr << "FMatrixCompute7Point Failure\n";
00368     double final = 0.0;
00369     unsigned int num = 0;
00370     for (unsigned int l = 0; l < ref.size(); l++) {
00371       vnl_vector<double> res = calculate_residuals(ref[l]);
00372       double so_far = 0.0;
00373       for (unsigned int m = 0; m < res.size(); m++)
00374         so_far += res[m];
00375 //      vcl_cerr << "so_far : " << so_far << vcl_endl;
00376       if (so_far < final) {
00377         final = so_far;
00378         num = l;
00379       }
00380     }
00381     ret = *ref[num];
00382 
00383     for (unsigned int l = 0; l < ref.size(); l++)
00384       delete ref[l];
00385 
00386     return ret;
00387   }
00388 }
00389 
00390 // Forms a map of the different rank-2 structures for the F Matrix
00391 void FMatrixComputeNonLinear::get_plan(int &r1, int &c1, int &r2, int &c2)
00392 {
00393   switch (p_) {
00394     case 0 :
00395       c1 = 1;
00396       c2 = 2;
00397       break;
00398     case 1 :
00399       c1 = 0;
00400       c2 = 2;
00401       break;
00402     case 2 :
00403       c1 = 0;
00404       c2 = 1;
00405       break;
00406     default: // this cannot happen
00407       break;
00408   }
00409   switch (q_) {
00410     case 0 :
00411       r1 = 1;
00412       r2 = 2;
00413       break;
00414     case 1 :
00415       r1 = 0;
00416       r2 = 2;
00417       break;
00418     case 2 :
00419       r1 = 0;
00420       r2 = 1;
00421       break;
00422     default: // this cannot happen
00423       break;
00424   }
00425 }
00426 
00427 static double perp_dist_squared(vgl_homg_point_2d<double> const& p,
00428                                 vgl_homg_line_2d <double> const& l)
00429 {
00430   double r = l.a()*p.x() + l.b()*p.y() + l.c()*p.w();
00431   if (r == 0) return 0.0;
00432   r /= p.w();
00433   return r * r / (l.a()*l.a() + l.b()*l.b());
00434 }
00435 
00436 vnl_vector<double> FMatrixComputeNonLinear::calculate_residuals(FMatrix* F)
00437 {
00438   vnl_vector<double> fx(data_size_);
00439   vnl_matrix<double> f(3, 3, 0.0);
00440   F->get(&f);
00441   f /= f.rms();
00442 #if 0
00443   vnl_matrix<double> ft = f.transpose();
00444   vnl_matrix<double> z(3, 3, 0.0);
00445   z.put(0, 0, 1.0);z.put(1, 1, 1.0);
00446   vnl_matrix<double> pre1 = ft*z*f;
00447   vnl_matrix<double> pre2 = f*z*ft;
00448 #endif
00449 
00450   for (int i = 0; i < data_size_; i++)
00451   {
00452     vgl_homg_point_2d<double> one = points1_[i], two = points2_[i];
00453     double t = 0.0;
00454     vgl_homg_line_2d<double> l1 = F->image2_epipolar_line(one);
00455     vgl_homg_line_2d<double> l2 = F->image1_epipolar_line(two);
00456     t += perp_dist_squared(two, l1);
00457     t += perp_dist_squared(one, l2);
00458 #if 0
00459     vnl_double_3 oi(1.0); oi[0]=one.x()/one.w(); oi[1]=one.y()/one.w();
00460     vnl_double_3 ti(1.0); ti[0]=two.x()/two.w(); ti[1]=two.x()/two.w();
00461     vnl_double_3 p1 = ti*pre1;
00462     vnl_double_3 p2 = oi*pre2;
00463     double p11 = p1[0]*oi[0] + p1[1]*oi[1] + p1[2]*oi[2];
00464     double p21 = p2[0]*ti[0] + p2[1]*ti[1] + p2[2]*ti[2];
00465     double factor = 1.0/ p11 + 1.0/ p21;
00466 #endif
00467     fx[i] = t;//*factor;
00468   }
00469   return fx;
00470 }