contrib/oxl/mvl/FManifoldProject.cxx
Go to the documentation of this file.
00001 // This is oxl/mvl/FManifoldProject.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 //  \file
00007 
00008 #include "FManifoldProject.h"
00009 
00010 #include <vnl/vnl_double_2x2.h>
00011 #include <vnl/vnl_double_4.h>
00012 #include <vnl/algo/vnl_symmetric_eigensystem.h>
00013 #include <vnl/algo/vnl_rpoly_roots.h>
00014 #include <vnl/vnl_real_polynomial.h>
00015 #include <vnl/vnl_transpose.h>
00016 #include <vnl/algo/vnl_svd.h>
00017 #include <vnl/vnl_diag_matrix.h>
00018 #include <vnl/vnl_math.h>
00019 #include <vgl/vgl_homg_point_2d.h>
00020 
00021 #include <mvl/HomgPoint2D.h>
00022 #include <mvl/FMatrix.h>
00023 #include <mvl/HomgOperator2D.h>
00024 
00025 //: Construct an FManifoldProject object which will use the given F to correct point pairs.
00026 FManifoldProject::FManifoldProject(const FMatrix& Fobj)
00027 {
00028   set_F(Fobj);
00029 }
00030 
00031 //: Construct an FManifoldProject object with the intention of later setting its F.
00032 FManifoldProject::FManifoldProject() {}
00033 
00034 //: Use the given F to correct point pairs.
00035 void FManifoldProject::set_F(const FMatrix& Fobj)
00036 {
00037   F_ = Fobj.get_matrix();
00038 
00039   F_.assert_finite();
00040 
00041   // Top left corner of F
00042   vnl_double_2x2 f22 = F_.extract(2,2);
00043 
00044   // A := 0.5*[O f22'; f22 O];
00045   A_.fill(0.0);
00046   A_.update(0.5*f22.transpose(), 0, 2);
00047   A_.update(0.5*f22, 2, 0);
00048 
00049   vnl_double_4 b(F_(2,0), F_(2,1), F_(0,2), F_(1,2));
00050 
00051   double c = F_(2,2);
00052 
00053   // Compute eig(A) to translate and rotate the quadric
00054   vnl_symmetric_eigensystem<double>  eig(A_);
00055 
00056 #ifdef DEBUG
00057   vcl_cerr << vnl_svd<double>(F_);
00058   MATLABPRINT(F_);
00059   MATLABPRINT(eig.D);
00060 #endif // DEBUG
00061 
00062   // If all eigs are 0, had an affine F
00063   affine_F_ = eig.D(3,3) < 1e-6;
00064   if (affine_F_) {
00065 #ifdef DEBUG
00066     vcl_cerr << "FManifoldProject: Affine F = " << F_ << vcl_endl;
00067 #endif // DEBUG
00068     double s = 1.0 / b.magnitude();
00069     t_ = b * s;
00070     d_[0] = c * s;
00071   }
00072   else {
00073     // Translate Quadric so that b = 0. (Translates to the epipoles)
00074     t_ = -0.5 * eig.solve(b);
00075 
00076     vnl_double_4 At = A_*t_;
00077     vnl_double_4 Bprime = 2.0*At + b;
00078     double tAt = dot_product(t_, At);
00079     double Cprime = tAt + dot_product(t_, b) + c;
00080 
00081     // Now C is zero cos F is rank 2
00082     if (vnl_math_abs(Cprime) > 1e-6) {
00083       vcl_cerr << "FManifoldProject: ** HartleySturm: F = " << F_ << vcl_endl
00084                << "FManifoldProject: ** HartleySturm: B = " << Bprime << vcl_endl
00085                << "FManifoldProject: ** HartleySturm: Cerror = " << Cprime << vcl_endl
00086                << "FManifoldProject: ** HartleySturm: F not rank 2 ?\n"
00087                << "FManifoldProject: singular values are "  << vnl_svd<double>(F_).W() << vcl_endl;
00088     }
00089     // **** Now have quadric x'*A*x = 0 ****
00090 
00091     // Rotate A
00092 
00093     // Group the sign-conjugates
00094     // Re-sort the eigensystem so that it is -a a -b b
00095     {
00096       int I[] = { 0, 3, 1, 2 };
00097       for (int col = 0; col < 4; ++col) {
00098         int from_col = I[col];
00099         d_[col] = eig.D(from_col,from_col);
00100         for (int r=0;r<4;++r)
00101           V_(r,col) = eig.V(r, from_col);
00102       }
00103     }
00104   }
00105 }
00106 
00107 //: Find the points out1, out2 which minimize d(out1,p1) + d(out2,p2) subject to out1'*F*out2 = 0.
00108 //  Returns the minimum distance squared: ||x[1..4] - p[1..4]||^2.
00109 double FManifoldProject::correct(vgl_homg_point_2d<double> const& p1,
00110                                  vgl_homg_point_2d<double> const& p2,
00111                                  vgl_homg_point_2d<double>& out1,
00112                                  vgl_homg_point_2d<double>& out2) const
00113 {
00114   if (p1.w()==0 || p2.w()==0) {
00115     vcl_cerr << "FManifoldProject: p1 or p2 at infinity\n";
00116     out1 = p1; out2 = p2; return 1e33;
00117   }
00118 
00119   double p_out[4];
00120   double d = correct(p1.x()/p1.w(), p1.y()/p1.w(), p2.x()/p2.w(), p2.y()/p2.w(),
00121                      p_out, p_out+1, p_out+2, p_out+3);
00122 
00123   out1.set(p_out[0], p_out[1], 1.0);
00124   out2.set(p_out[2], p_out[3], 1.0);
00125   return d;
00126 }
00127 
00128 //: Find the points out1, out2 which minimize d(out1,p1) + d(out2,p2) subject to out1'*F*out2 = 0.
00129 //  Returns the minimum distance squared: ||x[1..4] - p[1..4]||^2.
00130 double FManifoldProject::correct(const HomgPoint2D& p1, const HomgPoint2D& p2, HomgPoint2D* out1, HomgPoint2D* out2) const
00131 {
00132   double p[4];
00133   if (!p1.get_nonhomogeneous(p[0], p[1]) ||
00134       !p2.get_nonhomogeneous(p[2], p[3])) {
00135     vcl_cerr << "FManifoldProject: p1 or p2 at infinity\n";
00136     *out1 = p1;
00137     *out2 = p2;
00138     return 1e30;
00139   }
00140 
00141   double p_out[4];
00142   double d = correct(p[0], p[1], p[2], p[3], &p_out[0], &p_out[1], &p_out[2], &p_out[3]);
00143 
00144   out1->set(p_out[0], p_out[1], 1.0);
00145   out2->set(p_out[2], p_out[3], 1.0);
00146   return d;
00147 }
00148 
00149 //: Find the points out1, out2 which minimize d(out1,p1) + d(out2,p2) subject to out1'*F*out2 = 0.
00150 //  Returns the minimum distance squared: ||x[1..4] - p[1..4]||^2.
00151 double FManifoldProject::correct(double   x1, double   y1, double   x2, double   y2,
00152                                  double *ox1, double *oy1, double *ox2, double *oy2) const
00153 {
00154   // Make the query point
00155   vnl_double_4 p;
00156   p[0] = x1;
00157   p[1] = y1;
00158   p[2] = x2;
00159   p[3] = y2;
00160 
00161   if (affine_F_) {
00162     // Easy case for affine F, F is a plane.
00163     // t_ = n;
00164     // d_[0] = d;
00165     // pout = x - n (n p + d)
00166     const vnl_double_4& n = t_;
00167     double d = d_[0];
00168 
00169     double distance = (dot_product(n, p) + d);
00170     *ox1 = p[0];
00171     *oy1 = p[1];
00172     *ox2 = p[2];
00173     *oy2 = p[3];
00174 
00175     vnl_double_3 l = F_ * vnl_double_3(p[2], p[3], 1.0);
00176     double EPIDIST = (l[0] * p[0] + l[1] * p[1] + l[2])/vcl_sqrt(l[0]*l[0]+l[1]*l[1]);
00177     if (EPIDIST > 1e-4) {
00178       vcl_cerr << "FManifoldProject: Affine F: EPIDIST = " << EPIDIST << vcl_endl
00179                << "FManifoldProject: Affine F: p = " << (dot_product(p,n) + d) << vcl_endl;
00180 #if 0
00181       double EPI1 = dot_product(out2->get_vector(), F_*out1->get_vector());
00182       double EPI2 = dot_product(p, n) + d;
00183       vcl_cerr << "t = " << n << ' ' << d << vcl_endl
00184                << "F_ = " << F_ << vcl_endl
00185                << "FManifoldProject: Affine F: E = " << (EPI1 - EPI2) << vcl_endl;
00186       vcl_abort();
00187 #endif // 0
00188     }
00189 
00190     return distance * distance;
00191   }
00192 
00193   // Transform the query point
00194   vnl_double_4 pprime = vnl_transpose(V_) * (p - t_);
00195 
00196   // Solve p' (I - lambda D)^-1 D (I - lambda D)^-1 p = 0
00197   double b1 = 1./d_[0]; double a1 = vnl_math_sqr(pprime[0])*b1;
00198   double b2 = 1./d_[1]; double a2 = vnl_math_sqr(pprime[1])*b2;
00199   double b3 = 1./d_[2]; double a3 = vnl_math_sqr(pprime[2])*b3;
00200   double b4 = 1./d_[3]; double a4 = vnl_math_sqr(pprime[3])*b4;
00201 
00202   if (vnl_math_max(vnl_math_abs(b1 + b2), vnl_math_abs(b3 + b4)) > 1e-7) {
00203     vcl_cerr << "FManifoldProject: B = [" <<b1<< ' ' <<b2<< ' ' <<b3<< ' ' <<b4<< "];\n"
00204              << "FManifoldProject: b1 != -b2 or b3 != -b4\n";
00205   }
00206 
00207   // a11 ../ (b1 - x).^2 + a12 ../ (b1 + x).^2 + a21 ../ (b2 - x).^2 + a22 ../ (b2 + x).^2
00208   // a11 = p1^2*b1
00209   //                 2         2              2         2              2         2          2              2         2         2
00210   //     (a3*(x - b1)  (x - b2)  + a2*(x - b1)  (x - b3)  + a1*(x - b2)  (x - b3) ) (x - b4)  + a4*(x - b1)  (x - b2)  (x - b3)
00211   // Coeffs from mma, assuming /. { b4 -> -b3, b2 -> -b1 }
00212   static vnl_vector<double> coeffs_(7);
00213   double b12 = b1*b1;
00214   double b32 = b3*b3;
00215   double b14 = b12*b12;
00216   double b34 = b32*b32;
00217 
00218   coeffs_[6] = a3*b14*b32 + a4*b14*b32 + a1*b12*b34 + a2*b12*b34;
00219   coeffs_[5] = (2*a3*b14*b3 - 2*a4*b14*b3 + 2*a1*b1*b34 - 2*a2*b1*b34);
00220   coeffs_[4] = (a3*b14 + a4*b14 - 2*(a1 +a2 + a3 + a4)*b12*b32 + a1*b34 + a2*b34);
00221   coeffs_[3] = (-4*a3*b12*b3 + 4*a4*b12*b3 - 4*a1*b1*b32 + 4*a2*b1*b32);
00222   coeffs_[2] = (a1*b12 + a2*b12 - 2*a3*b12 - 2*a4*b12 - 2*a1*b32 - 2*a2*b32 + a3*b32 + a4*b32);
00223   coeffs_[1] = 2*(b3*(a3 - a4) + b1*(a1 - a2));
00224   coeffs_[0] = (a1 + a2 + a3 + a4);
00225 
00226   // Don't try this: c = c ./ [1e0 1e2 1e4 1e6 1e8 1e10 1e12]
00227   coeffs_ /= coeffs_.magnitude();
00228 
00229   vnl_real_polynomial poly(coeffs_);
00230   vnl_rpoly_roots roots(coeffs_);
00231   double dmin = 1e30;
00232   vnl_double_4 Xmin;
00233   vnl_vector<double> realroots = roots.realroots(1e-8);
00234   int errs = 0;
00235   bool got_one = false;
00236   for (unsigned i = 0; i < realroots.size(); ++i) {
00237     double lambda = realroots[i];
00238 
00239     // Some roots to the multiplied out poly are not roots to the rational polynomial.
00240     double RATPOLY_RESIDUAL = (a1/vnl_math_sqr(b1 - lambda) +
00241                                a2/vnl_math_sqr(b2 - lambda) +
00242                                a3/vnl_math_sqr(b3 - lambda) +
00243                                a4/vnl_math_sqr(b4 - lambda));
00244 
00245     if (vcl_fabs(RATPOLY_RESIDUAL) > 1e-8)
00246       continue;
00247 
00248     vnl_diag_matrix<double> Dinv(1.0 - lambda * d_);
00249     Dinv.invert_in_place();
00250     vnl_double_4 Xp = Dinv * pprime.as_ref();
00251     vnl_double_4 X = V_ * Xp + t_;
00252 
00253     // Paranoia check
00254     {
00255       HomgPoint2D X1(X[0], X[1]);
00256       HomgPoint2D X2(X[2], X[3]);
00257       double EPIDIST = HomgOperator2D::perp_dist_squared(X2, HomgLine2D(F_*X1.get_vector()));
00258       if (0 && EPIDIST > 1e-12) {
00259         // This can happen in reasonable circumstances -- notably when one
00260         // epipole is at infinity.
00261         vcl_cerr << "FManifoldProject: A root has epidist = " << vcl_sqrt(EPIDIST) << vcl_endl
00262                  << "  coeffs: " << coeffs_ << vcl_endl
00263                  << "  root = " << lambda << vcl_endl
00264                  << "  poly residual = " << poly.evaluate(lambda) << vcl_endl
00265                  << "  rational poly residual = " << RATPOLY_RESIDUAL << vcl_endl;
00266         ++ errs;
00267         break;
00268       }
00269     }
00270 
00271     double dist = (X - p).squared_magnitude();
00272     if (!got_one || dist < dmin) {
00273       dmin = dist;
00274       Xmin = X;
00275       got_one = true;
00276     }
00277   }
00278 
00279   if (!got_one) {
00280     vcl_cerr << "FManifoldProject: AROOGAH. Final epipolar distance =  " << dmin << ", errs = " << errs << vcl_endl;
00281     *ox1 = x1;
00282     *oy1 = y1;
00283     *ox2 = x2;
00284     *oy2 = y2;
00285   }
00286   else {
00287     *ox1 = Xmin[0];
00288     *oy1 = Xmin[1];
00289     *ox2 = Xmin[2];
00290     *oy2 = Xmin[3];
00291   }
00292 
00293   return dmin;
00294 }