contrib/oxl/mvl/FMatrix.cxx
Go to the documentation of this file.
00001 // This is oxl/mvl/FMatrix.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 
00008 #include "FMatrix.h"
00009 
00010 #include <vcl_cassert.h>
00011 #include <vcl_iostream.h>
00012 #include <vcl_fstream.h>
00013 
00014 #include <vul/vul_printf.h>
00015 
00016 #include <vnl/vnl_math.h>
00017 #include <vnl/vnl_matrix.h>
00018 #include <vnl/vnl_double_3x3.h>
00019 #include <vnl/vnl_double_3.h>
00020 #include <vnl/vnl_transpose.h>
00021 #include <vnl/vnl_cross_product_matrix.h>
00022 #include <vnl/algo/vnl_svd.h>
00023 #include <vnl/algo/vnl_rpoly_roots.h>
00024 #include <vgl/algo/vgl_homg_operators_2d.h>
00025 
00026 #include <mvl/HomgOperator2D.h>
00027 
00028 //--------------------------------------------------------------
00029 //
00030 //: Default constructor.
00031 // Sets matrices to size 3x3, zero-filled.
00032 
00033 FMatrix::FMatrix()
00034 {
00035   rank2_flag_ = false;
00036 }
00037 
00038 //--------------------------------------------------------------
00039 //
00040 //: Constructor.  Load from vcl_istream.
00041 
00042 FMatrix::FMatrix(vcl_istream& f)
00043 {
00044   rank2_flag_ = false;
00045   read_ascii(f);
00046 }
00047 
00048 //--------------------------------------------------------------
00049 //
00050 //: Constructor.
00051 
00052 FMatrix::FMatrix(const double *f_matrix)
00053 {
00054   rank2_flag_ = false;
00055   set(f_matrix);
00056 }
00057 
00058 //--------------------------------------------------------------
00059 //
00060 //: Constructor
00061 
00062 FMatrix::FMatrix(const vnl_matrix<double>& f_matrix)
00063 {
00064   rank2_flag_ = false;
00065   set(f_matrix);
00066 }
00067 
00068 
00069 //--------------------------------------------------------------
00070 //
00071 //: Construct from two P matrices
00072 
00073 FMatrix::FMatrix(const PMatrix& P1, const PMatrix& P2)
00074 {
00075   rank2_flag_ = false;
00076   set(P1, P2);
00077 }
00078 
00079 //--------------------------------------------------------------
00080 //
00081 //: Construct from one P matrix, the other is assumed to be [I 0].
00082 
00083 FMatrix::FMatrix(const PMatrix& P2)
00084 {
00085   rank2_flag_ = false;
00086   set(P2);
00087 }
00088 
00089 //--------------------------------------------------------------
00090 //
00091 //: Destructor
00092 FMatrix::~FMatrix()
00093 {
00094 }
00095 
00096 //---------------------------------------------------------------
00097 //: Read from ASCII vcl_istream
00098 bool FMatrix::read_ascii(vcl_istream& s)
00099 {
00100   s >> f_matrix_;
00101   if (!(s.good() || s.eof()))
00102     return false;
00103 
00104   ft_matrix_ = f_matrix_.transpose();
00105   rank2_flag_ = false;
00106   set_rank2_using_svd();
00107 
00108   return true;
00109 }
00110 
00111 FMatrix FMatrix::read(char const* filename)
00112 {
00113   vcl_ifstream f(filename);
00114   FMatrix F;
00115   if (!F.read_ascii(f))
00116     vcl_cerr << "FMatrix: Error reading from [" << filename << "]\n";
00117   return F;
00118 }
00119 
00120 //---------------------------------------------------------------
00121 //: Read from ASCII vcl_istream
00122 vcl_istream& operator>>(vcl_istream& s, FMatrix& F)
00123 {
00124   F.read_ascii(s);
00125   return s;
00126 }
00127 
00128 //---------------------------------------------------------------
00129 //: Read from ASCII vcl_istream
00130 FMatrix FMatrix::read(vcl_istream& s)
00131 {
00132   return FMatrix(s);
00133 }
00134 
00135 // == OPERATORS ==
00136 
00137 //--------------------------------------------------------------
00138 //
00139 //: Return the epipolar line  $l_1$ in image 1: $l_1 = F^\top x_2$
00140 vgl_homg_line_2d<double> FMatrix::image1_epipolar_line(const vgl_homg_point_2d<double>& x2) const
00141 {
00142   vgl_homg_point_2d<double> p = ft_matrix_ * x2;
00143   return vgl_homg_line_2d<double>(p.x(),p.y(),p.w()); // dual point
00144 }
00145 
00146 //--------------------------------------------------------------
00147 //
00148 //: Return the epipolar line  $l_1$ in image 1: $l_1 = F^\top x_2$
00149 HomgLine2D FMatrix::image1_epipolar_line(const HomgPoint2D& x2) const
00150 {
00151   return HomgLine2D(ft_matrix_ * x2.get_vector());
00152 }
00153 
00154 //----------------------------------------------------------------
00155 //
00156 //: Return the epipolar line $l_2$ in image 2: $l_2 = F x_1$
00157 
00158 vgl_homg_line_2d<double> FMatrix::image2_epipolar_line(const vgl_homg_point_2d<double>& x1) const
00159 {
00160   vgl_homg_point_2d<double> p = f_matrix_ * x1;
00161   return vgl_homg_line_2d<double>(p.x(),p.y(),p.w()); // dual point
00162 }
00163 
00164 //----------------------------------------------------------------
00165 //
00166 //: Return the epipolar line $l_2$ in image 2: $l_2 = F x_1$
00167 
00168 HomgLine2D FMatrix::image2_epipolar_line(const HomgPoint2D& x1) const
00169 {
00170   return HomgLine2D(f_matrix_ * x1.get_vector());
00171 }
00172 
00173 //-----------------------------------------------------------------
00174 //
00175 // For the specified match, return the perpendicular distance squared
00176 // between the epipolar line and the point, in image 1.
00177 
00178 double
00179 FMatrix::image1_epipolar_distance_squared(vgl_homg_point_2d<double> const& p1,
00180                                           vgl_homg_point_2d<double> const& p2) const
00181 {
00182   vgl_homg_line_2d<double> epipolar_line = image1_epipolar_line (p2);
00183   return vgl_homg_operators_2d<double>::perp_dist_squared (p1, epipolar_line);
00184 }
00185 
00186 //-----------------------------------------------------------------
00187 //
00188 // For the specified match, return the perpendicular distance squared
00189 // between the epipolar line and the point, in image 1.
00190 
00191 double
00192 FMatrix::image1_epipolar_distance_squared(HomgPoint2D *point1_ptr,
00193                                           HomgPoint2D *point2_ptr) const
00194 {
00195   HomgLine2D epipolar_line = image1_epipolar_line (*point2_ptr);
00196   return HomgOperator2D::perp_dist_squared (*point1_ptr, epipolar_line);
00197 }
00198 
00199 //-------------------------------------------------------------------
00200 //
00201 // For the specified match, return the perpendicular distance squared
00202 // between the epipolar line and the point, in image 2.
00203 
00204 double
00205 FMatrix::image2_epipolar_distance_squared(vgl_homg_point_2d<double> const& p1,
00206                                           vgl_homg_point_2d<double> const& p2) const
00207 {
00208   vgl_homg_line_2d<double> epipolar_line = image2_epipolar_line(p1);
00209   return vgl_homg_operators_2d<double>::perp_dist_squared(p2, epipolar_line);
00210 }
00211 
00212 //-------------------------------------------------------------------
00213 //
00214 // For the specified match, return the perpendicular distance squared
00215 // between the epipolar line and the point, in image 2.
00216 
00217 double
00218 FMatrix::image2_epipolar_distance_squared(HomgPoint2D *point1_ptr,
00219                                           HomgPoint2D *point2_ptr) const
00220 {
00221   HomgLine2D epipolar_line = image2_epipolar_line (*point1_ptr);
00222   return HomgOperator2D::perp_dist_squared (*point2_ptr, epipolar_line);
00223 }
00224 
00225 //---------------------------------------------------------------
00226 //: Print to vcl_ostream
00227 vcl_ostream& operator<<(vcl_ostream& os, const FMatrix& F)
00228 {
00229   const vnl_double_3x3& m = F.get_matrix();
00230   for (unsigned long i = 0; i < m.rows(); i++) {    // For each row in matrix
00231     for (unsigned long j = 0; j < m.columns(); j++) // For each column in matrix
00232       vul_printf(os, "%24.16e ", m(i,j));           // Output data element
00233     os << '\n';                                     // Output newline
00234   }
00235   return os;
00236 }
00237 
00238 // == COMPUTATIONS ==
00239 //-------------------------------------------------------------------
00240 
00241 //: Return an FMatrix which corresponds to the reverse of this one.
00242 FMatrix FMatrix::transpose() const
00243 {
00244   return FMatrix(ft_matrix_);
00245 }
00246 
00247 //: Compute the epipoles (left and right nullspaces of F) using vnl_svd<double>.
00248 // Return false if the rank of F is not 2, and set approximate epipoles,
00249 // (the left and right singular vectors corresponding to the smallest
00250 // singular value of F).
00251 
00252 bool
00253 FMatrix::get_epipoles(vgl_homg_point_2d<double>& epipole1,
00254                       vgl_homg_point_2d<double>& epipole2) const
00255 {
00256   // fm_compute_epipoles
00257   vnl_svd<double> svd(f_matrix_);
00258   vnl_double_3 v = svd.nullvector();
00259   epipole1.set(v[0],v[1],v[2]);
00260   v = svd.left_nullvector();
00261   epipole2.set(v[0],v[1],v[2]);
00262   return svd.W(2,2) == 0;
00263 }
00264 
00265 //: Compute the epipoles (left and right nullspaces of F) using vnl_svd<double>.
00266 // Return false if the rank of F is not 2, and set approximate epipoles,
00267 // (the left and right singular vectors corresponding to the smallest
00268 // singular value of F).
00269 
00270 bool
00271 FMatrix::get_epipoles(HomgPoint2D*epipole1_ptr, HomgPoint2D*epipole2_ptr) const
00272 {
00273   // fm_compute_epipoles
00274   vnl_svd<double> svd(f_matrix_);
00275   epipole1_ptr->set(svd.nullvector());
00276   epipole2_ptr->set(svd.left_nullvector());
00277   return svd.W(2,2) == 0;
00278 }
00279 
00280 
00281 //-----------------------------------------------------------------------------
00282 //
00283 //: Find nearest match which agrees with F.
00284 // For a specified pair of matching points, find the nearest (minimum sum
00285 // of squared image distances) match which is in perfect agreement with
00286 // the epipolar geometry of the F matrix.
00287 // (see R.I. Hartley and P. Sturm, ``Triangulation''. In
00288 // {\em Proceedings, Computer Analysis of Images and Patterns},
00289 // Prague, 1995).
00290 
00291 void
00292 FMatrix::find_nearest_perfect_match(vgl_homg_point_2d<double> const& point1,
00293                                     vgl_homg_point_2d<double> const& point2,
00294                                     vgl_homg_point_2d<double>& perfect_point1,
00295                                     vgl_homg_point_2d<double>& perfect_point2) const
00296 {
00297   vgl_homg_point_2d<double> epipole1, epipole2;
00298   get_epipoles(epipole1, epipole2);
00299 
00300   find_nearest_perfect_match(point1, point2, epipole1, epipole2, perfect_point1, perfect_point2);
00301 }
00302 
00303 //-----------------------------------------------------------------------------
00304 //
00305 //: Find nearest match which agrees with F.
00306 // For a specified pair of matching points, find the nearest (minimum sum
00307 // of squared image distances) match which is in perfect agreement with
00308 // the epipolar geometry of the F matrix.
00309 // (see R.I. Hartley and P. Sturm, ``Triangulation''. In
00310 // {\em Proceedings, Computer Analysis of Images and Patterns},
00311 // Prague, 1995).
00312 
00313 void
00314 FMatrix::find_nearest_perfect_match(const HomgPoint2D& point1,
00315                                     const HomgPoint2D& point2,
00316                                     HomgPoint2D *perfect_point1_ptr,
00317                                     HomgPoint2D *perfect_point2_ptr) const
00318 {
00319   HomgPoint2D epipole1, epipole2;
00320   get_epipoles(&epipole1, &epipole2);
00321 
00322   find_nearest_perfect_match(point1, point2, epipole1, epipole2, perfect_point1_ptr, perfect_point2_ptr);
00323 }
00324 
00325 //: Faster Hartley-Sturm using precomputed epipoles
00326 void
00327 FMatrix::find_nearest_perfect_match(vgl_homg_point_2d<double> const& point1,
00328                                     vgl_homg_point_2d<double> const& point2,
00329                                     vgl_homg_point_2d<double> const& epipole1,
00330                                     vgl_homg_point_2d<double> const& epipole2,
00331                                     vgl_homg_point_2d<double>& perfect_point1,
00332                                     vgl_homg_point_2d<double>& perfect_point2) const
00333 {
00334   vgl_homg_line_2d<double> line_horiz(0,1,0);
00335 
00336   vgl_homg_line_2d<double> line1 = vgl_homg_operators_2d<double>::join_oriented(point1, epipole1);
00337   double angle1 = vgl_homg_operators_2d<double>::angle_between_oriented_lines (line1, line_horiz);
00338 
00339   vgl_homg_line_2d<double> line2 = vgl_homg_operators_2d<double>::join_oriented(point2, epipole2);
00340   double angle2 = vgl_homg_operators_2d<double>::angle_between_oriented_lines (line2, line_horiz);
00341 
00342   // If the transformation from the transformed frame to the raw image frame is Pi, i=1,2, then
00343   // the transformed F matrix is P2^T F P1.
00344 
00345   double x1 = point1.x()/point1.w(), y1 = point1.y()/point1.w(),
00346          x2 = point2.x()/point2.w(), y2 = point2.y()/point2.w();
00347 
00348   // c s x
00349   //-s c y
00350   // 0 0 1
00351   vnl_double_3x3 p1_matrix;
00352   p1_matrix(0, 0) = vcl_cos (angle1);
00353   p1_matrix(0, 1) = vcl_sin (angle1);
00354   p1_matrix(0, 2) = x1;
00355   p1_matrix(1, 0)= -vcl_sin (angle1);
00356   p1_matrix(1, 1) = vcl_cos (angle1);
00357   p1_matrix(1, 2) = y1;
00358   p1_matrix(2, 0) = 0;
00359   p1_matrix(2, 1) = 0;
00360   p1_matrix(2, 2) = 1;
00361 
00362   vnl_double_3x3 p2_matrix;
00363   p2_matrix(0, 0) = vcl_cos (angle2);
00364   p2_matrix(0, 1) = vcl_sin (angle2);
00365   p2_matrix(0, 2) = x2;
00366   p2_matrix(1, 0)= -vcl_sin (angle2);
00367   p2_matrix(1, 1) = vcl_cos (angle2);
00368   p2_matrix(1, 2) = y2;
00369   p2_matrix(2, 0) = 0;
00370   p2_matrix(2, 1) = 0;
00371   p2_matrix(2, 2) = 1;
00372 
00373   vnl_double_3x3 special_f_matrix= vnl_transpose(p2_matrix) *f_matrix_ *p1_matrix;
00374 
00375   double f = -special_f_matrix(1, 0) / special_f_matrix(1, 2);
00376   double f2 = -special_f_matrix(2, 0) / special_f_matrix(2, 2);
00377   double g = -special_f_matrix(0, 1) / special_f_matrix(2, 1);
00378   double g2 = -special_f_matrix(0, 2) / special_f_matrix(2, 2);
00379   if (vcl_fabs ((f-f2) / f) > 0.05 || vcl_fabs ((g-g2) / g) > 0.05)
00380     vcl_cerr << "F matrix isn't rank 2.\n";
00381 
00382   // section 4.2 of the paper.
00383 
00384   double a = special_f_matrix(1, 1);
00385   double b = special_f_matrix(1, 2);
00386   double c = special_f_matrix(2, 1);
00387   double d = special_f_matrix(2, 2);
00388 
00389   // generated from equation (6) in the paper, using mathematica.
00390   vnl_vector<double> coeffs(7);
00391   coeffs[0] = b*b*c*d - a*b*d*d;
00392   coeffs[1] = b*b*b*b + b*b*c*c - a*a*d*d + 2.0*b*b*d*d*g*g + d*d*d*d*g*g*g*g;
00393   coeffs[2] = 4.0*a*b*b*b + a*b*c*c - a*a*c*d + 2.0*b*b*c*d*f*f - 2.0*a*b*d*d*f*f
00394               + 4.0*b*b*c*d*g*g + 4.0*a*b*d*d*g*g + 4.0*c*d*d*d*g*g*g*g;
00395   coeffs[3] = 6.0*a*a*b*b + 2.0*b*b*c*c*f*f - 2.0*a*a*d*d*f*f + 2.0*b*b*c*c*g*g
00396               + 8*a*b*c*d*g*g + 2.0*a*a*d*d*g*g + 6.0*c*c*d*d*g*g*g*g;
00397   coeffs[4] = 4.0*a*a*a*b + 2.0*a*b*c*c*f*f - 2.0*a*a*c*d*f*f + b*b*c*d*f*f*f*f
00398               - a*b*d*d*f*f*f*f + 4.0*a*b*c*c*g*g + 4.0*a*a*c*d*g*g + 4.0*c*c*c*d*g*g*g*g;
00399   coeffs[5] = a*a*a*a + b*b*c*c*f*f*f*f - a*a*d*d*f*f*f*f + 2.0*a*a*c*c*g*g + c*c*c*c*g*g*g*g;
00400   coeffs[6] = a*b*c*c*f*f*f*f - a*a*c*d*f*f*f*f;
00401 
00402   vnl_rpoly_roots roots(coeffs);
00403 
00404   // Hartley mentions the special case of t=infinity in his paper
00405   // i.e. the corrected point is at the epipole. not implemented here...
00406 
00407   bool real_root_flag = false;
00408   double s_min = 1e20;
00409   double t_min = 0;
00410   for (int root_index = 0; root_index < 6; root_index++)
00411     if (roots.imag(root_index) == 0) {
00412       // equation (4) in the paper.
00413       double t = roots.real(root_index);
00414       double s = t * t / vnl_math_sqr(1.0 + f * f * t * t) +
00415         vnl_math_sqr(c * t + d) /
00416         (vnl_math_sqr(a * t + b) + g * g * vnl_math_sqr(c * t + d));
00417       if (s < s_min) {
00418         real_root_flag = true;
00419         t_min = t;
00420         s_min = s;
00421       }
00422     }
00423 
00424   if (!real_root_flag) {
00425     vcl_cerr << "FMatrix::find_nearest_perfect_match -- no real root\n";
00426     return;
00427   }
00428 
00429   // if (residual_sum_squared_ptr) *residual_sum_squared_ptr = s_min;
00430 
00431   // the epipolar lines in the two images.
00432   vgl_homg_line_2d<double> epipolar_line1(t_min * f, 1, -t_min);
00433   vgl_homg_line_2d<double> epipolar_line2(-g * (c*t_min + d), a*t_min + b, c*t_min + d);
00434   vgl_homg_point_2d<double> origin(0,0,1);
00435 
00436   perfect_point1 = p1_matrix * vgl_homg_operators_2d<double>::perp_projection(epipolar_line1, origin);
00437   perfect_point2 = p2_matrix * vgl_homg_operators_2d<double>::perp_projection(epipolar_line2, origin);
00438 }
00439 
00440 //: Faster Hartley-Sturm using precomputed epipoles
00441 void
00442 FMatrix::find_nearest_perfect_match(const HomgPoint2D& point1,
00443                                     const HomgPoint2D& point2,
00444                                     const HomgPoint2D& epipole1,
00445                                     const HomgPoint2D& epipole2,
00446                                     HomgPoint2D *perfect_point1_ptr,
00447                                     HomgPoint2D *perfect_point2_ptr) const
00448 {
00449   HomgLine2D line_horiz(0,1,0);
00450 
00451   HomgLine2D line1 = HomgOperator2D::join_oriented(point1, epipole1);
00452   double angle1 = HomgOperator2D::angle_between_oriented_lines (line1, line_horiz);
00453 
00454   HomgLine2D line2 = HomgOperator2D::join_oriented(point2, epipole2);
00455   double angle2 = HomgOperator2D::angle_between_oriented_lines (line2, line_horiz);
00456 
00457   // If the transformation from the transformed frame to the raw image frame is Pi, i=1,2, then
00458   // the transformed F matrix is P2^T F P1.
00459 
00460   double x1, y1;
00461   point1.get_nonhomogeneous(x1, y1);
00462 
00463   double x2, y2;
00464   point2.get_nonhomogeneous(x2, y2);
00465 
00466   // c s x
00467   //-s c y
00468   // 0 0 1
00469   vnl_double_3x3 p1_matrix;
00470   p1_matrix(0, 0) = vcl_cos (angle1);
00471   p1_matrix(0, 1) = vcl_sin (angle1);
00472   p1_matrix(0, 2) = x1;
00473   p1_matrix(1, 0)= -vcl_sin (angle1);
00474   p1_matrix(1, 1) = vcl_cos (angle1);
00475   p1_matrix(1, 2) = y1;
00476   p1_matrix(2, 0) = 0;
00477   p1_matrix(2, 1) = 0;
00478   p1_matrix(2, 2) = 1;
00479 
00480   vnl_double_3x3 p2_matrix;
00481   p2_matrix(0, 0) = vcl_cos (angle2);
00482   p2_matrix(0, 1) = vcl_sin (angle2);
00483   p2_matrix(0, 2) = x2;
00484   p2_matrix(1, 0)= -vcl_sin (angle2);
00485   p2_matrix(1, 1) = vcl_cos (angle2);
00486   p2_matrix(1, 2) = y2;
00487   p2_matrix(2, 0) = 0;
00488   p2_matrix(2, 1) = 0;
00489   p2_matrix(2, 2) = 1;
00490 
00491   vnl_double_3x3 special_f_matrix= vnl_transpose(p2_matrix) *f_matrix_ *p1_matrix;
00492 
00493   double f = -special_f_matrix(1, 0) / special_f_matrix(1, 2);
00494   double f2 = -special_f_matrix(2, 0) / special_f_matrix(2, 2);
00495   double g = -special_f_matrix(0, 1) / special_f_matrix(2, 1);
00496   double g2 = -special_f_matrix(0, 2) / special_f_matrix(2, 2);
00497   if (vcl_fabs ((f-f2) / f) > 0.05 || vcl_fabs ((g-g2) / g) > 0.05)
00498     vcl_cerr << "F matrix isn't rank 2.\n";
00499 
00500   // section 4.2 of the paper.
00501 
00502   double a = special_f_matrix(1, 1);
00503   double b = special_f_matrix(1, 2);
00504   double c = special_f_matrix(2, 1);
00505   double d = special_f_matrix(2, 2);
00506 
00507   // generated from equation (6) in the paper, using mathematica.
00508   vnl_vector<double> coeffs(7);
00509   coeffs[0] = b*b*c*d - a*b*d*d;
00510   coeffs[1] = b*b*b*b + b*b*c*c - a*a*d*d + 2.0*b*b*d*d*g*g + d*d*d*d*g*g*g*g;
00511   coeffs[2] = 4.0*a*b*b*b + a*b*c*c - a*a*c*d + 2.0*b*b*c*d*f*f - 2.0*a*b*d*d*f*f
00512               + 4.0*b*b*c*d*g*g + 4.0*a*b*d*d*g*g + 4.0*c*d*d*d*g*g*g*g;
00513   coeffs[3] = 6.0*a*a*b*b + 2.0*b*b*c*c*f*f - 2.0*a*a*d*d*f*f + 2.0*b*b*c*c*g*g
00514               + 8*a*b*c*d*g*g + 2.0*a*a*d*d*g*g + 6.0*c*c*d*d*g*g*g*g;
00515   coeffs[4] = 4.0*a*a*a*b + 2.0*a*b*c*c*f*f - 2.0*a*a*c*d*f*f + b*b*c*d*f*f*f*f
00516               - a*b*d*d*f*f*f*f + 4.0*a*b*c*c*g*g + 4.0*a*a*c*d*g*g + 4.0*c*c*c*d*g*g*g*g;
00517   coeffs[5] = a*a*a*a + b*b*c*c*f*f*f*f - a*a*d*d*f*f*f*f + 2.0*a*a*c*c*g*g + c*c*c*c*g*g*g*g;
00518   coeffs[6] = a*b*c*c*f*f*f*f - a*a*c*d*f*f*f*f;
00519 
00520   vnl_rpoly_roots roots(coeffs);
00521 
00522   // Hartley mentions the special case of t=infinity in his paper
00523   // i.e. the corrected point is at the epipole. not implemented here...
00524 
00525   bool real_root_flag = false;
00526   double s_min = 1e20;
00527   double t_min = 0;
00528   for (int root_index = 0; root_index < 6; root_index++)
00529     if (roots.imag(root_index) == 0) {
00530       // equation (4) in the paper.
00531       double t = roots.real(root_index);
00532       double s = t * t / vnl_math_sqr(1.0 + f * f * t * t) +
00533         vnl_math_sqr(c * t + d) /
00534         (vnl_math_sqr(a * t + b) + g * g * vnl_math_sqr(c * t + d));
00535       if (s < s_min) {
00536         real_root_flag = true;
00537         t_min = t;
00538         s_min = s;
00539       }
00540     }
00541 
00542   if (!real_root_flag) {
00543     vcl_cerr << "FMatrix::find_nearest_perfect_match -- no real root\n";
00544     return;
00545   }
00546 
00547   // if (residual_sum_squared_ptr) *residual_sum_squared_ptr = s_min;
00548 
00549   if (perfect_point1_ptr) {
00550     // the epipolar lines in the two images.
00551     HomgLine2D epipolar_line1(t_min * f, 1, -t_min);
00552     HomgLine2D epipolar_line2(-g * (c*t_min + d), a*t_min + b, c*t_min + d);
00553     HomgPoint2D origin(0,0,1);
00554 
00555     *perfect_point1_ptr = p1_matrix * HomgOperator2D::perp_projection(epipolar_line1, origin).get_vector();
00556     *perfect_point2_ptr = p2_matrix * HomgOperator2D::perp_projection(epipolar_line2, origin).get_vector();
00557   }
00558 }
00559 
00560 //-------------------------------------------------------------------
00561 
00562 void FMatrix::compute_P_matrix(vnl_matrix<double> &P2) const
00563 {
00564   HomgPoint2D e1, e2;
00565   get_epipoles(&e1, &e2);
00566 
00567   vnl_double_3x3 A;
00568   vnl_double_3 a = e2.get_vector();
00569 
00570   vnl_cross_product_matrix e2x(a);
00571   A = e2x * f_matrix_;
00572 
00573   P2.set_columns(0, A);
00574   P2.set_column(3, a);
00575 }
00576 
00577 //-------------------------------------------------------------------
00578 //
00579 //: Ensure the current Fundamental matrix is rank 2.
00580 // Does this by taking its vnl_svd<double>, setting the smallest singular value
00581 // to zero, and recomposing.  Sets the rank2_flag_ to true.
00582 
00583 void FMatrix::set_rank2_using_svd(void)
00584 {
00585   // ma2_static_set_rank
00586   vnl_svd<double> svd(f_matrix_);
00587   svd.W(2) = 0;
00588   f_matrix_ = svd.recompose();
00589   ft_matrix_ = f_matrix_.transpose();
00590   rank2_flag_ = true;
00591 }
00592 
00593 //-----------------------------------------------------------------------------
00594 //
00595 //: Decompose F to the product of a skew-symmetric matrix and a rank 3 matrix.
00596 
00597 void
00598 FMatrix::decompose_to_skew_rank3(vnl_matrix<double>*, vnl_matrix<double>*) const
00599 {
00600   assert(!"Not implemented\n");
00601 }
00602 
00603 // == DATA ACCESS ==
00604 
00605 //----------------------------------------------------------------
00606 //
00607 //: Return the element of the matrix at the specified indices (zero-based)
00608 double FMatrix::get (unsigned int row_index, unsigned int col_index) const
00609 {
00610   return f_matrix_(row_index, col_index);
00611 }
00612 
00613 
00614 //----------------------------------------------------------------
00615 //
00616 //: Copy the fundamental matrix into a 2D array of doubles for `C' compatibility.
00617 void FMatrix::get (double *c) const
00618 {
00619   for (int row_index = 0; row_index < 3; row_index++)
00620     for (int col_index = 0; col_index < 3; col_index++)
00621       *c++ = f_matrix_(row_index, col_index);
00622 }
00623 
00624 //----------------------------------------------------------------
00625 //
00626 //: Copy the fundamental matrix into a vnl_matrix<double>
00627 void FMatrix::get (vnl_matrix<double>* f_matrix) const
00628 {
00629   *f_matrix = f_matrix_;
00630 }
00631 
00632 
00633 //----------------------------------------------------------------
00634 //
00635 //: Return the rank2_flag_
00636 bool FMatrix::get_rank2_flag (void) const
00637 {
00638   return rank2_flag_;
00639 }
00640 
00641 //----------------------------------------------------------------
00642 //
00643 //: Set the rank2_flag_
00644 void FMatrix::set_rank2_flag (bool rank2_flag)
00645 {
00646   rank2_flag_ = rank2_flag;
00647 }
00648 
00649 //--------------------------------------------------------------
00650 //
00651 //: Set the fundamental matrix using the C-storage array c_matrix, and cache the transpose.
00652 // Always returns true for the base class - showing the set was a success.
00653 // When overridden by derived classes
00654 // it may return false, to indicate that the matrix violates the
00655 // constraints imposed by the derived classes.
00656 
00657 bool FMatrix::set (const double *c_matrix)
00658 {
00659   for (int row_index = 0; row_index < 3; row_index++)
00660     for (int col_index = 0; col_index < 3; col_index++) {
00661       double v = *c_matrix++;
00662       f_matrix_(row_index, col_index) = v;
00663       ft_matrix_(col_index, row_index) = v;
00664     }
00665 
00666   return true;
00667 }
00668 
00669 //--------------------------------------------------------------
00670 //
00671 //: Set the fundamental matrix using the vnl_matrix<double> f_matrix.
00672 // Always returns true for the base class - showing the set was a success.
00673 // When overridden by derived classes it may return false, to indicate
00674 // that the matrix violates the constraints imposed by the derived classes.
00675 
00676 bool FMatrix::set (const vnl_matrix<double>& f_matrix)
00677 {
00678   f_matrix_ = f_matrix;
00679   ft_matrix_ = f_matrix.transpose();
00680 
00681   return true;
00682 }
00683 
00684 //: Set from two P matrices
00685 void FMatrix::set (const PMatrix& P1, const PMatrix& P2)
00686 {
00687   vnl_svd<double>* svd = P1.svd();
00688 
00689   vnl_cross_product_matrix e2x(P2.get_matrix() * svd->nullvector());
00690 
00691   set(e2x * P2.get_matrix() * svd->inverse());
00692 }
00693 
00694 //: Set from one P matrix, the second.  The first is assumed to be [I O].
00695 void FMatrix::set (const PMatrix& P2)
00696 {
00697   vnl_double_3x3 A;
00698   vnl_double_3 a;
00699   P2.get(&A.as_ref().non_const(), &a.as_ref().non_const());
00700 
00701   vnl_cross_product_matrix e2x(a);
00702 
00703   set(e2x * A);
00704 }
00705 
00706 //: Set from one P matrix, the second.  The first is assumed to be [I O].
00707 void FMatrix::set (const FMatrix& F)
00708 {
00709   *this = F;
00710 }