vgl_h_matrix_2d_compute_rigid_body.cxx
Go to the documentation of this file.
1 // This is core/vgl/algo/vgl_h_matrix_2d_compute_rigid_body.cxx
2 #include <iostream>
3 #include <cmath>
5 //:
6 // \file
7 
8 #ifdef _MSC_VER
9 # include <vcl_msvc_warnings.h>
10 #endif
11 #include <cassert>
12 #include <vnl/vnl_inverse.h>
13 #include <vnl/vnl_transpose.h>
14 #include <vnl/algo/vnl_svd.h>
16 
17 //: Construct a vgl_h_matrix_2d_compute_rigid_body object.
19 
20 constexpr int TM_UNKNOWNS_COUNT = 3;
21 constexpr double DEGENERACY_THRESHOLD = 0.01;
22 
23 //-----------------------------------------------------------------------------
24 //
25 //: Compute the rigid body transformation between point sets
26 //
29  std::vector<vgl_homg_point_2d<double> > const& p1,
30  std::vector<vgl_homg_point_2d<double> > const& p2,
32 {
33  //transform the point sets and fill the design matrix
34  vnl_matrix<double> D(equ_count, TM_UNKNOWNS_COUNT+2);
35  int n = p1.size();
36  int row = 0;
37  for (int i = 0; i < n; i++) {
38  D(row, 0) = p1[i].x();
39  D(row, 1) = -p1[i].y();
40  D(row, 2) = 1.0;
41  D(row, 3) = 0.0;
42  D(row, 4) = -p2[i].x();
43  ++row;
44 
45  D(row, 0) = p1[i].y();
46  D(row, 1) = p1[i].x();
47  D(row, 2) = 0;
48  D(row, 3) = 1.0;
49  D(row, 4) = -p2[i].y();
50  ++row;
51  }
52 
53  D.normalize_rows();
54  vnl_svd<double> svd(D);
55  vnl_vector<double> nullv = svd.nullvector();
56  //last singular value should be zero for ideal data
57  if (svd.W(4)>DEGENERACY_THRESHOLD*svd.W(3)) {
58  std::cout << "vgl_h_matrix_2d_compute_rigid_body : inaccurate solution probably due to inconsistent point correspondences\n"
59  << "W\n" << svd.W() << std::endl;
60  return false;
61  }
62 
63  // form the matrix from the nullvector
64  // normalize by the last value
65  double norm = nullv[4];
66  nullv /= norm;
67  // convert to rotation
68  double y = nullv[1];
69  double x = nullv[0];
70  double angle = std::atan2(y,x);
71  double c = std::cos(angle);
72  double s = std::sin(angle);
73  vnl_matrix_fixed<double, 3,3> M;
74  M[0][0]=c; M[0][1]= -s; M[0][2] = nullv[2];
75  M[1][0]=s; M[1][1]= c; M[1][2] = nullv[3];
76  M[2][0]=0; M[2][1]= 0; M[2][2] = 1;
77  H.set(M);
78  return true;
79 }
80 
82 compute_p(std::vector<vgl_homg_point_2d<double> > const& points1,
83  std::vector<vgl_homg_point_2d<double> > const& points2,
85 {
86  //number of points must be the same
87  assert(points1.size() == points2.size());
88  int n = points1.size();
89 
90  int equ_count = n * (2);
91  if (n * 2 < TM_UNKNOWNS_COUNT) {
92  std::cerr << "vgl_h_matrix_2d_compute_rigid_body: Need at least 2 matches.\n";
93  if (n == 0) std::cerr << "Could be std::vector setlength idiosyncrasies!\n";
94  return false;
95  }
96  //compute the normalizing transforms
98  if (!tr1.compute_from_points(points1))
99  return false;
100  if (!tr2.compute_from_points(points2))
101  return false;
102  std::vector<vgl_homg_point_2d<double> > tpoints1, tpoints2;
103  for (int i = 0; i<n; i++)
104  {
105  tpoints1.push_back(tr1(points1[i]));
106  tpoints2.push_back(tr2(points2[i]));
107  }
109  if (!solve_rigid_body_problem(equ_count, tpoints1, tpoints2, hh))
110  return false;
111  //
112  // Next, hh has to be transformed back to the coordinate system of
113  // the original point sets, i.e.,
114  // p1' = tr1 p1 , p2' = tr2 p2
115  // hh was determined from the transform relation
116  // p2' = hh p1', thus
117  // (tr2 p2) = hh (tr1 p1)
118  // p2 = (tr2^-1 hh tr1) p1 = H p1
119  vgl_h_matrix_2d<double> tr2_inv = tr2.get_inverse();
120  H = tr2_inv*hh*tr1;
121  return true;
122 }
123 
125 compute_l(std::vector<vgl_homg_line_2d<double> > const& lines1,
126  std::vector<vgl_homg_line_2d<double> > const& lines2,
128 {
129  //number of lines must be the same
130  assert(lines1.size() == lines2.size());
131  int n = lines1.size();
132  int equ_count = 2*n;
133  //compute the normalizing transforms. By convention, these are point
134  //transformations.
135  vgl_norm_trans_2d<double> tr1, tr2;
136  if (!tr1.compute_from_lines(lines1))
137  return false;
138  if (!tr2.compute_from_lines(lines2))
139  return false;
140  std::vector<vgl_homg_point_2d<double> > tlines1, tlines2;
141  for (const auto & lit : lines1)
142  {
143  // transform the lines according to the normalizing transform
144  vgl_homg_line_2d<double> l = tr1(lit);
145  // convert the line to a point to use the same rigid_body code
146  vgl_homg_point_2d<double> p(l.a(), l.b(), l.c());
147  tlines1.push_back(p);
148  }
149  for (const auto & lit : lines2)
150  {
151  // transform the lines according to the normalizing transform
152  vgl_homg_line_2d<double> l = tr2(lit);
153  // convert the line to a point to use the same rigid_body code
154  vgl_homg_point_2d<double> p(l.a(), l.b(), l.c());
155  tlines2.push_back(p);
156  }
157 
158  vgl_h_matrix_2d<double> hl,hp,tr2inv;
159  if (!solve_rigid_body_problem(equ_count, tlines1, tlines2, hl))
160  return false;
161  // The result is a transform on lines so we need to convert it to
162  // a point transform, i.e., hp = hl^-t.
163  vnl_matrix_fixed<double, 3, 3> const & Ml = hl.get_matrix();
164  vnl_matrix_fixed<double, 3, 3> Mp = vnl_inverse_transpose(Ml);
165  hp.set(Mp);
166  //
167  // Next, hp has to be transformed back to the coordinate system of
168  // the original lines, i.e.,
169  // l1' = tr1 l1 , l2' = tr2 l2
170  // hp was determined from the transform relation
171  // l2' = hh l1', thus
172  // (tr2 l2) = hh (tr1 l1)
173  // p2 = (tr2^-1 hh tr1) p1 = H p1
174  tr2inv = tr2.get_inverse();
175  H = tr2inv*hp*tr1;
176  return true;
177 }
178 
180 compute_pl(std::vector<vgl_homg_point_2d<double> > const& points1,
181  std::vector<vgl_homg_point_2d<double> > const& points2,
182  std::vector<vgl_homg_line_2d<double> > const& lines1,
183  std::vector<vgl_homg_line_2d<double> > const& lines2,
185 {
186  //number of points must be the same
187  assert(points1.size() == points2.size());
188  int np = points1.size();
189  //number of lines must be the same
190  assert(lines1.size() == lines2.size());
191  int nl = lines1.size();
192 
193  int equ_count = np * 2 + 2*nl;
194  if ((np+nl)*2 < TM_UNKNOWNS_COUNT)
195  {
196  std::cerr << "vgl_h_matrix_2d_compute_rigid_body: Need at least 4 matches.\n";
197  if (np+nl == 0) std::cerr << "Could be std::vector setlength idiosyncrasies!\n";
198  return false;
199  }
200  //compute the normalizing transforms
201  vgl_norm_trans_2d<double> tr1, tr2;
202  if (!tr1.compute_from_points_and_lines(points1,lines1))
203  return false;
204  if (!tr2.compute_from_points_and_lines(points2,lines2))
205  return false;
206  std::vector<vgl_homg_point_2d<double> > tpoints1, tpoints2;
207  for (int i = 0; i<np; i++)
208  {
209  tpoints1.push_back(tr1(points1[i]));
210  tpoints2.push_back(tr2(points2[i]));
211  }
212  for (int i = 0; i<nl; i++)
213  {
214  double a=lines1[i].a(), b=lines1[i].b(), c=lines1[i].c(), d=std::sqrt(a*a+b*b);
215  tpoints1.push_back(tr1(vgl_homg_point_2d<double>(-a*c,-b*c,d)));
216  a=lines2[i].a(), b=lines2[i].b(), c=lines2[i].c(), d = std::sqrt(a*a+b*b);
217  tpoints2.push_back(tr2(vgl_homg_point_2d<double>(-a*c,-b*c,d)));
218  }
220  if (!solve_rigid_body_problem(equ_count, tpoints1, tpoints2, hh))
221  return false;
222 
223  vgl_h_matrix_2d<double> tr2_inv = tr2.get_inverse();
224  H = tr2_inv*hh*tr1;
225  return true;
226 }
227 
229 compute_l(std::vector<vgl_homg_line_2d<double> > const& /* lines1 */,
230  std::vector<vgl_homg_line_2d<double> > const& /* lines2 */,
231  std::vector<double> const& /* weights */,
232  vgl_h_matrix_2d<double>& /* H */)
233 {
234  return false;
235 }
236 
237 #if 0 // do later
238 //--------------------------------------------------------
239 //:
240 // The solution equations should be weighted by the length of
241 // the corresponding line matches. This weighting is given by w.
242 //
243 // The two equations resulting from l1i<->l2i should be
244 // weighted by wi. Form a m x m diagonal matrix W with elements from w,
245 // with m = 2*Nc, where Nc=l1.size()=l2.size() is the number of
246 // corresponding line pairs. The weighted least squares problem is
247 // expressed as:
248 //
249 // (D^tWD)x = Mx = 0
250 //
251 // where D is the design matrix and x is the 9 element vector of unknown
252 // homography matrix elements. This problem can be solved using SVD as in the
253 // case of unweighted least squares.
254 //
255 bool vgl_h_matrix_2d_compute_rigid_body::
256 solve_weighted_least_squares(std::vector<vgl_homg_line_2d<double> > const& l1,
257  std::vector<vgl_homg_line_2d<double> > const& l2,
258  std::vector<double> const& w,
260 {
261  int Nc = l1.size();
262  // Note the w has size Nc so we need to form a 2*Nc vector with
263  // repeated values
264  vnl_vector<double> two_w(2*Nc);
265  int j =0;
266  for (int i = 0; i<Nc; i++, j+=2)
267  {
268  two_w[j]=w[i];
269  two_w[j+1]=w[i];
270  }
271  vnl_diag_matrix<double> W(two_w);
272 
273  //Form the design matrix, D
274  vnl_matrix<double> D(2*Nc, TM_UNKNOWNS_COUNT);
275  vnl_matrix<double> M(TM_UNKNOWNS_COUNT, TM_UNKNOWNS_COUNT);
276 
277  int row = 0;
278  for (int i = 0; i < Nc; i++)
279  {
280  D(row, 0) = l1[i].a() * l2[i].c();
281  D(row, 1) = l1[i].b() * l2[i].c();
282  D(row, 2) = l1[i].c() * l2[i].c();
283  D(row, 3) = 0;
284  D(row, 4) = 0;
285  D(row, 5) = 0;
286  D(row, 6) = -l1[i].a() * l2[i].a();
287  D(row, 7) = -l1[i].b() * l2[i].a();
288  D(row, 8) = -l1[i].c() * l2[i].a();
289  ++row;
290 
291  D(row, 0) = 0;
292  D(row, 1) = 0;
293  D(row, 2) = 0;
294  D(row, 3) = l1[i].a() * l2[i].c();
295  D(row, 4) = l1[i].b() * l2[i].c();
296  D(row, 5) = l1[i].c() * l2[i].c();
297  D(row, 6) = -l1[i].a() * l2[i].b();
298  D(row, 7) = -l1[i].b() * l2[i].b();
299  D(row, 8) = -l1[i].c() * l2[i].b();
300  ++row;
301  }
302  M = vnl_transpose(D)*W*D;
303  D.normalize_rows();
304  vnl_svd<double> svd(D);
305 
306  //
307  // FSM added :
308  //
309  if (svd.W(7)<DEGENERACY_THRESHOLD*svd.W(8)) {
310  std::cerr << "vgl_h_matrix_2d_compute_rigid_body : design matrix has rank < 8\n"
311  << "vgl_h_matrix_2d_compute_rigid_body : probably due to degenerate point configuration\n";
312  return false;
313  }
314  // form the matrix from the nullvector
315  H.set(svd.nullvector().data_block());
316  return true;
317 }
318 
320 compute_l(std::vector<vgl_homg_line_2d<double> > const& lines1,
321  std::vector<vgl_homg_line_2d<double> > const& lines2,
322  std::vector<double> const & weights,
324 {
325  //number of lines must be the same
326  assert(lines1.size() == lines2.size());
327  //int n = lines1.size();
328  //compute the normalizing transforms. By convention, these are point
329  //transformations.
330  vgl_norm_trans_2d<double> tr1, tr2;
331  if (!tr1.compute_from_lines(lines1))
332  return false;
333  if (!tr2.compute_from_lines(lines2))
334  return false;
335  std::vector<vgl_homg_line_2d<double> > tlines1, tlines2;
336  for (std::vector<vgl_homg_line_2d<double> >::const_iterator
337  lit = lines1.begin(); lit != lines1.end(); lit++)
338  {
339  // transform the lines according to the normalizing transform
340  vgl_homg_line_2d<double> l = tr1(*lit);
341  tlines1.push_back(l);
342  }
343  for (std::vector<vgl_homg_line_2d<double> >::const_iterator
344  lit = lines2.begin(); lit != lines2.end(); lit++)
345  {
346  // transform the lines according to the normalizing transform
347  vgl_homg_line_2d<double> l = tr2(*lit);
348  tlines2.push_back(l);
349  }
350 
351  vgl_h_matrix_2d<double> hl,hp,tr2inv;
352  if (!solve_weighted_least_squares(tlines1, tlines2, weights, hl))
353  return false;
354  // The result is a transform on lines so we need to convert it to
355  // a point transform, i.e., hp = hl^-t.
356  vnl_matrix_fixed<double, 3, 3> const & Ml = hl.get_matrix();
357  vnl_matrix_fixed<double, 3, 3> Mp = vnl_inverse_transpose(Ml);
358  hp.set(Mp);
359  //
360  // Next, hp has to be transformed back to the coordinate system of
361  // the original lines, i.e.,
362  // l1' = tr1 l1 , l2' = tr2 l2
363  // hp was determined from the transform relation
364  // l2' = hh l1', thus
365  // (tr2 l2) = hh (tr1 l1)
366  // p2 = (tr2^-1 hh tr1) p1 = H p1
367  tr2inv = tr2.get_inverse();
368  H = tr2inv*hp*tr1;
369  return true;
370 }
371 #endif // 0
constexpr int TM_UNKNOWNS_COUNT
vnl_matrix_fixed< T, 3, 3 > const & get_matrix() const
Return the 3x3 homography matrix.
constexpr double DEGENERACY_THRESHOLD
vgl_h_matrix_2d_compute_rigid_body()
Construct a vgl_h_matrix_2d_compute_rigid_body object.
Represents a homogeneous 2D line.
Definition: vgl_fwd.h:14
vgl_h_matrix_2d & set(unsigned int row_index, unsigned int col_index, T value)
Set an element of the 3x3 homography matrix.
The similarity transform that normalizes a point set.
contains class vgl_h_matrix_2d_compute_rigid_body
bool compute_pl(std::vector< vgl_homg_point_2d< double > > const &points1, std::vector< vgl_homg_point_2d< double > > const &points2, std::vector< vgl_homg_line_2d< double > > const &lines1, std::vector< vgl_homg_line_2d< double > > const &lines2, vgl_h_matrix_2d< double > &H) override
compute from matched points and lines.
bool compute_from_lines(std::vector< vgl_homg_line_2d< T > > const &lines, bool isotropic=true)
The normalizing transform for lines is computed from the set of points defined by the intersection of...
bool compute_l(std::vector< vgl_homg_line_2d< double > > const &lines1, std::vector< vgl_homg_line_2d< double > > const &lines2, vgl_h_matrix_2d< double > &H) override
compute from matched lines.
bool compute_from_points_and_lines(std::vector< vgl_homg_point_2d< T > > const &pts, std::vector< vgl_homg_line_2d< T > > const &lines, bool isotropic=true)
The normalizing transform for points and lines is computed from the set of points used by compute_fro...
bool compute_p(std::vector< vgl_homg_point_2d< double > > const &points1, std::vector< vgl_homg_point_2d< double > > const &points2, vgl_h_matrix_2d< double > &H) override
compute from matched points.
bool compute_from_points(std::vector< vgl_homg_point_2d< T > > const &points, bool isotropic=true)
compute the normalizing transform.
#define l
double angle(vgl_ray_3d< Type > const &r0, vgl_ray_3d< Type > const &r1)
public functions.
Definition: vgl_ray_3d.h:102
vgl_h_matrix_2d get_inverse() const
Return the inverse homography.
Represents a homogeneous 2D point.
Definition: vgl_fwd.h:8
bool solve_rigid_body_problem(int equ_count, std::vector< vgl_homg_point_2d< double > >const &p1, std::vector< vgl_homg_point_2d< double > >const &p2, vgl_h_matrix_2d< double > &H)
Assumes all corresponding points have equal weight.