contrib/gel/mrc/vpgl/vpgl_proj_camera.txx
Go to the documentation of this file.
00001 // This is gel/mrc/vpgl/vpgl_proj_camera.txx
00002 #ifndef vpgl_proj_camera_txx_
00003 #define vpgl_proj_camera_txx_
00004 //:
00005 // \file
00006 
00007 #include "vpgl_proj_camera.h"
00008 #include <vcl_iostream.h>
00009 #include <vgl/vgl_point_2d.h>
00010 #include <vgl/vgl_point_3d.h>
00011 #include <vnl/vnl_vector_fixed.h>
00012 #include <vnl/io/vnl_io_matrix_fixed.h>
00013 #include <vsl/vsl_binary_loader.h>
00014 
00015 
00016 // CONSTRUCTORS:--------------------------------------------------------------------
00017 
00018 //------------------------------------
00019 template <class T>
00020 vpgl_proj_camera<T>::vpgl_proj_camera() :
00021   cached_svd_(NULL)
00022 {
00023   P_ = vnl_matrix_fixed<T,3,4>( (T)0 );
00024   P_(0,0) = P_(1,1) = P_(2,2) = (T)1;
00025 }
00026 
00027 //------------------------------------
00028 template <class T>
00029 vpgl_proj_camera<T>::vpgl_proj_camera( const vnl_matrix_fixed<T,3,4>& camera_matrix ) :
00030   P_( camera_matrix ),
00031   cached_svd_(NULL)
00032 {
00033 }
00034 
00035 //------------------------------------
00036 template <class T>
00037 vpgl_proj_camera<T>::vpgl_proj_camera( const T* camera_matrix ) :
00038   P_( camera_matrix ),
00039   cached_svd_(NULL)
00040 {
00041 }
00042 
00043 //------------------------------------
00044 template <class T>
00045 vpgl_proj_camera<T>::vpgl_proj_camera( const vpgl_proj_camera& cam ) :
00046   vpgl_camera<T>(),
00047   P_( cam.get_matrix() ),
00048   cached_svd_(NULL)
00049 {
00050 }
00051 
00052 //------------------------------------
00053 template <class T>
00054 const vpgl_proj_camera<T>& vpgl_proj_camera<T>::operator=( const vpgl_proj_camera& cam )
00055 {
00056   P_ = cam.get_matrix();
00057   if ( cached_svd_ != NULL ) delete cached_svd_;
00058   cached_svd_ = NULL;
00059   return *this;
00060 }
00061 
00062 //------------------------------------
00063 template <class T>
00064 vpgl_proj_camera<T>::~vpgl_proj_camera()
00065 {
00066   if ( cached_svd_ != NULL ) delete cached_svd_;
00067   cached_svd_ = NULL;
00068 }
00069 
00070 template <class T>
00071 vpgl_proj_camera<T>* vpgl_proj_camera<T>::clone(void) const
00072 {
00073   return new vpgl_proj_camera<T>(*this);
00074 }
00075 
00076 
00077 // PROJECTIONS AND BACKPROJECTIONS:----------------------------------------------
00078 
00079 //------------------------------------
00080 template <class T>
00081 vgl_homg_point_2d<T> vpgl_proj_camera<T>::project( const vgl_homg_point_3d<T>& world_point ) const
00082 {
00083   // For efficiency, manually compute the multiplication rather than converting to
00084   // vnl and converting back.
00085   vgl_homg_point_2d<T> image_point(
00086     P_(0,0)*world_point.x() + P_(0,1)*world_point.y() +
00087     P_(0,2)*world_point.z() + P_(0,3)*world_point.w(),
00088 
00089     P_(1,0)*world_point.x() + P_(1,1)*world_point.y() +
00090     P_(1,2)*world_point.z() + P_(1,3)*world_point.w(),
00091 
00092     P_(2,0)*world_point.x() + P_(2,1)*world_point.y() +
00093     P_(2,2)*world_point.z() + P_(2,3)*world_point.w() );
00094   return image_point;
00095 }
00096 
00097 // -----------------------------------
00098 template <class T>
00099 void
00100 vpgl_proj_camera<T>::project(const T x, const T y, const T z, T& u, T& v) const
00101 {
00102   vgl_homg_point_3d<T> world_point(x, y, z);
00103   vgl_homg_point_2d<T> image_point = this->project(world_point);
00104   if (image_point.ideal(static_cast<T>(1.0e-10)))
00105   {
00106     u = 0; v = 0;
00107     vcl_cerr << "Warning: projection to ideal image point in vpgl_proj_camera -"
00108              << " result not valid\n";
00109     return;
00110   }
00111   u = image_point.x()/image_point.w();
00112   v = image_point.y()/image_point.w();
00113 }
00114 
00115 //------------------------------------
00116 template <class T>
00117 vgl_line_segment_2d<T> vpgl_proj_camera<T>::project(
00118   const vgl_line_segment_3d<T>& world_line ) const
00119 {
00120   vgl_homg_point_3d<T> point1_w( world_line.point1() );
00121   vgl_homg_point_3d<T> point2_w( world_line.point2() );
00122   vgl_point_2d<T> point1_im( project( point1_w ) );
00123   vgl_point_2d<T> point2_im( project( point2_w ) );
00124   vgl_line_segment_2d<T> image_line( point1_im, point2_im );
00125   return image_line;
00126 }
00127   //: Project an infinite line in the world onto an infinite line in the image plane.
00128 template <class T>
00129 vgl_line_2d<T> vpgl_proj_camera<T>::project( const vgl_infinite_line_3d<T>& world_line ) const
00130 {
00131   vgl_homg_point_3d<T> point1_w( world_line.point() );
00132   vgl_homg_point_3d<T> point2_w( world_line.point_t(T(1)) );
00133   vgl_point_2d<T> point1_im( project( point1_w ) );
00134   vgl_point_2d<T> point2_im( project( point2_w ) );
00135   vgl_line_2d<T> image_line( point1_im, point2_im );
00136   return image_line;
00137 }
00138 //------------------------------------
00139 template <class T>
00140 vgl_homg_line_3d_2_points<T> vpgl_proj_camera<T>::backproject(
00141   const vgl_homg_point_2d<T>& image_point ) const
00142 {
00143   // First find any point in the world that projects to the "image_point".
00144   vnl_vector_fixed<T,4> vnl_wp = svd()->solve(
00145     vnl_vector_fixed<T,3>( image_point.x(), image_point.y(), image_point.w() ) );
00146   vgl_homg_point_3d<T> wp( vnl_wp[0], vnl_wp[1], vnl_wp[2], vnl_wp[3] );
00147 
00148   // The ray is then defined by that point and the camera center.
00149   if ( wp.ideal(.000001) ) // modified 11/6/05 by tpollard
00150     return vgl_homg_line_3d_2_points<T>( camera_center(), wp );
00151   return vgl_homg_line_3d_2_points<T>( wp, camera_center() );
00152 }
00153 
00154 
00155 //------------------------------------
00156 template <class T>
00157 vgl_homg_plane_3d<T> vpgl_proj_camera<T>::backproject(
00158   const vgl_homg_line_2d<T>& image_line ) const
00159 {
00160   vnl_vector_fixed<T,3> image_line_vnl(image_line.a(),image_line.b(),image_line.c());
00161   vnl_vector_fixed<T,4> world_plane  = P_.transpose() * image_line_vnl;
00162   return vgl_homg_plane_3d<T>( world_plane(0), world_plane(1),
00163                                world_plane(2), world_plane(3) );
00164 }
00165 
00166 
00167 // MISC CAMERA FUNCTIONS:-----------------------------------------------------
00168 
00169 //------------------------------------
00170 template <class T>
00171 vgl_homg_point_3d<T> vpgl_proj_camera<T>::camera_center() const
00172 {
00173   vnl_matrix<T> ns = svd()->nullspace();
00174   return vgl_homg_point_3d<T>(ns(0,0), ns(1,0), ns(2,0), ns(3,0));
00175 }
00176 
00177 
00178 // ACCESSORS:-----------------------------------------------------------------
00179 
00180 //------------------------------------
00181 template <class T>
00182 vnl_svd<T>* vpgl_proj_camera<T>::svd() const
00183 {
00184   // Check if the cached copy is valid, if not recompute it.
00185   if ( cached_svd_ == NULL )
00186   {
00187     cached_svd_ = new vnl_svd<T>(P_);
00188 
00189     // Check that the projection matrix isn't degenerate.
00190     if ( cached_svd_->rank() != 3 )
00191       vcl_cerr << "vpgl_proj_camera::svd()\n"
00192                << "  Warning: Projection matrix is not rank 3, errors may occur.\n";
00193   }
00194   return cached_svd_;
00195 }
00196 
00197 //------------------------------------
00198 template <class T>
00199 bool vpgl_proj_camera<T>::set_matrix( const vnl_matrix_fixed<T,3,4>& new_camera_matrix )
00200 {
00201   P_ = new_camera_matrix;
00202   if ( cached_svd_ != NULL ) delete cached_svd_;
00203   cached_svd_ = NULL;
00204   return true;
00205 }
00206 
00207 //------------------------------------
00208 template <class T>
00209 bool vpgl_proj_camera<T>::set_matrix( const T* new_camera_matrix )
00210 {
00211   P_ = vnl_matrix_fixed<T,3,4>( new_camera_matrix );
00212     if ( cached_svd_ != NULL ) delete cached_svd_;
00213     cached_svd_ = NULL;
00214   return true;
00215 }
00216 
00217 
00218 // I/O :------------------------------------------------
00219 
00220 //-------------------------------
00221 template <class T> void vpgl_proj_camera<T>::
00222 b_read(vsl_b_istream &is)
00223 {
00224   if (!is) return;
00225 
00226   short ver;
00227   vsl_b_read(is, ver);
00228   vnl_matrix_fixed<T,3,4> Pnew;
00229   switch (ver)
00230   {
00231    case 1:
00232      vsl_b_read(is, Pnew);
00233      set_matrix(Pnew);
00234     break;
00235    default:
00236     vcl_cerr << "I/O ERROR: vpgl_proj_camera::b_read(vsl_b_istream&)\n"
00237              << "           Unknown version number "<< ver << '\n';
00238     is.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00239     return;
00240   }
00241 }
00242 
00243 //-------------------------------
00244 //: Binary save self to stream.
00245 // \remark cached_svd_ not written
00246 template <class T> void vpgl_proj_camera<T>::
00247 b_write(vsl_b_ostream &os) const
00248 {
00249   vsl_b_write(os, this->version());
00250   vsl_b_write(os, this->P_);
00251 }
00252 
00253 
00254 // EXTERNAL FUNCTIONS:------------------------------------------------
00255 
00256 //: Write vpgl_perspective_camera to stream
00257 template <class Type>
00258 vcl_ostream&  operator<<(vcl_ostream& s,
00259                          vpgl_proj_camera<Type> const& p)
00260 {
00261   s << "projective:"
00262     << "\nP\n" << p.get_matrix() << vcl_endl;
00263 
00264   return s ;
00265 }
00266 
00267 //: Read vpgl_perspective_camera from stream
00268 template <class Type>
00269 vcl_istream&  operator>>(vcl_istream& s,
00270                          vpgl_proj_camera<Type>& p)
00271 {
00272   vnl_matrix_fixed<Type,3,4> new_matrix;
00273   s >> new_matrix;
00274   p.set_matrix( new_matrix );
00275 
00276   return s ;
00277 }
00278 
00279 //: Allows derived class to be loaded by base-class pointer.
00280 //  A loader object exists which is invoked by calls
00281 //  of the form "vsl_b_read(os,base_ptr);".  This loads derived class
00282 //  objects from the stream, places them on the heap and
00283 //  returns a base class pointer.
00284 //  In order to work the loader object requires
00285 //  an instance of each derived class that might be
00286 //  found.  This function gives the model class to
00287 //  the appropriate loader.
00288 template <class T>
00289 void vsl_add_to_binary_loader(vpgl_proj_camera<T> const& b)
00290 {
00291   vsl_binary_loader<vpgl_proj_camera<T> >::instance().add(b);
00292 }
00293 
00294 //-------------------------------
00295 template <class T>
00296 vgl_h_matrix_3d<T> get_canonical_h( vpgl_proj_camera<T>& camera )
00297 {
00298   // If P is a 3x4 rank 3 matrix, Pinv is the pseudo-inverse of P, and l is a
00299   // vector such that P*l = 0, then P*[Pinv | l] = [I | 0].
00300   vnl_matrix_fixed<T,4,3> Pinv = camera.svd()->pinverse();
00301   vnl_vector<T> l = camera.svd()->solve( vnl_vector<T>(3,(T)0) );
00302 
00303   vnl_matrix_fixed<T,4,4> H;
00304   for ( int i = 0; i < 4; i++ ){
00305     for ( int j = 0; j < 3; j++ )
00306       H(i,j) = Pinv(i,j);
00307     H(i,3) = l(i);
00308   }
00309   return vgl_h_matrix_3d<T>( H );
00310 }
00311 
00312 //--------------------------------
00313 template <class T>
00314 void fix_cheirality( vpgl_proj_camera<T>& /*camera*/ )
00315 {
00316   vcl_cerr << "fix_cheirality( vpgl_proj_camera<T>& ) not implemented\n";
00317 }
00318 
00319 //--------------------------------
00320 template <class T>
00321 void make_cannonical( vpgl_proj_camera<T>& camera )
00322 {
00323   vnl_matrix_fixed<T,3,4> can_cam( (T)0 );
00324   can_cam(0,0) = can_cam(1,1) = can_cam(2,2) = (T)1;
00325   camera.set_matrix( can_cam );
00326 }
00327 
00328 //--------------------------------
00329 template <class T>
00330 vpgl_proj_camera<T> premultiply( const vpgl_proj_camera<T>& in_camera,
00331                                  const vnl_matrix_fixed<T,3,3>& transform )
00332 {
00333   return vpgl_proj_camera<T>( transform*in_camera.get_matrix() );
00334 }
00335 
00336 //--------------------------------
00337 template <class T>
00338 vpgl_proj_camera<T> postmultiply( const vpgl_proj_camera<T>& in_camera,
00339                                   const vnl_matrix_fixed<T,4,4>& transform )
00340 {
00341   return vpgl_proj_camera<T>( in_camera.get_matrix()*transform );
00342 }
00343 
00344 //--------------------------------
00345 template <class T>
00346 vgl_point_3d<T> triangulate_3d_point(const vpgl_proj_camera<T>& c1,
00347                                      const vgl_point_2d<T>& x1,
00348                                      const vpgl_proj_camera<T>& c2,
00349                                      const vgl_point_2d<T>& x2)
00350 {
00351   vnl_matrix_fixed<T,4,4> A;
00352   vnl_matrix_fixed<T,3,4> P1 = c1.get_matrix();
00353   vnl_matrix_fixed<T,3,4> P2 = c2.get_matrix();
00354   for (int i=0; i<4; i++) {
00355     A[0][i] = x1.x()*P1[2][i] - P1[0][i];
00356     A[1][i] = x1.y()*P1[2][i] - P1[1][i];
00357     A[2][i] = x2.x()*P2[2][i] - P2[0][i];
00358     A[3][i] = x2.y()*P2[2][i] - P2[1][i];
00359   }
00360   vnl_svd<T> svd_solver(A);
00361   vnl_vector_fixed<T, 4> p = svd_solver.nullvector();
00362   vgl_homg_point_3d<T> hp(p[0],p[1],p[2],p[3]);
00363   return vgl_point_3d<T>(hp);
00364 }
00365 
00366 
00367 //: Compute the image projection Jacobians at each point
00368 //  The returned matrices map a differential change in 3D
00369 //  to a differential change in the 2D image at each specified 3D point
00370 template <class T>
00371 vcl_vector<vnl_matrix_fixed<T,2,3> >
00372 image_jacobians(const vpgl_proj_camera<T>& camera,
00373                 const vcl_vector<vgl_point_3d<T> >& pts)
00374 {
00375   const vnl_matrix_fixed<T,3,4>& P = camera.get_matrix();
00376   vnl_vector_fixed<T,4> denom = P.get_row(2);
00377 
00378   vnl_matrix_fixed<T,3,4> Du;
00379   Du(0,0) = Du(1,1) = Du(2,2) = 0.0;
00380   Du(0,1) = P(0,0)*P(2,1) - P(0,1)*P(2,0);
00381   Du(0,2) = P(0,0)*P(2,2) - P(0,2)*P(2,0);
00382   Du(1,2) = P(0,1)*P(2,2) - P(0,2)*P(2,1);
00383   Du(0,3) = P(0,0)*P(2,3) - P(0,3)*P(2,0);
00384   Du(1,3) = P(0,1)*P(2,3) - P(0,3)*P(2,1);
00385   Du(2,3) = P(0,2)*P(2,3) - P(0,3)*P(2,2);
00386   Du(1,0) = -Du(0,1);
00387   Du(2,0) = -Du(0,2);
00388   Du(2,1) = -Du(1,2);
00389 
00390   vnl_matrix_fixed<T,3,4> Dv;
00391   Dv(0,0) = Dv(1,1) = Dv(2,2) = 0.0;
00392   Dv(0,1) = P(1,0)*P(2,1) - P(1,1)*P(2,0);
00393   Dv(0,2) = P(1,0)*P(2,2) - P(1,2)*P(2,0);
00394   Dv(1,2) = P(1,1)*P(2,2) - P(1,2)*P(2,1);
00395   Dv(0,3) = P(1,0)*P(2,3) - P(1,3)*P(2,0);
00396   Dv(1,3) = P(1,1)*P(2,3) - P(1,3)*P(2,1);
00397   Dv(2,3) = P(1,2)*P(2,3) - P(1,3)*P(2,2);
00398   Dv(1,0) = -Dv(0,1);
00399   Dv(2,0) = -Dv(0,2);
00400   Dv(2,1) = -Dv(1,2);
00401 
00402 
00403   const unsigned int num_pts = pts.size();
00404   vcl_vector<vnl_matrix_fixed<T,2,3> > img_jac(num_pts);
00405 
00406   for (unsigned int i=0; i<num_pts; ++i)
00407   {
00408     const vgl_point_3d<T>& pt = pts[i];
00409     vnl_matrix_fixed<double,2,3>& J = img_jac[i];
00410     vnl_vector_fixed<T,4>  hpt(pt.x(),pt.y(),pt.z(),1.0);
00411 
00412     double d = dot_product(denom,hpt);
00413     d *= d;
00414     J.set_row(0,Du*hpt);
00415     J.set_row(1,Dv*hpt);
00416     J /= d;
00417   }
00418 
00419   return img_jac;
00420 }
00421 
00422 
00423 // Code for easy instantiation.
00424 #undef vpgl_PROJ_CAMERA_INSTANTIATE
00425 #define vpgl_PROJ_CAMERA_INSTANTIATE(T) \
00426 template class vpgl_proj_camera<T >; \
00427 template vgl_h_matrix_3d<T > get_canonical_h( vpgl_proj_camera<T >& camera ); \
00428 template void fix_cheirality( vpgl_proj_camera<T >& camera ); \
00429 template void make_cannonical( vpgl_proj_camera<T >& camera ); \
00430 template vpgl_proj_camera<T > premultiply( const vpgl_proj_camera<T >& in_camera, \
00431                                            const vnl_matrix_fixed<T,3,3>& transform ); \
00432 template vpgl_proj_camera<T > postmultiply(const vpgl_proj_camera<T >& in_camera, \
00433                                            const vnl_matrix_fixed<T,4,4>& transform ); \
00434 template vgl_point_3d<T > triangulate_3d_point(const vpgl_proj_camera<T >& c1, \
00435                                                const vgl_point_2d<T >& x1, \
00436                                                const vpgl_proj_camera<T >& c2, \
00437                                                const vgl_point_2d<T >& x2); \
00438 template vcl_vector<vnl_matrix_fixed<T,2,3> > \
00439          image_jacobians(const vpgl_proj_camera<T >& camera, \
00440                          const vcl_vector<vgl_point_3d<T > >& pts); \
00441 template void vsl_add_to_binary_loader(vpgl_proj_camera<T > const& b); \
00442 template vcl_ostream& operator<<(vcl_ostream&, const vpgl_proj_camera<T >&); \
00443 template vcl_istream& operator>>(vcl_istream&, vpgl_proj_camera<T >&)
00444 
00445 #endif // vpgl_proj_camera_txx_