vpgl_rational_adjust_multipt.cxx
Go to the documentation of this file.
1 #include <cmath>
2 #include <limits>
3 #include <utility>
5 //:
6 // \file
7 #include <cassert>
8 #include <vgl/vgl_point_3d.h>
14 
15 #ifdef _MSC_VER
16 # include <vcl_msvc_warnings.h>
17 #endif
18 
19 double compute_projection_error(std::vector<vpgl_rational_camera<double> > const& cams,
20  std::vector<vgl_point_2d<double> > const& corrs, vgl_point_3d<double>& intersection)
21 {
22  auto cit = cams.begin();
23  auto rit = corrs.begin();
24  double error = 0.0;
25  for (; cit!=cams.end() && rit!=corrs.end(); ++cit, ++rit)
26  {
27  vgl_point_2d<double> uvp = (*cit).project(intersection);
28  vgl_point_2d<double> uv = *rit;
29  double err = std::sqrt(std::pow(uv.x()-uvp.x(), 2.0) + std::pow(uv.y()-uvp.y(), 2));
30  error += err;
31  }
32  return error;
33 }
34 
35 double error_corr(vpgl_rational_camera<double> const& cam, vgl_point_2d<double> const& corr, vgl_point_3d<double> const& intersection)
36 {
38  return std::sqrt(std::pow(corr.x()-uvp.x(), 2.0) + std::pow(corr.y()-uvp.y(), 2));
39 }
40 
41 //: assumes an initial estimate for intersection values, only refines the intersection and computes a re-projection error
42 double re_projection_error(std::vector<vpgl_rational_camera<double> > const& cams,
43  std::vector<float> const& cam_weights,
44  std::vector<std::vector<vgl_point_2d<double> > > const& corrs, // for each 3d corr (outer vector), 2d locations for each cam (inner vector)
45  std::vector<vgl_point_3d<double> > const& intersections,
46  std::vector<vgl_point_3d<double> >& finals)
47 {
48  double error = 100000.0;
49 
50  finals.clear();
51  for (unsigned int i = 0; i < corrs.size(); ++i) {
53  if (!vpgl_rational_adjust_onept::refine_intersection_pt(cams, cam_weights, corrs[i],intersections[i], final))
54  return error;
55  finals.push_back(final);
56  }
57 
58  error = 0;
59  for (unsigned int i = 0; i < corrs.size(); ++i) {
60  error += compute_projection_error(cams, corrs[i], finals[i]);
61  }
62  return error;
63 }
64 
65 //: assumes an initial estimate for intersection values, only refines the intersection and computes a re-projection error for each corr separately
66 void re_projection_error(std::vector<vpgl_rational_camera<double> > const& cams,
67  std::vector<float> const& cam_weights,
68  std::vector<std::vector<vgl_point_2d<double> > > const& corrs, // for each 3d corr (outer vector), 2d locations for each cam (inner vector)
69  std::vector<vgl_point_3d<double> > const& intersections,
70  std::vector<vgl_point_3d<double> >& finals,
71  vnl_vector<double>& errors)
72 {
73  double error = 100000.0;
74  errors.fill(error);
75 
76  finals.clear();
77  for (unsigned int i = 0; i < corrs.size(); ++i) {
79  if (!vpgl_rational_adjust_onept::refine_intersection_pt(cams, cam_weights, corrs[i],intersections[i], final))
80  return;
81  finals.push_back(final);
82  }
83 
84  unsigned k = 0;
85  // return an error value for each cam for each corr
86  for (unsigned int i = 0; i < corrs.size(); ++i) {
87  for (unsigned int j = 0; j < cams.size(); ++j) {
88  errors[k] = error_corr(cams[j], corrs[i][j], intersections[i]);
89  k++;
90  }
91  }
92 }
93 
94 
95 void print_perm(std::vector<unsigned>& params_indices)
96 {
97  for (unsigned int params_indice : params_indices)
98  std::cout << params_indice << ' ';
99  std::cout << std::endl;
100 }
101 
102 //: to generate the permutations, always increment the one at the very end by one; if it exceeds max, then increment the one before as well, etc.
103 bool increment_perm(std::vector<unsigned>& params_indices, unsigned size)
104 {
105  if (!params_indices.size()) // no need to permute!!
106  return true;
107 
108  params_indices[params_indices.size()-1] += 1;
109  if (params_indices[params_indices.size()-1] == size) { // carry on
110  params_indices[params_indices.size()-1] = 0;
111 
112  if (params_indices.size() < 2)
113  return true; // we're done
114 
115  int current_i = (int)params_indices.size()-2;
116  while (current_i >= 0) {
117  params_indices[current_i] += 1;
118  if (params_indices[current_i] < size)
119  break;
120  if (current_i == 0)
121  return true;
122  params_indices[current_i] = 0;
123  current_i -= 1;
124  }
125  }
126  return false;
127 }
128 
129 //: performs an exhaustive search in the parameter space of the cameras.
131 adjust(std::vector<vpgl_rational_camera<double> > const& cams,
132  std::vector<float> const& cam_weights,
133  std::vector<std::vector< vgl_point_2d<double> > > const& corrs, // a vector of correspondences for each cam
134  double radius, int n, // divide radius into n intervals to generate camera translation space
135  std::vector<vgl_vector_2d<double> >& cam_translations, // output translations for each cam
136  std::vector<vgl_point_3d<double> >& intersections)
137 {
138  cam_translations.clear();
139  intersections.clear();
140  intersections.resize(corrs.size());
141  if (!cams.size() || !corrs.size() || cams.size() != corrs.size())
142  return false;
143  if (!corrs[0].size())
144  return false;
145  auto cnt_corrs_for_each_cam = (unsigned)corrs[0].size();
146  for (unsigned int i = 1; i < corrs.size(); ++i)
147  if (corrs[i].size() != cnt_corrs_for_each_cam) // there needs to be same number of corrs for each cam
148  return false;
149 
150  // turn the correspondences into the format that we'll need
151  std::vector<vgl_point_2d<double> > temp(cams.size());
152  std::vector<std::vector<vgl_point_2d<double> > > corrs_reformatted(cnt_corrs_for_each_cam, temp);
153 
154  for (unsigned int i = 0; i < cnt_corrs_for_each_cam; ++i) { // for each corr
155  for (unsigned int j = 0; j < corrs.size(); ++j) // for each cam (corr size and cams size are equal)
156  corrs_reformatted[i][j] = corrs[j][i];
157  }
158  // find the best intersections for all the correspondences using the given cameras to compute good initial estimates for z of each correspondence
159  std::vector<vgl_point_3d<double> > intersections_initial;
160  for (const auto & i : corrs_reformatted) {
162  if (!vpgl_rational_adjust_onept::find_intersection_point(cams, cam_weights, i, pt))
163  return false;
164  intersections_initial.push_back(pt);
165  }
166 
167  // search the camera translation space
168  int param_cnt = 2*(int)cams.size();
169  double increment = radius/n;
170  std::vector<double> param_values;
171  param_values.push_back(0.0);
172  for (int i = 1; i <= n; ++i) {
173  param_values.push_back(i*increment);
174  param_values.push_back(-i*increment);
175  }
176  for (double param_value : param_values)
177  std::cout << param_value << ' ';
178  std::cout << '\n';
179 
180  // now for each param go through all possible param values
181  std::vector<unsigned> params_indices(param_cnt, 0);
182  int cnt = (int)std::pow((float)param_cnt, (float)param_values.size());
183  std::cout << "will try: " << cnt << " param combinations: ";
184  std::cout.flush();
185  bool done = false;
186  double big_value = 10000000.0;
187  double min_error = big_value;
188  std::vector<unsigned> params_indices_best(params_indices);
189  while (!done) {
190  std::cout << '.';
191  std::cout.flush();
192  std::vector<vpgl_rational_camera<double> > current_cams(cams);
193  // translate current cams
194  for (unsigned int i = 0; i < current_cams.size(); ++i) {
195  double u_off,v_off;
196  current_cams[i].image_offset(u_off, v_off);
197  current_cams[i].set_image_offset(u_off + param_values[params_indices[i*2]], v_off + param_values[params_indices[i*2+1]]);
198  }
199 
200  // use the initial estimates to compute re-projection errors
201  std::vector<vgl_point_3d<double> > finals;
202  double err = re_projection_error(current_cams, cam_weights, corrs_reformatted, intersections_initial, finals);
203 
204  if (err < min_error) {
205  min_error = err;
206  params_indices_best = params_indices;
207  intersections = finals;
208  }
209  done = increment_perm(params_indices, (unsigned)param_values.size());
210  }
211  if (min_error < big_value) {
212  std::cout << " done! found global min! min error: " << min_error << '\n';
213  std::vector<vpgl_rational_camera<double> > current_cams(cams);
214  // return translations
215  std::cout << "translations for each camera:" << std::endl;
216  for (unsigned int i = 0; i < current_cams.size(); ++i) {
217  vgl_vector_2d<double> tr(param_values[params_indices_best[i*2]], param_values[params_indices_best[i*2+1]]);
218  std::cout << tr << std::endl;
219  cam_translations.push_back(tr);
220  double u_off,v_off;
221  current_cams[i].image_offset(u_off,v_off);
222  current_cams[i].set_image_offset(u_off + param_values[params_indices_best[i*2]], v_off + param_values[params_indices_best[i*2+1]]);
223  }
224  }
225  else {
226  std::cout << " done! no global min!\n";
227  return false;
228  }
229  return true;
230 }
231 
234  std::vector<float> cam_weights,
235  std::vector< std::vector<vgl_point_2d<double> > > const& image_pts, // for each 3D corr, an array of 2D corrs for each camera
236  std::vector< vgl_point_3d<double> > initial_pts)
237  : vnl_least_squares_function(2*(unsigned)cams.size(), (unsigned)(cams.size()*image_pts.size()), vnl_least_squares_function::no_gradient),
238  initial_pts_(std::move(initial_pts)),
239  cameras_(cams),
240  cam_weights_(std::move(cam_weights)),
241  corrs_(image_pts)
242 {}
243 
244 void vpgl_cam_trans_search_lsqr::f(vnl_vector<double> const& translation, // size is 2*cams.size()
245  vnl_vector<double>& projection_errors) // size is cams.size()*image_pts.size() --> compute a residual for each 3D corr point
246 {
247  // compute the new set of cameras with the current cam parameters
248  std::vector<vpgl_rational_camera<double> > current_cams(cameras_);
249  // translate current cams
250  for (unsigned int i = 0; i < current_cams.size(); ++i) {
251  double u_off,v_off;
252  current_cams[i].image_offset(u_off, v_off);
253  current_cams[i].set_image_offset(u_off + translation[i*2], v_off + translation[i*2+1]);
254  }
255  // compute the projection error for each cam for each corr
256  // use the initial estimates to compute re-projection errors
257  re_projection_error(current_cams, cam_weights_, corrs_, initial_pts_, finals_, projection_errors);
258 }
259 
261 {
262  finals = finals_;
263 }
264 
265 //: run Lev-Marq optimization to search the param space to find the best parameter setting
268  std::vector<float> const& cam_weights,
269  std::vector<std::vector< vgl_point_2d<double> > > const& corrs, // a vector of correspondences for each cam
270  std::vector<vgl_vector_2d<double> >& cam_translations, // output translations for each cam
271  std::vector<vgl_point_3d<double> >& intersections) // output 3d locations for each correspondence
272 {
273  cam_translations.clear();
274  intersections.clear();
275  intersections.resize(corrs.size());
276  if (!cams.size() || !corrs.size() || cams.size() != corrs.size())
277  return false;
278  if (!corrs[0].size())
279  return false;
280  auto cnt_corrs_for_each_cam = (unsigned)corrs[0].size();
281  for (unsigned int i = 1; i < corrs.size(); ++i)
282  if (corrs[i].size() != cnt_corrs_for_each_cam) // there needs to be same number of corrs for each cam
283  return false;
284 
285  // turn the correspondences into the format that we'll need
286  std::vector<vgl_point_2d<double> > temp(cams.size());
287  std::vector<std::vector<vgl_point_2d<double> > > corrs_reformatted(cnt_corrs_for_each_cam, temp);
288 
289  for (unsigned int i = 0; i < cnt_corrs_for_each_cam; ++i) { // for each corr
290  for (unsigned int j = 0; j < corrs.size(); ++j) // for each cam (corr size and cams size are equal)
291  corrs_reformatted[i][j] = corrs[j][i];
292  }
293  // find the best intersections for all the correspondences using the given cameras to compute good initial estimates for z of each correspondence
294  std::vector<vgl_point_3d<double> > intersections_initial;
295  for (const auto & i : corrs_reformatted) {
297  if (!vpgl_rational_adjust_onept::find_intersection_point(cams, cam_weights, i, pt))
298  return false;
299  intersections_initial.push_back(pt);
300  }
301  // now refine those using Lev_Marqs
302  for (unsigned int i = 0; i < corrs_reformatted.size(); ++i) {
303  vgl_point_3d<double> final;
304  if (!vpgl_rational_adjust_onept::refine_intersection_pt(cams, cam_weights, corrs_reformatted[i],intersections_initial[i], final))
305  return false;
306  intersections_initial[i] = final;
307  }
308  for (const auto & i : intersections_initial)
309  std::cout << "before adjustment initial 3D intersection point: " << i << std::endl;
310 
311  // search the camera translation space using Lev-Marq
312  vpgl_cam_trans_search_lsqr transsf(cams, cam_weights, corrs_reformatted, intersections_initial);
313  vnl_levenberg_marquardt levmarq(transsf);
314  levmarq.set_verbose(true);
315  // Set the x-tolerance. When the length of the steps taken in X (variables)
316  // are no longer than this, the minimization terminates.
317  levmarq.set_x_tolerance(1e-10);
318  // Set the epsilon-function. This is the step length for FD Jacobian.
319  levmarq.set_epsilon_function(0.01);
320  // Set the f-tolerance. When the successive RMS errors are less than this,
321  // minimization terminates.
322  levmarq.set_f_tolerance(1e-15);
323  // Set the maximum number of iterations
324  levmarq.set_max_function_evals(10000);
325  vnl_vector<double> translations(2*(unsigned)cams.size(), 0.0);
326  std::cout << "Minimization x epsilon: " << levmarq.get_f_tolerance() << std::endl;
327 
328  // Minimize the error and get the best intersection point
329  levmarq.minimize(translations);
330  levmarq.diagnose_outcome();
331  transsf.get_finals(intersections);
332  std::cout << "final translations:" << std::endl;
333  for (unsigned int i = 0; i < cams.size(); ++i) {
334  // if cam weight is 1 for one of them, it is a special case, then pass 0 as translation for that one, cause projections still cause a tiny translation, ignore that
335  vgl_vector_2d<double> trans(0.0, 0.0);
336  if (cam_weights[i] == 1.0f) {
337  cam_translations.push_back(trans);
338  } else {
339  trans.set(translations[2*i], translations[2*i+1]);
340  cam_translations.push_back(trans);
341  }
342  // sanity check
343  if (std::abs(trans.x()) > 200 || std::abs(trans.y()) > 200) {
344  std::cerr << " trans: " << trans << " failed sanity check! returning false!\n";
345  return false;
346  }
347  std::cout << trans << '\n';
348  }
349  return true;
350 }
351 
352 // run Lev-Marq optimization to search the param spae to find the best parameter setting
353 bool vpgl_rational_adjust_multiple_pts::adjust_lev_marq(std::vector<vpgl_rational_camera<double> > const& cams, // cameras that will be corrected
354  std::vector<float> const& cam_weights, // camera weight parameters
355  std::vector<std::vector<vgl_point_2d<double> > > const& corrs, // a vector of correspondences for each cam
356  vgl_point_3d<double> const& initial_pt, // initial 3-d point for back-projection
357  double const& zmin, // minimum allowed height of the 3-d intersection point
358  double const& zmax, // maximum allowed height of the 3-d intersection point
359  std::vector<vgl_vector_2d<double> >& cam_translations, // output translations for each camera
360  std::vector<vgl_point_3d<double> >& intersections, // output 3-d locations for each correspondence
361  double const relative_diameter)
362 {
363  cam_translations.clear();
364  intersections.clear();
365  intersections.clear();
366  intersections.resize(corrs.size());
367  if (!cams.size() || !corrs.size() || cams.size() != corrs.size())
368  return false;
369  if (!corrs[0].size())
370  return false;
371  auto cnt_corrs_for_each_cam = (unsigned)corrs[0].size();
372  for (unsigned i = 1; i < corrs.size(); ++i)
373  if (corrs[i].size() != cnt_corrs_for_each_cam)
374  return false;
375 
376  // reformat the correspondences array
377  std::vector<vgl_point_2d<double> > temp(cams.size());
378  std::vector<std::vector<vgl_point_2d<double> > > corrs_reformatted(cnt_corrs_for_each_cam, temp);
379 
380  for (unsigned int i = 0; i < cnt_corrs_for_each_cam; ++i) { // for each corr
381  for (unsigned int j = 0; j < corrs.size(); ++j) // for each cam (corr size and cams size are equal)
382  corrs_reformatted[i][j] = corrs[j][i];
383  }
384  // find the best 3-d intersections for all the correspondences using the given cameras to compute good initial estimates for z of each correspondence
385  std::vector<vgl_point_3d<double> > intersections_initial;
386  for (const auto & i : corrs_reformatted) {
388  if (!vpgl_rational_adjust_onept::find_intersection_point(cams, cam_weights, i, initial_pt, zmin, zmax, pt, relative_diameter))
389  return false;
390  intersections_initial.push_back(pt);
391  }
392 
393  // now refine the intersections using Lev_Marqs
394  for (unsigned i = 0; i < corrs_reformatted.size(); i++) {
395  vgl_point_3d<double> final;
396  if (!vpgl_rational_adjust_onept::refine_intersection_pt(cams, cam_weights, corrs_reformatted[i],intersections_initial[i], final, relative_diameter))
397  return false;
398  intersections_initial[i] = final;
399  }
400  for (const auto & i : intersections_initial)
401  std::cout << "before adjustment initial 3D intersection point: " << i << std::endl;
402 
403  // search the camera translation space using Lev-Marq
404  vpgl_cam_trans_search_lsqr transsf(cams, cam_weights, corrs_reformatted, intersections_initial);
405  vnl_levenberg_marquardt levmarq(transsf);
406  levmarq.set_verbose(true);
407  // Set the x-tolerance. Minimization terminates when the length of the steps taken in X (variables) are less than input x-tolerance
408  levmarq.set_x_tolerance(1e-10);
409  // Set the epsilon-function. This is the step length for FD Jacobian
410  levmarq.set_epsilon_function(0.01);
411  // Set the f-tolerance. Minimization terminates when the successive RSM errors are less then this
412  levmarq.set_f_tolerance(1e-15);
413  // Set the maximum number of iterations
414  levmarq.set_max_function_evals(10000);
415  vnl_vector<double> translations(2*(unsigned)cams.size(), 0.0);
416  std::cout << "Minimization x epsilon: " << levmarq.get_f_tolerance() << std::endl;
417 
418  // Minimize the error and get the best intersection point
419  levmarq.minimize(translations);
420  levmarq.diagnose_outcome();
421  transsf.get_finals(intersections);
422  std::cout << "final translations:" << std::endl;
423  for (unsigned i = 0; i < cams.size(); i++) {
424  // if cam weight is 1 for one of them, it is a special case, then pass 0 as translation for that one, cause projections still cause a tiny translation, ignore that
425  vgl_vector_2d<double> trans(0.0, 0.0);
426  if (cam_weights[i] == 1.0f) {
427  cam_translations.push_back(trans);
428  }
429  else {
430  trans.set(translations[2*i], translations[2*i+1]);
431  cam_translations.push_back(trans);
432  }
433  if (std::abs(trans.x()) > 200 || std::abs(trans.y()) > 200) {
434  std::cerr << " trans: " << trans << " failed sanity check! returning false!\n";
435  return false;
436  }
437  std::cout << trans << '\n';
438  }
439  return true;
440 }
void set_f_tolerance(double v)
vgl_homg_point_3d< Type > intersection(l const &l1, l const &l2)
This algorithm finds a set of minimum translations that registers the input set of images using multi...
static bool adjust_lev_marq(std::vector< vpgl_rational_camera< double > > const &cams, std::vector< float > const &cam_weights, std::vector< std::vector< vgl_point_2d< double > > > const &corrs, std::vector< vgl_vector_2d< double > > &cam_translations, std::vector< vgl_point_3d< double > > &intersections)
run Lev-Marq optimization to search the param space to find the best parameter setting.
static bool adjust(std::vector< vpgl_rational_camera< double > > const &cams, std::vector< float > const &cam_weights, std::vector< std::vector< vgl_point_2d< double > > > const &corrs, double radius, int n, std::vector< vgl_vector_2d< double > > &cam_translations, std::vector< vgl_point_3d< double > > &intersections)
exhaustively searches the parameter space to find the best parameter setting.
T y() const
void set_verbose(bool verb)
void set(T vx, T vy)
void get_finals(std::vector< vgl_point_3d< double > > &finals)
std::vector< std::vector< vgl_point_2d< double > > > corrs_
void f(vnl_vector< double > const &translation, vnl_vector< double > &projection_errors) override
The main function.
double get_f_tolerance() const
Type & y()
double compute_projection_error(std::vector< vpgl_rational_camera< double > > const &cams, std::vector< vgl_point_2d< double > > const &corrs, vgl_point_3d< double > &intersection)
void clear()
void set_max_function_evals(int v)
vnl_vector & fill(double const &v)
void increment(vnl_bignum &bnum)
static bool find_intersection_point(std::vector< vpgl_rational_camera< double > > const &cams, std::vector< float > const &cam_weights, std::vector< vgl_point_2d< double > > const &corrs, vgl_point_3d< double > &p_3d)
Find the 3-d point closest to the intersection of rays from >= 2 cameras.
Adjust image offsets to register a set of rational cameras.
bool increment_perm(std::vector< unsigned > &params_indices, unsigned size)
to generate the permutations, always increment the one at the very end by one; if it exceeds max,...
void set_epsilon_function(double v)
double error_corr(vpgl_rational_camera< double > const &cam, vgl_point_2d< double > const &corr, vgl_point_3d< double > const &intersection)
void diagnose_outcome() const
T x() const
void print_perm(std::vector< unsigned > &params_indices)
static bool refine_intersection_pt(std::vector< vpgl_rational_camera< double > > const &cams, std::vector< float > const &cam_weights, std::vector< vgl_point_2d< double > > const &image_pts, vgl_point_3d< double > const &initial_pt, vgl_point_3d< double > &final_pt, double const &relative_diameter=1.0)
std::vector< vgl_point_3d< double > > finals_
std::vector< vgl_point_3d< double > > initial_pts_
std::vector< vpgl_rational_camera< double > > cameras_
bool minimize(vnl_vector< double > &x)
double re_projection_error(std::vector< vpgl_rational_camera< double > > const &cams, std::vector< float > const &cam_weights, std::vector< std::vector< vgl_point_2d< double > > > const &corrs, std::vector< vgl_point_3d< double > > const &intersections, std::vector< vgl_point_3d< double > > &finals)
assumes an initial estimate for intersection values, only refines the intersection and computes a re-...
Methods for back_projecting from cameras to 3-d geometric structures.
Adjust image offsets to register a set of rational cameras using multiple correspondence points.
Type & x()
void project(const T x, const T y, const T z, T &u, T &v) const override
The generic camera interface. u represents image column, v image row.
void set_x_tolerance(double v)