core/vpgl/vpgl_perspective_camera.txx
Go to the documentation of this file.
00001 // This is core/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 
00009 #include <vgl/vgl_point_2d.h>
00010 #include <vgl/vgl_vector_3d.h>
00011 #include <vgl/vgl_homg_plane_3d.h>
00012 #include <vgl/vgl_line_3d_2_points.h>
00013 #include <vgl/algo/vgl_h_matrix_3d.h>
00014 #include <vnl/vnl_det.h>
00015 #include <vnl/algo/vnl_qr.h>
00016 #include <vnl/vnl_matrix_fixed.h>
00017 #if 0
00018 #include <vgl/io/vgl_io_point_3d.h>
00019 #include <vnl/io/vnl_io_matrix_fixed.h>
00020 #include <vnl/io/vnl_io_vector_fixed.h>
00021 #endif
00022 #include <vul/vul_file.h>
00023 #include <vul/vul_file_iterator.h>
00024 
00025 #include <vcl_cassert.h>
00026 #include <vcl_iostream.h>
00027 #include <vcl_fstream.h>
00028 #include <vcl_algorithm.h> // for std::sort()
00029 
00030 #include <vnl/vnl_trace.h>
00031 
00032 //-------------------------------------------
00033 template <class T>
00034 vpgl_perspective_camera<T>::vpgl_perspective_camera()
00035 {
00036   R_.set_identity();
00037   camera_center_.set( (T)0, (T)0, (T)0 );
00038   recompute_matrix();
00039 }
00040 
00041 
00042 //-------------------------------------------
00043 template <class T>
00044 vpgl_perspective_camera<T>::vpgl_perspective_camera(
00045   const vpgl_calibration_matrix<T>& K,
00046   const vgl_point_3d<T>& camera_center,
00047   const vgl_rotation_3d<T>& R ) :
00048   K_( K ), camera_center_( camera_center ), R_( R )
00049 {
00050   recompute_matrix();
00051 }
00052 
00053 //-------------------------------------------
00054 template <class T>
00055 vpgl_perspective_camera<T>::vpgl_perspective_camera(
00056   const vpgl_calibration_matrix<T>& K,
00057   const vgl_rotation_3d<T>& R,
00058   const vgl_vector_3d<T>& t) :
00059   K_( K ),  R_( R )
00060 {
00061   this->set_translation(t);
00062   recompute_matrix();
00063 }
00064 
00065 //-------------------------------------------
00066 template <class T>
00067 vpgl_perspective_camera<T>::vpgl_perspective_camera( const vpgl_perspective_camera& that)
00068   : vpgl_proj_camera<T>(that),
00069   K_(that.K_),
00070   camera_center_(that.camera_center_),
00071   R_(that.R_)
00072 {
00073 }
00074 
00075 //-------------------------------------------
00076 template <class T>
00077 vpgl_proj_camera<T>* vpgl_perspective_camera<T>::clone(void) const
00078 {
00079   return new vpgl_perspective_camera<T>(*this);
00080 }
00081 
00082 //------------------------------------
00083 template <class T>
00084 vgl_homg_line_3d_2_points<T> vpgl_perspective_camera<T>::backproject(
00085   const vgl_homg_point_2d<T>& image_point ) const
00086 {
00087   // First find a point that projects to "image_point".
00088   vnl_vector_fixed<T,4> vnl_wp = this->svd()->solve(
00089     vnl_vector_fixed<T,3>( image_point.x(), image_point.y(), image_point.w() ).as_ref() );
00090   vgl_homg_point_3d<T> wp( vnl_wp[0], vnl_wp[1], vnl_wp[2], vnl_wp[3] );
00091   // The ray is then defined by that point and the camera center.
00092   return vgl_homg_line_3d_2_points<T>( vgl_homg_point_3d<T>(camera_center_), wp );
00093 }
00094 
00095 //------------------------------------
00096 template <class T>
00097 vgl_line_3d_2_points<T> vpgl_perspective_camera<T>::backproject(
00098   const vgl_point_2d<T>& image_point ) const
00099 {
00100   // First find a point in front of the camera that projects to "image_point".
00101   vnl_vector_fixed<T,4> vnl_wp = this->svd()->solve(
00102     vnl_vector_fixed<T,3>( image_point.x(), image_point.y(), 1.0 ).as_ref() );
00103   vgl_homg_point_3d<T> wp_homg( vnl_wp[0], vnl_wp[1], vnl_wp[2], vnl_wp[3] );
00104   vgl_point_3d<T> wp;
00105   if ( !wp_homg.ideal() )
00106     wp.set( wp_homg.x()/wp_homg.w(), wp_homg.y()/wp_homg.w(), wp_homg.z()/wp_homg.w() );
00107   else
00108     wp.set( camera_center_.x()+wp_homg.x(),
00109             camera_center_.y()+wp_homg.y(),
00110             camera_center_.z()+wp_homg.z() );
00111   if ( is_behind_camera( vgl_homg_point_3d<T>( wp ) ) )
00112     wp = camera_center_ + ( camera_center_-wp );
00113   // The ray is then defined by that point and the camera center.
00114   return vgl_line_3d_2_points<T>( camera_center_, wp );
00115 }
00116 
00117 template <class T>
00118 vgl_ray_3d<T> vpgl_perspective_camera<T>::
00119 backproject_ray( const vgl_point_2d<T>& image_point ) const
00120 {
00121   vgl_line_3d_2_points<T> l2 = this->backproject(image_point);
00122   return vgl_ray_3d<T>(l2.point1(), l2.direction());
00123 }
00124 
00125 //-------------------------------------------
00126 template <class T>
00127 vgl_vector_3d<T> vpgl_perspective_camera<T>::principal_axis() const
00128 {
00129   // See H&Z pg 147
00130   // P = [M|p4] : We do not need to compute det(M) because we enforce
00131   // det(K)>0 and det(R)=1 in the construction of P. Thus det(M)>0;
00132   const vnl_matrix_fixed<T,3,4>& P = this->get_matrix();
00133   return normalized(vgl_vector_3d<T>(P(2,0),P(2,1),P(2,2)));
00134 }
00135 
00136 //------------------------------------
00137 template <class T>
00138 bool vpgl_perspective_camera<T>::is_behind_camera(
00139   const vgl_homg_point_3d<T>& world_point ) const
00140 {
00141   vgl_homg_plane_3d<T> l = this->principal_plane();
00142   T dot = world_point.x()*l.a() + world_point.y()*l.b() +
00143                world_point.z()*l.c() + world_point.w()*l.d();
00144   if (world_point.w() < (T)0) dot = ((T)(-1))*dot;
00145   return dot < 0;
00146 }
00147 
00148 //-------------------------------------------
00149 template <class T>
00150 void vpgl_perspective_camera<T>::set_calibration( const vpgl_calibration_matrix<T>& K)
00151 {
00152   K_ = K;
00153   recompute_matrix();
00154 }
00155 
00156 //-------------------------------------------
00157 template <class T>
00158 void vpgl_perspective_camera<T>::set_camera_center(
00159   const vgl_point_3d<T>& camera_center )
00160 {
00161   camera_center_ = camera_center;
00162   recompute_matrix();
00163 }
00164 
00165 //-------------------------------------------
00166 template <class T>
00167 void vpgl_perspective_camera<T>::set_translation(const vgl_vector_3d<T>& t)
00168 {
00169   vgl_rotation_3d<T> Rt = R_.transpose();
00170   vgl_vector_3d<T> cv = -(Rt * t);
00171   camera_center_.set(cv.x(), cv.y(), cv.z());
00172   recompute_matrix();
00173 }
00174 
00175 //-------------------------------------------
00176 template <class T>
00177 void vpgl_perspective_camera<T>::set_rotation( const vgl_rotation_3d<T>& R )
00178 {
00179   R_ = R;
00180   recompute_matrix();
00181 }
00182 
00183 //-------------------------------------------
00184 template <class T>
00185 vgl_vector_3d<T> vpgl_perspective_camera<T>::get_translation() const
00186 {
00187   vgl_vector_3d<T> c(camera_center_.x(), camera_center_.y(),camera_center_.z());
00188   vgl_vector_3d<T> temp = R_*c;
00189   return -temp;
00190 }
00191 
00192 //: Rotate the camera about its center such that it looks at the given point
00193 //  The camera should also be rotated about its principal axis such that
00194 //  the vertical image direction is closest to \p up in the world
00195 template <class T>
00196 void vpgl_perspective_camera<T>::look_at(const vgl_homg_point_3d<T>& point,
00197                                          const vgl_vector_3d<T>& up )
00198 {
00199   vgl_vector_3d<T> u = normalized(up);
00200   vgl_vector_3d<T> look = point - camera_center();
00201   normalize(look);
00202   vcl_cout << "look at vector: " << look << vcl_endl;
00203 
00204 #if 0
00205   T dp = dot_product(look, up);
00206   bool singularity = vcl_fabs(vcl_fabs(static_cast<double>(dp))-1.0)<1e-08;
00207   assert(!singularity);
00208 #endif
00209   vgl_vector_3d<T> z = look;
00210 
00211   if (vcl_fabs(dot_product<T>(u,z)-T(1))<1e-5)
00212   {
00213     T r[] = { 1, 0, 0,
00214               0, 1, 0,
00215               0, 0, 1 };
00216 
00217     vnl_matrix_fixed<T,3,3> R(r);
00218     set_rotation(vgl_rotation_3d<T>(R));
00219   }
00220   else if (vcl_fabs(dot_product<T>(u,z)-T(-1))<1e-5)
00221   {
00222     T r[] = { 1, 0, 0,
00223             0, 1, 0,
00224             0, 0, -1 };
00225 
00226     vnl_matrix_fixed<T,3,3> R(r);
00227     set_rotation(vgl_rotation_3d<T>(R));
00228   }
00229   else
00230   {
00231     vgl_vector_3d<T> x = cross_product(-u,z);
00232     vgl_vector_3d<T> y = cross_product(z,x);
00233     normalize(x);
00234     normalize(y);
00235     normalize(z);
00236 
00237     T r[] = { x.x(), x.y(), x.z(),
00238               y.x(), y.y(), y.z(),
00239               z.x(), z.y(), z.z() };
00240 
00241     vnl_matrix_fixed<T,3,3> R(r);
00242     set_rotation(vgl_rotation_3d<T>(R));
00243   }
00244 }
00245 
00246 
00247 //-------------------------------------------
00248 template <class T>
00249 void vpgl_perspective_camera<T>::recompute_matrix()
00250 {
00251    vnl_matrix_fixed<T,3,4> Pnew( (T)0 );
00252 
00253    // Set new projection matrix to [ I | -C ].
00254    for ( int i = 0; i < 3; i++ )
00255      Pnew(i,i) = (T)1;
00256    Pnew(0,3) = -camera_center_.x();
00257    Pnew(1,3) = -camera_center_.y();
00258    Pnew(2,3) = -camera_center_.z();
00259 
00260    // Then multiply on left to get KR[ I | -C ].
00261    this->set_matrix(K_.get_matrix()*R_.as_matrix()*Pnew);
00262 }
00263 
00264 
00265 //-------------------------------------------
00266 template <class T>
00267 bool vpgl_perspective_decomposition( const vnl_matrix_fixed<T,3,4>& camera_matrix,
00268                                      vpgl_perspective_camera<T>& p_camera )
00269 {
00270   // Extract the left sub matrix H from [ H t ] and check that it has rank 3.
00271   vnl_matrix_fixed<T,3,3> H = camera_matrix.extract(3,3);
00272   vnl_vector_fixed<T,3> t = camera_matrix.get_column(3);
00273 
00274   T det = vnl_det(H);
00275   if ( det == (T)0 ) return false;
00276   // To insure a true rotation (determinant = 1) we must start with a positive
00277   // determinant H.  This is decomposed into K and R, each with positive determinant.
00278   if ( det < (T)0 ) {
00279     H *= (T)-1;
00280     t *= (T)-1;
00281   }
00282 
00283   // Now find the RQ decomposition of the sub matrix and use these to find the params.
00284   // This will involve some trickery as VXL has no RQ decomposition, but does have QR.
00285   // Define a matrix "flipping" operation by f(A)ij = An-j,n-i i.e. f flips the matrix
00286   // about its LL-UR diagonal.  One can prove that if f(A) = B*C then A = f(A)*f(B). So
00287   // we get the RQ decomposition of A by flipping A, then taking the QR decomposition,
00288   // then flipping both back, noting that the flipped Q and R will remain orthogonal and
00289   // upper right triangular respectively.
00290   vnl_matrix_fixed<T,3,3> Hf;
00291   for ( int i = 0; i < 3; i++ )
00292     for ( int j = 0; j < 3; j++ )
00293       Hf(i,j) = H(2-j,2-i);
00294   vnl_qr<T> QR( Hf.as_ref() );
00295   vnl_matrix_fixed<T,3,3> q,r,Qf,Rf;
00296   q = QR.Q();
00297   r = QR.R();
00298   for ( int i = 0; i < 3; i++ ) {
00299     for ( int j = 0; j < 3; j++ ) {
00300       Qf(i,j) = q(2-j,2-i);
00301       Rf(i,j) = r(2-j,2-i);
00302     }
00303   }
00304 
00305   // We almost have the K and R parameter blocks, but we must be sure that the diagonal
00306   // entries of K are positive.
00307   int r0pos = Rf(0,0) > 0 ? 1 : -1;
00308   int r1pos = Rf(1,1) > 0 ? 1 : -1;
00309   int r2pos = Rf(2,2) > 0 ? 1 : -1;
00310   int diag[3] = { r0pos, r1pos, r2pos };
00311   vnl_matrix_fixed<T,3,3> K1,R1;
00312   for ( int i = 0; i < 3; i++ ) {
00313     for ( int j = 0; j < 3; j++ ) {
00314       K1(i,j) = diag[j]*Rf(i,j);
00315       R1(i,j) = diag[i]*Qf(i,j);
00316     }
00317   }
00318   K1 = K1/K1(2,2);
00319 
00320   // Now we extract the parameters from the matrices we've computed;
00321   vpgl_calibration_matrix<T> new_K( K1 );
00322   p_camera.set_calibration( new_K );
00323 
00324   vnl_qr<T> QRofH(H.as_ref()); // size 3x3
00325   vnl_vector<T> c1 = -QRofH.solve(t.as_ref());
00326   vgl_point_3d<T> new_c( c1(0), c1(1), c1(2) );
00327   p_camera.set_camera_center( new_c );
00328 
00329   p_camera.set_rotation( vgl_rotation_3d<T>(R1) );
00330 
00331   return true;
00332 }
00333 
00334 
00335 //-------------------------------------------
00336 template <class T>
00337 vpgl_perspective_camera<T> vpgl_align_down(
00338   const vpgl_perspective_camera<T>& p0,
00339   const vpgl_perspective_camera<T>& p1 )
00340 {
00341   vpgl_perspective_camera<T> new_camera;
00342   new_camera.set_calibration( p0.get_calibration() );
00343   new_camera.set_rotation( p1.get_rotation()*p0.get_rotation().inverse() );
00344   vgl_point_3d<T> a0 = p0.get_rotation()*p0.get_camera_center();
00345   vgl_point_3d<T> a1 = p0.get_rotation()*p1.get_camera_center();
00346   vgl_point_3d<T> new_camera_center(a1.x() - a0.x(),
00347                                     a1.y() - a0.y(),
00348                                     a1.z() - a0.z() );
00349   new_camera.set_camera_center( new_camera_center );
00350   return new_camera;
00351 }
00352 
00353 
00354 //-------------------------------------------
00355 template <class T>
00356 vpgl_perspective_camera<T> vpgl_align_up(
00357   const vpgl_perspective_camera<T>& p0,
00358   const vpgl_perspective_camera<T>& p1 )
00359 {
00360   vpgl_perspective_camera<T> new_camera;
00361   new_camera.set_calibration( p0.get_calibration() );
00362   new_camera.set_rotation( p1.get_rotation()*p0.get_rotation() );
00363   vgl_point_3d<T> a = p0.get_rotation().inverse()*p1.get_camera_center();
00364   vgl_point_3d<T> new_camera_center( p0.get_camera_center().x() + a.x(),
00365                                      p0.get_camera_center().y() + a.y(),
00366                                      p0.get_camera_center().z() + a.z() );
00367   new_camera.set_camera_center( new_camera_center );
00368   return new_camera;
00369 }
00370 
00371 
00372 //Post-multiply this perspective camera with a 3-d Euclidean transformation
00373 // Must check if the transformation is Euclidean, i.e. rotation matrix
00374 // and translation.   Since we can only work with the external interface
00375 // the update due to the postmultiplication is:
00376 //   K' = K
00377 //   R' = R*Re
00378 //   cc' = transpose(Re)(cc - te)
00379 // where Re and te are the rotation matrix and
00380 //  translation vector of the euclidean transform
00381 template <class T> vpgl_perspective_camera<T>
00382 vpgl_perspective_camera<T>::postmultiply( const vpgl_perspective_camera<T>& in_cam, const vgl_h_matrix_3d<T>& euclid_trans)
00383 {
00384   assert(euclid_trans.is_euclidean());
00385   const vpgl_calibration_matrix<T>& K = in_cam.get_calibration();
00386   const vgl_rotation_3d<T>& R = in_cam.get_rotation();
00387   const vgl_point_3d<T>& cc = in_cam.get_camera_center();
00388   vgl_rotation_3d<T> Re(euclid_trans.get_upper_3x3());
00389   vgl_homg_point_3d<T> t = euclid_trans.get_translation();
00390 
00391   //The transformed rotation matrix
00392   vgl_rotation_3d<T> Rp(R*Re);
00393 
00394   //must have Euclidean quantities to proceed
00395   assert(!t.ideal());
00396 
00397   //Transform the camera center
00398   //get the Euclidean components
00399   vgl_vector_3d<T> te(t.x()/t.w(), t.y()/t.w(), t.z()/t.w());
00400 
00401   //construct the transformed center
00402   vgl_point_3d<T> ccp = Re.inverse()*(cc-te);
00403 
00404   return vpgl_perspective_camera<T>(K, ccp, Rp);
00405 }
00406 
00407 template <class T>
00408 vpgl_perspective_camera<T> vpgl_perspective_camera<T>::
00409 postmultiply(const vpgl_perspective_camera<T>& camera,
00410              const vgl_rotation_3d<T>& rot, const vgl_vector_3d<T>& trans)
00411 {
00412   vgl_h_matrix_3d<T> H;
00413   H.set_identity();
00414   H.set_rotation_matrix(rot.as_matrix());
00415   H.set_translation(trans.x(), trans.y(), trans.z());
00416   return vpgl_perspective_camera<T>::postmultiply(camera, H);
00417 }
00418 
00419 // I/O :------------------------------------------------
00420 
00421 //: Write vpgl_perspective_camera to stream
00422 template <class Type>
00423 vcl_ostream&  operator<<(vcl_ostream& s,
00424                          vpgl_perspective_camera<Type> const& p)
00425 {
00426   vnl_matrix_fixed<Type, 3, 3> k = p.get_calibration().get_matrix();
00427   vgl_rotation_3d<Type> rot = p.get_rotation();
00428   vnl_matrix_fixed<Type, 3, 3> Rm = rot.as_matrix();
00429   vgl_vector_3d<Type> t = p.get_translation();
00430   s << k << '\n'
00431     << Rm << '\n'
00432     << t.x() << ' ' << t.y() << ' ' << t.z() << '\n';
00433   return s ;
00434 }
00435 
00436 //: Read camera from stream
00437 template <class Type>
00438 vcl_istream&  operator >>(vcl_istream& s,
00439                           vpgl_perspective_camera<Type>& p)
00440 {
00441   vnl_matrix_fixed<Type, 3, 3> k, Rm;
00442   vnl_vector_fixed<Type, 3> tv;
00443   s >> k;
00444   s >> Rm;
00445   s >> tv;
00446   vpgl_calibration_matrix<Type> K(k);
00447   vgl_rotation_3d<Type> rot(Rm);
00448   vgl_vector_3d<Type> t(tv[0], tv[1], tv[2]);
00449   p.set_calibration(K);
00450   p.set_rotation(rot);
00451   p.set_translation(t);
00452   return s ;
00453 }
00454 
00455 //: Save in ascii format
00456 template <class Type>
00457 void vpgl_perspective_camera<Type>::save(vcl_string cam_path)
00458 {
00459   vcl_ofstream os(cam_path.c_str());
00460   if (!os.is_open()) {
00461     vcl_cout << "unable to open output stream in vpgl_proj_camera<T>::save(.)\n";
00462     return;
00463   }
00464   os << *this << '\n';
00465   os.close();
00466 }
00467 
00468 //: Write vpgl_perspective_camera to a vrml file
00469 template <class Type>
00470 void vrml_write(vcl_ostream& str, vpgl_perspective_camera<Type> const& p, double rad)
00471 {
00472   vgl_point_3d<Type> cent =  p.get_camera_center();
00473     str << "Transform {\n"
00474         << "translation " << cent.x() << ' ' << cent.y() << ' '
00475         << ' ' << cent.z() << '\n'
00476         << "children [\n"
00477         << "Shape {\n"
00478         << " appearance Appearance{\n"
00479         << "   material Material\n"
00480         << "    {\n"
00481         << "      diffuseColor " << 1 << ' ' << 1.0 << ' ' << 0.0 << '\n'
00482         << "      transparency " << 0.0 << '\n'
00483         << "    }\n"
00484         << "  }\n"
00485         << " geometry Sphere\n"
00486         <<   "{\n"
00487         << "  radius " << rad << '\n'
00488         <<  "   }\n"
00489         <<  "  }\n"
00490         <<  " ]\n"
00491         << "}\n";
00492     vgl_vector_3d<Type> r = p.principal_axis();
00493     vcl_cout<<"principal axis :" <<r<<vcl_endl;
00494     vnl_vector_fixed<Type,3> yaxis(0.0, 1.0, 0.0), pvec(r.x(), r.y(), r.z());
00495     vgl_rotation_3d<Type> rot(yaxis, pvec);
00496     vnl_quaternion<Type> q = rot.as_quaternion();
00497 
00498     //vnl_double_3 axis = q.axis();
00499     vnl_vector_fixed<Type,3> axis = q.axis();
00500     vcl_cout<<"quaternion "<<axis<< " angle "<<q.angle()<<"\n\n";
00501     double ang = q.angle();
00502     str <<  "Transform {\n"
00503         << " translation " << cent.x()+6*rad*r.x() << ' ' << cent.y()+6*rad*r.y()
00504         << ' ' << cent.z()+6*rad*r.z() << '\n'
00505         << " rotation " << axis[0] << ' ' << axis[1] << ' ' << axis[2] << ' ' <<  ang << '\n'
00506         << "children [\n"
00507         << " Shape {\n"
00508         << " appearance Appearance{\n"
00509         << "  material Material\n"
00510         << "   {\n"
00511         << "     diffuseColor 1 0 0\n"
00512         << "     transparency 0\n"
00513         << "    }\n"
00514         << "  }\n"
00515         << " geometry Cylinder\n"
00516         << "{\n"
00517         << " radius "<<rad/3<<'\n'
00518         << " height " << 12*rad << '\n'
00519         << " }\n"
00520         << " }\n"
00521         << "]\n"
00522         << "}\n";
00523 }
00524 
00525 
00526 //: Return a list of camera's, loaded from the (name sorted) files from the given directory
00527 template <class T>
00528 vcl_vector<vpgl_perspective_camera<T> > cameras_from_directory(vcl_string dir, T)
00529 {
00530   vcl_vector<vpgl_perspective_camera<T> > camlist;
00531   if (!vul_file::is_directory(dir.c_str()) ) {
00532     vcl_cerr << "cameras_from_directory: " << dir << " is not a directory\n";
00533     return camlist;
00534   }
00535 
00536   //get all of the cam and image files, sort them
00537   vcl_string camglob=dir+"/*";
00538   vul_file_iterator file_it(camglob.c_str());
00539   vcl_vector<vcl_string> cam_files;
00540   while (file_it) {
00541     vcl_string camName(file_it());
00542     cam_files.push_back(camName);
00543     ++file_it;
00544   }
00545   vcl_sort(cam_files.begin(), cam_files.end());
00546 
00547   //take sorted lists and load from file
00548   for (vcl_vector<vcl_string>::iterator iter = cam_files.begin();
00549        iter != cam_files.end(); ++iter)
00550   {
00551     vcl_ifstream ifs(iter->c_str());
00552     vpgl_perspective_camera<T> pcam;
00553     if (!ifs.is_open()) {
00554       vcl_cerr << "Failed to open file " << *iter << '\n';
00555     }
00556     else  {
00557       ifs >> pcam;
00558       camlist.push_back(pcam);
00559     }
00560   }
00561   return camlist;
00562 }
00563 
00564 template <class T>
00565 double vpgl_persp_cam_distance( const vpgl_perspective_camera<T>& cam1, const vpgl_perspective_camera<T>& cam2)
00566 {
00567   vgl_vector_3d<T> ray1 = cam1.principal_axis();
00568   vgl_vector_3d<T> ray2 = cam2.principal_axis();
00569 
00570   vgl_rotation_3d<T> R(ray1, ray2);
00571   double trace = vnl_trace(R.as_matrix());
00572   return vcl_acos((trace-1.0)/2.0);  // dist is theta
00573 }
00574 
00575 
00576 // Code for easy instantiation.
00577 #undef vpgl_PERSPECTIVE_CAMERA_INSTANTIATE
00578 #define vpgl_PERSPECTIVE_CAMERA_INSTANTIATE(T) \
00579 template class vpgl_perspective_camera<T >; \
00580 template bool vpgl_perspective_decomposition(const vnl_matrix_fixed<T,3,4>& camera_matrix, \
00581                                              vpgl_perspective_camera<T >& p_camera ); \
00582 template vpgl_perspective_camera<T > vpgl_align_down(const vpgl_perspective_camera<T >& p0, \
00583                                                      const vpgl_perspective_camera<T >& p1 ); \
00584 template vpgl_perspective_camera<T > vpgl_align_up(const vpgl_perspective_camera<T >& p0, \
00585                                                    const vpgl_perspective_camera<T >& p1 ); \
00586 template vpgl_perspective_camera<T > postmultiply(const vpgl_perspective_camera<T >& in_cam, \
00587                                                   const vgl_h_matrix_3d<T >& euclid_trans); \
00588 template double vpgl_persp_cam_distance(const vpgl_perspective_camera<T >& cam1, \
00589                                         const vpgl_perspective_camera<T >& cam2); \
00590 template void vrml_write(vcl_ostream &s, const vpgl_perspective_camera<T >&, double rad); \
00591 template vcl_vector<vpgl_perspective_camera<T > > cameras_from_directory(vcl_string dir, T); \
00592 template vcl_ostream& operator<<(vcl_ostream&, const vpgl_perspective_camera<T >&); \
00593 template vcl_istream& operator>>(vcl_istream&, vpgl_perspective_camera<T >&)
00594 
00595 #endif // vpgl_perspective_camera_txx_