core/vpgl/algo/vpgl_bundle_adjust_lsqr.cxx
Go to the documentation of this file.
00001 // This is vpgl/algo/vpgl_bundle_adjust_lsqr.cxx
00002 #include "vpgl_bundle_adjust_lsqr.h"
00003 //:
00004 // \file
00005 
00006 #include <vnl/vnl_vector_ref.h>
00007 #include <vnl/vnl_double_3.h>
00008 
00009 #include <vcl_iostream.h>
00010 #include <vcl_algorithm.h>
00011 #include <vcl_cassert.h>
00012 
00013 
00014 //: Constructor
00015 vpgl_bundle_adjust_lsqr::
00016 vpgl_bundle_adjust_lsqr(unsigned int num_params_per_a,
00017                         unsigned int num_params_per_b,
00018                         unsigned int num_params_c,
00019                         const vcl_vector<vgl_point_2d<double> >& image_points,
00020                         const vcl_vector<vcl_vector<bool> >& mask)
00021  : vnl_sparse_lst_sqr_function(mask.size(),num_params_per_a,
00022                                mask[0].size(),num_params_per_b,
00023                                num_params_c,mask,2,use_gradient,use_weights),
00024    image_points_(image_points),
00025    use_covars_(false),
00026    scale2_(1.0),
00027    iteration_count_(0)
00028 {
00029 }
00030 
00031 
00032 //: Constructor
00033 //  Each image point is assigned an inverse covariance (error projector) matrix
00034 // \note image points are not homogeneous because they require finite points to
00035 //       measure projection error
00036 vpgl_bundle_adjust_lsqr::
00037 vpgl_bundle_adjust_lsqr(unsigned int num_params_per_a,
00038                         unsigned int num_params_per_b,
00039                         unsigned int num_params_c,
00040                         const vcl_vector<vgl_point_2d<double> >& image_points,
00041                         const vcl_vector<vnl_matrix<double> >& inv_covars,
00042                         const vcl_vector<vcl_vector<bool> >& mask)
00043  : vnl_sparse_lst_sqr_function(mask.size(),num_params_per_a,
00044                                mask[0].size(),num_params_per_b,
00045                                num_params_c,mask,2,use_gradient,use_weights),
00046    image_points_(image_points),
00047    use_covars_(true),
00048    scale2_(1.0),
00049    iteration_count_(0)
00050 {
00051   assert(image_points.size() == inv_covars.size());
00052   vnl_matrix<double> U(2,2,0.0);
00053   for (unsigned i=0; i<inv_covars.size(); ++i)
00054   {
00055     const vnl_matrix<double>& S = inv_covars[i];
00056     if (S(0,0) > 0.0) {
00057       U(0,0) = vcl_sqrt(S(0,0));
00058       U(0,1) = S(0,1)/U(0,0);
00059       double U11 = S(1,1)-S(0,1)*S(0,1)/S(0,0);
00060       U(1,1) = (U11>0.0)?vcl_sqrt(U11):0.0;
00061     }
00062     else if (S(1,1) > 0.0) {
00063       assert(S(0,1) == 0.0);
00064       U(0,0) = 0.0;
00065       U(0,1) = 0.0;
00066       U(1,1) = vcl_sqrt(S(1,1));
00067     }
00068     else {
00069       vcl_cout << "warning: not positive definite"<<vcl_endl;
00070       U.fill(0.0);
00071     }
00072     factored_inv_covars_.push_back(U);
00073   }
00074 }
00075 
00076 
00077 //: Compute all the reprojection errors
00078 //  Given the parameter vectors a, b, and c, compute the vector of residuals e.
00079 //  e has been sized appropriately before the call.
00080 //  The parameters in a for each camera are {wx, wy, wz, tx, ty, tz}
00081 //  where w is the Rodrigues vector of the rotation and t is the translation.
00082 //  The parameters in b for each 3D point are {px, py, pz}
00083 //  the non-homogeneous position.
00084 void
00085 vpgl_bundle_adjust_lsqr::f(vnl_vector<double> const& a,
00086                            vnl_vector<double> const& b,
00087                            vnl_vector<double> const& c,
00088                            vnl_vector<double>& e)
00089 {
00090   typedef vnl_crs_index::sparse_vector::iterator sv_itr;
00091   for (unsigned int i=0; i<number_of_a(); ++i)
00092   {
00093     //: Construct the ith camera
00094     vnl_double_3x4 Pi = param_to_cam_matrix(i,a,c);
00095 
00096     vnl_crs_index::sparse_vector row = residual_indices_.sparse_row(i);
00097     for (sv_itr r_itr=row.begin(); r_itr!=row.end(); ++r_itr)
00098     {
00099       unsigned int j = r_itr->second;
00100       unsigned int k = r_itr->first;
00101 
00102       // Construct the jth point
00103       vnl_vector_fixed<double,4> Xj = param_to_pt_vector(j,b,c);
00104 
00105       // Project jth point with the ith camera
00106       vnl_vector_fixed<double,3> xij = Pi*Xj;
00107 
00108       double* eij = e.data_block()+index_e(k);
00109       eij[0] = xij[0]/xij[2] - image_points_[k].x();
00110       eij[1] = xij[1]/xij[2] - image_points_[k].y();
00111       if (use_covars_){
00112         // multiple this error by upper triangular Sij
00113         const vnl_matrix<double>& Sij = factored_inv_covars_[k];
00114         eij[0] *= Sij(0,0);
00115         eij[0] += eij[1]*Sij(0,1);
00116         eij[1] *= Sij(1,1);
00117       }
00118     }
00119   }
00120 }
00121 
00122 
00123 //: Compute the residuals from the ith component of a and the jth component of b.
00124 //  This is not normally used because f() has a self-contained efficient implementation
00125 //  It is used for finite-differencing if the gradient is marked as unavailable
00126 void
00127 vpgl_bundle_adjust_lsqr::fij(int i, int j,
00128                              vnl_vector<double> const& ai,
00129                              vnl_vector<double> const& bj,
00130                              vnl_vector<double> const& c,
00131                              vnl_vector<double>& fij)
00132 {
00133   //: Construct the ith camera
00134   vnl_double_3x4 Pi = param_to_cam_matrix(i,ai.data_block(),c);
00135 
00136   // Construct the jth point
00137   vnl_vector_fixed<double,4> Xj = param_to_pt_vector(j,bj.data_block(),c);
00138 
00139   // Project jth point with the ith camera
00140   vnl_vector_fixed<double,3> xij = Pi*Xj;
00141 
00142   int k = residual_indices_(i,j);
00143   fij[0] = xij[0]/xij[2] - image_points_[k].x();
00144   fij[1] = xij[1]/xij[2] - image_points_[k].y();
00145   if (use_covars_){
00146     // multiple this error by upper triangular Sij
00147     const vnl_matrix<double>& Sij = factored_inv_covars_[k];
00148     fij[0] *= Sij(0,0);
00149     fij[0] += fij[1]*Sij(0,1);
00150     fij[1] *= Sij(1,1);
00151   }
00152 }
00153 
00154 
00155 //: Compute the sparse Jacobian in block form.
00156 void
00157 vpgl_bundle_adjust_lsqr::jac_blocks(vnl_vector<double> const& a,
00158                                     vnl_vector<double> const& b,
00159                                     vnl_vector<double> const& c,
00160                                     vcl_vector<vnl_matrix<double> >& A,
00161                                     vcl_vector<vnl_matrix<double> >& B,
00162                                     vcl_vector<vnl_matrix<double> >& C)
00163 {
00164   typedef vnl_crs_index::sparse_vector::iterator sv_itr;
00165   for (unsigned int i=0; i<number_of_a(); ++i)
00166   {
00167     //: Construct the ith camera
00168     vnl_double_3x4 Pi = param_to_cam_matrix(i,a,c);
00169 
00170     // This is semi const incorrect - there is no vnl_vector_ref_const
00171     const vnl_vector_ref<double> ai(number_of_params_a(i),
00172                                     const_cast<double*>(a.data_block())+index_a(i));
00173 
00174     vnl_crs_index::sparse_vector row = residual_indices_.sparse_row(i);
00175     for (sv_itr r_itr=row.begin(); r_itr!=row.end(); ++r_itr)
00176     {
00177       unsigned int j = r_itr->second;
00178       unsigned int k = r_itr->first;
00179       // This is semi const incorrect - there is no vnl_vector_ref_const
00180       const vnl_vector_ref<double> bj(number_of_params_b(j),
00181                                       const_cast<double*>(b.data_block())+index_b(j));
00182 
00183       jac_Aij(i,j,Pi,ai,bj,c,A[k]); // compute Jacobian A_ij
00184       jac_Bij(i,j,Pi,ai,bj,c,B[k]); // compute Jacobian B_ij
00185       jac_Cij(i,j,Pi,ai,bj,c,C[k]); // compute Jacobian C_ij
00186       if (use_covars_){
00187         const vnl_matrix<double>& Sij = factored_inv_covars_[k];
00188         A[k] = Sij*A[k];
00189         B[k] = Sij*B[k];
00190         C[k] = Sij*C[k];
00191       }
00192     }
00193   }
00194 }
00195 
00196 
00197 void
00198 vpgl_bundle_adjust_lsqr::compute_weight_ij(int /*i*/, int /*j*/,
00199                                            vnl_vector<double> const& /*ai*/,
00200                                            vnl_vector<double> const& /*bj*/,
00201                                            vnl_vector<double> const& /*c*/,
00202                                            vnl_vector<double> const& fij,
00203                                            double& weight)
00204 {
00205   double u2 = fij.squared_magnitude()/scale2_;
00206 
00207   // Beaton-Tukey
00208   weight = (u2 > 1.0) ? 0.0 : 1 - u2;
00209 
00210   // Cauchy
00211   //weight = vcl_sqrt(1 / (1 + u2));
00212 }
00213 
00214 
00215 //: compute the 2x3 Jacobian of camera projection with respect to point location df/dpt where $f(pt) = P*pt$
00216 void vpgl_bundle_adjust_lsqr::
00217 jac_inhomg_3d_point(vnl_double_3x4 const& P,
00218                     vnl_vector<double> const& pt,
00219                     vnl_matrix<double>& J)
00220 {
00221   double denom = P(2,0)*pt[0] + P(2,1)*pt[1] + P(2,2)*pt[2] + P(2,3);
00222   denom *= denom;
00223 
00224   double txy = P(0,0)*P(2,1) - P(0,1)*P(2,0);
00225   double txz = P(0,0)*P(2,2) - P(0,2)*P(2,0);
00226   double tyz = P(0,1)*P(2,2) - P(0,2)*P(2,1);
00227   double tx  = P(0,0)*P(2,3) - P(0,3)*P(2,0);
00228   double ty  = P(0,1)*P(2,3) - P(0,3)*P(2,1);
00229   double tz  = P(0,2)*P(2,3) - P(0,3)*P(2,2);
00230 
00231   J(0,0) = ( txy*pt[1] + txz*pt[2] + tx) / denom;
00232   J(0,1) = (-txy*pt[0] + tyz*pt[2] + ty) / denom;
00233   J(0,2) = (-txz*pt[0] - tyz*pt[1] + tz) / denom;
00234 
00235   txy = P(1,0)*P(2,1) - P(1,1)*P(2,0);
00236   txz = P(1,0)*P(2,2) - P(1,2)*P(2,0);
00237   tyz = P(1,1)*P(2,2) - P(1,2)*P(2,1);
00238   tx  = P(1,0)*P(2,3) - P(1,3)*P(2,0);
00239   ty  = P(1,1)*P(2,3) - P(1,3)*P(2,1);
00240   tz  = P(1,2)*P(2,3) - P(1,3)*P(2,2);
00241 
00242   J(1,0) = ( txy*pt[1] + txz*pt[2] + tx) / denom;
00243   J(1,1) = (-txy*pt[0] + tyz*pt[2] + ty) / denom;
00244   J(1,2) = (-txz*pt[0] - tyz*pt[1] + tz) / denom;
00245 }
00246 
00247 
00248 //: compute the 2x3 Jacobian of camera projection with respect to camera center df/dC where $f(C) = [M | -M*C]*pt$
00249 void vpgl_bundle_adjust_lsqr::jac_camera_center(vnl_double_3x3 const& M,
00250                                                 vnl_vector<double> const& C,
00251                                                 vnl_vector<double> const& pt,
00252                                                 vnl_matrix<double>& J)
00253 {
00254   // compute by swapping the role of the camera center and point position
00255   // then reused the jac_inhomg_3d_point code
00256   vnl_double_3x4 P;
00257   P.update(M);
00258   P.set_column(3,-(M*pt));
00259   jac_inhomg_3d_point(P,C,J);
00260 }
00261 
00262 
00263 //: compute the 2x3 Jacobian of camera projection with respect to camera rotation df/dr where $f(r) = K*rod_to_matrix(r)*[I | -C]*pt$
00264 //  Here r is a Rodrigues vector, K is an upper triangular calibration matrix
00265 void vpgl_bundle_adjust_lsqr::jac_camera_rotation(vnl_double_3x3 const& K,
00266                                                   vnl_vector<double> const& C,
00267                                                   vnl_vector<double> const& r,
00268                                                   vnl_vector<double> const& pt,
00269                                                   vnl_matrix<double>& J)
00270 {
00271   vnl_double_3 t(pt[0]-C[0], pt[1]-C[1], pt[2]-C[2]);
00272 
00273   const double& x = r[0];
00274   const double& y = r[1];
00275   const double& z = r[2];
00276   double x2 = x*x, y2 = y*y, z2 = z*z;
00277   double m2 = x2 + y2 + z2;
00278 
00279   // special case for the identity rotation
00280   if (m2 == 0.0)
00281   {
00282     double inv_tz2 = 1.0/(t[2]*t[2]);
00283     J(0,0) = -t[0]*t[1] * inv_tz2;
00284     J(1,0) = -1 - t[1]*t[1] * inv_tz2;
00285     J(0,1) = 1 + t[0]*t[0] * inv_tz2;
00286     J(1,1) = t[0]*t[1] * inv_tz2;
00287     J(0,2) = -t[1] / t[2];
00288     J(1,2) = t[0] / t[2];
00289   }
00290   else
00291   {
00292     double m = vcl_sqrt(m2);  // Rodrigues magnitude = rotation angle
00293     double c = vcl_cos(m);
00294     double s = vcl_sin(m);
00295 
00296     // common trig terms
00297     double ct = (1-c)/m2;
00298     double st = s/m;
00299 
00300     // derivative coefficients for common trig terms
00301     // ds = d/dx_i{st}/x_i
00302     // dc = d/dx_i{ct}/x_i
00303     double dct = s/(m*m2);
00304     double dst = c/m2 - dct;
00305     dct -=  2*(1-c)/(m2*m2);
00306 
00307     double utc = t[2]*x*z + t[1]*x*y - t[0]*(y2+z2);
00308     double uts = t[2]*y - t[1]*z;
00309     double vtc = t[0]*x*y + t[2]*y*z - t[1]*(x2+z2);
00310     double vts = t[0]*z - t[2]*x;
00311     double wtc = t[0]*x*z + t[1]*y*z - t[2]*(x2+y2);
00312     double wts = t[1]*x - t[0]*y;
00313 
00314     // projection of the point into normalized homogeneous coordinates
00315     // should be equal to inv(K)*P*[pt|1]
00316     double u = ct*utc + st*uts + t[0];
00317     double v = ct*vtc + st*vts + t[1];
00318     double w = ct*wtc + st*wts + t[2];
00319 
00320     double w2 = w*w;
00321 
00322     double dw = dct*x*wtc + ct*(t[0]*z - 2*t[2]*x)
00323               + dst*x*wts + st*t[1];
00324     J(0,0) = (w*(dct*x*utc + ct*(t[2]*z + t[1]*y)
00325                   + dst*x*uts) - u*dw)/w2;
00326     J(1,0) = (w*(dct*x*vtc + ct*(t[0]*y - 2*t[1]*x)
00327                   + dst*x*vts - st*t[2]) - v*dw)/w2;
00328 
00329     dw = dct*y*wtc + ct*(t[1]*z - 2*t[2]*y)
00330         + dst*y*wts - st*t[0];
00331     J(0,1) = (w*(dct*y*utc + ct*(t[1]*x - 2*t[0]*y)
00332                   + dst*y*uts + st*t[2]) - u*dw)/w2;
00333     J(1,1) = (w*(dct*y*vtc + ct*(t[0]*x + t[2]*z)
00334                   + dst*y*vts) - v*dw)/w2;
00335 
00336     dw = dct*z*wtc + ct*(t[0]*x + t[1]*y)
00337         + dst*z*wts;
00338     J(0,2) = (w*(dct*z*utc + ct*(t[2]*x - 2*t[0]*z)
00339                   + dst*z*uts - st*t[1]) - u*dw)/w2;
00340     J(1,2) = (w*(dct*z*vtc + ct*(t[2]*y - 2*t[1]*z)
00341                   + dst*z*vts + st*t[0]) - v*dw)/w2;
00342   }
00343 
00344   // account for the calibration matrix
00345   J(0,0) *= K(0,0);
00346   J(0,0) += J(1,0)*K(0,1);
00347   J(1,0) *= K(1,1);
00348   J(0,1) *= K(0,0);
00349   J(0,1) += J(1,1)*K(0,1);
00350   J(1,1) *= K(1,1);
00351   J(0,2) *= K(0,0);
00352   J(0,2) += J(1,2)*K(0,1);
00353   J(1,2) *= K(1,1);
00354 }
00355 
00356 
00357 //: Fast conversion of rotation from Rodrigues vector to matrix
00358 vnl_double_3x3
00359 vpgl_bundle_adjust_lsqr::rod_to_matrix(vnl_vector<double> const& r)
00360 {
00361   double x2 = r[0]*r[0], y2 = r[1]*r[1], z2 = r[2]*r[2];
00362   double m = x2 + y2 + z2;
00363   double theta = vcl_sqrt(m);
00364   double s = vcl_sin(theta) / theta;
00365   double c = (1 - vcl_cos(theta)) / m;
00366 
00367   vnl_matrix_fixed<double,3,3> R(0.0);
00368   R(0,0) = R(1,1) = R(2,2) = 1.0;
00369   if (m == 0.0)
00370     return R;
00371 
00372   R(0,0) -= (y2 + z2) * c;
00373   R(1,1) -= (x2 + z2) * c;
00374   R(2,2) -= (x2 + y2) * c;
00375   R(0,1) = R(1,0) = r[0]*r[1]*c;
00376   R(0,2) = R(2,0) = r[0]*r[2]*c;
00377   R(1,2) = R(2,1) = r[1]*r[2]*c;
00378 
00379   double t = r[0]*s;
00380   R(1,2) -= t;
00381   R(2,1) += t;
00382   t = r[1]*s;
00383   R(0,2) += t;
00384   R(2,0) -= t;
00385   t = r[2]*s;
00386   R(0,1) -= t;
00387   R(1,0) += t;
00388 
00389   return R;
00390 }
00391