vpgl_bundle_adjust_lsqr.cxx
Go to the documentation of this file.
1 // This is vpgl/algo/vpgl_bundle_adjust_lsqr.cxx
2 #include <algorithm>
3 #include <iostream>
4 #include <utility>
6 //:
7 // \file
8 
9 #include <vnl/vnl_vector_ref.h>
10 #include <vnl/vnl_double_3.h>
11 
12 #ifdef _MSC_VER
13 # include <vcl_msvc_warnings.h>
14 #endif
15 #include <cassert>
16 
17 
18 //: Constructor
20 vpgl_bundle_adjust_lsqr(unsigned int num_params_per_a,
21  unsigned int num_params_per_b,
22  unsigned int num_params_c,
23  std::vector<vgl_point_2d<double> > image_points,
24  const std::vector<std::vector<bool> >& mask)
25  : vnl_sparse_lst_sqr_function(mask.size(),num_params_per_a,
26  mask[0].size(),num_params_per_b,
27  num_params_c,mask,2,use_gradient,use_weights),
28  image_points_(std::move(image_points)),
29  use_covars_(false),
30  scale2_(1.0),
31  iteration_count_(0)
32 {
33 }
34 
35 
36 //: Constructor
37 // Each image point is assigned an inverse covariance (error projector) matrix
38 // \note image points are not homogeneous because they require finite points to
39 // measure projection error
41 vpgl_bundle_adjust_lsqr(unsigned int num_params_per_a,
42  unsigned int num_params_per_b,
43  unsigned int num_params_c,
44  const std::vector<vgl_point_2d<double> >& image_points,
45  const std::vector<vnl_matrix<double> >& inv_covars,
46  const std::vector<std::vector<bool> >& mask)
47  : vnl_sparse_lst_sqr_function(mask.size(),num_params_per_a,
48  mask[0].size(),num_params_per_b,
49  num_params_c,mask,2,use_gradient,use_weights),
50  image_points_(image_points),
51  use_covars_(true),
52  scale2_(1.0),
53  iteration_count_(0)
54 {
55  assert(image_points.size() == inv_covars.size());
56  vnl_matrix<double> U(2,2,0.0);
57  for (const auto & S : inv_covars)
58  {
59  if (S(0,0) > 0.0) {
60  U(0,0) = std::sqrt(S(0,0));
61  U(0,1) = S(0,1)/U(0,0);
62  double U11 = S(1,1)-S(0,1)*S(0,1)/S(0,0);
63  U(1,1) = (U11>0.0)?std::sqrt(U11):0.0;
64  }
65  else if (S(1,1) > 0.0) {
66  assert(S(0,1) == 0.0);
67  U(0,0) = 0.0;
68  U(0,1) = 0.0;
69  U(1,1) = std::sqrt(S(1,1));
70  }
71  else {
72  std::cout << "warning: not positive definite"<<std::endl;
73  U.fill(0.0);
74  }
75  factored_inv_covars_.push_back(U);
76  }
77 }
78 
79 
80 //: Compute all the reprojection errors
81 // Given the parameter vectors a, b, and c, compute the vector of residuals e.
82 // e has been sized appropriately before the call.
83 // The parameters in a for each camera are {wx, wy, wz, tx, ty, tz}
84 // where w is the Rodrigues vector of the rotation and t is the translation.
85 // The parameters in b for each 3D point are {px, py, pz}
86 // the non-homogeneous position.
87 void
89  vnl_vector<double> const& b,
90  vnl_vector<double> const& c,
92 {
93  for (unsigned int i=0; i<number_of_a(); ++i)
94  {
95  //: Construct the ith camera
97 
99  for (auto & r_itr : row)
100  {
101  unsigned int j = r_itr.second;
102  unsigned int k = r_itr.first;
103 
104  // Construct the jth point
106 
107  // Project jth point with the ith camera
108  vnl_vector_fixed<double,3> xij = Pi*Xj;
109 
110  double* eij = e.data_block()+index_e(k);
111  eij[0] = xij[0]/xij[2] - image_points_[k].x();
112  eij[1] = xij[1]/xij[2] - image_points_[k].y();
113  if (use_covars_){
114  // multiple this error by upper triangular Sij
116  eij[0] *= Sij(0,0);
117  eij[0] += eij[1]*Sij(0,1);
118  eij[1] *= Sij(1,1);
119  }
120  }
121  }
122 }
123 
124 
125 //: Compute the residuals from the ith component of a and the jth component of b.
126 // This is not normally used because f() has a self-contained efficient implementation
127 // It is used for finite-differencing if the gradient is marked as unavailable
128 void
130  vnl_vector<double> const& ai,
131  vnl_vector<double> const& bj,
132  vnl_vector<double> const& c,
133  vnl_vector<double>& fij)
134 {
135  //: Construct the ith camera
137 
138  // Construct the jth point
140 
141  // Project jth point with the ith camera
142  vnl_vector_fixed<double,3> xij = Pi*Xj;
143 
144  int k = residual_indices_(i,j);
145  fij[0] = xij[0]/xij[2] - image_points_[k].x();
146  fij[1] = xij[1]/xij[2] - image_points_[k].y();
147  if (use_covars_){
148  // multiple this error by upper triangular Sij
150  fij[0] *= Sij(0,0);
151  fij[0] += fij[1]*Sij(0,1);
152  fij[1] *= Sij(1,1);
153  }
154 }
155 
156 
157 //: Compute the sparse Jacobian in block form.
158 void
160  vnl_vector<double> const& b,
161  vnl_vector<double> const& c,
162  std::vector<vnl_matrix<double> >& A,
163  std::vector<vnl_matrix<double> >& B,
164  std::vector<vnl_matrix<double> >& C)
165 {
166  for (unsigned int i=0; i<number_of_a(); ++i)
167  {
168  //: Construct the ith camera
170 
171  // This is semi const incorrect - there is no vnl_vector_ref_const
173  const_cast<double*>(a.data_block())+index_a(i));
174 
176  for (auto & r_itr : row)
177  {
178  unsigned int j = r_itr.second;
179  unsigned int k = r_itr.first;
180  // This is semi const incorrect - there is no vnl_vector_ref_const
182  const_cast<double*>(b.data_block())+index_b(j));
183 
184  jac_Aij(i,j,Pi,ai,bj,c,A[k]); // compute Jacobian A_ij
185  jac_Bij(i,j,Pi,ai,bj,c,B[k]); // compute Jacobian B_ij
186  jac_Cij(i,j,Pi,ai,bj,c,C[k]); // compute Jacobian C_ij
187  if (use_covars_){
189  A[k] = Sij*A[k];
190  B[k] = Sij*B[k];
191  C[k] = Sij*C[k];
192  }
193  }
194  }
195 }
196 
197 
198 void
200  vnl_vector<double> const& /*ai*/,
201  vnl_vector<double> const& /*bj*/,
202  vnl_vector<double> const& /*c*/,
203  vnl_vector<double> const& fij,
204  double& weight)
205 {
206  double u2 = fij.squared_magnitude()/scale2_;
207 
208  // Beaton-Tukey
209  weight = (u2 > 1.0) ? 0.0 : 1 - u2;
210 
211  // Cauchy
212  //weight = std::sqrt(1 / (1 + u2));
213 }
214 
215 
216 //: compute the 2x3 Jacobian of camera projection with respect to point location df/dpt where $f(pt) = P*pt$
219  vnl_vector<double> const& pt,
221 {
222  double denom = P(2,0)*pt[0] + P(2,1)*pt[1] + P(2,2)*pt[2] + P(2,3);
223  denom *= denom;
224 
225  double txy = P(0,0)*P(2,1) - P(0,1)*P(2,0);
226  double txz = P(0,0)*P(2,2) - P(0,2)*P(2,0);
227  double tyz = P(0,1)*P(2,2) - P(0,2)*P(2,1);
228  double tx = P(0,0)*P(2,3) - P(0,3)*P(2,0);
229  double ty = P(0,1)*P(2,3) - P(0,3)*P(2,1);
230  double tz = P(0,2)*P(2,3) - P(0,3)*P(2,2);
231 
232  J(0,0) = ( txy*pt[1] + txz*pt[2] + tx) / denom;
233  J(0,1) = (-txy*pt[0] + tyz*pt[2] + ty) / denom;
234  J(0,2) = (-txz*pt[0] - tyz*pt[1] + tz) / denom;
235 
236  txy = P(1,0)*P(2,1) - P(1,1)*P(2,0);
237  txz = P(1,0)*P(2,2) - P(1,2)*P(2,0);
238  tyz = P(1,1)*P(2,2) - P(1,2)*P(2,1);
239  tx = P(1,0)*P(2,3) - P(1,3)*P(2,0);
240  ty = P(1,1)*P(2,3) - P(1,3)*P(2,1);
241  tz = P(1,2)*P(2,3) - P(1,3)*P(2,2);
242 
243  J(1,0) = ( txy*pt[1] + txz*pt[2] + tx) / denom;
244  J(1,1) = (-txy*pt[0] + tyz*pt[2] + ty) / denom;
245  J(1,2) = (-txz*pt[0] - tyz*pt[1] + tz) / denom;
246 }
247 
248 
249 //: compute the 2x3 Jacobian of camera projection with respect to camera center df/dC where $f(C) = [M | -M*C]*pt$
251  vnl_vector<double> const& C,
252  vnl_vector<double> const& pt,
254 {
255  // compute by swapping the role of the camera center and point position
256  // then reused the jac_inhomg_3d_point code
257  vnl_double_3x4 P;
258  P.update(M);
259  P.set_column(3,-(M*pt));
260  jac_inhomg_3d_point(P,C,J);
261 }
262 
263 
264 //: 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$
265 // Here r is a Rodrigues vector, K is an upper triangular calibration matrix
267  vnl_vector<double> const& C,
268  vnl_vector<double> const& r,
269  vnl_vector<double> const& pt,
271 {
272  vnl_double_3 t(pt[0]-C[0], pt[1]-C[1], pt[2]-C[2]);
273 
274  const double& x = r[0];
275  const double& y = r[1];
276  const double& z = r[2];
277  double x2 = x*x, y2 = y*y, z2 = z*z;
278  double m2 = x2 + y2 + z2;
279 
280  // special case for the identity rotation
281  if (m2 == 0.0)
282  {
283  double inv_tz2 = 1.0/(t[2]*t[2]);
284  J(0,0) = -t[0]*t[1] * inv_tz2;
285  J(1,0) = -1 - t[1]*t[1] * inv_tz2;
286  J(0,1) = 1 + t[0]*t[0] * inv_tz2;
287  J(1,1) = t[0]*t[1] * inv_tz2;
288  J(0,2) = -t[1] / t[2];
289  J(1,2) = t[0] / t[2];
290  }
291  else
292  {
293  double m = std::sqrt(m2); // Rodrigues magnitude = rotation angle
294  double c = std::cos(m);
295  double s = std::sin(m);
296 
297  // common trig terms
298  double ct = (1-c)/m2;
299  double st = s/m;
300 
301  // derivative coefficients for common trig terms
302  // ds = d/dx_i{st}/x_i
303  // dc = d/dx_i{ct}/x_i
304  double dct = s/(m*m2);
305  double dst = c/m2 - dct;
306  dct -= 2*(1-c)/(m2*m2);
307 
308  double utc = t[2]*x*z + t[1]*x*y - t[0]*(y2+z2);
309  double uts = t[2]*y - t[1]*z;
310  double vtc = t[0]*x*y + t[2]*y*z - t[1]*(x2+z2);
311  double vts = t[0]*z - t[2]*x;
312  double wtc = t[0]*x*z + t[1]*y*z - t[2]*(x2+y2);
313  double wts = t[1]*x - t[0]*y;
314 
315  // projection of the point into normalized homogeneous coordinates
316  // should be equal to inv(K)*P*[pt|1]
317  double u = ct*utc + st*uts + t[0];
318  double v = ct*vtc + st*vts + t[1];
319  double w = ct*wtc + st*wts + t[2];
320 
321  double w2 = w*w;
322 
323  double dw = dct*x*wtc + ct*(t[0]*z - 2*t[2]*x)
324  + dst*x*wts + st*t[1];
325  J(0,0) = (w*(dct*x*utc + ct*(t[2]*z + t[1]*y)
326  + dst*x*uts) - u*dw)/w2;
327  J(1,0) = (w*(dct*x*vtc + ct*(t[0]*y - 2*t[1]*x)
328  + dst*x*vts - st*t[2]) - v*dw)/w2;
329 
330  dw = dct*y*wtc + ct*(t[1]*z - 2*t[2]*y)
331  + dst*y*wts - st*t[0];
332  J(0,1) = (w*(dct*y*utc + ct*(t[1]*x - 2*t[0]*y)
333  + dst*y*uts + st*t[2]) - u*dw)/w2;
334  J(1,1) = (w*(dct*y*vtc + ct*(t[0]*x + t[2]*z)
335  + dst*y*vts) - v*dw)/w2;
336 
337  dw = dct*z*wtc + ct*(t[0]*x + t[1]*y)
338  + dst*z*wts;
339  J(0,2) = (w*(dct*z*utc + ct*(t[2]*x - 2*t[0]*z)
340  + dst*z*uts - st*t[1]) - u*dw)/w2;
341  J(1,2) = (w*(dct*z*vtc + ct*(t[2]*y - 2*t[1]*z)
342  + dst*z*vts + st*t[0]) - v*dw)/w2;
343  }
344 
345  // account for the calibration matrix
346  J(0,0) *= K(0,0);
347  J(0,0) += J(1,0)*K(0,1);
348  J(1,0) *= K(1,1);
349  J(0,1) *= K(0,0);
350  J(0,1) += J(1,1)*K(0,1);
351  J(1,1) *= K(1,1);
352  J(0,2) *= K(0,0);
353  J(0,2) += J(1,2)*K(0,1);
354  J(1,2) *= K(1,1);
355 }
356 
357 
358 //: Fast conversion of rotation from Rodrigues vector to matrix
361 {
362  double x2 = r[0]*r[0], y2 = r[1]*r[1], z2 = r[2]*r[2];
363  double m = x2 + y2 + z2;
364  double theta = std::sqrt(m);
365  double s = std::sin(theta) / theta;
366  double c = (1 - std::cos(theta)) / m;
367 
369  R(0,0) = R(1,1) = R(2,2) = 1.0;
370  if (m == 0.0)
371  return R;
372 
373  R(0,0) -= (y2 + z2) * c;
374  R(1,1) -= (x2 + z2) * c;
375  R(2,2) -= (x2 + y2) * c;
376  R(0,1) = R(1,0) = r[0]*r[1]*c;
377  R(0,2) = R(2,0) = r[0]*r[2]*c;
378  R(1,2) = R(2,1) = r[1]*r[2]*c;
379 
380  double t = r[0]*s;
381  R(1,2) -= t;
382  R(2,1) += t;
383  t = r[1]*s;
384  R(0,2) += t;
385  R(2,0) -= t;
386  t = r[2]*s;
387  R(0,1) -= t;
388  R(1,0) += t;
389 
390  return R;
391 }
unsigned int number_of_params_a(int i) const
void jac_blocks(vnl_vector< double > const &a, vnl_vector< double > const &b, vnl_vector< double > const &c, std::vector< vnl_matrix< double > > &A, std::vector< vnl_matrix< double > > &B, std::vector< vnl_matrix< double > > &C) override
Compute the sparse Jacobian in block form.
unsigned int index_a(int i) const
void fij(int i, int j, vnl_vector< double > const &ai, vnl_vector< double > const &bj, vnl_vector< double > const &c, vnl_vector< double > &fij) override
Compute the residuals from the ith component of a, the jth component of b, and all of c.
static void jac_camera_rotation(vnl_double_3x3 const &K, vnl_vector< double > const &C, vnl_vector< double > const &r, vnl_vector< double > const &pt, vnl_matrix< double > &J)
compute the 2x3 Jacobian of camera projection with respect to camera rotation df/dr where $f(r) = K*r...
vnl_vector_fixed< double, 4 > param_to_pt_vector(int j, const vnl_vector< double > &b, const vnl_vector< double > &c) const
construct the
unsigned int index_b(int j) const
static vnl_double_3x3 rod_to_matrix(vnl_vector< double > const &r)
Fast conversion of rotation from Rodrigues vector to matrix.
double scale2_
The square of the scale of the robust estimator.
static void jac_camera_center(vnl_double_3x3 const &M, vnl_vector< double > const &C, vnl_vector< double > const &pt, vnl_matrix< double > &J)
compute the 2x3 Jacobian of camera projection with respect to camera center df/dC where $f(C) = [M | ...
vnl_crs_index residual_indices_
unsigned int number_of_a() const
void compute_weight_ij(int i, int j, vnl_vector< double > const &ai, vnl_vector< double > const &bj, vnl_vector< double > const &c, vnl_vector< double > const &fij, double &weight) override
Use an M-estimator to compute weights.
#define m
virtual void jac_Cij(unsigned int i, unsigned int j, vnl_double_3x4 const &Pi, vnl_vector< double > const &ai, vnl_vector< double > const &bj, vnl_vector< double > const &c, vnl_matrix< double > &Cij)=0
compute the Jacobian Cij.
double const * data_block() const
vnl_matrix_fixed & update(vnl_matrix< T > const &, unsigned top=0, unsigned left=0)
unsigned int index_e(int k) const
vnl_double_3x4 param_to_cam_matrix(int i, const vnl_vector< double > &a, const vnl_vector< double > &c) const
construct the
virtual void jac_Aij(unsigned int i, unsigned int j, vnl_double_3x4 const &Pi, vnl_vector< double > const &ai, vnl_vector< double > const &bj, vnl_vector< double > const &c, vnl_matrix< double > &Aij)=0
compute the Jacobian Aij.
#define v
std::vector< vgl_point_2d< double > > image_points_
The corresponding points in the image.
sparse_vector sparse_row(int i) const
void f(vnl_vector< double > const &a, vnl_vector< double > const &b, vnl_vector< double > const &c, vnl_vector< double > &e) override
Compute all the reprojection errors.
Bundle adjustment sparse least squares base class.
static void jac_inhomg_3d_point(vnl_double_3x4 const &P, vnl_vector< double > const &pt, vnl_matrix< double > &J)
compute the 2x3 Jacobian of camera projection with respect to point location df/dpt where $f(pt) = P*...
bool use_covars_
Flag to enable covariance weighted errors.
std::vector< vnl_matrix< double > > factored_inv_covars_
The Cholesky factored inverse covariances for each image point.
vpgl_bundle_adjust_lsqr(unsigned int num_params_per_a, unsigned int num_params_per_b, unsigned int num_params_c, std::vector< vgl_point_2d< double > > image_points, const std::vector< std::vector< bool > > &mask)
Constructor.
std::vector< idx_pair > sparse_vector
unsigned int number_of_params_b(int j) const
vnl_matrix_fixed & set_column(unsigned i, T const *v)
virtual void jac_Bij(unsigned int i, unsigned int j, vnl_double_3x4 const &Pi, vnl_vector< double > const &ai, vnl_vector< double > const &bj, vnl_vector< double > const &c, vnl_matrix< double > &Bij)=0
compute the Jacobian Bij.