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