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