vpgl_rational_adjust_onept.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_plane_3d.h>
9 #include <vgl/vgl_point_3d.h>
10 #include <vnl/vnl_numeric_traits.h>
14 
15 #ifdef _MSC_VER
16 # include <vcl_msvc_warnings.h>
17 #endif
18 
19 //#define TRANS_ONE_DEBUG
20 
21 static const double vpgl_trans_z_step = 2.0;//meters
22 
23 #if 0 // replace with weighted version
24 static double
25 scatter_var(std::vector<vpgl_rational_camera<double> > const& cams,
26  std::vector<vgl_point_2d<double> > const& image_pts,
27  vgl_point_3d<double> const& initial_pt,
28  double elevation, double& xm, double& ym)
29 {
30  unsigned int n = cams.size();
31  vgl_plane_3d<double> pl(0, 0, 1, -elevation);
32  double xsq = 0, ysq = 0;
33  xm = 0; ym = 0;
34  for (unsigned int i = 0; i<n; ++i)
35  {
37  if (!vpgl_backproject::bproj_plane(cams[i],
38  image_pts[i], pl,
39  initial_pt, pb_pt))
41  double x = pb_pt.x(), y = pb_pt.y();
42  xm+=x; ym +=y;
43  xsq+=x*x; ysq+=y*y;
44  }
45  xm/=n; ym/=n;
46  double xvar = xsq-(n*xm*xm);
47  double yvar = ysq-(n*ym*ym);
48  xvar/=n; yvar/=n;
49  double var = std::sqrt(xvar*xvar + yvar*yvar);
50  return var;
51 }
52 #endif
53 // do not use weights in calculating scatter, cause we want to minimize the scatter of all observations around a 'weighted' mean
54 static double
55 scatter_var(std::vector<vpgl_rational_camera<double> > const& cams,
56  std::vector<float> const& cam_weights,
57  std::vector<vgl_point_2d<double> > const& image_pts,
58  vgl_point_3d<double> const& initial_pt,
59  double elevation, double& xm, double& ym,
60  double const& relative_diameter = 1.0)
61 {
62  unsigned int n = cams.size();
63  vgl_plane_3d<double> pl(0, 0, 1, -elevation);
64  double xsq = 0, ysq = 0;
65  xm = 0; ym = 0;
66  std::vector<vgl_point_3d<double> > pb_pts;
67  for (unsigned int i = 0; i<n; ++i)
68  {
69  // no need to perform back-projection for zero weight camera
70  if (cam_weights[i] == 0)
71  {
72  pb_pts.emplace_back(0,0,0);
73  continue;
74  }
76  if (!vpgl_backproject::bproj_plane(cams[i],image_pts[i], pl, initial_pt, pb_pt, 0.05, relative_diameter))
77  {
79  }
80  pb_pts.push_back(pb_pt);
81  }
82  double weight_sum = 0.0;
83  for (unsigned i = 0; i < n; i++)
84  {
85  double x = pb_pts[i].x(), y = pb_pts[i].y();
86  xm+=cam_weights[i]*x; ym +=cam_weights[i]*y;
87  weight_sum += cam_weights[i];
88  }
89  xm /= weight_sum;
90  ym /= weight_sum;
91  for (unsigned i = 0; i < n; i++)
92  {
93  double x = pb_pts[i].x(), y = pb_pts[i].y();
94  xsq+=cam_weights[i]*(x-xm)*(x-xm); ysq+=cam_weights[i]*(y-ym)*(y-ym);
95  }
96  //xsq/=n; ysq/=n;
97  xsq /= weight_sum; ysq /= weight_sum;
98  double var = std::sqrt(xsq*xsq + ysq*ysq);
99  return var;
100 }
101 
102 
103 
106  std::vector<float> cam_weights,
107  std::vector<vgl_point_2d<double> > image_pts,
108  vgl_point_3d<double> const& initial_pt,
109  double const& relative_diameter)
111  vnl_least_squares_function::no_gradient ),
112  initial_pt_(initial_pt),
113  cameras_(std::move(cams)),
114  cam_weights_(std::move(cam_weights)),
115  image_pts_(std::move(image_pts)),
116  xm_(0), ym_(0),
117  relative_diameter_(relative_diameter)
118 {}
119 
121  vnl_vector<double>& variance)
122 {
123  variance[0] = scatter_var(cameras_, cam_weights_, image_pts_,initial_pt_, elevation[0], xm_, ym_, relative_diameter_);
124 }
125 
128  std::vector<float> const& cam_weights,
129  std::vector<vgl_point_2d<double> > const& corrs,
130  vgl_point_3d<double>& p_3d)
131 {
132  unsigned int n = cams.size();
133  if (!n || n!=corrs.size())
134  return false;
135  //the average view volume center
136  double x0=0, y0=0;
137  // Get the lower bound on elevation range from the cameras
138  double zmax = vnl_numeric_traits<double>::maxval, zmin = -zmax;
139  for (const auto & cam : cams)
140  {
141  x0+=cam.offset(vpgl_rational_camera<double>::X_INDX);
142  y0+=cam.offset(vpgl_rational_camera<double>::Y_INDX);
143 
144  double zoff = cam.offset(vpgl_rational_camera<double>::Z_INDX);
145  double zscale = cam.scale(vpgl_rational_camera<double>::Z_INDX);
146  double zplus = zoff+zscale;
147  double zminus = zoff-zscale;
148  if (zminus>zmin) zmin = zminus;
149  if (zplus<zmax) zmax = zplus;
150  }
151  assert(zmin<=zmax);
152  x0/=n; y0/=n;
153 
154  double error = vnl_numeric_traits<double>::maxval;
155  vgl_point_3d<double> initial_point(x0, y0, zmin);
156  double xopt=0, yopt=0, zopt = zmin;
157  for (double z = zmin; z<=zmax; z+=vpgl_trans_z_step)
158  {
159  double xm = 0, ym = 0;
160  //double var = scatter_var(cams, corrs,initial_point, z, xm, ym);
161  double var = scatter_var(cams, cam_weights, corrs,initial_point, z, xm, ym);
162  if (var<error)
163  {
164  error = var;
165  xopt = xm;
166  yopt = ym;
167  zopt = z;
168  }
169  initial_point.set(xm, ym, z);
170 #ifdef TRANS_ONE_DEBUG
171  std::cout << z << '\t' << var << '\n';
172 #endif
173  }
174  // at this point the best common intersection point is known.
175  // do some sanity checks
176  if (zopt == zmin||zopt == zmax)
177  return false;
178  p_3d.set(xopt, yopt, zopt);
179  return true;
180 }
181 
184  std::vector<float> const& cam_weights,
185  std::vector<vgl_point_2d<double> > const& corrs,
186  vgl_point_3d<double> const& initial_pt,
187  double const& zmin,
188  double const& zmax,
189  vgl_point_3d<double> & p_3d,
190  double const& relative_diameter)
191 {
192  auto n = static_cast<unsigned int>(cams.size());
193  if (!n || n != corrs.size())
194  return false;
195  // define the iteration layer along z
196  double min_z = zmin - vpgl_trans_z_step;
197  double max_z = zmax + vpgl_trans_z_step;
198  if (min_z > initial_pt.z()) min_z = initial_pt.z() - vpgl_trans_z_step;
199  if (max_z < initial_pt.z()) max_z = initial_pt.z() + vpgl_trans_z_step;
200  double error = vnl_numeric_traits<double>::maxval;
201  vgl_point_3d<double> initial_point;
202  initial_point.set(initial_pt.x(), initial_pt.y(), min_z);
203  double xopt=0, yopt=0, zopt = min_z;
204  for (double z = min_z; z <= max_z; z += vpgl_trans_z_step)
205  {
206  double xm = 0, ym = 0;
207  double var = scatter_var(cams, cam_weights, corrs, initial_point, z, xm, ym, relative_diameter);
208  if (var < error)
209  {
210  error = var;
211  xopt = xm;
212  yopt = ym;
213  zopt = z;
214  }
215  initial_point.set(xm, ym, z);
216 #ifdef TRANS_ONE_DEBUG
217  std::cout << z << '\t' << var << '\t' << initial_point << '\n';
218 #endif
219  }
220  // at this point the bset common intersection point is known
221  // do some sanity checks
222  if (zopt == min_z || zopt == max_z)
223  return false;
224  p_3d.set(xopt, yopt, zopt);
225  return true;
226 }
227 
230  std::vector<float> const& cam_weights,
231  std::vector<vgl_point_2d<double> > const& image_pts,
232  vgl_point_3d<double> const& initial_pt,
233  vgl_point_3d<double>& final_pt,
234  double const& relative_diameter)
235 {
236  vpgl_z_search_lsqr zsf(cams, cam_weights, image_pts, initial_pt, relative_diameter);
237  vnl_levenberg_marquardt levmarq(zsf);
238 #ifdef TRANS_ONE_DEBUG
239  levmarq.set_verbose(true);
240 #endif
241  // Set the x-tolerance. When the length of the steps taken in X (variables)
242  // are no longer than this, the minimization terminates.
243  levmarq.set_x_tolerance(1e-10);
244 
245  // Set the epsilon-function. This is the step length for FD Jacobian.
246  levmarq.set_epsilon_function(1);
247 
248  // Set the f-tolerance. When the successive RMS errors are less than this,
249  // minimization terminates.
250  levmarq.set_f_tolerance(1e-15);
251 
252  // Set the maximum number of iterations
253  levmarq.set_max_function_evals(10000);
254 
255  vnl_vector<double> elevation(1);
256  elevation[0]=initial_pt.z();
257 
258  // Minimize the error and get the best intersection point
259  levmarq.minimize(elevation);
260 #ifdef TRANS_ONE_DEBUG
261  levmarq.diagnose_outcome();
262 #endif
263  final_pt.set(zsf.xm(), zsf.ym(), elevation[0]);
264  return true;
265 }
266 
268 adjust(std::vector<vpgl_rational_camera<double> > const& cams,
269  std::vector<vgl_point_2d<double> > const& corrs,
270  std::vector<vgl_vector_2d<double> >& cam_translations,
271  vgl_point_3d<double>& final)
272 {
273  cam_translations.clear();
275  std::vector<float> cam_weights(cams.size(), 1.0f/cams.size());
276  if (!find_intersection_point(cams, cam_weights, corrs,intersection))
277  return false;
278 
279  if (!refine_intersection_pt(cams, cam_weights, corrs,intersection, final))
280  return false;
281  auto cit = cams.begin();
282  auto rit = corrs.begin();
283  for (; cit!=cams.end() && rit!=corrs.end(); ++cit, ++rit)
284  {
285  vgl_point_2d<double> uvp = (*cit).project(final);
286  vgl_point_2d<double> uv = *rit;
287  vgl_vector_2d<double> t(uv.x()-uvp.x(), uv.y()-uvp.y());
288  cam_translations.push_back(t);
289  }
290  return true;
291 }
292 
294 adjust(std::vector<vpgl_rational_camera<double> > const& cams,
295  std::vector<vgl_point_2d<double> > const& corrs,
296  vgl_point_3d<double> const& initial_pt,
297  double const& zmin,
298  double const& zmax,
299  std::vector<vgl_vector_2d<double> >& cam_translations,
300  vgl_point_3d<double>& final,
301  double const& relative_diameter)
302 {
303  cam_translations.clear();
305  std::vector<float> cam_weights(cams.size(), 1.0f/cams.size());
306  if (!find_intersection_point(cams, cam_weights, corrs, initial_pt, zmin, zmax, intersection, relative_diameter))
307  return false;
308 
309  if (!refine_intersection_pt(cams, cam_weights, corrs, intersection, final))
310  return false;
311 
312  auto cit = cams.begin();
313  auto rit = corrs.begin();
314  for (; cit != cams.end() && rit != corrs.end(); ++cit, ++rit)
315  {
316  vgl_point_2d<double> uvp = (*cit).project(final);
317  vgl_point_2d<double> uv = *rit;
318  vgl_vector_2d<double> t(uv.x()-uvp.x(), uv.y()-uvp.y());
319  cam_translations.push_back(t);
320  }
321  return true;
322 }
323 
324 // pass a weight for each camera, the weights should add up to 1.0
327  std::vector<float> weights,
328  std::vector<vgl_point_2d<double> > const& corrs,
329  std::vector<vgl_vector_2d<double> >& cam_translations,
330  vgl_point_3d<double>& final)
331 {
332  cam_translations.clear();
334  if (!find_intersection_point(cams, weights, corrs,intersection))
335  return false;
336  if (!refine_intersection_pt(cams, weights, corrs,intersection, final))
337  return false;
338  auto cit = cams.begin();
339  auto rit = corrs.begin();
340  std::vector<float>::const_iterator wit = weights.begin();
341  for (; cit!=cams.end() && rit!=corrs.end(); ++cit, ++rit, ++wit)
342  {
343  // if weight is 1, it's a special case, do not change it at all (the projection still yields a tiny translation so just ignore that)
344  if ((*wit) == 1.0f) {
345  vgl_vector_2d<double> t(0.0, 0.0);
346  cam_translations.push_back(t);
347  continue;
348  }
349  vgl_point_2d<double> uvp = (*cit).project(final);
350  vgl_point_2d<double> uv = *rit;
351  vgl_vector_2d<double> t(uv.x()-uvp.x(), uv.y()-uvp.y());
352  cam_translations.push_back(t);
353  }
354  return true;
355 }
356 
358 adjust_with_weights(std::vector<vpgl_rational_camera<double> > const& cams, std::vector<float> const& weights,
359  std::vector<vgl_point_2d<double> > const& corrs,
360  vgl_point_3d<double> const& initial_pt,
361  double const& zmin,
362  double const& zmax,
363  std::vector<vgl_vector_2d<double> >& cam_translations,
364  vgl_point_3d<double>& final,
365  double const& relative_diameter)
366 {
367  cam_translations.clear();
369  if (!find_intersection_point(cams, weights, corrs, initial_pt, zmin, zmax, intersection, relative_diameter))
370  return false;
371  if (!refine_intersection_pt(cams, weights, corrs, intersection, final))
372  return false;
373  auto cit = cams.begin();
374  auto rit = corrs.begin();
375  auto wit = weights.begin();
376  for (; cit != cams.end() && rit != corrs.end(); ++cit, ++rit, ++wit)
377  {
378  // if weight is 1, it's a special case, do not change it at all (the projection still yields a tiny translation so just ignore that)
379  if ((*wit) == 1.0f) {
380  vgl_vector_2d<double> t(0.0, 0.0);
381  cam_translations.push_back(t);
382  continue;
383  }
384  vgl_point_2d<double> uvp = (*cit).project(final);
385  vgl_point_2d<double> uv = *rit;
386  vgl_vector_2d<double> t(uv.x()-uvp.x(), uv.y()-uvp.y());
387  cam_translations.push_back(t);
388  }
389  return true;
390 }
void set_f_tolerance(double v)
static bool adjust(std::vector< vpgl_rational_camera< double > > const &cams, std::vector< vgl_point_2d< double > > const &corrs, std::vector< vgl_vector_2d< double > > &cam_translations, vgl_point_3d< double > &intersection)
static bool adjust_with_weights(std::vector< vpgl_rational_camera< double > > const &cams, std::vector< float > weights, std::vector< vgl_point_2d< double > > const &corrs, std::vector< vgl_vector_2d< double > > &cam_translations, vgl_point_3d< double > &intersection)
vgl_homg_point_3d< Type > intersection(l const &l1, l const &l2)
The image offsets of rational cameras typically must be adjusted to compensate for errors in geograph...
void set_verbose(bool verb)
Type & z()
void f(vnl_vector< double > const &elevation, vnl_vector< double > &projection_error) override
The main function.
Type & y()
void set_max_function_evals(int v)
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.
Type & x()
void set_epsilon_function(double v)
vgl_point_3d< double > initial_pt_
void diagnose_outcome() const
std::vector< float > cam_weights_
void set(Type px, Type py, Type pz)
std::vector< vgl_point_2d< double > > image_pts_
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)
bool minimize(vnl_vector< double > &x)
static bool bproj_plane(const vpgl_camera< double > *cam, vnl_double_2 const &image_point, vnl_double_4 const &plane, vnl_double_3 const &initial_guess, vnl_double_3 &world_point, double error_tol=0.05, double relative_diameter=1.0)
Generic camera interfaces (pointer for abstract class).
Methods for back_projecting from cameras to 3-d geometric structures.
std::vector< vpgl_rational_camera< double > > cameras_
Type & y()
Type & x()
void set_x_tolerance(double v)