core/vpgl/algo/vpgl_fm_compute_7_point.cxx
Go to the documentation of this file.
00001 // This is core/vpgl/algo/vpgl_fm_compute_7_point.cxx
00002 #ifndef vpgl_fm_compute_7_point_cxx_
00003 #define vpgl_fm_compute_7_point_cxx_
00004 //:
00005 // \file
00006 
00007 #include "vpgl_fm_compute_7_point.h"
00008 //
00009 #include <vnl/vnl_fwd.h>
00010 #include <vnl/vnl_math.h>
00011 #include <vgl/algo/vgl_norm_trans_2d.h>
00012 #include <vcl_iostream.h>
00013 
00014 //-------------------------------------------
00015 bool
00016 vpgl_fm_compute_7_point::compute(
00017   const vcl_vector< vgl_homg_point_2d<double> >& pr,
00018   const vcl_vector< vgl_homg_point_2d<double> >& pl,
00019   vcl_vector< vpgl_fundamental_matrix<double>* >& fm )
00020 {
00021   // Check that there are at least 7 points.
00022   if ( pr.size() < 7 || pl.size() < 7 ) {
00023     vcl_cerr << "vpgl_fm_compute_7_point: Need at least 7 point pairs.\n"
00024              << "Number in each set: " << pr.size() << ", " << pl.size() << vcl_endl;
00025     return false;
00026   }
00027 
00028   // Check that the correspondence lists are the same size.
00029   if ( pr.size() != pl.size() ) {
00030     vcl_cerr << "vpgl_fm_compute_7_point: Need correspondence lists of same size.\n";
00031     return false;
00032   }
00033 
00034   // Condition if necessary.
00035   vcl_vector< vgl_homg_point_2d<double> > pr_norm, pl_norm;
00036   vgl_norm_trans_2d<double> prnt, plnt;
00037   if ( precondition_ ) {
00038     prnt.compute_from_points(pr);
00039     plnt.compute_from_points(pl);
00040     for ( unsigned i = 0; i < pl.size(); i++ ) {
00041       pr_norm.push_back( prnt*pr[i] );
00042       pl_norm.push_back( plnt*pl[i] );
00043     }
00044   }
00045   else {
00046     for ( unsigned i = 0; i < pl.size(); i++ ) {
00047       pr_norm.push_back( pr[i] );
00048       pl_norm.push_back( pl[i] );
00049     }
00050   }
00051 
00052   // Construct the design matrix from the point correspondences.
00053   vnl_matrix<double> design_matrix(pr_norm.size(),9);
00054   for ( unsigned r = 0; r < pr_norm.size(); r++ ) {
00055     design_matrix(r,0) = pr_norm[r].x()*pl_norm[r].x();
00056     design_matrix(r,1) = pr_norm[r].y()*pl_norm[r].x();
00057     design_matrix(r,2) = pr_norm[r].w()*pl_norm[r].x();
00058     design_matrix(r,3) = pr_norm[r].x()*pl_norm[r].y();
00059     design_matrix(r,4) = pr_norm[r].y()*pl_norm[r].y();
00060     design_matrix(r,5) = pr_norm[r].w()*pl_norm[r].y();
00061     design_matrix(r,6) = pr_norm[r].x()*pl_norm[r].w();
00062     design_matrix(r,7) = pr_norm[r].y()*pl_norm[r].w();
00063     design_matrix(r,8) = pr_norm[r].w()*pl_norm[r].w();
00064   }
00065 
00066   design_matrix.normalize_rows();
00067   vnl_svd<double> design_svd( design_matrix );
00068   vnl_matrix<double> W = design_svd.nullspace();
00069 
00070   // Take the first and second nullvectors from the nullspace
00071   // Since rank 2 these should be the only associated with non-zero
00072   // root (probably need conditioning first to be actually rank 2)
00073 
00074   vnl_double_3x3 F1(W.get_column(0).data_block());
00075   vnl_double_3x3 F2(W.get_column(1).data_block());
00076 
00077   // Using the fact that Det(alpha*F1 +(1 - alpha)*F2) == 0
00078   // find the real roots of the cubic equation that satisfy
00079   vcl_vector<double> a = get_coeffs(F1, F2);
00080   vcl_vector<double> roots = solve_cubic(a);
00081   for (unsigned int i = 0; i < roots.size(); i++) {
00082     vpgl_fundamental_matrix<double> F_temp( F1.as_ref()*roots[0] + F2*(1 - roots[i]) );
00083     if ( precondition_ )
00084       F_temp.set_matrix( plnt.get_matrix().transpose()*F_temp.get_matrix()*prnt.get_matrix() );
00085     fm.push_back( new vpgl_fundamental_matrix<double>(F_temp) );
00086   }
00087   return true;
00088 };
00089 
00090 
00091 //------------------------------------------------
00092 vcl_vector<double>
00093 vpgl_fm_compute_7_point::get_coeffs( vnl_double_3x3 const& F1,
00094                                      vnl_double_3x3 const& F2 )
00095 {
00096   // All the following computed with Mapl_norme for oxl/mvl/FMatrixCompute7Point.
00097   double a=F1(0,0), j=F2(0,0), aa=a-j,
00098          b=F1(0,1), k=F2(0,1), bb=b-k,
00099          c=F1(0,2), l=F2(0,2), cc=c-l,
00100          d=F1(1,0), m=F2(1,0), dd=d-m,
00101          e=F1(1,1), n=F2(1,1), ee=e-n,
00102          f=F1(1,2), o=F2(1,2), ff=f-o,
00103          g=F1(2,0), p=F2(2,0), gg=g-p,
00104          h=F1(2,1), q=F2(2,1), hh=h-q,
00105          i=F1(2,2), r=F2(2,2), ii=i-r;
00106 
00107   double a1=ee*ii-ff*hh, b1=ee*r+ii*n-ff*q-hh*o, c1=r*n-o*q;
00108   double d1=bb*ii-cc*hh, e1=bb*r+ii*k-cc*q-hh*l, f1=r*k-l*q;
00109   double g1=bb*ff-cc*ee, h1=bb*o+ff*k-cc*n-ee*l, i1=o*k-l*n;
00110 
00111   vcl_vector<double> v;
00112   v.push_back(aa*a1-dd*d1+gg*g1);
00113   v.push_back(aa*b1+a1*j-dd*e1-d1*m+gg*h1+g1*p);
00114   v.push_back(aa*c1+b1*j-dd*f1-e1*m+gg*i1+h1*p);
00115   v.push_back(c1*j-f1*m+i1*p);
00116 
00117   return v;
00118 };
00119 
00120 
00121 //------------------------------------------------
00122 vcl_vector<double>
00123 vpgl_fm_compute_7_point::solve_quadratic( vcl_vector<double> v )
00124 {
00125   double a = v[1], b = v[2], c = v[3];
00126   double s = (b > 0.0) ? 1.0 : -1.0;
00127   double d = b * b - 4 * a * c;
00128 
00129   // round off error
00130   if ( d > -1e-5 && d < 0)
00131     d = 0.0;
00132 
00133   if (d < 0.0) // doesn't work for compl_normex roots
00134     return vcl_vector<double>(); // empty list
00135 
00136   double q = -0.5 * ( b + s * vcl_sqrt(d));
00137   vcl_vector<double> l; l.push_back(q/a); l.push_back(c/q);
00138   return l;
00139 };
00140 
00141 
00142 //------------------------------------------------
00143 vcl_vector<double>
00144 vpgl_fm_compute_7_point::solve_cubic( vcl_vector<double> v )
00145 {
00146   double a = v[0], b = v[1], c = v[2], d = v[3];
00147 
00148   // firstly check to see if we have appr_normoximately a quadratic
00149   double len = a*a + b*b + c*c + d*d;
00150   if (vcl_abs(a*a/len) < 1e-6 )
00151     return solve_quadratic(v);
00152 
00153   b /= a; c /= a; d /= a; b /= 3;
00154   // With the substitution x = y-b, the equation becomes y^3-3qy+2r = 0:
00155   double q = b*b - c/3;
00156   double r = b*(b*b-c/2) + d/2;
00157   // At this point, a, c and d are no longer needed (c and d will be reused).
00158 
00159   if (q == 0) {
00160     vcl_vector<double> w;
00161     double cbrt = (r<0) ? vcl_exp(vcl_log(-2*r)/3.0) : -vcl_exp(vcl_log(2*r)/3.0);
00162     w.push_back(cbrt - b);
00163     return w;
00164   }
00165 
00166   // With the Vieta substitution y = z+q/z this becomes z^6+2rz^3+q^3 = 0
00167   // which is essentially a quadratic equation:
00168 
00169   d = r*r - q*q*q;
00170   if ( d >= 0.0 ) {
00171     // Compute a cube root
00172     double z;
00173     if ( -r + vcl_sqrt(d) >= 0 ) z = vcl_exp(vcl_log(-r + vcl_sqrt(d))/3.0);
00174     else z = -vcl_exp(vcl_log(-r + vcl_sqrt(d))/3.0);
00175 
00176     // The case z=0 is excluded since this is q==0 which is handled above
00177     vcl_vector<double> w; w.push_back(z + q/z - b); return w;
00178   }
00179 
00180   // And finally the "irreducible case" (with 3 solutions):
00181   c = vcl_sqrt(q);
00182   double theta = vcl_acos( r/q/c ) / 3;
00183   vcl_vector<double> l;
00184   l.push_back(-2.0*c*vcl_cos(theta)                    - b);
00185   l.push_back(-2.0*c*vcl_cos(theta + 2*vnl_math::pi/3) - b);
00186   l.push_back(-2.0*c*vcl_cos(theta - 2*vnl_math::pi/3) - b);
00187   return l;
00188 };
00189 
00190 
00191 #endif // vpgl_fm_compute_7_point_cxx_