contrib/gel/mrc/vpgl/vpgl_perspective_camera.txx
Go to the documentation of this file.
00001 // This is gel/mrc/vpgl/vpgl_perspective_camera.txx
00002 #ifndef vpgl_perspective_camera_txx_
00003 #define vpgl_perspective_camera_txx_
00004 //:
00005 // \file
00006 
00007 #include "vpgl_perspective_camera.h"
00008 #include <vcl_cassert.h>
00009 #include <vcl_iostream.h>
00010 #include <vgl/vgl_point_2d.h>
00011 #include <vgl/vgl_vector_3d.h>
00012 #include <vgl/vgl_homg_plane_3d.h>
00013 #include <vgl/vgl_line_3d_2_points.h>
00014 #include <vgl/algo/vgl_h_matrix_3d.h>
00015 #include <vnl/vnl_det.h>
00016 #include <vnl/algo/vnl_qr.h>
00017 #include <vnl/vnl_matrix_fixed.h>
00018 
00019 #include <vgl/io/vgl_io_point_3d.h>
00020 #include <vnl/io/vnl_io_matrix_fixed.h>
00021 #include <vnl/io/vnl_io_vector_fixed.h>
00022 
00023 //-------------------------------------------
00024 template <class T>
00025 vpgl_perspective_camera<T>::vpgl_perspective_camera()
00026 {
00027   R_.set_identity();
00028   camera_center_.set( (T)0, (T)0, (T)0 );
00029   recompute_matrix();
00030 }
00031 
00032 
00033 //-------------------------------------------
00034 template <class T>
00035 vpgl_perspective_camera<T>::vpgl_perspective_camera(
00036   const vpgl_calibration_matrix<T>& K,
00037   const vgl_point_3d<T>& camera_center,
00038   const vgl_rotation_3d<T>& R ) :
00039   K_( K ), camera_center_( camera_center ), R_( R )
00040 {
00041   recompute_matrix();
00042 }
00043 
00044 //-------------------------------------------
00045 template <class T>
00046 vpgl_perspective_camera<T>::vpgl_perspective_camera(
00047   const vpgl_calibration_matrix<T>& K,
00048   const vgl_rotation_3d<T>& R,
00049   const vgl_vector_3d<T>& t) :
00050   K_( K ),  R_( R )
00051 {
00052   this->set_translation(t);
00053   recompute_matrix();
00054 }
00055 
00056 //-------------------------------------------
00057 template <class T>
00058 vpgl_perspective_camera<T>::vpgl_perspective_camera( const vpgl_perspective_camera& that)
00059   : vpgl_proj_camera<T>(that),
00060   K_(that.K_),
00061   camera_center_(that.camera_center_),
00062   R_(that.R_)
00063 {
00064 }
00065 
00066 //-------------------------------------------
00067 template <class T>
00068 vpgl_proj_camera<T>* vpgl_perspective_camera<T>::clone(void) const
00069 {
00070   return new vpgl_perspective_camera<T>(*this);
00071 }
00072 
00073 
00074 //------------------------------------
00075 template <class T>
00076 vgl_line_3d_2_points<T> vpgl_perspective_camera<T>::backproject(
00077   const vgl_point_2d<T>& image_point ) const
00078 {
00079   // First find a point in front of the camera that projects to "image_point".
00080   vnl_vector_fixed<T,4> vnl_wp = this->svd()->solve(
00081     vnl_vector_fixed<T,3>( image_point.x(), image_point.y(), 1.0 ) );
00082   vgl_homg_point_3d<T> wp_homg( vnl_wp[0], vnl_wp[1], vnl_wp[2], vnl_wp[3] );
00083   vgl_point_3d<T> wp;
00084   if ( !wp_homg.ideal() )
00085     wp.set( wp_homg.x()/wp_homg.w(), wp_homg.y()/wp_homg.w(), wp_homg.z()/wp_homg.w() );
00086   else
00087     wp.set( camera_center_.x()+wp_homg.x(),
00088             camera_center_.y()+wp_homg.y(),
00089             camera_center_.z()+wp_homg.z() );
00090   if ( is_behind_camera( vgl_homg_point_3d<T>( wp ) ) )
00091     wp = camera_center_ + ( camera_center_-wp );
00092   // The ray is then defined by that point and the camera center.
00093   return vgl_line_3d_2_points<T>( camera_center_, wp );
00094 }
00095 
00096 
00097 //-------------------------------------------
00098 template <class T>
00099 vgl_vector_3d<T> vpgl_perspective_camera<T>::principal_axis() const
00100 {
00101   // See H&Z pg 147
00102   // P = [M|p4] : We do not need to compute det(M) because we enforce
00103   // det(K)>0 and det(R)=1 in the construction of P. Thus det(M)>0;
00104   const vnl_matrix_fixed<T,3,4>& P = this->get_matrix();
00105   return normalized(vgl_vector_3d<T>(P(2,0),P(2,1),P(2,2)));
00106 }
00107 
00108 
00109 //------------------------------------
00110 template <class T>
00111 bool vpgl_perspective_camera<T>::is_behind_camera(
00112   const vgl_homg_point_3d<T>& world_point ) const
00113 {
00114   vgl_homg_plane_3d<T> l = this->principal_plane();
00115   T dot = world_point.x()*l.a() + world_point.y()*l.b() +
00116                world_point.z()*l.c() + world_point.w()*l.d();
00117   if (world_point.w() < (T)0) dot = ((T)(-1))*dot;
00118   return dot < 0;
00119 }
00120 
00121 
00122 //-------------------------------------------
00123 template <class T>
00124 void vpgl_perspective_camera<T>::set_calibration( const vpgl_calibration_matrix<T>& K)
00125 {
00126   K_ = K;
00127   recompute_matrix();
00128 }
00129 
00130 
00131 //-------------------------------------------
00132 template <class T>
00133 void vpgl_perspective_camera<T>::set_camera_center(
00134   const vgl_point_3d<T>& camera_center )
00135 {
00136   camera_center_ = camera_center;
00137   recompute_matrix();
00138 }
00139 
00140 //-------------------------------------------
00141 template <class T>
00142 void vpgl_perspective_camera<T>::set_translation(const vgl_vector_3d<T>& t)
00143 {
00144   vgl_rotation_3d<T> Rt = R_.transpose();
00145   vgl_vector_3d<T> cv = -(Rt * t);
00146   camera_center_.set(cv.x(), cv.y(), cv.z());
00147   recompute_matrix();
00148 }
00149 
00150 //-------------------------------------------
00151 template <class T>
00152 void vpgl_perspective_camera<T>::set_rotation( const vgl_rotation_3d<T>& R )
00153 {
00154   R_ = R;
00155   recompute_matrix();
00156 }
00157 
00158 //-------------------------------------------
00159 template <class T>
00160 vgl_vector_3d<T> vpgl_perspective_camera<T>::get_translation() const
00161 {
00162   vgl_vector_3d<T> c(camera_center_.x(), camera_center_.y(),camera_center_.z());
00163   vgl_vector_3d<T> temp = R_*c;
00164   return -temp;
00165 }
00166 
00167 //: Rotate the camera about its center such that it looks at the given point
00168 //  The camera should also be rotated about its principal axis such that
00169 //  the vertical image direction is closest to \p up in the world
00170 template <class T>
00171 void vpgl_perspective_camera<T>::look_at(const vgl_homg_point_3d<T>& point,
00172                                          const vgl_vector_3d<T>& up )
00173 {
00174   vgl_vector_3d<T> u = normalized(up);
00175   vgl_vector_3d<T> look = point - camera_center();
00176   normalize(look);
00177   T dp = dot_product(look, up);
00178 
00179 #if 0
00180   bool singularity = vcl_fabs(vcl_fabs(static_cast<double>(dp))-1.0)<1e-08;
00181   assert(!singularity);
00182 #endif
00183   vgl_vector_3d<T> z = look;
00184 
00185   if(vcl_fabs(dot_product<T>(u,z)-T(1))<1e-5)
00186   {
00187   
00188     T r[] = { 1, 0, 0,
00189               0, 1, 0,
00190               0, 0, 1 };
00191 
00192   vnl_matrix_fixed<T,3,3> R(r);
00193   set_rotation(vgl_rotation_3d<T>(R));
00194 
00195   }
00196   else if(vcl_fabs(dot_product<T>(u,z)-T(-1))<1e-5)
00197   {
00198       T r[] = { 1, 0, 0,
00199               0, 1, 0,
00200               0, 0, -1 };
00201 
00202   vnl_matrix_fixed<T,3,3> R(r);
00203   set_rotation(vgl_rotation_3d<T>(R));
00204 
00205   }
00206   else
00207   {
00208   vgl_vector_3d<T> x = cross_product(-u,z);
00209   vgl_vector_3d<T> y = cross_product(z,x);
00210   normalize(x);
00211   normalize(y);
00212   normalize(z);
00213 
00214   T r[] = { x.x(), x.y(), x.z(),
00215             y.x(), y.y(), y.z(),
00216             z.x(), z.y(), z.z() };
00217 
00218   vnl_matrix_fixed<T,3,3> R(r);
00219   set_rotation(vgl_rotation_3d<T>(R));
00220   }
00221 }
00222 
00223 
00224 //-------------------------------------------
00225 template <class T>
00226 void vpgl_perspective_camera<T>::recompute_matrix()
00227 {
00228     vnl_matrix_fixed<T,3,4> Pnew( (T)0 );
00229 
00230    // Set new projection matrix to [ I | -C ].
00231    for ( int i = 0; i < 3; i++ )
00232      Pnew(i,i) = (T)1;
00233    Pnew(0,3) = -camera_center_.x();
00234    Pnew(1,3) = -camera_center_.y();
00235    Pnew(2,3) = -camera_center_.z();
00236 
00237    // Then multiply on left to get KR[ I | -C ].
00238    this->set_matrix(K_.get_matrix()*R_.as_matrix()*Pnew);
00239 }
00240 
00241 
00242 //-------------------------------------------
00243 template <class T>
00244 bool vpgl_perspective_decomposition( const vnl_matrix_fixed<T,3,4>& camera_matrix,
00245                                      vpgl_perspective_camera<T>& p_camera )
00246 {
00247   // Extract the left sub matrix H from [ H t ] and check that it has rank 3.
00248   vnl_matrix_fixed<T,3,3> H = camera_matrix.extract(3,3);
00249   vnl_vector_fixed<T,3> t = camera_matrix.get_column(3);
00250 
00251   T det = vnl_det(H);
00252   if ( det == (T)0 ) return false;
00253   // To insure a true rotation (determinant = 1) we must start with a positive
00254   // determinant H.  This is decomposed into K and R, each with positive determinant.
00255   if ( det < (T)0 ){
00256     H *= (T)-1;
00257     t *= (T)-1;
00258   }
00259 
00260   // Now find the RQ decomposition of the sub matrix and use these to find the params.
00261   // This will involve some trickery as VXL has no RQ decomposition, but does have QR.
00262   // Define a matrix "flipping" operation by f(A)ij = An-j,n-i i.e. f flips the matrix
00263   // about its LL-UR diagonal.  One can prove that if f(A) = B*C then A = f(A)*f(B). So
00264   // we get the RQ decomposition of A by flipping A, then taking the QR decomposition,
00265   // then flipping both back, noting that the flipped Q and R will remain orthogonal and
00266   // upper right triangular respectively.
00267   vnl_matrix_fixed<T,3,3> Hf;
00268   for ( int i = 0; i < 3; i++ )
00269     for ( int j = 0; j < 3; j++ )
00270       Hf(i,j) = H(2-j,2-i);
00271   vnl_qr<T> QR( Hf );
00272   vnl_matrix_fixed<T,3,3> q,r,Qf,Rf;
00273   q = QR.Q();
00274   r = QR.R();
00275   for ( int i = 0; i < 3; i++ ){
00276     for ( int j = 0; j < 3; j++ ){
00277       Qf(i,j) = q(2-j,2-i);
00278       Rf(i,j) = r(2-j,2-i);
00279     }
00280   }
00281 
00282   // We almost have the K and R parameter blocks, but we must be sure that the diagonal
00283   // entries of K are positive.
00284   int r0pos = Rf(0,0) > 0 ? 1 : -1;
00285   int r1pos = Rf(1,1) > 0 ? 1 : -1;
00286   int r2pos = Rf(2,2) > 0 ? 1 : -1;
00287   int diag[3] = { r0pos, r1pos, r2pos };
00288   vnl_matrix_fixed<T,3,3> K1,R1;
00289   for ( int i = 0; i < 3; i++ ){
00290     for ( int j = 0; j < 3; j++ ){
00291       K1(i,j) = diag[j]*Rf(i,j);
00292       R1(i,j) = diag[i]*Qf(i,j);
00293     }
00294   }
00295   K1 = K1/K1(2,2);
00296 
00297   // Now we extract the parameters from the matrices we've computed;
00298   vpgl_calibration_matrix<T> new_K( K1 );
00299   p_camera.set_calibration( new_K );
00300 
00301   vnl_qr<T> QRofH(H);
00302   vnl_vector<T> c1 = -QRofH.solve(t);
00303   vgl_point_3d<T> new_c( c1(0), c1(1), c1(2) );
00304   p_camera.set_camera_center( new_c );
00305 
00306   p_camera.set_rotation( vgl_rotation_3d<T>(R1) );
00307 
00308   return true;
00309 }
00310 
00311 
00312 //-------------------------------------------
00313 template <class T>
00314 vpgl_perspective_camera<T> vpgl_align_down(
00315   const vpgl_perspective_camera<T>& p0,
00316   const vpgl_perspective_camera<T>& p1 )
00317 {
00318   vpgl_perspective_camera<T> new_camera;
00319   new_camera.set_calibration( p0.get_calibration() );
00320   new_camera.set_rotation( p1.get_rotation()*p0.get_rotation().inverse() );
00321   vgl_point_3d<T> a0 = p0.get_rotation()*p0.get_camera_center();
00322   vgl_point_3d<T> a1 = p0.get_rotation()*p1.get_camera_center();
00323   vgl_point_3d<T> new_camera_center(a1.x() - a0.x(),
00324                                     a1.y() - a0.y(),
00325                                     a1.z() - a0.z() );
00326   new_camera.set_camera_center( new_camera_center );
00327   return new_camera;
00328 }
00329 
00330 
00331 //-------------------------------------------
00332 template <class T>
00333 vpgl_perspective_camera<T> vpgl_align_up(
00334   const vpgl_perspective_camera<T>& p0,
00335   const vpgl_perspective_camera<T>& p1 )
00336 {
00337   vpgl_perspective_camera<T> new_camera;
00338   new_camera.set_calibration( p0.get_calibration() );
00339   new_camera.set_rotation( p1.get_rotation()*p0.get_rotation() );
00340   vgl_point_3d<T> a = p0.get_rotation().inverse()*p1.get_camera_center();
00341   vgl_point_3d<T> new_camera_center( p0.get_camera_center().x() + a.x(),
00342                                      p0.get_camera_center().y() + a.y(),
00343                                      p0.get_camera_center().z() + a.z() );
00344   new_camera.set_camera_center( new_camera_center );
00345   return new_camera;
00346 }
00347 
00348 
00349 //Post-multiply this perspective camera with a 3-d Euclidean transformation
00350 // Must check if the transformation is Euclidean, i.e. rotation matrix
00351 // and translation.   Since we can only work with the external interface
00352 // the update due to the postmultiplication is:
00353 //   K' = K
00354 //   R' = R*Re
00355 //   cc' = transpose(Re)(cc - te)
00356 // where Re and te are the rotation matrix and
00357 //  translation vector of the euclidean transform
00358 template <class T> vpgl_perspective_camera<T>
00359 vpgl_perspective_camera<T>::postmultiply( const vpgl_perspective_camera<T>& in_cam, const vgl_h_matrix_3d<T>& euclid_trans)
00360 {
00361   assert(euclid_trans.is_euclidean());
00362   const vpgl_calibration_matrix<T>& K = in_cam.get_calibration();
00363   const vgl_rotation_3d<T>& R = in_cam.get_rotation();
00364   const vgl_point_3d<T>& cc = in_cam.get_camera_center();
00365   vgl_rotation_3d<T> Re(euclid_trans.get_upper_3x3());
00366   vgl_homg_point_3d<T> t = euclid_trans.get_translation();
00367 
00368   //The transformed rotation matrix
00369   vgl_rotation_3d<T> Rp(R*Re);
00370 
00371   //must have Euclidean quantities to proceed
00372   assert(!t.ideal());
00373 
00374   //Transform the camera center
00375   //get the Euclidean components
00376   vgl_vector_3d<T> te(t.x()/t.w(), t.y()/t.w(), t.z()/t.w());
00377 
00378   //construct the transformed center
00379   vgl_point_3d<T> ccp = Re.inverse()*(cc-te);
00380 
00381   return vpgl_perspective_camera<T>(K, ccp, Rp);
00382 }
00383 
00384 // I/O :------------------------------------------------
00385 
00386 //: Write vpgl_perspective_camera to stream
00387 template <class Type>
00388 vcl_ostream&  operator<<(vcl_ostream& s,
00389                          vpgl_perspective_camera<Type> const& p)
00390 {
00391   vnl_matrix_fixed<Type, 3, 3> k = p.get_calibration().get_matrix();
00392   vgl_rotation_3d<Type> rot = p.get_rotation();
00393   vnl_matrix_fixed<Type, 3, 3> Rm = rot.as_matrix();
00394   vgl_vector_3d<Type> t = p.get_translation();
00395   s << k << '\n'
00396     << Rm << '\n'
00397     << t.x() << ' ' << t.y() << ' ' << t.z() << '\n';
00398   return s ;
00399 }
00400 
00401 //: Read camera from stream
00402 template <class Type>
00403 vcl_istream&  operator >>(vcl_istream& s,
00404                           vpgl_perspective_camera<Type>& p)
00405 {
00406   vnl_matrix_fixed<Type, 3, 3> k, Rm;
00407   vnl_vector_fixed<Type, 3> tv;
00408   s >> k;
00409   s >> Rm;
00410   s >> tv;
00411   vpgl_calibration_matrix<Type> K(k);
00412   vgl_rotation_3d<Type> rot(Rm);
00413   vgl_vector_3d<Type> t(tv[0], tv[1], tv[2]);
00414   p.set_calibration(K);
00415   p.set_rotation(rot);
00416   p.set_translation(t);
00417   return s ;
00418 }
00419 
00420 //-------------------------------
00421 template <class T> void vpgl_perspective_camera<T>::
00422 b_read(vsl_b_istream &is)
00423 {
00424   if (!is) return;
00425 
00426   vnl_matrix_fixed<T,4,4> Rot;
00427   vnl_vector_fixed<T,4> q;
00428 
00429   short ver;
00430   vsl_b_read(is, ver);
00431   switch (ver)
00432   {
00433    case 1:
00434      vpgl_proj_camera<T>::b_read(is);
00435      K_.b_read(is);
00436      vsl_b_read(is, camera_center_);
00437      vsl_b_read(is, Rot);
00438      R_ = vgl_rotation_3d<T>(vgl_h_matrix_3d<T>(Rot));
00439     break;
00440    case 2:
00441      vpgl_proj_camera<T>::b_read(is);
00442      K_.b_read(is);
00443      vsl_b_read(is, camera_center_);
00444      vsl_b_read(is, q);
00445      R_ = vgl_rotation_3d<T>(vnl_quaternion<T>(q));
00446     break;
00447    default:
00448     vcl_cerr << "I/O ERROR: vpgl_persperctive_camera::b_read(vsl_b_istream&)\n"
00449              << "           Unknown version number "<< ver << '\n';
00450     is.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00451     return;
00452   }
00453   this->recompute_matrix();
00454 }
00455 
00456 //-------------------------------
00457 //: Binary save self to stream.
00458 // \remark cached_svd_ not written
00459 template <class T> void vpgl_perspective_camera<T>::
00460 b_write(vsl_b_ostream &os) const
00461 {
00462   vsl_b_write(os, this->version());
00463   vpgl_proj_camera<T>::b_write(os);
00464   K_.b_write(os);
00465   vsl_b_write(os, camera_center_);
00466   vsl_b_write(os, static_cast<vnl_vector_fixed<T,4> >(R_.as_quaternion()));
00467 }
00468 
00469 //: Binary save
00470 template <class T> void
00471 vsl_b_write(vsl_b_ostream &os, const vpgl_perspective_camera<T> * p)
00472 {
00473   if (p==0) {
00474     vsl_b_write(os, false); // Indicate null pointer stored
00475   }
00476   else{
00477     vsl_b_write(os,true); // Indicate non-null pointer stored
00478     p->b_write(os);
00479   }
00480 }
00481 
00482 
00483 //: Binary load
00484 template <class T> void
00485 vsl_b_read(vsl_b_istream &is, vpgl_perspective_camera<T>* &p)
00486 {
00487   delete p;
00488   bool not_null_ptr;
00489   vsl_b_read(is, not_null_ptr);
00490   if (not_null_ptr) {
00491     p = new vpgl_perspective_camera<T>();
00492     p->b_read(is);
00493   }
00494   else
00495     p = 0;
00496 }
00497 
00498 
00499 // Code for easy instantiation.
00500 #undef vpgl_PERSPECTIVE_CAMERA_INSTANTIATE
00501 #define vpgl_PERSPECTIVE_CAMERA_INSTANTIATE(T) \
00502 template class vpgl_perspective_camera<T >; \
00503 template bool vpgl_perspective_decomposition(const vnl_matrix_fixed<T,3,4>& camera_matrix, \
00504                                              vpgl_perspective_camera<T >& p_camera ); \
00505 template vpgl_perspective_camera<T > vpgl_align_down(const vpgl_perspective_camera<T >& p0, \
00506                                                      const vpgl_perspective_camera<T >& p1 ); \
00507 template vpgl_perspective_camera<T > vpgl_align_up(const vpgl_perspective_camera<T >& p0, \
00508                                                    const vpgl_perspective_camera<T >& p1 ); \
00509 template void vsl_b_read(vsl_b_istream &is, vpgl_perspective_camera<T >* &p); \
00510 template void vsl_b_write(vsl_b_ostream &os, const vpgl_perspective_camera<T > * p); \
00511 template vcl_ostream& operator<<(vcl_ostream&, const vpgl_perspective_camera<T >&); \
00512 template vcl_istream& operator>>(vcl_istream&, vpgl_perspective_camera<T >&)
00513 
00514 #endif // vpgl_perspective_camera_txx_