core/vpgl/algo/vpgl_rational_adjust.cxx
Go to the documentation of this file.
00001 #include "vpgl_rational_adjust.h"
00002 //:
00003 // \file
00004 
00005 #include <vcl_cmath.h>
00006 #include <vgl/vgl_plane_3d.h>
00007 #include <vgl/vgl_distance.h>
00008 #include <vgl/vgl_point_2d.h>
00009 #include <vnl/algo/vnl_levenberg_marquardt.h>
00010 #include <vpgl/algo/vpgl_backproject.h>
00011 #include <vpgl/algo/vpgl_ray_intersect.h>
00012 #define ADJUST_DEBUG
00013 
00014 vpgl_adjust_lsqr::
00015 vpgl_adjust_lsqr(vpgl_rational_camera<double>  const& rcam,
00016                  vcl_vector<vgl_point_2d<double> > const& img_pts,
00017                  vcl_vector<vgl_point_3d<double> > const& geo_pts,
00018                  unsigned num_unknowns, unsigned num_residuals)
00019   : vnl_least_squares_function(num_unknowns, num_residuals,
00020                                vnl_least_squares_function::no_gradient),
00021     rcam_(rcam), img_pts_(img_pts), geo_pts_(geo_pts)
00022 {
00023   num_corrs_ = img_pts.size();
00024 }
00025 // The virtual least-squares cost function.
00026 // The unknowns are [xscale, xoff, yscale, yoff, zscale, zoff]
00027 // The error residual vector elements are image errors for each point
00028 void vpgl_adjust_lsqr::f( const vnl_vector<double>& unknowns,
00029                           vnl_vector<double> & projection_error)
00030 {
00031   // set the scales and offsets for the rational camera
00032 #if 0
00033   rcam_.set_scale(vpgl_rational_camera<double>::X_INDX, unknowns[0]);
00034   rcam_.set_offset(vpgl_rational_camera<double>::X_INDX, unknowns[1]);
00035   rcam_.set_scale(vpgl_rational_camera<double>::Y_INDX, unknowns[2]);
00036   rcam_.set_offset(vpgl_rational_camera<double>::Y_INDX, unknowns[3]);
00037   rcam_.set_scale(vpgl_rational_camera<double>::Z_INDX, unknowns[4]);
00038   rcam_.set_offset(vpgl_rational_camera<double>::Z_INDX, unknowns[5]);
00039 
00040   vcl_cout << "x(" << unknowns[0] << ' ' << unknowns[1] << ")\n"
00041            << "y(" << unknowns[2] << ' ' << unknowns[3] << ")\n"
00042            << "z(" << unknowns[4] << ' ' << unknowns[5] << ")\n";
00043 #endif
00044   rcam_.set_offset(vpgl_rational_camera<double>::X_INDX, unknowns[0]);
00045   rcam_.set_offset(vpgl_rational_camera<double>::Y_INDX, unknowns[1]);
00046   rcam_.set_offset(vpgl_rational_camera<double>::Z_INDX, unknowns[2]);
00047 
00048   // project the geo points using the camera
00049   unsigned ir = 0;
00050   for (unsigned i = 0; i<num_corrs_; ++i)
00051   {
00052     vgl_point_2d<double> pp = rcam_.project(geo_pts_[i]);
00053     vgl_point_2d<double> c = img_pts_[i];
00054     projection_error[ir++] = (pp.x()-c.x())*(pp.x()-c.x());
00055     projection_error[ir++] = (pp.y()-c.y())*(pp.y()-c.y());
00056 
00057 #if 0
00058     projection_error[ir++] = vcl_fabs(pp.x()-c.x());
00059     projection_error[ir++] = vcl_fabs(pp.y()-c.y());
00060 
00061     vcl_cout << "perror[" << i << "](" << projection_error[ir-2] << ' '
00062              << projection_error[ir-1] << ")\n";
00063 #endif
00064   }
00065 }
00066 
00067 //This method works by projecting the image points onto a plane that is
00068 //at the average elevation of the 3-d points. zoff is set to this value.
00069 //xoff and yoff are adjusted to remove the average translation between
00070 //The backprojected image points and their actual 3-d locations.
00071 //
00072 static bool initial_offsets(vpgl_rational_camera<double> const& initial_rcam,
00073                             vcl_vector<vgl_point_2d<double> > const& img_pts,
00074                             vcl_vector<vgl_point_3d<double> > const& geo_pts,
00075                             double& xoff,
00076                             double& yoff,
00077                             double& zoff)
00078 {
00079   unsigned npts = img_pts.size();
00080   // get the average elevation
00081   zoff = 0;
00082   for (unsigned i = 0; i<npts; ++i)
00083     zoff += geo_pts[i].z();
00084   zoff /= npts;
00085   // construct a x-y plane with this elevation
00086   vgl_plane_3d<double> pl(0, 0, 1, -zoff);
00087 
00088   // an initial point for the backprojection
00089   double xo = initial_rcam.offset(vpgl_rational_camera<double>::X_INDX);
00090   double yo = initial_rcam.offset(vpgl_rational_camera<double>::Y_INDX);
00091   vgl_point_3d<double> initial_pt(xo, yo, zoff);
00092 
00093   double xshift = 0, yshift = 0;
00094   // backproject the image points onto this plane
00095   for (unsigned i = 0; i<npts; ++i)
00096   {
00097     vgl_point_3d<double> bp_pt;
00098     if (!vpgl_backproject::bproj_plane(initial_rcam,
00099                                        img_pts[i], pl,
00100                                        initial_pt, bp_pt))
00101       return false;
00102     xshift += geo_pts[i].x()- bp_pt.x();
00103     yshift += geo_pts[i].y()- bp_pt.y();
00104   }
00105   xoff = xo + xshift/npts;
00106   yoff = yo + yshift/npts;
00107   return true;
00108 }
00109 
00110 //: A function adjust the rational camera 3-d scales and offsets
00111 // Returns true if successful, else false
00112 bool vpgl_rational_adjust::
00113 adjust(vpgl_rational_camera<double> const& initial_rcam,
00114        vcl_vector<vgl_point_2d<double> > img_pts,
00115        vcl_vector<vgl_point_3d<double> > geo_pts,
00116        vpgl_rational_camera<double> & adj_rcam)
00117 {
00118   // Get initial offsets by backprojection
00119   double xoff=0, yoff=0, zoff=0;
00120   if (!initial_offsets(initial_rcam, img_pts, geo_pts, xoff, yoff, zoff))
00121     return false;
00122   unsigned num_corrs = img_pts.size();
00123   unsigned num_unknowns = 3;
00124   unsigned num_residuals = num_corrs*2;
00125   // Initialize the least squares function
00126   vpgl_adjust_lsqr lsf(initial_rcam, img_pts, geo_pts,
00127                        num_unknowns, num_residuals);
00128 
00129   // Create the Levenberg Marquardt minimizer
00130   vnl_levenberg_marquardt levmarq(lsf);
00131 #ifdef ADJUST_DEBUG
00132   levmarq.set_verbose(true);
00133   levmarq.set_trace(true);
00134 #endif
00135 #if 0
00136   // Set the x-tolerance.  When the length of the steps taken in X (variables)
00137   // are no longer than this, the minimization terminates.
00138   levmarq.set_x_tolerance(1e-5);
00139 
00140   // Set the epsilon-function.  This is the step length for FD Jacobian.
00141   levmarq.set_epsilon_function(1.0);
00142 
00143   // Set the f-tolerance.  When the successive RMS errors are less than this,
00144   // minimization terminates.
00145   levmarq.set_f_tolerance(1e-10);
00146 #endif
00147   // Set the maximum number of iterations
00148   levmarq.set_max_function_evals(10000);
00149 
00150   // Create an initial values of parameters with which to start
00151   vnl_vector<double> unknowns(num_unknowns);
00152 
00153 #if 0
00154   unknowns[0] = initial_rcam.scale(vpgl_rational_camera<double>::X_INDX);
00155   unknowns[1] = xoff;
00156   unknowns[2] = initial_rcam.scale(vpgl_rational_camera<double>::Y_INDX);
00157   unknowns[3] = yoff;
00158   unknowns[4] = initial_rcam.scale(vpgl_rational_camera<double>::Z_INDX);
00159   unknowns[5] = zoff;
00160 #endif
00161   unknowns[0] = xoff;
00162   unknowns[1] = yoff;
00163   unknowns[2] = zoff;
00164 
00165   // Minimize the error and get best correspondence vertices and translations
00166   levmarq.minimize(unknowns);
00167 
00168   // Summarize the results
00169   levmarq.diagnose_outcome();
00170 #ifdef ADJUST_DEBUG
00171   vcl_cout << "Min error of " << levmarq.get_end_error() << " at the following local minima : " << '\n';
00172 #endif
00173   // set up the camera for return
00174   adj_rcam = initial_rcam;
00175   // set the scales and offsets for the rational camera
00176 
00177   adj_rcam.set_offset(vpgl_rational_camera<double>::X_INDX, unknowns[0]);
00178   adj_rcam.set_offset(vpgl_rational_camera<double>::Y_INDX, unknowns[1]);
00179   adj_rcam.set_offset(vpgl_rational_camera<double>::Z_INDX, unknowns[2]);
00180 
00181 #ifdef ADJUST_DEBUG
00182   for (unsigned i = 0; i<num_corrs; ++i)
00183   {
00184     vgl_point_2d<double> pp = adj_rcam.project(geo_pts[i]);
00185     vgl_point_2d<double> c = img_pts[i];
00186     double d = vgl_distance<double>(c, pp);
00187     vcl_cout << "p[" << i << "]->(" << pp.x() << ' ' << pp.y() << ")\n"
00188              << "c(" << c.x() << ' ' << c.y() << "): " << d << '\n';
00189   }
00190 #endif
00191   // Return success
00192   return true;
00193 }