contrib/gel/mrc/vpgl/algo/vpgl_camera_compute.cxx
Go to the documentation of this file.
00001 // This is gel/mrc/vpgl/algo/vpgl_camera_compute.cxx
00002 #ifndef vpgl_camera_compute_cxx_
00003 #define vpgl_camera_compute_cxx_
00004 
00005 #include "vpgl_camera_compute.h"
00006 //:
00007 // \file
00008 #include <vcl_iostream.h>
00009 #include <vcl_cassert.h>
00010 #include <vcl_cstdlib.h> // for rand()
00011 #include <vcl_cmath.h>
00012 #include <vnl/vnl_numeric_traits.h>
00013 #include <vnl/vnl_det.h>
00014 #include <vnl/vnl_inverse.h>
00015 #include <vnl/vnl_vector_fixed.h>
00016 #include <vnl/vnl_double_3.h>
00017 #include <vnl/vnl_matrix_fixed.h>
00018 #include <vnl/algo/vnl_svd.h>
00019 #include <vnl/algo/vnl_qr.h>
00020 #include <vgl/algo/vgl_rotation_3d.h>
00021 #include <vgl/algo/vgl_h_matrix_2d_compute_linear.h>
00022 #include <vgl/vgl_homg_point_3d.h>
00023 #include <vpgl/algo/vpgl_ortho_procrustes.h>
00024 #include <vpgl/algo/vpgl_optimize_camera.h>
00025 #include <vgl/vgl_point_2d.h>
00026 #include <vgl/vgl_point_3d.h>
00027 #include <vgl/vgl_box_3d.h>
00028 #include <vgl/vgl_homg_point_2d.h>
00029 
00030 #include <vpgl/bgeo/bgeo_lvcs.h>
00031 
00032 //#define CAMERA_DEBUG
00033 //------------------------------------------
00034 bool
00035 vpgl_proj_camera_compute::compute(
00036   const vcl_vector< vgl_point_2d<double> >& image_pts,
00037   const vcl_vector< vgl_point_3d<double> >& world_pts,
00038   vpgl_proj_camera<double>& camera )
00039 {
00040   vcl_vector< vgl_homg_point_2d<double> > image_pts2;
00041   vcl_vector< vgl_homg_point_3d<double> > world_pts2;
00042   for (unsigned int i = 0; i < image_pts.size(); ++i)
00043     image_pts2.push_back( vgl_homg_point_2d<double>( image_pts[i] ) );
00044   for (unsigned int i = 0; i < world_pts.size(); ++i)
00045     world_pts2.push_back( vgl_homg_point_3d<double>( world_pts[i] ) );
00046   return compute( image_pts2, world_pts2, camera );
00047 }
00048 
00049 
00050 //------------------------------------------
00051 bool
00052 vpgl_proj_camera_compute::compute(
00053   const vcl_vector< vgl_homg_point_2d<double> >& image_pts,
00054   const vcl_vector< vgl_homg_point_3d<double> >& world_pts,
00055   vpgl_proj_camera<double>& camera )
00056 {
00057   unsigned int num_correspondences = image_pts.size();
00058   if ( world_pts.size() < num_correspondences ) num_correspondences = world_pts.size();
00059   assert( num_correspondences >= 6 );
00060 
00061   // Form the solution matrix.
00062   vnl_matrix<double> S( 2*num_correspondences, 12, 0);
00063   for ( unsigned i = 0; i < num_correspondences; ++i ) {
00064     S(2*i,0) = -image_pts[i].w()*world_pts[i].x();
00065     S(2*i,1) = -image_pts[i].w()*world_pts[i].y();
00066     S(2*i,2) = -image_pts[i].w()*world_pts[i].z();
00067     S(2*i,3) = -image_pts[i].w()*world_pts[i].w();
00068     S(2*i,8) = image_pts[i].x()*world_pts[i].x();
00069     S(2*i,9) = image_pts[i].x()*world_pts[i].y();
00070     S(2*i,10) = image_pts[i].x()*world_pts[i].z();
00071     S(2*i,11) = image_pts[i].x()*world_pts[i].w();
00072     S(2*i+1,4) = -image_pts[i].w()*world_pts[i].x();
00073     S(2*i+1,5) = -image_pts[i].w()*world_pts[i].y();
00074     S(2*i+1,6) = -image_pts[i].w()*world_pts[i].z();
00075 
00076     S(2*i+1,7) = -image_pts[i].w()*world_pts[i].w();
00077     S(2*i+1,8) = image_pts[i].y()*world_pts[i].x();
00078     S(2*i+1,9) = image_pts[i].y()*world_pts[i].y();
00079     S(2*i+1,10) = image_pts[i].y()*world_pts[i].z();
00080     S(2*i+1,11) = image_pts[i].y()*world_pts[i].w();
00081   }
00082   vnl_svd<double> svd( S );
00083   vnl_vector<double> c = svd.nullvector();
00084   vnl_matrix_fixed<double,3,4> cm;
00085   cm(0,0)=c(0); cm(0,1)=c(1); cm(0,2)=c(2); cm(0,3)=c(3);
00086   cm(1,0)=c(4); cm(1,1)=c(5); cm(1,2)=c(6); cm(1,3)=c(7);
00087   cm(2,0)=c(8); cm(2,1)=c(9); cm(2,2)=c(10); cm(2,3)=c(11);
00088   camera = vpgl_proj_camera<double>( cm );
00089   return true;
00090 }
00091 
00092 
00093 //------------------------------------------
00094 bool
00095 vpgl_affine_camera_compute::compute(
00096   const vcl_vector< vgl_point_2d<double> >& image_pts,
00097   const vcl_vector< vgl_point_3d<double> >& world_pts,
00098   vpgl_affine_camera<double>& camera )
00099 {
00100   assert( image_pts.size() == world_pts.size() );
00101   assert( image_pts.size() > 3 );
00102 
00103   // Form the solution matrix.
00104   vnl_matrix<double> A( world_pts.size(), 4, 1 );
00105   for (unsigned int i = 0; i < world_pts.size(); ++i) {
00106     A(i,0) = world_pts[i].x(); A(i,1) = world_pts[i].y(); A(i,2) = world_pts[i].z();
00107   }
00108   vnl_vector<double> b1( image_pts.size() );
00109   vnl_vector<double> b2( image_pts.size() );
00110   for (unsigned int i = 0; i < image_pts.size(); ++i) {
00111     b1(i) = image_pts[i].x(); b2(i) = image_pts[i].y();
00112   }
00113   vnl_matrix<double> AtA = A.transpose()*A;
00114   vnl_svd<double> svd(AtA);
00115   if ( svd.rank() < 4 ) {
00116     vcl_cerr << "vpgl_affine_camera_compute:compute() cannot compute,\n"
00117              << "    input data has insufficient rank.\n";
00118     return false;
00119   }
00120   vnl_matrix<double> S = svd.inverse()*A.transpose();
00121   vnl_vector_fixed<double,4> x1, x2;
00122   x1 = S*b1;
00123   x2 = S*b2;
00124 
00125   // Fill in the camera.
00126   camera.set_rows( x1, x2 );
00127   return true;
00128 }
00129 
00130 static vcl_vector<double>
00131 pvector(const double x, const double y, const double z)
00132 {  vcl_vector<double> pv(20);
00133   pv[0]= x*x*x;
00134   pv[1]= x*x*y;
00135   pv[2]= x*x*z;
00136   pv[3]= x*x;
00137   pv[4]= x*y*y;
00138   pv[5]= x*y*z;
00139   pv[6]= x*y;
00140   pv[7]= x*z*z;
00141   pv[8]= x*z;
00142   pv[9]= x;
00143   pv[10]= y*y*y;
00144   pv[11]= y*y*z;
00145   pv[12]= y*y;
00146   pv[13]= y*z*z;
00147   pv[14]= y*z;
00148   pv[15]= y;
00149   pv[16]= z*z*z;
00150   pv[17]= z*z;
00151   pv[18]= z;
00152   pv[19]= 1;
00153   return pv;
00154 }
00155 
00156 static vcl_vector<double>
00157 power_vector_dx(const double x, const double y, const double z)
00158 {
00159   //fill the vector
00160   vcl_vector<double> pv(20, 0.0);
00161   pv[0]= 3*x*x;
00162   pv[1]= 2*x*y;
00163   pv[2]= 2*x*z;
00164   pv[3]= 2*x;
00165   pv[4]= y*y;
00166   pv[5]= y*z;
00167   pv[6]= y;
00168   pv[7]= z*z;
00169   pv[8]= z;
00170   pv[9]= 1;
00171   return pv;
00172 }
00173 
00174 static vcl_vector<double>
00175 power_vector_dy(const double x, const double y, const double z)
00176 {
00177   //fill the vector
00178   vcl_vector<double> pv(20, 0.0);
00179   pv[1]= x*x;
00180   pv[4]= 2*x*y;
00181   pv[5]= x*z;
00182   pv[6]= x;
00183   pv[10]= 3*y*y;
00184   pv[11]= 2*y*z;
00185   pv[12]= 2*y;
00186   pv[13]= z*z;
00187   pv[14]= z;
00188   pv[15]= 1;
00189   return pv;
00190 }
00191 
00192 static vcl_vector<double>
00193 power_vector_dz(const double x, const double y, const double z)
00194 {
00195   //fill the vector
00196   vcl_vector<double> pv(20, 0.0);
00197   pv[2]= x*x;
00198   pv[5]= x*y;
00199   pv[7]= 2*x*z;
00200   pv[8]= x;
00201   pv[11]= y*y;
00202   pv[13]= 2*y*z;
00203   pv[14]= y;
00204   pv[16]= 3*z*z;
00205   pv[17]= 2*z;
00206   pv[18]= 1;
00207   return pv;
00208 }
00209 
00210 //compute the value of the polynomial
00211 static double pval(vcl_vector<double> const& pv, vcl_vector<double>const& coef)
00212 {
00213   double sum = 0;
00214   for (unsigned i = 0; i<20; ++i)
00215     sum += pv[i]*coef[i];
00216   return sum;
00217 }
00218 
00219 // Find an approximate projective camera that approximates a rational camera
00220 // at the world center.
00221 bool vpgl_proj_camera_compute::
00222 compute( vpgl_rational_camera<double> const& rat_cam,
00223          vgl_point_3d<double> const& world_center,
00224          vpgl_proj_camera<double>& camera)
00225 {
00226   double x0 = world_center.x(), y0 = world_center.y(), z0 = world_center.z();
00227   //normalized world center
00228   double nx0 =
00229     rat_cam.scl_off(vpgl_rational_camera<double>::X_INDX).normalize(x0);
00230   double ny0 =
00231     rat_cam.scl_off(vpgl_rational_camera<double>::Y_INDX).normalize(y0);
00232   double nz0 =
00233     rat_cam.scl_off(vpgl_rational_camera<double>::Z_INDX).normalize(z0);
00234 
00235   // get the rational coefficients
00236   vcl_vector<vcl_vector<double> > coeffs = rat_cam.coefficients();
00237   vcl_vector<double> neu_u = coeffs[0];
00238   vcl_vector<double> den_u = coeffs[1];
00239   vcl_vector<double> neu_v = coeffs[2];
00240   vcl_vector<double> den_v = coeffs[3];
00241 
00242   // normalize for numerical precision
00243   double nmax_u = -vnl_numeric_traits<double>::maxval;
00244   double dmax_u  = nmax_u, nmax_v = nmax_u,dmax_v = nmax_u;
00245   for (unsigned i = 0; i<20; ++i)
00246   {
00247     if (vcl_fabs(neu_u[i])>nmax_u)
00248       nmax_u=vcl_fabs(neu_u[i]);
00249     if (vcl_fabs(den_u[i])>dmax_u)
00250       dmax_u=vcl_fabs(den_u[i]);
00251     if (vcl_fabs(neu_v[i])>nmax_v)
00252       nmax_v=vcl_fabs(neu_v[i]);
00253     if (vcl_fabs(den_v[i])>dmax_v)
00254       dmax_v=vcl_fabs(den_v[i]);
00255   }
00256   //Normalize polys so that ratio of neumerator and denominator is unchanged
00257   double norm_u = nmax_u, norm_v = nmax_v;
00258   if (norm_u<dmax_u) norm_u = dmax_u;
00259   if (norm_v<dmax_v) norm_v = dmax_v;
00260   for (unsigned i = 0; i<20; ++i)
00261   {
00262     neu_u[i] /= norm_u; den_u[i] /= norm_u;
00263     neu_v[i] /= norm_v; den_v[i] /= norm_v;
00264   }
00265 
00266   // Compute linear approximations to each poly
00267   //lin_p(x, y, z)=p(0,0,0) + (dp/dx)(x-nx0) + (dp/dy)(y-ny0) + (dp/dz)(z-nz0)
00268   vcl_vector<double> pv0 = pvector(nx0,ny0,nz0);
00269   double neu_u_0 = pval(pv0,neu_u), den_u_0 = pval(pv0,den_u);
00270   double neu_v_0 = pval(pv0,neu_v), den_v_0 = pval(pv0,den_v);
00271   vcl_vector<double> pv_dx0 = power_vector_dx(nx0,ny0,nz0);
00272   double neu_u_dx0 = pval(pv_dx0,neu_u), den_u_dx0 = pval(pv_dx0,den_u);
00273   double neu_v_dx0 = pval(pv_dx0,neu_v), den_v_dx0 = pval(pv_dx0,den_v);
00274   vcl_vector<double> pv_dy0 = power_vector_dy(nx0,ny0,nz0);
00275   double neu_u_dy0 = pval(pv_dy0,neu_u), den_u_dy0 = pval(pv_dy0,den_u);
00276   double neu_v_dy0 = pval(pv_dy0,neu_v), den_v_dy0 = pval(pv_dy0,den_v);
00277   vcl_vector<double> pv_dz0 = power_vector_dz(nx0,ny0,nz0);
00278   double neu_u_dz0 = pval(pv_dz0,neu_u), den_u_dz0 = pval(pv_dz0,den_u);
00279   double neu_v_dz0 = pval(pv_dz0,neu_v), den_v_dz0 = pval(pv_dz0,den_v);
00280 
00281   //Construct the matrix to compute the center of projection
00282   vnl_matrix<double> C(4,4);
00283   C[0][0]=neu_u_dx0;   C[0][1]=neu_u_dy0; C[0][2]=neu_u_dz0; C[0][3]=neu_u_0;
00284   C[1][0]=den_u_dx0;   C[1][1]=den_u_dy0; C[1][2]=den_u_dz0; C[1][3]=den_u_0;
00285   C[2][0]=neu_v_dx0;   C[2][1]=neu_v_dy0; C[2][2]=neu_v_dz0; C[2][3]=neu_v_0;
00286   C[3][0]=den_v_dx0;   C[3][1]=den_v_dy0; C[3][2]=den_v_dz0; C[3][3]=den_v_0;
00287 
00288   vnl_svd<double> svd(C);
00289   vnl_vector<double> nv = svd.nullvector();
00290   //assume not at infinity
00291   nv/=nv[3];
00292 #if 1
00293   vcl_cout << "Center of projection\n" << nv << '\n'
00294            << "Residual\n" << C*nv << '\n';
00295 #endif
00296   //Normalize with respect to the principal plane normal (principal ray)
00297   double ndu = vcl_sqrt(den_u_dx0*den_u_dx0 + den_u_dy0*den_u_dy0 +
00298                         den_u_dz0*den_u_dz0);
00299   double ndv = vcl_sqrt(den_v_dx0*den_v_dx0 + den_v_dy0*den_v_dy0 +
00300                         den_v_dz0*den_v_dz0);
00301 
00302   // determine if the projection is affine
00303   if (ndu/vcl_fabs(den_u_0)<1.0e-10||ndv/vcl_fabs(den_v_0)<1.0e-10)
00304   {
00305     vcl_cout << "Camera is nearly affine - approximation not implemented!\n";
00306     return false;
00307   }
00308   //Construct M by joined scale factor vector
00309   vnl_matrix_fixed<double, 3, 3> M;
00310   for (unsigned i = 0; i<3; ++i)
00311   {
00312     M[0][i]=C[0][i]/ndu;
00313     M[1][i]=C[2][i]/ndv;
00314     M[2][i]=(C[1][i]/ndu + C[3][i]/ndv)/2;
00315   }
00316 #if 1
00317   vcl_cout << "M matrix\n" << M << '\n';
00318 
00319   vnl_matrix_fixed<double,3,3> Mf;
00320   for ( int i = 0; i < 3; i++ )
00321     for ( int j = 0; j < 3; j++ )
00322       Mf(i,j) = M(2-j,2-i);
00323   vnl_qr<double> QR( Mf );
00324   vnl_matrix_fixed<double,3,3> q,r,Qf,Rf, uq,ur;
00325   q = QR.Q();
00326   r = QR.R();
00327   for ( int i = 0; i < 3; i++ ){
00328     for ( int j = 0; j < 3; j++ ){
00329       Qf(i,j) = q(2-j,2-i);
00330       Rf(i,j) = r(2-j,2-i);
00331     }
00332   }
00333   vcl_cout << "Flipped Rotation\n" << Qf << '\n'
00334            << "Flipped Upper Triangular\n" << Rf << '\n';
00335   vnl_qr<double> uqr(M);
00336   uq = uqr.Q();
00337   ur = uqr.R();
00338   vcl_cout << "UnFlipped Rotation\n" << uq << '\n'
00339            << "UnFlipped Upper Triangular\n" << ur << '\n'
00340            << "Det uq " << vnl_det<double>(uq) << '\n';
00341   //Normalized denominators
00342   vnl_vector<double> c1(3), c3(3);
00343   for (unsigned i = 0; i<3; ++i){
00344     c1[i]=C[1][i]/ndu;
00345     c3[i]=C[3][i]/ndv;
00346   }
00347 
00348 
00349 vcl_cout << "Denominators\n"
00350          << "C1 " << c1
00351          << "C3 " << c3;
00352 #endif
00353   //compute p3 the fourth column of the projection matrix
00354   vnl_vector_fixed<double, 3> c;
00355   for (unsigned i = 0; i<3; ++i)
00356     c[i]=nv[i];
00357   vnl_vector_fixed<double, 3> p3 = -M*c;
00358   //Form the full projection matrix
00359   vnl_matrix_fixed<double, 3, 4> pmatrix;
00360   for (unsigned r = 0; r<3; ++r)
00361     for (unsigned c = 0; c<3; ++c)
00362       pmatrix[r][c] = M[r][c];
00363   for (unsigned r = 0; r<3; ++r)
00364     pmatrix[r][3] = p3[r];
00365   //account for the image scale and offsets
00366   double uscale = rat_cam.scale(vpgl_rational_camera<double>::U_INDX);
00367   double uoff = rat_cam.offset(vpgl_rational_camera<double>::U_INDX);
00368   double vscale = rat_cam.scale(vpgl_rational_camera<double>::V_INDX);
00369   double voff = rat_cam.offset(vpgl_rational_camera<double>::V_INDX);
00370   vnl_matrix_fixed<double, 3, 3> Kr;
00371   Kr.fill(0.0);
00372   Kr[0][0]=uscale;   Kr[0][2]=uoff;
00373   Kr[1][1]=vscale;   Kr[1][2]=voff;
00374   Kr[2][2]=1.0;
00375 #if 1
00376   vcl_cout << "Kr\n" << Kr << '\n';
00377   vnl_matrix_fixed<double,3,3> KRf, KR=Kr*uq;
00378   for ( int i = 0; i < 3; i++ )
00379     for ( int j = 0; j < 3; j++ )
00380       KRf(i,j) = KR(2-j,2-i);
00381   vnl_qr<double> krQR( KRf );
00382   vnl_matrix_fixed<double,3,3> krq,krr,krQf,krRf;
00383   krq = krQR.Q();
00384   krr = krQR.R();
00385   for ( int i = 0; i < 3; i++ ){
00386     for ( int j = 0; j < 3; j++ ){
00387       krQf(i,j) = krq(2-j,2-i);
00388       krRf(i,j) = krr(2-j,2-i);
00389     }
00390   }
00391   vcl_cout << "Flipped Rotation (KR)\n" << krQf << '\n'
00392            << "Flipped Upper Triangular (KR)\n" << krRf << '\n';
00393 
00394   int r0pos = krRf(0,0) > 0 ? 1 : -1;
00395   int r1pos = krRf(1,1) > 0 ? 1 : -1;
00396   int r2pos = krRf(2,2) > 0 ? 1 : -1;
00397   int diag[3] = { r0pos, r1pos, r2pos };
00398   vnl_matrix_fixed<double,3,3> K1,R1;
00399   for ( int i = 0; i < 3; i++ ){
00400     for ( int j = 0; j < 3; j++ ){
00401       K1(i,j) = diag[j]*krRf(i,j);
00402       R1(i,j) = diag[i]*krQf(i,j);
00403     }
00404   }
00405   K1 = K1/K1(2,2);
00406   vcl_cout << "K1\n" << K1 <<'\n'
00407            << "R1\n" << R1 << '\n'
00408            << "Det R1 " << vnl_det<double>(R1) << '\n';
00409 #endif
00410 
00411 
00412   //Need to offset x0, y0 and z0 as well.
00413     vnl_matrix_fixed<double, 4, 4> T;
00414     T.fill(0.0);
00415     T[0][0]=1.0; T[1][1]=1.0; T[2][2]=1.0; T[3][3]=1.0;
00416     T[0][3] = -nx0; T[1][3]= -ny0; T[2][3]=-nz0;
00417   pmatrix = Kr*pmatrix*T;
00418 #if 0
00419    vcl_cout << "P Matrix\n" << pmatrix << '\n';
00420 #endif
00421   camera.set_matrix(pmatrix);
00422   return true;
00423 }
00424 
00425 //:obtain a scaling transformation to normalize world geographic coordinates
00426 //The resulting values will be on the range [-1, 1]
00427 //The transform is valid anywhere the rational camera is valid
00428 vgl_h_matrix_3d<double>
00429 vpgl_proj_camera_compute::norm_trans(vpgl_rational_camera<double> const& rat_cam)
00430 {
00431   double xscale = rat_cam.scale(vpgl_rational_camera<double>::X_INDX);
00432   double xoff = rat_cam.offset(vpgl_rational_camera<double>::X_INDX);
00433   double yscale = rat_cam.scale(vpgl_rational_camera<double>::Y_INDX);
00434   double yoff = rat_cam.offset(vpgl_rational_camera<double>::Y_INDX);
00435   double zscale = rat_cam.scale(vpgl_rational_camera<double>::Z_INDX);
00436   double zoff = rat_cam.offset(vpgl_rational_camera<double>::Z_INDX);
00437   vgl_h_matrix_3d<double> T;
00438   T.set_identity();
00439   T.set(0,0,1/xscale); T.set(1,1,1/yscale); T.set(2,2,1/zscale);
00440   T.set(0,3, -xoff/xscale);   T.set(1,3, -yoff/yscale);
00441   T.set(2,3, -zoff/zscale);
00442   return T;
00443 }
00444 
00445 //Compute the rotation matrix and translation vector for a
00446 //perspective camera given world to image correspondences and
00447 //the calibration matrix
00448 bool vpgl_perspective_camera_compute::
00449 compute( const vcl_vector< vgl_point_2d<double> >& image_pts,
00450          const vcl_vector< vgl_point_3d<double> >& world_pts,
00451          const vpgl_calibration_matrix<double>& K,
00452          vpgl_perspective_camera<double>& camera )
00453 {
00454   unsigned N = world_pts.size();
00455   if (image_pts.size()!=N)
00456   {
00457     vcl_cout << "Unequal points sets in"
00458              << " vpgl_perspective_camera_compute::compute()\n";
00459     return false;
00460   }
00461   if (N<6)
00462   {
00463     vcl_cout << "Need at least 6 points for"
00464              << " vpgl_perspective_camera_compute::compute()\n";
00465     return false;
00466   }
00467 
00468   //get the inverse calibration map
00469   vnl_matrix_fixed<double, 3, 3> km = K.get_matrix();
00470   vnl_matrix_fixed<double, 3, 3> k_inv = vnl_inverse<double>(km);
00471 
00472   //Form the world point matrix
00473 
00474   //Solve for the unknown point depths (projective scale factors)
00475   vnl_matrix<double> wp(4, N);
00476   for (unsigned c = 0; c<N; ++c)
00477   {
00478     vgl_point_3d<double> p = world_pts[c];
00479     wp[0][c] = p.x(); wp[1][c] = p.y(); wp[2][c] = p.z();
00480     wp[3][c] = 1.0;
00481   }
00482 #ifdef CAMERA_DEBUG
00483   vcl_cout << "World Points\n" << wp << '\n';
00484 #endif
00485   vnl_svd<double> svd(wp);
00486   unsigned rank = svd.rank();
00487   if (rank != 4)
00488   {
00489     vcl_cout << "Insufficient rank for world point"
00490              << " matrix in vpgl_perspective_camera_compute::compute()\n";
00491     return false;
00492   }
00493   //extract the last N-4 columns of V as the null space of wp
00494   vnl_matrix<double> V = svd.V();
00495   unsigned nr = V.rows(), nc = V.columns();
00496   vnl_matrix<double> null_space(nr, nc-4);
00497   for (unsigned c = 4; c<nc; ++c)
00498     for (unsigned r = 0; r<nr; ++r)
00499       null_space[r][c-4] = V[r][c];
00500 #ifdef CAMERA_DEBUG
00501   vcl_cout << "Null Space\n" << null_space << '\n';
00502 #endif
00503    //form Kronecker product of the null space (transpose) with K inverse
00504   unsigned nrk = 3*(nc-4), nck = 3*nr;
00505   vnl_matrix<double> v2k(nrk, nck);
00506   for (unsigned r = 0; r<(nc-4); ++r)
00507     for (unsigned c = 0; c<nr; ++c)
00508       for (unsigned rk = 0; rk<3; ++rk)
00509         for (unsigned ck = 0; ck<3; ++ck)
00510           v2k[rk+3*r][ck+3*c] = k_inv[rk][ck]*null_space[c][r];
00511 #ifdef CAMERA_DEBUG
00512   vcl_cout << "V2K\n" << v2k << '\n';
00513 #endif
00514   //Stack the image points in homogeneous form in a diagonal matrix
00515   vnl_matrix<double> D(3*N, N);
00516   D.fill(0);
00517   for (unsigned c = 0; c<N; ++c)
00518   {
00519     vgl_point_2d<double> p = image_pts[c];
00520     D[3*c][c] = p.x();  D[3*c+1][c] = p.y(); D[3*c+2][c] = 1.0;
00521   }
00522 #ifdef CAMERA_DEBUG
00523   vcl_cout << "D\n" << D << '\n';
00524 #endif
00525   //form the singular matrix
00526   vnl_matrix<double> M = v2k*D;
00527   vnl_svd<double> svdm(M);
00528 
00529   //The point depth solution
00530   vnl_vector<double> depth = svdm.nullvector();
00531 
00532 #ifdef CAMERA_DEBUG
00533   vcl_cout << "depths\n" << depth << '\n';
00534 #endif
00535 
00536   //Check if depths are all approximately the same (near affine projection)
00537   double average_depth = 0;
00538   unsigned nd = depth.size();
00539   for (unsigned i = 0; i<nd; ++i)
00540     average_depth += depth[i];
00541   average_depth /= nd;
00542   double max_dev = 0;
00543   for (unsigned i = 0; i<nd; ++i)
00544   {
00545     double dev = vcl_fabs(depth[i]-average_depth);
00546     if (dev>max_dev)
00547       max_dev = dev;
00548   }
00549   double norm_max_dev = max_dev/average_depth;
00550   //if depths are nearly the same make them exactly equal
00551   //since variations are not meaningful
00552   if (norm_max_dev < 0.01)
00553     for (unsigned i = 0; i<nd; ++i)
00554       depth[i]=vcl_fabs(average_depth);
00555 
00556   //Set up point sets for ortho Procrustes
00557   vnl_matrix<double> X(3,N), Y(3,N);
00558   for (unsigned c = 0; c<N; ++c)
00559   {
00560     vgl_point_2d<double> pi = image_pts[c];
00561     vgl_point_3d<double> pw = world_pts[c];
00562     //image points are multiplied by projective scale factor (depth)
00563     X[0][c] = pi.x()*depth[c]; X[1][c] = pi.y()*depth[c]; X[2][c] = depth[c];
00564  // X[0][c] = pi.x();          X[1][c] = pi.y();          X[2][c] = 1.0;
00565     Y[0][c] = pw.x();          Y[1][c] = pw.y();          Y[2][c] = pw.z();
00566   }
00567 
00568   vpgl_ortho_procrustes op(X, Y);
00569   if (!op.compute_ok())
00570     return false;
00571 
00572   vgl_rotation_3d<double> R = op.R();
00573   vnl_matrix_fixed<double, 3, 3> rr = R.as_matrix();
00574 
00575   vnl_vector_fixed<double, 3> t = op.t();
00576 #ifdef CAMERA_DEBUG
00577   vcl_cout << "translation\n" << t << '\n'
00578            << "scale = " << op.s() << '\n'
00579            << "residual = " << op.residual_mean_sq_error() << '\n';
00580 #endif
00581 
00582   vnl_vector_fixed<double, 3> center = -(rr.transpose())*t;
00583   vgl_point_3d<double> vgl_center(center[0],center[1],center[2]);
00584   vpgl_perspective_camera<double> tcam;
00585   tcam.set_calibration(K);
00586   tcam.set_camera_center(vgl_center);
00587   tcam.set_rotation(R);
00588 
00589   //perform a final non-linear optimization
00590   vcl_vector<vgl_homg_point_3d<double> > h_world_pts;
00591   for (unsigned i = 0; i<N; ++i)
00592     h_world_pts.push_back(vgl_homg_point_3d<double>(world_pts[i]));
00593   camera = vpgl_optimize_camera::opt_orient_pos_cal(tcam, h_world_pts, image_pts, 0.00005, 20000);
00594   return true;
00595 }
00596 
00597 
00598 //: Compute from two sets of corresponding 2D points (image and ground plane).
00599 // \param ground_pts are 2D points representing world points with Z=0
00600 // The calibration matrix of \a camera is enforced
00601 // This computation is simplier than the general case above and only requires 4 points
00602 // Put the resulting camera into camera, return true if successful.
00603 bool vpgl_perspective_camera_compute::
00604 compute( const vcl_vector< vgl_point_2d<double> >& image_pts,
00605          const vcl_vector< vgl_point_2d<double> >& ground_pts,
00606                vpgl_perspective_camera<double>& camera )
00607 {
00608   unsigned num_pts = ground_pts.size();
00609   if (image_pts.size()!=num_pts)
00610   {
00611     vcl_cout << "Unequal points sets in"
00612              << " vpgl_perspective_camera_compute::compute()\n";
00613     return false;
00614   }
00615   if (num_pts<4)
00616   {
00617     vcl_cout << "Need at least 4 points for"
00618              << " vpgl_perspective_camera_compute::compute()\n";
00619     return false;
00620   }
00621 
00622   vcl_vector<vgl_homg_point_2d<double> > pi, pg;
00623   for(unsigned i=0; i<num_pts; ++i){
00624 #ifdef CAMERA_DEBUG
00625     vcl_cout << "("<<image_pts[i].x()<<", "<<image_pts[i].y()<<") -> "
00626              << "("<<ground_pts[i].x()<<", "<<ground_pts[i].y()<<")"<<vcl_endl;
00627 #endif
00628     pi.push_back(vgl_homg_point_2d<double>(image_pts[i].x(),image_pts[i].y()));
00629     pg.push_back(vgl_homg_point_2d<double>(ground_pts[i].x(),ground_pts[i].y()));
00630   }
00631 
00632   // compute a homography from the ground plane to image plane
00633   vgl_h_matrix_2d_compute_linear est_H;
00634   vnl_double_3x3 H = est_H.compute(pg,pi).get_matrix();
00635   if(vnl_det(H) > 0)
00636     H *= -1.0;
00637 
00638   // invert the effects of intrinsic parameters
00639   vnl_double_3x3 Kinv = vnl_inverse(camera.get_calibration().get_matrix());
00640   vnl_double_3x3 A(Kinv*H);
00641   // get the translation vector (up to a scale)
00642   vnl_vector_fixed<double,3> t = A.get_column(2);
00643   t.normalize();
00644 
00645   // compute the closest rotation matrix
00646   A.set_column(2, vnl_cross_3d(A.get_column(0), A.get_column(1)));
00647   vnl_svd<double> svdA(A);
00648   vnl_double_3x3 R = svdA.U()*svdA.V().conjugate_transpose();
00649 
00650   // find the point farthest from the origin
00651   int max_idx = 0;
00652   double max_dist = 0.0;
00653   for(unsigned int i=0; i < ground_pts.size(); ++i){
00654     double d = (ground_pts[i]-vgl_point_2d<double>(0,0)).length();
00655     if(d >= max_dist){
00656       max_dist = d;
00657       max_idx = i;
00658     }
00659   }
00660 
00661   // compute the unknown scale
00662   vnl_vector_fixed<double,3> i1 = Kinv*vnl_double_3(image_pts[max_idx].x(),image_pts[max_idx].y(),1.0);
00663   vnl_vector_fixed<double,3> t1 = vnl_cross_3d(i1, t);
00664   vnl_vector_fixed<double,3> p1 = vnl_cross_3d(i1, R*vnl_double_3(ground_pts[max_idx].x(),ground_pts[max_idx].y(),1.0));
00665   double s = p1.magnitude()/t1.magnitude();
00666 
00667   // compute the camera center
00668   t *= s;
00669   t = -R.transpose()*t;
00670 
00671   camera.set_rotation(vgl_rotation_3d<double>(R));
00672   camera.set_camera_center(vgl_point_3d<double>(t[0],t[1],t[2]));
00673 
00674 
00675 
00676   //perform a final non-linear optimization
00677   vcl_vector<vgl_homg_point_3d<double> > h_world_pts;
00678   for (unsigned i = 0; i<num_pts; ++i){
00679     h_world_pts.push_back(vgl_homg_point_3d<double>(ground_pts[i].x(),ground_pts[i].y(),0,1));
00680     if(camera.is_behind_camera(h_world_pts.back())){
00681       vcl_cout << "behind camera" << vcl_endl;
00682       return false;
00683     }
00684   }
00685   camera = vpgl_optimize_camera::opt_orient_pos(camera, h_world_pts, image_pts);
00686 
00687   return true;
00688 }
00689 
00690 
00691 bool vpgl_perspective_camera_compute::
00692 compute( vpgl_rational_camera<double> const& rat_cam,
00693          vgl_box_3d<double> const& approximation_volume,
00694          vpgl_perspective_camera<double>& camera,
00695          vgl_h_matrix_3d<double>& norm_trans)
00696 {
00697   vpgl_scale_offset<double> sou =
00698     rat_cam.scl_off(vpgl_rational_camera<double>::U_INDX);
00699   vpgl_scale_offset<double> sov =
00700     rat_cam.scl_off(vpgl_rational_camera<double>::V_INDX);
00701   vpgl_scale_offset<double> sox =
00702     rat_cam.scl_off(vpgl_rational_camera<double>::X_INDX);
00703   vpgl_scale_offset<double> soy =
00704     rat_cam.scl_off(vpgl_rational_camera<double>::Y_INDX);
00705   vpgl_scale_offset<double> soz =
00706     rat_cam.scl_off(vpgl_rational_camera<double>::Z_INDX);
00707   unsigned ni = static_cast<unsigned>(2*sou.scale());//# image columns
00708   unsigned nj = static_cast<unsigned>(2*sov.scale());//# image rows
00709   norm_trans.set_identity();
00710   norm_trans.set(0,0,1/sox.scale()); norm_trans.set(1,1,1/soy.scale());
00711   norm_trans.set(2,2,1/soz.scale());
00712   norm_trans.set(0,3, -sox.offset()/sox.scale());
00713   norm_trans.set(1,3, -soy.offset()/soy.scale());
00714   norm_trans.set(2,3, -soz.offset()/soz.scale());
00715 
00716   vgl_point_3d<double> minp = approximation_volume.min_point();
00717   vgl_point_3d<double> maxp = approximation_volume.max_point();
00718   double xmin = minp.x(), ymin = minp.y(), zmin = minp.z();
00719   double xrange = maxp.x()-xmin, yrange = maxp.y()-ymin,
00720     zrange = maxp.z()-zmin;
00721   if (xrange<0||yrange<0||zrange<0)
00722     return false;
00723   //Randomly generate points
00724   unsigned n = 100;
00725   vcl_vector<vgl_point_3d<double> > world_pts;
00726   unsigned count = 0, ntrials = 0;
00727   while (count<n)
00728   {
00729     ntrials++;
00730     double rx = xrange*(vcl_rand()/(RAND_MAX+1.0));
00731     double ry = yrange*(vcl_rand()/(RAND_MAX+1.0));
00732     double rz = zrange*(vcl_rand()/(RAND_MAX+1.0));
00733     vgl_point_3d<double> wp(xmin+rx, ymin+ry, zmin+rz);
00734     vgl_point_2d<double> ip = rat_cam.project(wp);
00735     if (ip.x()<0||ip.x()>ni||ip.y()<0||ip.y()>nj)
00736       continue;
00737     world_pts.push_back(wp);
00738     count++;
00739   }
00740   vcl_cout << "Ntrials " << ntrials << '\n';
00741 
00742   //Normalize world and image points to the range [-1,1]
00743   vcl_vector<vgl_point_3d<double> > norm_world_pts;
00744   vcl_vector<vgl_point_2d<double> > image_pts, norm_image_pts;
00745   unsigned N = world_pts.size();
00746   for (unsigned i = 0; i<N; ++i)
00747   {
00748     vgl_point_3d<double> wp = world_pts[i];
00749     vgl_point_2d<double> ip = rat_cam.project(wp);
00750     image_pts.push_back(ip);
00751     vgl_point_2d<double> nip(sou.normalize(ip.x()), sov.normalize(ip.y()));
00752     norm_image_pts.push_back(nip);
00753     vgl_point_3d<double> nwp(sox.normalize(wp.x()),
00754                              soy.normalize(wp.y()),
00755                              soz.normalize(wp.z()));
00756     norm_world_pts.push_back(nwp);
00757   }
00758   //Assume identity calibration matrix initially, since image point
00759   //normalization remove any scale and offset from image coordinates
00760   vnl_matrix_fixed<double, 3, 3> kk;
00761   kk.fill(0);
00762   kk[0][0]= 1.0;
00763   kk[1][1]= 1.0;
00764   kk[2][2]=1.0;
00765   //Compute solution for rotation and translation and calibration matrix of
00766   //the perspective camera
00767   vpgl_calibration_matrix<double> K(kk);
00768   bool good = vpgl_perspective_camera_compute::compute(norm_image_pts,
00769                                                        norm_world_pts,
00770                                                        K, camera);
00771   if (!good)
00772     return false;
00773   vcl_cout << camera << '\n';
00774   //form the full camera by premultiplying by the image normalization
00775   vpgl_calibration_matrix<double> Kmin = camera.get_calibration();
00776   vnl_matrix_fixed<double, 3, 3> kk_min;
00777   kk_min = Kmin.get_matrix();
00778   kk[0][0]= sou.scale(); kk[0][2]= sou.offset();
00779   kk[1][1]= sov.scale(); kk[1][2]= sov.offset();
00780   kk *= kk_min;
00781   camera.set_calibration(kk);
00782 
00783   //project the points approximated
00784   double err_max = 0, err_min = 1e10;
00785   vgl_point_3d<double> min_pt, max_pt;
00786   for (unsigned i = 0; i<N; ++i)
00787   {
00788     vgl_point_3d<double> nwp = norm_world_pts[i];
00789     double U ,V;
00790     camera.project(nwp.x(), nwp.y(), nwp.z(), U, V);
00791     vgl_point_2d<double> ip = image_pts[i];
00792     double error = vcl_sqrt((ip.x()-U)*(ip.x()-U) + (ip.y()-V)*(ip.y()-V));
00793     if ( error > err_max )
00794     {
00795       err_max = error;
00796       max_pt = world_pts[i];
00797     }
00798     if (error < err_min)
00799     {
00800       err_min = error;
00801       min_pt = world_pts[i];
00802     }
00803   }
00804   vcl_cout << "Max Error = " << err_max << " at " << max_pt << '\n'
00805            << "Min Error = " << err_min << " at " << min_pt << '\n'
00806 
00807            << "final cam\n" << camera << '\n';
00808   return true;
00809 }
00810 
00811 
00812 bool vpgl_perspective_camera_compute::
00813 compute_local( vpgl_rational_camera<double> const& rat_cam,
00814                vgl_box_3d<double> const& approximation_volume,
00815                vpgl_perspective_camera<double>& camera,
00816                vgl_h_matrix_3d<double>& norm_trans)
00817 {
00818   // Set up the geo converter.
00819   double lon_low = approximation_volume.min_x();
00820   double lon_high = approximation_volume.max_x();
00821   double lat_low = approximation_volume.min_y();
00822   double lat_high = approximation_volume.max_y();
00823   assert( lat_low < lat_high && lon_low < lon_high );
00824   bgeo_lvcs lvcs_converter( lat_low, lon_low,
00825                             .5*(approximation_volume.min_z()+approximation_volume.max_z()),
00826                             bgeo_lvcs::wgs84, bgeo_lvcs::DEG );
00827 
00828   // Get a new local bounding box.
00829   double min_lx=1e99, min_ly=1e99, min_lz=1e99, max_lx=0, max_ly=0, max_lz=0;
00830   for ( int cx = 0; cx < 2; cx++ ) {
00831     for ( int cy = 0; cy < 2; cy++ ) {
00832       for ( int cz = 0; cz < 2; cz++ ) {
00833         vgl_point_3d<double> wc(
00834           approximation_volume.min_x()*cx + approximation_volume.max_x()*(1-cx),
00835           approximation_volume.min_y()*cy + approximation_volume.max_y()*(1-cy),
00836           approximation_volume.min_z()*cz + approximation_volume.max_z()*(1-cz) );
00837         double lcx, lcy, lcz;
00838         lvcs_converter.global_to_local(
00839           wc.x(), wc.y(), wc.z(), bgeo_lvcs::wgs84, lcx, lcy, lcz );
00840         vgl_point_3d<double> wc_loc( lcx, lcy, lcz );
00841         if ( cx == 0 && cy == 0 && cz == 0 ){
00842           min_lx = wc_loc.x(); max_lx = wc_loc.x();
00843           min_ly = wc_loc.y(); max_ly = wc_loc.y();
00844           min_lz = wc_loc.z(); max_lz = wc_loc.z();
00845           continue;
00846         }
00847         if ( wc_loc.x() < min_lx ) min_lx = wc_loc.x();
00848         if ( wc_loc.y() < min_ly ) min_ly = wc_loc.y();
00849         if ( wc_loc.z() < min_lz ) min_lz = wc_loc.z();
00850         if ( wc_loc.x() > max_lx ) max_lx = wc_loc.x();
00851         if ( wc_loc.y() > max_ly ) max_ly = wc_loc.y();
00852         if ( wc_loc.z() > max_lz ) max_lz = wc_loc.z();
00853       }
00854     }
00855   }
00856   double dlx = max_lx-min_lx, dly = max_ly-min_ly, dlz = max_lz-min_lz;
00857 
00858   norm_trans.set_identity();
00859   norm_trans.set(0,0,2/dlx); norm_trans.set(1,1,2/dly); norm_trans.set(2,2,2/dlz);
00860   norm_trans.set(0,3, -1-2*min_lx/dlx );
00861   norm_trans.set(1,3, -1-2*min_ly/dly);
00862   norm_trans.set(2,3, -1-2*min_lz/dlz);
00863 
00864   vpgl_scale_offset<double> sou =
00865     rat_cam.scl_off(vpgl_rational_camera<double>::U_INDX);
00866   vpgl_scale_offset<double> sov =
00867     rat_cam.scl_off(vpgl_rational_camera<double>::V_INDX);
00868   vpgl_scale_offset<double> sox =
00869     rat_cam.scl_off(vpgl_rational_camera<double>::X_INDX);
00870   vpgl_scale_offset<double> soy =
00871     rat_cam.scl_off(vpgl_rational_camera<double>::Y_INDX);
00872   vpgl_scale_offset<double> soz =
00873     rat_cam.scl_off(vpgl_rational_camera<double>::Z_INDX);
00874   unsigned ni = static_cast<unsigned>(2*sou.scale());//# image columns
00875   unsigned nj = static_cast<unsigned>(2*sov.scale());//# image rows
00876 
00877 
00878   vgl_point_3d<double> minp = approximation_volume.min_point();
00879   vgl_point_3d<double> maxp = approximation_volume.max_point();
00880   double xmin = minp.x(), ymin = minp.y(), zmin = minp.z();
00881   double xrange = maxp.x()-xmin, yrange = maxp.y()-ymin,
00882     zrange = maxp.z()-zmin;
00883   if (xrange<0||yrange<0||zrange<0)
00884     return false;
00885   //Randomly generate points
00886   unsigned n = 100;
00887   vcl_vector<vgl_point_3d<double> > world_pts;
00888   unsigned count = 0, ntrials = 0;
00889   while (count<n)
00890   {
00891     ntrials++;
00892     double rx = xrange*(vcl_rand()/(RAND_MAX+1.0));
00893     double ry = yrange*(vcl_rand()/(RAND_MAX+1.0));
00894     double rz = zrange*(vcl_rand()/(RAND_MAX+1.0));
00895     vgl_point_3d<double> wp(xmin+rx, ymin+ry, zmin+rz);
00896 
00897     vgl_point_2d<double> ip = rat_cam.project(wp);
00898     if (ip.x()<0||ip.x()>ni||ip.y()<0||ip.y()>nj)
00899       continue;
00900     world_pts.push_back(wp);
00901     count++;
00902   }
00903   vcl_cout << "Ntrials " << ntrials << '\n';
00904 
00905   //Normalize world and image points to the range [-1,1]
00906   vcl_vector<vgl_point_3d<double> > norm_world_pts;
00907   vcl_vector<vgl_point_2d<double> > image_pts, norm_image_pts;
00908   unsigned N = world_pts.size();
00909   for (unsigned i = 0; i<N; ++i)
00910   {
00911     vgl_point_3d<double> wp = world_pts[i];
00912     vgl_point_2d<double> ip = rat_cam.project(wp);
00913     image_pts.push_back(ip);
00914     vgl_point_2d<double> nip(sou.normalize(ip.x()), sov.normalize(ip.y()));
00915     norm_image_pts.push_back(nip);
00916 
00917     // Convert to local coords.
00918     double lcx, lcy, lcz;
00919     lvcs_converter.global_to_local(
00920       wp.x(), wp.y(), wp.z(), bgeo_lvcs::wgs84, lcx, lcy, lcz );
00921     vgl_homg_point_3d<double> wp_loc( lcx, lcy, lcz );
00922 
00923     vgl_homg_point_3d<double> nwp = norm_trans*wp_loc;
00924     assert(   vcl_fabs(nwp.x()) <= 1
00925            && vcl_fabs(nwp.y()) <= 1
00926            && vcl_fabs(nwp.z()) <= 1 );
00927     norm_world_pts.push_back(vgl_point_3d<double>(nwp) );
00928   }
00929   //Assume identity calibration matrix initially, since image point
00930   //normalization remove any scale and offset from image coordinates
00931   vnl_matrix_fixed<double, 3, 3> kk;
00932   kk.fill(0);
00933   kk[0][0]= 1.0;
00934   kk[1][1]= 1.0;
00935   kk[2][2]=1.0;
00936   //Compute solution for rotation and translation and calibration matrix of
00937   //the perspective camera
00938   vpgl_calibration_matrix<double> K(kk);
00939   bool good = vpgl_perspective_camera_compute::compute(norm_image_pts,
00940                                                        norm_world_pts,
00941                                                        K, camera);
00942   if (!good)
00943     return false;
00944   vcl_cout << camera << '\n';
00945   //form the full camera by premultiplying by the image normalization
00946   vpgl_calibration_matrix<double> Kmin = camera.get_calibration();
00947   vnl_matrix_fixed<double, 3, 3> kk_min;
00948   kk_min = Kmin.get_matrix();
00949   kk[0][0]= sou.scale(); kk[0][2]= sou.offset();
00950   kk[1][1]= sov.scale(); kk[1][2]= sov.offset();
00951   kk *= kk_min;
00952   camera.set_calibration(kk);
00953 
00954   //project the points approximated
00955   double err_max = 0, err_min = 1e10;
00956   vgl_point_3d<double> min_pt, max_pt;
00957   for (unsigned i = 0; i<N; ++i)
00958   {
00959     vgl_point_3d<double> nwp = norm_world_pts[i];
00960     double U ,V;
00961     camera.project(nwp.x(), nwp.y(), nwp.z(), U, V);
00962     vgl_point_2d<double> ip = image_pts[i];
00963     double error = vcl_sqrt((ip.x()-U)*(ip.x()-U) + (ip.y()-V)*(ip.y()-V));
00964     if ( error > err_max )
00965     {
00966       err_max = error;
00967       max_pt = world_pts[i];
00968     }
00969     if (error < err_min)
00970     {
00971       err_min = error;
00972       min_pt = world_pts[i];
00973     }
00974   }
00975   vcl_cout << "Max Error = " << err_max << " at " << max_pt << '\n'
00976            << "Min Error = " << err_min << " at " << min_pt << '\n'
00977 
00978            << "final cam\n" << camera << '\n';
00979   return true;
00980 }
00981 
00982 #endif // vpgl_camera_compute_cxx_