contrib/oxl/mvl/PMatrix.cxx
Go to the documentation of this file.
00001 // This is oxl/mvl/PMatrix.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 
00008 #include "PMatrix.h"
00009 
00010 #include <vcl_iostream.h>
00011 #include <vcl_fstream.h>
00012 #include <vcl_cmath.h>
00013 #include <vcl_cassert.h>
00014 
00015 #include <vnl/vnl_matrix.h>
00016 #include <vnl/vnl_matlab_print.h>
00017 #include <vnl/algo/vnl_svd.h>
00018 #include <vnl/algo/vnl_determinant.h>
00019 #include <vgl/vgl_homg_plane_3d.h>
00020 #include <vgl/vgl_point_3d.h>
00021 
00022 #include <mvl/HomgPrettyPrint.h>
00023 #include <mvl/HomgLine2D.h>
00024 #include <mvl/HomgLineSeg2D.h>
00025 #include <mvl/HomgPoint2D.h>
00026 #include <mvl/HomgLine3D.h>
00027 #include <mvl/HomgLineSeg3D.h>
00028 #include <mvl/HomgPoint3D.h>
00029 #include <mvl/HomgPlane3D.h>
00030 #include <mvl/HomgOperator2D.h>
00031 #include <mvl/PMatrixDecompAa.h>
00032 #include <mvl/HMatrix3D.h>
00033 #include <mvl/HMatrix2D.h>
00034 
00035 //--------------------------------------------------------------
00036 //
00037 //: Constructor. Set up a canonical P matrix.
00038 //
00039 
00040 PMatrix::PMatrix ()
00041   : svd_(0)
00042 {
00043   for (int row_index = 0; row_index < 3; row_index++)
00044     for (int col_index = 0; col_index < 4; col_index++)
00045       if (row_index == col_index)
00046         p_matrix_. put (row_index, col_index, 1);
00047       else
00048         p_matrix_. put (row_index, col_index, 0);
00049 }
00050 
00051 //--------------------------------------------------------------
00052 //
00053 //: Construct by loading from vcl_istream.
00054 // \code
00055 //   PMatrix P(cin);
00056 // \endcode
00057 PMatrix::PMatrix (vcl_istream& i)
00058   : svd_(0)
00059 {
00060   read_ascii(i);
00061 }
00062 
00063 //--------------------------------------------------------------
00064 //
00065 //: Construct from 3x4 matrix
00066 
00067 PMatrix::PMatrix (vnl_double_3x4 const& pmatrix)
00068   : p_matrix_ (pmatrix),
00069   svd_(0)
00070 {
00071 }
00072 
00073 //--------------------------------------------------------------
00074 //
00075 //: Construct from 3x3 matrix A and vector a. P = [A a].
00076 
00077 PMatrix::PMatrix (const vnl_matrix<double>& A, const vnl_vector<double>& a)
00078   : svd_(0)
00079 {
00080   set(A,a);
00081 }
00082 
00083 //--------------------------------------------------------------
00084 //
00085 //: Construct from row-stored array of 12 doubles
00086 
00087 PMatrix::PMatrix (const double *c_matrix)
00088   : p_matrix_ (c_matrix),
00089   svd_(0)
00090 {
00091 }
00092 
00093 //--------------------------------------------------------------
00094 //
00095 // - Copy ctor
00096 
00097 PMatrix::PMatrix (const PMatrix& that)
00098   : vbl_ref_count(), p_matrix_(that.get_matrix()), svd_(0)
00099 {
00100 }
00101 
00102 // - Assignment
00103 PMatrix& PMatrix::operator=(const PMatrix& that)
00104 {
00105   p_matrix_ = that.get_matrix();
00106   svd_ = 0;
00107   return *this;
00108 }
00109 
00110 //--------------------------------------------------------------
00111 //
00112 // - Destructor
00113 PMatrix::~PMatrix()
00114 {
00115   delete svd_; svd_ = 0;
00116 }
00117 
00118 // OPERATIONS
00119 
00120 //-----------------------------------------------------------------------------
00121 //
00122 //: Return the image point which is the projection of the specified 3D point X
00123 HomgPoint2D PMatrix::project (const HomgPoint3D& X) const
00124 {
00125   vnl_double_3 x = p_matrix_ * X.get_vector();
00126   return HomgPoint2D(x);
00127 }
00128 
00129 
00130 //-----------------------------------------------------------------------------
00131 //
00132 //: Return the image line which is the projection of the specified 3D line L
00133 vgl_homg_line_2d<double> PMatrix::project (const vgl_homg_line_3d_2_points<double>& L) const
00134 {
00135   return vgl_homg_line_2d<double>(project(L.point_finite()), project(L.point_infinite()));
00136 }
00137 
00138 HomgLine2D PMatrix::project (const HomgLine3D& L) const
00139 {
00140   return HomgOperator2D::join(project(L.get_point_finite()), project(L.get_point_infinite()));
00141 }
00142 
00143 
00144 //-----------------------------------------------------------------------------
00145 //
00146 //: Return the image linesegment which is the projection of the specified 3D linesegment L
00147 vgl_line_segment_2d<double> PMatrix::project(vgl_line_segment_3d<double> const& L) const
00148 {
00149   vgl_point_3d<double> p1 = L.point1(), p2 = L.point2();
00150   vgl_homg_point_3d<double> q1 (p1.x(),p1.y(),p1.z()), q2 (p2.x(),p2.y(),p2.z());
00151   return vgl_line_segment_2d<double>(project(q1), project(q2));
00152 }
00153 
00154 HomgLineSeg2D PMatrix::project (const HomgLineSeg3D& L) const
00155 {
00156   return HomgLineSeg2D(project(L.get_point1()), project(L.get_point2()));
00157 }
00158 
00159 //-----------------------------------------------------------------------------
00160 //
00161 //: Return the 3D point $\vec X$ which is $\vec X = P^+ \vec x$.
00162 // Equivalently, the 3D point of smallest norm such that $P \vec X = \vec x$.
00163 // Uses svd().
00164 vgl_homg_point_3d<double> PMatrix::backproject_pseudoinverse (const vgl_homg_point_2d<double>& x) const
00165 {
00166   vnl_double_4 p = svd()->solve(vnl_double_3(x.x(),x.y(),x.w()).as_ref());
00167   return vgl_homg_point_3d<double>(p[0],p[1],p[2],p[3]);
00168 }
00169 
00170 HomgPoint3D PMatrix::backproject_pseudoinverse (const HomgPoint2D& x) const
00171 {
00172   return svd()->solve(x.get_vector().as_ref());
00173 }
00174 
00175 //-----------------------------------------------------------------------------
00176 //
00177 //: Return the 3D line which is the backprojection of the specified image point, x.
00178 // Uses svd().
00179 vgl_homg_line_3d_2_points<double> PMatrix::backproject (const vgl_homg_point_2d<double>& x) const
00180 {
00181   return vgl_homg_line_3d_2_points<double>(get_focal(), backproject_pseudoinverse(x));
00182 }
00183 
00184 HomgLine3D PMatrix::backproject (const HomgPoint2D& x) const
00185 {
00186   return HomgLine3D(get_focal_point(), backproject_pseudoinverse(x));
00187 }
00188 
00189 //-----------------------------------------------------------------------------
00190 //
00191 //: Return the 3D plane which is the backprojection of the specified line l in the image
00192 vgl_homg_plane_3d<double> PMatrix::backproject (const vgl_homg_line_2d<double>& l) const
00193 {
00194   return p_matrix_.transpose() * l;
00195 }
00196 
00197 HomgPlane3D PMatrix::backproject (const HomgLine2D& l) const
00198 {
00199   return HomgPlane3D(p_matrix_.transpose() * l.get_vector());
00200 }
00201 
00202 //-----------------------------------------------------------------------------
00203 //: Print p on vcl_ostream
00204 vcl_ostream& operator<<(vcl_ostream& s, const PMatrix& p)
00205 {
00206   if (HomgPrettyPrint::pretty)
00207     return vnl_matlab_print(s, p.get_matrix(), "");
00208   else
00209     return s << p.get_matrix();
00210 }
00211 
00212 //-----------------------------------------------------------------------------
00213 //: Load p from ascii vcl_istream
00214 vcl_istream& operator>>(vcl_istream& i, PMatrix& p)
00215 {
00216   p.read_ascii(i);
00217   return i;
00218 }
00219 
00220 static bool ok(vcl_istream& f) { return f.good() || f.eof(); }
00221 
00222 //: Load from file.
00223 // \code
00224 // P.read_ascii("file.P");
00225 // \endcode
00226 bool PMatrix::read_ascii(vcl_istream& f)
00227 {
00228   vnl_matrix<double> hold(3,4);
00229   f >> hold;
00230 
00231   for (int i=0;i<3;i++)
00232     for (int j=0;j<4;j++)
00233       p_matrix_(i,j) = hold(i,j);
00234 
00235   clear_svd();
00236 
00237   if (!ok(f)) {
00238     vcl_cerr << "PMatrix::read_ascii: Failed to load P matrix from stream\n";
00239     return false;
00240   }
00241 
00242   return true;
00243 }
00244 
00245 //: Load from file.
00246 // Static method, so you can say
00247 // \code
00248 // PMatrix P = PMatrix::read("file.P");
00249 // \endcode
00250 PMatrix PMatrix::read(const char* filename)
00251 {
00252   vcl_ifstream f(filename);
00253   if (!ok(f)) {
00254     vcl_cerr << "PMatrix::read: Failed to open P matrix file " << filename << '\n';
00255     return PMatrix();
00256   }
00257 
00258   PMatrix P;
00259   if (!P.read_ascii(f))
00260     vcl_cerr << "PMatrix::read: Failed to read P matrix file " << filename << '\n';
00261 
00262   return P;
00263 }
00264 
00265 //: Load from vcl_istream
00266 PMatrix PMatrix::read(vcl_istream& s)
00267 {
00268   PMatrix P;
00269   s >> P;
00270   return P;
00271 }
00272 
00273 // COMPUTATIONS
00274 
00275 //-----------------------------------------------------------------------------
00276 //
00277 //: Compute the svd of this P and cache it, so that future operations that require it need not recompute it.
00278 vnl_svd<double>* PMatrix::svd() const
00279 {
00280   if (svd_ == 0) {
00281     // Need to make svd_ volatile for SGI g++ 2.7.2 optimizer bug.
00282     svd_ = new vnl_svd<double>(p_matrix_.as_ref()); // mutable const
00283   }
00284   return svd_;
00285 }
00286 
00287 //: Discredit the cached svd.
00288 //  This is necessary only in order to recover the space used by it if the PMatrix is not being deleted.
00289 void PMatrix::clear_svd() const
00290 {
00291   delete svd_; svd_ = 0;
00292 }
00293 
00294 //-----------------------------------------------------------------------------
00295 //
00296 //: Return the 3D point representing the focal point of the camera.
00297 // Uses svd().
00298 vgl_homg_point_3d<double> PMatrix::get_focal() const
00299 {
00300   if (svd()->singularities() > 1) {
00301     vcl_cerr << "PMatrix::get_focal:\n"
00302              << "  Nullspace dimension is " << svd()->singularities()
00303              << "\n  Returning an invalid point (a vector of zeros)\n";
00304     return vgl_homg_point_3d<double>(0,0,0,0);
00305   }
00306 
00307   vnl_matrix<double> ns = svd()->nullspace();
00308 
00309   return vgl_homg_point_3d<double>(ns(0,0), ns(1,0), ns(2,0), ns(3,0));
00310 }
00311 
00312 HomgPoint3D PMatrix::get_focal_point() const
00313 {
00314   // From st_compute_focal_point
00315   if (svd()->singularities() > 1) {
00316     vcl_cerr << "PMatrix::get_focal_point:\n"
00317              << "  Nullspace dimension is " << svd()->singularities()
00318              << "\n  Returning a vector of zeros\n";
00319     return HomgPoint3D(0,0,0,0);
00320   }
00321 
00322   vnl_matrix<double> nullspace = svd()->nullspace();
00323 
00324   if (nullspace(3,0) == 0)
00325     vcl_cerr << "PMatrix::get_focal_point: Focal point at infinity";
00326 
00327   return HomgPoint3D(nullspace(0,0),
00328                      nullspace(1,0),
00329                      nullspace(2,0),
00330                      nullspace(3,0));
00331 }
00332 
00333 //: Return the HMatrix3D s.t. P * H = [I 0].
00334 // If P = [A a], then H = [inv(A) -inv(A)*a; 0 0 0 1];
00335 HMatrix3D PMatrix::get_canonical_H() const
00336 {
00337 //
00338 //M1 = P1(1:3,1:3);
00339 //t1 = -inv(M1)*P1(:,4);
00340 //Hinverse = [inv(M1) t1; 0 0 0 1];
00341 //
00342   PMatrixDecompAa p(*this);
00343   vnl_svd<double> svd(p.A.as_ref());
00344   return HMatrix3D(svd.inverse(), -svd.solve(p.a.as_ref()));
00345 }
00346 
00347 //: Return true iff P is [I 0].
00348 // Equality is assumed if the max abs diff is less than tol.
00349 bool PMatrix::is_canonical(double tol) const
00350 {
00351   for (int r = 0; r < 3; ++r)
00352     for (int c = 0; c < 4; ++c) {
00353       double d = (r == c) ? (p_matrix_(r,c) - 1) : p_matrix_(r,c);
00354       if (vcl_fabs(d) > tol)
00355         return false;
00356     }
00357   return true;
00358 }
00359 
00360 //: Postmultiply PMatrix by HMatrix3D
00361 PMatrix operator*(const PMatrix& P, const HMatrix3D& H)
00362 {
00363   return PMatrix(P.get_matrix() * H);
00364 }
00365 
00366 // DATA ACCESS
00367 
00368 //-----------------------------------------------------------------------------
00369 //
00370 //: Return the element of the matrix at the specified indices
00371 double
00372 PMatrix::get (unsigned int row_index, unsigned int col_index) const
00373 {
00374   return p_matrix_. get (row_index, col_index);
00375 }
00376 
00377 //-----------------------------------------------------------------------------
00378 //
00379 //: Return the 3x4 projection matrix in the array, p_matrix
00380 void
00381 PMatrix::get (double* c_matrix) const
00382 {
00383   for (int row_index = 0; row_index < 3; row_index++)
00384     for (int col_index = 0; col_index < 4; col_index++)
00385       *c_matrix++ = p_matrix_. get (row_index, col_index);
00386 }
00387 
00388 //----------------------------------------------------------------
00389 //
00390 //: Return the 3x4 projection matrix in the vnl_matrix<double>, p_matrix
00391 void
00392 PMatrix::get(vnl_double_3x4* p_matrix) const
00393 {
00394   *p_matrix = p_matrix_;
00395 }
00396 
00397 //----------------------------------------------------------------
00398 //
00399 //: Return the 3x3 matrix and 3x1 column vector of P = [A a].
00400 void
00401 PMatrix::get(vnl_matrix<double>* A, vnl_vector<double>* a) const
00402 {
00403   A->put(0,0, p_matrix_(0,0));
00404   A->put(1,0, p_matrix_(1,0));
00405   A->put(2,0, p_matrix_(2,0));
00406 
00407   A->put(0,1, p_matrix_(0,1));
00408   A->put(1,1, p_matrix_(1,1));
00409   A->put(2,1, p_matrix_(2,1));
00410 
00411   A->put(0,2, p_matrix_(0,2));
00412   A->put(1,2, p_matrix_(1,2));
00413   A->put(2,2, p_matrix_(2,2));
00414 
00415   a->put(0, p_matrix_(0,3));
00416   a->put(1, p_matrix_(1,3));
00417   a->put(2, p_matrix_(2,3));
00418 }
00419 
00420 //----------------------------------------------------------------
00421 //
00422 //: Return the 3x3 matrix and 3x1 column vector of P = [A a].
00423 void
00424 PMatrix::get (vnl_double_3x3* A, vnl_double_3* a) const
00425 {
00426   A->put(0,0, p_matrix_(0,0));
00427   A->put(1,0, p_matrix_(1,0));
00428   A->put(2,0, p_matrix_(2,0));
00429 
00430   A->put(0,1, p_matrix_(0,1));
00431   A->put(1,1, p_matrix_(1,1));
00432   A->put(2,1, p_matrix_(2,1));
00433 
00434   A->put(0,2, p_matrix_(0,2));
00435   A->put(1,2, p_matrix_(1,2));
00436   A->put(2,2, p_matrix_(2,2));
00437 
00438   a->put(0, p_matrix_(0,3));
00439   a->put(1, p_matrix_(1,3));
00440   a->put(2, p_matrix_(2,3));
00441 }
00442 
00443 //----------------------------------------------------------------
00444 //
00445 //: Return the rows of P = [a b c]'.
00446 void
00447 PMatrix::get_rows (vnl_vector<double>* a, vnl_vector<double>* b, vnl_vector<double>* c) const
00448 {
00449   a->put(0, p_matrix_(0, 0));
00450   a->put(1, p_matrix_(0, 1));
00451   a->put(2, p_matrix_(0, 2));
00452   a->put(3, p_matrix_(0, 3));
00453 
00454   b->put(0, p_matrix_(1, 0));
00455   b->put(1, p_matrix_(1, 1));
00456   b->put(2, p_matrix_(1, 2));
00457   b->put(3, p_matrix_(1, 3));
00458 
00459   c->put(0, p_matrix_(2, 0));
00460   c->put(1, p_matrix_(2, 1));
00461   c->put(2, p_matrix_(2, 2));
00462   c->put(3, p_matrix_(2, 3));
00463 }
00464 
00465 //----------------------------------------------------------------
00466 //
00467 //: Return the rows of P = [a b c]'.
00468 void
00469 PMatrix::get_rows(vnl_double_4* a,
00470                   vnl_double_4* b,
00471                   vnl_double_4* c) const
00472 {
00473   a->put(0, p_matrix_(0, 0));
00474   a->put(1, p_matrix_(0, 1));
00475   a->put(2, p_matrix_(0, 2));
00476   a->put(3, p_matrix_(0, 3));
00477 
00478   b->put(0, p_matrix_(1, 0));
00479   b->put(1, p_matrix_(1, 1));
00480   b->put(2, p_matrix_(1, 2));
00481   b->put(3, p_matrix_(1, 3));
00482 
00483   c->put(0, p_matrix_(2, 0));
00484   c->put(1, p_matrix_(2, 1));
00485   c->put(2, p_matrix_(2, 2));
00486   c->put(3, p_matrix_(2, 3));
00487 }
00488 
00489 //-----------------------------------------------------------------------------
00490 //
00491 //: Set the 3x4 projective matrix with the matrix in the array, p_matrix
00492 void
00493 PMatrix::set (const double p_matrix [3][4])
00494 {
00495   for (int row_index = 0; row_index < 3; row_index++)
00496     for (int col_index = 0; col_index < 4; col_index++)
00497       p_matrix_. put (row_index, col_index, p_matrix [row_index][col_index]);
00498   clear_svd();
00499 }
00500 
00501 
00502 //: Set the 3x4 projective matrix with the matrix in the array, p_matrix
00503 void
00504 PMatrix::set (const vnl_matrix<double>& p_matrix)
00505 {
00506   assert(p_matrix.rows()==3 && p_matrix.cols()==4);
00507 
00508   for (int row_index = 0; row_index < 3; row_index++)
00509     for (int col_index = 0; col_index < 4; col_index++)
00510       p_matrix_. put (row_index, col_index, p_matrix (row_index,col_index));
00511   clear_svd();
00512 }
00513 
00514 //-----------------------------------------------------------------------------
00515 //
00516 //: Set the 3x4 projective matrix with the matrix in the array, p_matrix
00517 void
00518 PMatrix::set (const double *p)
00519 {
00520   for (int row_index = 0; row_index < 3; row_index++)
00521     for (int col_index = 0; col_index < 4; col_index++)
00522       p_matrix_. put (row_index, col_index, *p++);
00523   clear_svd();
00524 }
00525 
00526 
00527 //--------------------------------------------------------------
00528 //
00529 //: Set the fundamental matrix using the vnl_matrix<double> p_matrix.
00530 void
00531 PMatrix::set (vnl_double_3x4 const& p_matrix)
00532 {
00533   p_matrix_ = p_matrix;
00534   clear_svd();
00535 }
00536 
00537 
00538 //----------------------------------------------------------------
00539 //
00540 //: Set from 3x3 matrix and 3x1 column vector of P = [A a].
00541 void
00542 PMatrix::set (vnl_double_3x3 const& A, vnl_double_3 const& a)
00543 {
00544   p_matrix_(0,0) = A(0,0);
00545   p_matrix_(1,0) = A(1,0);
00546   p_matrix_(2,0) = A(2,0);
00547 
00548   p_matrix_(0,1) = A(0,1);
00549   p_matrix_(1,1) = A(1,1);
00550   p_matrix_(2,1) = A(2,1);
00551 
00552   p_matrix_(0,2) = A(0,2);
00553   p_matrix_(1,2) = A(1,2);
00554   p_matrix_(2,2) = A(2,2);
00555 
00556   p_matrix_(0,3) = a[0];
00557   p_matrix_(1,3) = a[1];
00558   p_matrix_(2,3) = a[2];
00559 }
00560 
00561 //: Scale P so determinant of first 3x3 is 1.
00562 void
00563 PMatrix::fix_cheirality()
00564 {
00565   PMatrixDecompAa p(*this);
00566   double det = vnl_determinant(p.A);
00567 
00568   double scale = 1;
00569 #if 0  // Used to scale by 1/det, but it's a bad idea if det is small
00570   if (vcl_fabs(det - 1) > 1e-8)
00571     vcl_cerr << "PMatrix::fix_cheirality: Flipping, determinant is " << det << '\n';
00572     scale = 1/det;
00573 #else
00574   if (det < 0)
00575     scale = -1;
00576 #endif // 0
00577 
00578   p_matrix_ *= scale;
00579   if (svd_)
00580     svd_->W() *= scale;
00581 }
00582 
00583 //: Return true if the 3D point X is behind the camera represented by this P.
00584 // This depends on the overall sign of the P matrix having been set correctly, a
00585 // la Hartley cheirality paper.
00586 bool
00587 PMatrix::is_behind_camera(const vgl_homg_point_3d<double>& hX)
00588 {
00589   vnl_double_4 p = p_matrix_.get_row(2);
00590   double dot = hX.x()*p[0]+hX.y()*p[1]+hX.z()*p[2]+hX.w()*p[3];
00591   if (hX.w() < 0) dot = -dot;
00592 
00593   return dot < 0;
00594 }
00595 
00596 bool
00597 PMatrix::is_behind_camera(const HomgPoint3D& hX)
00598 {
00599   vnl_double_4 X = hX.get_vector();
00600 
00601   int sign = +1;
00602   if (X[3] < 0)
00603     sign = -1;
00604 
00605   vnl_double_4 plane = p_matrix_.get_row(2);
00606 
00607   return sign * dot_product(plane, X) < 0;
00608 }
00609 
00610 //: Change the overall sign of the P matrix.
00611 void
00612 PMatrix::flip_sign()
00613 {
00614   p_matrix_ *= -1;
00615   if (svd_)
00616     svd_->W() *= -1;
00617 }
00618 
00619 //: Splendid hack that tries to detect if the P is an image-coords P or a normalized P.
00620 bool
00621 PMatrix::looks_conditioned()
00622 {
00623   double cond = svd()->W(0) / svd()->W(2);
00624   // vcl_cerr << "PMatrix::looks_conditioned: cond = " << cond << '\n';
00625   return cond < 100;
00626 }
00627 
00628 //: Postmultiply by 4x4 matrix.
00629 PMatrix PMatrix::postmultiply(vnl_double_4x4 const& H) const
00630 {
00631   return PMatrix(p_matrix_ * H);
00632 }
00633 
00634 //: Premultiply by 3x3 matrix.
00635 PMatrix PMatrix::premultiply(vnl_double_3x3 const& H) const
00636 {
00637   return PMatrix(H * p_matrix_);
00638 }
00639