core/vpgl/algo/vpgl_bundle_adjust.cxx
Go to the documentation of this file.
00001 // This is vpgl/algo/vpgl_bundle_adjust.cxx
00002 #include "vpgl_bundle_adjust.h"
00003 //:
00004 // \file
00005 
00006 
00007 #include <vnl/algo/vnl_sparse_lm.h>
00008 #include <vnl/vnl_double_3.h>
00009 
00010 #include <vgl/vgl_plane_3d.h>
00011 #include <vpgl/algo/vpgl_ba_fixed_k_lsqr.h>
00012 #include <vpgl/algo/vpgl_ba_shared_k_lsqr.h>
00013 #include <vgl/algo/vgl_rotation_3d.h>
00014 
00015 #include <vcl_fstream.h>
00016 #include <vcl_algorithm.h>
00017 
00018 
00019 vpgl_bundle_adjust::vpgl_bundle_adjust()
00020   : ba_func_(NULL),
00021     use_m_estimator_(false),
00022     m_estimator_scale_(1.0),
00023     use_gradient_(true),
00024     self_calibrate_(false),
00025     normalize_data_(true),
00026     verbose_(false),
00027     max_iterations_(1000),
00028     x_tol_(1e-8),
00029     g_tol_(1e-8),
00030     epsilon_(1e-3),
00031     start_error_(0.0),
00032     end_error_(0.0)
00033 {
00034 }
00035 
00036 vpgl_bundle_adjust::~vpgl_bundle_adjust()
00037 {
00038   delete ba_func_;
00039 }
00040 
00041 //: normalize image points to be mean centered with scale sqrt(2)
00042 //  \return parameters such that original point are recovered as (ns*x+nx, ns*y+ny)
00043 void
00044 vpgl_bundle_adjust::normalize_points(vcl_vector<vgl_point_2d<double> >& image_points,
00045                                      double& nx, double& ny, double& ns)
00046 {
00047   nx = ny = ns = 0.0;
00048   for (unsigned int i=0; i<image_points.size(); ++i)
00049   {
00050     double x = image_points[i].x();
00051     double y = image_points[i].y();
00052     nx += x;
00053     ny += y;
00054     ns += x*x + y*y;
00055   }
00056   nx /= image_points.size();
00057   ny /= image_points.size();
00058   ns /= image_points.size();
00059   ns -= nx*nx + ny*ny;
00060   ns /= 2;
00061   ns = vcl_sqrt(ns);
00062   for (unsigned int i=0; i<image_points.size(); ++i)
00063   {
00064     image_points[i].x() -= nx;
00065     image_points[i].y() -= ny;
00066     image_points[i].x() /= ns;
00067     image_points[i].y() /= ns;
00068   }
00069 }
00070 
00071 
00072 // reflect the points about a plane
00073 void
00074 vpgl_bundle_adjust::
00075 reflect_points(const vgl_plane_3d<double>& plane,
00076                vcl_vector<vgl_point_3d<double> >& points)
00077 {
00078   vgl_h_matrix_3d<double> H;
00079   H.set_reflection_plane(plane);
00080   for (unsigned int i=0; i<points.size(); ++i)
00081   {
00082     points[i] = H * vgl_homg_point_3d<double>(points[i]);
00083   }
00084 }
00085 
00086 
00087 // rotate the cameras 180 degrees around an axis
00088 void
00089 vpgl_bundle_adjust::
00090 rotate_cameras(const vgl_vector_3d<double>& axis,
00091                vcl_vector<vpgl_perspective_camera<double> >& cameras)
00092 {
00093   vnl_double_3 r(axis.x(), axis.y(), axis.z());
00094   r.normalize();
00095   r *= vnl_math::pi;
00096   vgl_rotation_3d<double> R(r);
00097   vgl_rotation_3d<double> R2(0.0, 0.0, vnl_math::pi);
00098   for (unsigned int j=0; j<cameras.size(); ++j)
00099   {
00100     vpgl_perspective_camera<double>& c = cameras[j];
00101     c.set_camera_center(R*c.get_camera_center());
00102     c.set_rotation(R2*c.get_rotation()*R);
00103   }
00104 }
00105 
00106 
00107 //: Approximately depth invert the scene.
00108 //  Apply this and re-optimize to get out of a common local minimum.
00109 //  Find the mean axis between cameras and points, mirror the points about
00110 //  a plane perpendicular to this axis, and rotate the cameras 180 degrees
00111 //  around this axis
00112 void
00113 vpgl_bundle_adjust::
00114 depth_reverse(vcl_vector<vpgl_perspective_camera<double> >& cameras,
00115               vcl_vector<vgl_point_3d<double> >& points)
00116 {
00117   vnl_double_3 pc(0.0,0.0,0.0), cc(0.0,0.0,0.0);
00118   // compute the mean of the points
00119   for (unsigned int i=0; i<points.size(); ++i)
00120   {
00121     pc += vnl_double_3(points[i].x(), points[i].y(), points[i].z());
00122   }
00123   pc /= points.size();
00124   vgl_point_3d<double> point_center(pc[0],pc[1],pc[2]);
00125 
00126   // compute the mean of the camera centers
00127   for (unsigned int j=0; j<cameras.size(); ++j)
00128   {
00129     vgl_point_3d<double> c = cameras[j].get_camera_center();
00130     cc += vnl_double_3(c.x(), c.y(), c.z());
00131   }
00132   cc /= cameras.size();
00133   vgl_point_3d<double> camera_center(cc[0],cc[1],cc[2]);
00134 
00135   // define the plane of reflection
00136   vgl_vector_3d<double> axis(camera_center-point_center);
00137   normalize(axis);
00138   vgl_plane_3d<double> reflect_plane(axis, point_center);
00139 
00140   reflect_points(reflect_plane,points);
00141   rotate_cameras(axis, cameras);
00142 }
00143 
00144 
00145 //: Bundle Adjust
00146 bool
00147 vpgl_bundle_adjust::optimize(vcl_vector<vpgl_perspective_camera<double> >& cameras,
00148                              vcl_vector<vgl_point_3d<double> >& world_points,
00149                              const vcl_vector<vgl_point_2d<double> >& image_points,
00150                              const vcl_vector<vcl_vector<bool> >& mask)
00151 {
00152   delete ba_func_;
00153 
00154   double nx=0.0, ny=0.0, ns=1.0;
00155   vcl_vector<vgl_point_2d<double> > norm_image_points(image_points);
00156   if (normalize_data_)
00157     normalize_points(norm_image_points,nx,ny,ns);
00158 
00159   // construct the bundle adjustment function
00160   if (self_calibrate_)
00161   {
00162     // Extract the camera and point parameters
00163     vpgl_ba_shared_k_lsqr::create_param_vector(cameras,a_,c_);
00164     c_[0] /= ns;
00165     b_ = vpgl_ba_shared_k_lsqr::create_param_vector(world_points);
00166     // Compute the average calibration matrix
00167     vnl_vector<double> K_vals(5,0.0);
00168     for (unsigned int i=0; i<cameras.size(); ++i){
00169       const vpgl_calibration_matrix<double>& Ki = cameras[i].get_calibration();
00170       K_vals[0] += Ki.focal_length()*Ki.x_scale();
00171       K_vals[1] += Ki.y_scale() / Ki.x_scale();
00172       K_vals[2] += Ki.principal_point().x();
00173       K_vals[3] += Ki.principal_point().y();
00174       K_vals[4] += Ki.skew();
00175     }
00176     K_vals /= cameras.size();
00177     vpgl_calibration_matrix<double> K(K_vals[0]/ns,
00178                                       vgl_point_2d<double>((K_vals[2]-nx)/ns,(K_vals[3]-ny)/ns),
00179                                       1.0,
00180                                       K_vals[1],
00181                                       K_vals[4]);
00182     ba_func_ = new vpgl_ba_shared_k_lsqr(K,norm_image_points,mask);
00183   }
00184   else
00185   {
00186     // Extract the camera and point parameters
00187     vcl_vector<vpgl_calibration_matrix<double> > K;
00188     a_ = vpgl_ba_fixed_k_lsqr::create_param_vector(cameras);
00189     b_ = vpgl_ba_fixed_k_lsqr::create_param_vector(world_points);
00190     for (unsigned int i=0; i<cameras.size(); ++i){
00191       vpgl_calibration_matrix<double> Ktmp = cameras[i].get_calibration();
00192       if (normalize_data_)
00193       {
00194         Ktmp.set_focal_length(Ktmp.focal_length()/ns);
00195         vgl_point_2d<double> pp = Ktmp.principal_point();
00196         pp.x() = (pp.x()-nx)/ns;
00197         pp.y() = (pp.y()-ny)/ns;
00198         Ktmp.set_principal_point(pp);
00199       }
00200       K.push_back(Ktmp);
00201     }
00202     ba_func_ = new vpgl_ba_fixed_k_lsqr(K,norm_image_points,mask);
00203   }
00204 
00205   // apply normalization to the scale of residuals
00206   ba_func_->set_residual_scale(m_estimator_scale_/ns);
00207 
00208   // do the bundle adjustment
00209   vnl_sparse_lm lm(*ba_func_);
00210   lm.set_trace(true);
00211   lm.set_verbose(verbose_);
00212 
00213   lm.set_max_function_evals(max_iterations_);
00214   lm.set_x_tolerance(x_tol_);
00215   lm.set_g_tolerance(g_tol_);
00216   lm.set_epsilon_function(epsilon_);
00217   if (!lm.minimize(a_,b_,c_,use_gradient_,use_m_estimator_) &&
00218       lm.get_num_iterations() < int(max_iterations_))
00219   {
00220     return false;
00221   }
00222 
00223   if (use_m_estimator_)
00224   {
00225     weights_ = vcl_vector<double>(lm.get_weights().begin(), lm.get_weights().end());
00226   }
00227   else
00228   {
00229     weights_.clear();
00230     weights_.resize(image_points.size(),1.0);
00231   }
00232 
00233   if (self_calibrate_ && verbose_)
00234     vcl_cout << "final focal length = "<<c_[0]*ns<<vcl_endl;
00235 
00236   start_error_ = lm.get_start_error()*ns;
00237   end_error_ = lm.get_end_error()*ns;
00238   num_iterations_ = lm.get_num_iterations();
00239 
00240   // Update the camera parameters
00241   for (unsigned int i=0; i<cameras.size(); ++i)
00242   {
00243     cameras[i] = ba_func_->param_to_cam(i,a_,c_);
00244     if (normalize_data_)
00245     {
00246       // undo the normalization in the camera calibration
00247       vpgl_calibration_matrix<double> K = cameras[i].get_calibration();
00248       K.set_focal_length(K.focal_length()*ns);
00249       vgl_point_2d<double> pp = K.principal_point();
00250       pp.x() = ns*pp.x() + nx;
00251       pp.y() = ns*pp.y() + ny;
00252       K.set_principal_point(pp);
00253       cameras[i].set_calibration(K);
00254     }
00255   }
00256   // Update the point locations
00257   for (unsigned int j=0; j<world_points.size(); ++j)
00258     world_points[j] = ba_func_->param_to_point(j,b_,c_);
00259 
00260   return true;
00261 }
00262 
00263 
00264 //: Write cameras and points to a file in VRML 2.0 for debugging
00265 void
00266 vpgl_bundle_adjust::write_vrml(const vcl_string& filename,
00267                                const vcl_vector<vpgl_perspective_camera<double> >& cameras,
00268                                const vcl_vector<vgl_point_3d<double> >& world_points)
00269 {
00270   vcl_ofstream os(filename.c_str());
00271   os << "#VRML V2.0 utf8\n\n";
00272 
00273   // vrml views are rotated 180 degrees around the X axis
00274   vgl_rotation_3d<double> rot180x(vnl_math::pi, 0.0, 0.0);
00275 
00276   for (unsigned int i=0; i<cameras.size(); ++i){
00277     vnl_double_3x3 K = cameras[i].get_calibration().get_matrix();
00278 
00279     vgl_rotation_3d<double> R = (rot180x*cameras[i].get_rotation()).inverse();
00280     vgl_point_3d<double> ctr = cameras[i].get_camera_center();
00281     double fov = 2.0*vcl_max(vcl_atan(K[1][2]/K[1][1]), vcl_atan(K[0][2]/K[0][0]));
00282     os  << "Viewpoint {\n"
00283         << "  position    "<< ctr.x() << ' ' << ctr.y() << ' ' << ctr.z() << '\n'
00284         << "  orientation "<< R.axis() << ' '<< R.angle() << '\n'
00285         << "  fieldOfView "<< fov << '\n'
00286         << "  description \"Camera" << i << "\"\n}\n";
00287   }
00288 
00289   os << "Shape {\n  appearance NULL\n    geometry PointSet {\n"
00290      << "      color Color { color [1 0 0] }\n      coord Coordinate{\n"
00291      << "       point[\n";
00292 
00293   for (unsigned int j=0; j<world_points.size(); ++j){
00294     os  << world_points[j].x() << ' '
00295         << world_points[j].y() << ' '
00296         << world_points[j].z() << '\n';
00297   }
00298   os << "   ]\n  }\n }\n}\n";
00299 
00300   os.close();
00301 }
00302