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