contrib/oxl/mvl/FMatrixCompute7Point.cxx
Go to the documentation of this file.
00001 // This is oxl/mvl/FMatrixCompute7Point.cxx
00002 #include "FMatrixCompute7Point.h"
00003 //:
00004 // \file
00005 // \author David N. McKinnon, UQ I.R.I.S
00006 // \date   25 Nov 2000
00007 //-----------------------------------------------------------------------------
00008 
00009 #include <vcl_vector.h>
00010 #include <vcl_iostream.h>
00011 #include <vcl_cmath.h>
00012 
00013 #include <vnl/vnl_matrix.h>
00014 #include <vnl/vnl_math.h>
00015 #include <vnl/algo/vnl_svd.h>
00016 
00017 #include <mvl/HomgMetric.h>
00018 #include <mvl/PairMatchSetCorner.h>
00019 #include <mvl/FDesignMatrix.h>
00020 #include <mvl/HomgNorm2D.h>
00021 
00022 FMatrixCompute7Point::FMatrixCompute7Point(bool precondition, bool rank2_truncate)
00023 : precondition_(precondition),
00024   rank2_truncate_(rank2_truncate)
00025 {
00026 }
00027 
00028 //-----------------------------------------------------------------------------
00029 //
00030 //: Compute a fundamental matrix for a set of 7 point matches.
00031 //
00032 // Return false if the calculation fails or there are fewer than eight point
00033 // matches in the list.
00034 
00035 bool FMatrixCompute7Point::compute(PairMatchSetCorner& matches, vcl_vector<FMatrix*>& F)
00036 {
00037   // Copy matching points from matchset.
00038   vcl_vector<HomgPoint2D> points1(matches.count());
00039   vcl_vector<HomgPoint2D> points2(matches.count());
00040   matches.extract_matches(points1, points2);
00041   return compute(points1, points2, F);
00042 }
00043 
00044 //-----------------------------------------------------------------------------
00045 bool FMatrixCompute7Point::compute (vcl_vector<vgl_homg_point_2d<double> >& points1,
00046                                     vcl_vector<vgl_homg_point_2d<double> >& points2,
00047                                     vcl_vector<FMatrix*>& F)
00048 {
00049   if (points1.size() < 7 || points2.size() < 7) {
00050     vcl_cerr << "FMatrixCompute7Point: Need at least 7 point pairs.\n"
00051              << "Number in each set: " << points1.size() << ", " << points2.size() << vcl_endl;
00052     return false;
00053   }
00054 
00055   if (precondition_) {
00056     // Condition points
00057     HomgNorm2D conditioned1(points1);
00058     HomgNorm2D conditioned2(points2);
00059 
00060     // Compute F with preconditioned points
00061     if (!compute_preconditioned(conditioned1.get_normalized_points(),
00062                                 conditioned2.get_normalized_points(), F))
00063       return false;
00064 
00065     // De-condition F
00066     for (unsigned int i = 0; i < F.size(); i++) {
00067       FMatrix* oldF = F[i];
00068       F[i] = new FMatrix(HomgMetric::homg_to_image_F(*F[i], &conditioned1,
00069                                                             &conditioned2));
00070       delete oldF;
00071     }
00072   }
00073   else
00074     if (!compute_preconditioned(points1, points2, F))
00075       return false;
00076 
00077   return true;
00078 }
00079 
00080 //-----------------------------------------------------------------------------
00081 bool FMatrixCompute7Point::compute(vcl_vector<HomgPoint2D>& points1,
00082                                    vcl_vector<HomgPoint2D>& points2,
00083                                    vcl_vector<FMatrix*>& F)
00084 {
00085   if (points1.size() < 7 || points2.size() < 7) {
00086     vcl_cerr << "FMatrixCompute7Point: Need at least 7 point pairs.\n"
00087              << "Number in each set: " << points1.size() << ", " << points2.size() << vcl_endl;
00088     return false;
00089   }
00090 
00091   if (precondition_) {
00092     // Condition points
00093     HomgNorm2D conditioned1(points1);
00094     HomgNorm2D conditioned2(points2);
00095 
00096     // Compute F with preconditioned points
00097     if (!compute_preconditioned(conditioned1.get_normalized_points(),
00098                                 conditioned2.get_normalized_points(), F))
00099       return false;
00100 
00101     // De-condition F
00102     for (unsigned int i = 0; i < F.size(); i++) {
00103       FMatrix* oldF = F[i];
00104       F[i] = new FMatrix(HomgMetric::homg_to_image_F(*F[i], &conditioned1,
00105                                                             &conditioned2));
00106       delete oldF;
00107     }
00108     return true;
00109   }
00110   else return compute_preconditioned(points1, points2, F);
00111 }
00112 
00113 //-----------------------------------------------------------------------------
00114 bool FMatrixCompute7Point::compute_preconditioned(vcl_vector<vgl_homg_point_2d<double> >& points1,
00115                                                   vcl_vector<vgl_homg_point_2d<double> >& points2,
00116                                                   vcl_vector<FMatrix*>& F)
00117 {
00118   // Create design matrix from conditioned points
00119   FDesignMatrix design(points1, points2);
00120 
00121   // Normalize rows for better conditioning
00122   design.normalize_rows();
00123 
00124   // Extract vnl_svd<double> of design matrix
00125   vnl_svd<double> svd(design);
00126 
00127   vnl_matrix<double> W = svd.nullspace();
00128 
00129   // Take the first and second nullvectors from the nullspace
00130   // Since rank 2 these should be the only associated with non-zero
00131   // root (Probably need conditioning first to be actually rank 2)
00132   FMatrix F1(vnl_double_3x3(W.get_column(0).data_block()));
00133   FMatrix F2(vnl_double_3x3(W.get_column(1).data_block()));
00134 
00135   // Using the fact that Det(alpha*F1 +(1 - alpha)*F2) == 0
00136   // find the real roots of the cubic equation that satisfy
00137   vcl_vector<double> a = FMatrixCompute7Point::GetCoef(F1, F2);
00138   vcl_vector<double> roots = FMatrixCompute7Point::solve_cubic(a);
00139 
00140   if (roots.empty())
00141     return false;
00142 
00143   for (unsigned int i = 0; i < roots.size(); i++) {
00144     vnl_matrix<double> F_temp =
00145       F1.get_matrix().as_ref()*roots[0] + F2.get_matrix()*(1 - roots[i]);
00146     F.push_back(new FMatrix(F_temp));
00147   }
00148   // Rank-truncate F
00149   if (rank2_truncate_) {
00150     for (unsigned int h = 0; h < F.size(); ++h) {
00151       F[h]->set_rank2_using_svd();
00152     }
00153   }
00154   return true;
00155 }
00156 
00157 //-----------------------------------------------------------------------------
00158 bool FMatrixCompute7Point::compute_preconditioned(vcl_vector<HomgPoint2D>& points1,
00159                                                   vcl_vector<HomgPoint2D>& points2,
00160                                                   vcl_vector<FMatrix*>& F)
00161 {
00162   // Create design matrix from conditioned points
00163   FDesignMatrix design(points1, points2);
00164 
00165   // Normalize rows for better conditioning
00166   design.normalize_rows();
00167 
00168   // Extract vnl_svd<double> of design matrix
00169   vnl_svd<double> svd(design);
00170 
00171   vnl_matrix<double> W = svd.nullspace();
00172 
00173   // Take the first and second nullvectors from the nullspace
00174   // Since rank 2 these should be the only associated with non-zero
00175   // root (Probably need conditioning first to be actually rank 2)
00176   FMatrix F1(vnl_double_3x3(W.get_column(0).data_block()));
00177   FMatrix F2(vnl_double_3x3(W.get_column(1).data_block()));
00178 
00179   // Using the fact that Det(alpha*F1 +(1 - alpha)*F2) == 0
00180   // find the real roots of the cubic equation that satisfy
00181   vcl_vector<double> a = FMatrixCompute7Point::GetCoef(F1, F2);
00182   vcl_vector<double> roots = FMatrixCompute7Point::solve_cubic(a);
00183 
00184   for (unsigned int i = 0; i < roots.size(); i++) {
00185     vnl_matrix<double> F_temp =
00186       F1.get_matrix().as_ref()*roots[0] + F2.get_matrix()*(1 - roots[i]);
00187     F.push_back(new FMatrix(F_temp));
00188   }
00189   // Rank-truncate F
00190   if (rank2_truncate_) {
00191     for (unsigned int h = 0; h < F.size(); ++h) {
00192       F[h]->set_rank2_using_svd();
00193     }
00194   }
00195   return true;
00196 }
00197 
00198 //
00199 // Det(alpha*F1 +(1 - alpha)*F2) == 0
00200 // (I obtained these coefficients from Maple (fingers crossed!!!))
00201 //
00202 vcl_vector<double> FMatrixCompute7Point::GetCoef(FMatrix const& F1, FMatrix const& F2)
00203 {
00204   double a=F1.get(0,0), j=F2.get(0,0), aa=a-j,
00205          b=F1.get(0,1), k=F2.get(0,1), bb=b-k,
00206          c=F1.get(0,2), l=F2.get(0,2), cc=c-l,
00207          d=F1.get(1,0), m=F2.get(1,0), dd=d-m,
00208          e=F1.get(1,1), n=F2.get(1,1), ee=e-n,
00209          f=F1.get(1,2), o=F2.get(1,2), ff=f-o,
00210          g=F1.get(2,0), p=F2.get(2,0), gg=g-p,
00211          h=F1.get(2,1), q=F2.get(2,1), hh=h-q,
00212          i=F1.get(2,2), r=F2.get(2,2), ii=i-r;
00213 
00214   double a1=ee*ii-ff*hh, b1=ee*r+ii*n-ff*q-hh*o, c1=r*n-o*q;
00215   double d1=bb*ii-cc*hh, e1=bb*r+ii*k-cc*q-hh*l, f1=r*k-l*q;
00216   double g1=bb*ff-cc*ee, h1=bb*o+ff*k-cc*n-ee*l, i1=o*k-l*n;
00217 
00218   vcl_vector<double> v;
00219   v.push_back(aa*a1-dd*d1+gg*g1);
00220   v.push_back(aa*b1+a1*j-dd*e1-d1*m+gg*h1+g1*p);
00221   v.push_back(aa*c1+b1*j-dd*f1-e1*m+gg*i1+h1*p);
00222   v.push_back(c1*j-f1*m+i1*p);
00223 
00224   return v;
00225 }
00226 
00227 //--------------------
00228 //:
00229 //  Gives solutions to 0x^3 + ax^2 + bx + c = 0.
00230 //  Returns the set of real solutions, so if both are imaginary it returns an
00231 //  empty list, otherwise a list of length two.
00232 //  v is a 4-vector of which the first element is ignored.
00233 //-------------------
00234 vcl_vector<double> FMatrixCompute7Point::solve_quadratic (vcl_vector<double> v)
00235 {
00236    double a = v[1], b = v[2], c = v[3];
00237    double s = (b > 0.0) ? 1.0 : -1.0;
00238    double d = b * b - 4 * a * c;
00239 
00240    // round off error
00241    if ( d > -1e-5 && d < 0)
00242      d = 0.0;
00243 
00244    if (d < 0.0) // doesn't work for complex roots
00245       return vcl_vector<double>(); // empty list
00246 
00247    double q = -0.5 * ( b + s * vcl_sqrt(d));
00248 
00249    vcl_vector<double> l; l.push_back(q/a); l.push_back(c/q);
00250    return l;
00251 }
00252 
00253 // Compute cube root of a positive or a negative number
00254 inline double my_cbrt(double x)
00255 {
00256    return (x>=0) ? vcl_exp(vcl_log(x)/3.0) : -vcl_exp(vcl_log(-x)/3.0);
00257 }
00258 
00259 //------------------
00260 //:
00261 //  Solves a cubic and returns the real solutions.
00262 //  Thus it returns a list of length 1 if there are 2 complex roots and 1 real,
00263 //  of length 2 if it is in fact a quadratic with 2 solutions,
00264 //  of length 3 if there are 3 real solutions
00265 //     a x^3 + b x^2 + c x + d = 0
00266 //-------------------
00267 // Rewritten and documented by Peter Vanroose, 23 October 2001.
00268 
00269 vcl_vector<double> FMatrixCompute7Point::solve_cubic(vcl_vector<double> v)
00270 {
00271    double a = v[0], b = v[1], c = v[2], d = v[3];
00272 
00273    /* firstly check to see if we have approximately a quadratic */
00274    double len = a*a + b*b + c*c + d*d;
00275    if (vcl_abs(a*a/len) < 1e-6 )
00276       return FMatrixCompute7Point::solve_quadratic(v);
00277 
00278    b /= a; c /= a; d /= a; b /= 3;
00279    // With the substitution x = y-b, the equation becomes y^3-3qy+2r = 0:
00280    double q = b*b - c/3;
00281    double r = b*(b*b-c/2) + d/2;
00282    // At this point, a, c and d are no longer needed (c and d will be reused).
00283 
00284    if (q == 0) {
00285       vcl_vector<double> w; w.push_back(my_cbrt(-2*r) - b); return w;
00286    }
00287 
00288    // With the Vieta substitution y = z+q/z this becomes z^6+2rz^3+q^3 = 0
00289    // which is essentially a quadratic equation:
00290 
00291    d = r*r - q*q*q;
00292    if ( d >= 0.0 )
00293    {
00294       double z  = my_cbrt(-r + vcl_sqrt(d));
00295       // The case z=0 is excluded since this is q==0 which is handled above
00296       vcl_vector<double> w; w.push_back(z + q/z - b); return w;
00297    }
00298 
00299    // And finally the "irreducible case" (with 3 solutions):
00300    c = vcl_sqrt(q);
00301    double theta = vcl_acos( r/q/c ) / 3;
00302    vcl_vector<double> l;
00303    l.push_back(-2.0*c*vcl_cos(theta)                    - b);
00304    l.push_back(-2.0*c*vcl_cos(theta + 2*vnl_math::pi/3) - b);
00305    l.push_back(-2.0*c*vcl_cos(theta - 2*vnl_math::pi/3) - b);
00306    return l;
00307 }