vgl_h_matrix_2d_optimize_lmq.cxx
Go to the documentation of this file.
1 // This is core/vgl/algo/vgl_h_matrix_2d_optimize_lmq.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/algo/vnl_levenberg_marquardt.h>
15 
16 //: Construct a vgl_h_matrix_2d_optimize_lmq object.
19  : vgl_h_matrix_2d_optimize(initial_h)
20 {
21 }
22 
23 //: optimize the normalized projection problem
25 optimize_h(std::vector<vgl_homg_point_2d<double> > const& points1,
26  std::vector<vgl_homg_point_2d<double> > const& points2,
27  vgl_h_matrix_2d<double> const& h_initial,
28  vgl_h_matrix_2d<double>& h_optimized)
29 {
30  projection_lsqf lsq(points1, points2);
31  vnl_vector<double> hv(9);
32  vnl_matrix_fixed<double,3,3> m = h_initial.get_matrix();
33  unsigned i = 0;
34  for (unsigned r=0; r<3; ++r)
35  for (unsigned c=0; c<3; ++c, ++i)
36  hv[i] = m[r][c];
37  vnl_levenberg_marquardt lm(lsq);
38  lm.set_verbose(verbose_);
39  lm.set_trace(trace_);
40  lm.set_x_tolerance(htol_);
41  lm.set_f_tolerance(ftol_);
42  lm.set_g_tolerance(gtol_);
43  bool success = lm.minimize(hv);
44  if (verbose_)
45  {
46  lm.diagnose_outcome(std::cout);
47  }
48 
49  if (success)
50  h_optimized.set(hv.data_block());
51  else
52  h_optimized = h_initial;
53  return success;
54 }
55 
57 optimize_p(std::vector<vgl_homg_point_2d<double> > const& points1,
58  std::vector<vgl_homg_point_2d<double> > const& points2,
60 {
61  //number of points must be the same
62  assert(points1.size() == points2.size());
63  int n = points1.size();
64  assert(n>4);
65 
66  //compute the normalizing transforms
68  if (!tr1.compute_from_points(points1))
69  return false;
70  if (!tr2.compute_from_points(points2))
71  return false;
72  //normalize the input point sets
73  std::vector<vgl_homg_point_2d<double> > tpoints1, tpoints2;
74  for (int i=0; i<n; ++i)
75  {
76  tpoints1.push_back(tr1(points1[i]));
77  tpoints2.push_back(tr2(points2[i]));
78  }
79  //Transform the initial homography into the normalized coordinate frame
80  // p1' = tr1 p1 , p2' = tr2 p2
81  // p2 = initial_h_(p1)
82  // (tr2^-1)p2' = initial_h_((tr1^-1)p1')
83  // p2' = (tr2*initial_h_*(tr1^-1))p1'
84  // thus initial_h_' = tr2*initial_h_*(tr1^-1)
85 
86  vgl_h_matrix_2d<double> tr1_inv = tr1.get_inverse();
87  vgl_h_matrix_2d<double> initial_h_norm = tr2*initial_h_*tr1_inv;
88 
89  //The Levenberg-Marquardt algorithm can now be applied
91  if (!optimize_h(tpoints1, tpoints2, initial_h_norm, hopt))
92  return false;
93 
94  // hopt has to be transformed back to the coordinate system of
95  // the original point sets, i.e.,
96  // p1' = tr1 p1 , p2' = tr2 p2
97  // hopt was determined from the transform relation
98  // p2' = hopt p1', thus
99  // (tr2 p2) = hopt (tr1 p1)
100  // p2 = (tr2^-1 hopt tr1) p1 = H p1
101  vgl_h_matrix_2d<double> tr2_inv = tr2.get_inverse();
102  H = tr2_inv*hopt*tr1;
103  return true;
104 }
105 
107 optimize_l(std::vector<vgl_homg_line_2d<double> > const& lines1,
108  std::vector<vgl_homg_line_2d<double> > const& lines2,
110 {
111  //number of lines must be the same
112  assert(lines1.size() == lines2.size());
113  assert(lines1.size() > 4);
114  //compute the normalizing transforms. By convention, these are point
115  //transformations that act properly if the input is a line,
116  // i.e., linei_norm = trx^-t(linei).
117  vgl_norm_trans_2d<double> tr1, tr2;
118  if (!tr1.compute_from_lines(lines1))
119  return false;
120  if (!tr2.compute_from_lines(lines2))
121  return false;
122  std::vector<vgl_homg_point_2d<double> > tlines1, tlines2;
123  for (const auto & lit : lines1)
124  {
125  // transform lines1 according to the normalizing transform
126  vgl_homg_line_2d<double> l = tr1(lit);
127  // convert the line to a point to use the same linear code
128  vgl_homg_point_2d<double> p(l.a(), l.b(), l.c());
129  tlines1.push_back(p);
130  }
131  for (const auto & lit : lines2)
132  {
133  // transform lines2 according to the normalizing transform
134  vgl_homg_line_2d<double> l = tr2(lit);
135  // convert the line to a point to use the same linear code
136  vgl_homg_point_2d<double> p(l.a(), l.b(), l.c());
137  tlines2.push_back(p);
138  }
139  // At this step, we have two line sets normalized as a set of points.
140  // The input to the point optimizer method must be a line transform,
141  // so the initial homography, which is by convention a point transform,
142  // must be converted as h_initial_line = h_initial_^-t
143 
144  // normalize the initial guess
145  vgl_h_matrix_2d<double> h_initial_line, h_line_opt, initial_h_norm;
146  vgl_h_matrix_2d<double> tr1_inv = tr1.get_inverse();
147  initial_h_norm = tr2*initial_h_*tr1_inv;
148  // convert to line form
149  vnl_matrix_fixed<double, 3, 3> const & Mp_init =
150  initial_h_norm.get_matrix();
151  vnl_matrix_fixed<double, 3, 3> Ml_init = vnl_inverse_transpose(Mp_init);
152  h_initial_line.set(Ml_init);
153 
154  //run the optimization to refine the line transform
155  if (!this->optimize_h(tlines1, tlines2, h_initial_line, h_line_opt))
156  return false;
157 
158  // Convert the optimized line transform back to point form.
159  vgl_h_matrix_2d<double> h_point_opt;
160  vnl_matrix_fixed<double, 3, 3> const & Ml_opt = h_line_opt.get_matrix();
161  vnl_matrix_fixed<double, 3, 3> Mp_opt = vnl_inverse_transpose(Ml_opt);
162  h_point_opt.set(Mp_opt);
163 
164  // Finally, h_point_opt has to be transformed back to the coordinate
165  // system of the original lines, i.e.,
166  // l1' = tr1 l1 , l2' = tr2 l2
167  // h_point_opt was determined from the transform relation
168  // l2' = h_point_opt l1', thus
169  // (tr2 l2) = hh (tr1 l1)
170  // p2 = (tr2^-1 hh tr1) p1 = H p1
171  vgl_h_matrix_2d<double> tr2inv = tr2.get_inverse();
172  H = tr2inv*h_point_opt*tr1;
173  return true;
174 }
175 
177 optimize_pl(std::vector<vgl_homg_point_2d<double> > const& points1,
178  std::vector<vgl_homg_point_2d<double> > const& points2,
179  std::vector<vgl_homg_line_2d<double> > const& lines1,
180  std::vector<vgl_homg_line_2d<double> > const& lines2,
182 {
183  //number of points must be the same
184  assert(points1.size() == points2.size());
185  int np = points1.size();
186  //number of lines must be the same
187  assert(lines1.size() == lines2.size());
188  int nl = lines1.size();
189  // Must have enough equations
190  assert((np+nl)>4);
191  //compute the normalizing transforms
192  vgl_norm_trans_2d<double> tr1, tr2;
193  if (!tr1.compute_from_points_and_lines(points1,lines1))
194  return false;
195  if (!tr2.compute_from_points_and_lines(points2,lines2))
196  return false;
197  std::vector<vgl_homg_point_2d<double> > tpoints1, tpoints2;
198  for (int i=0; i<np; ++i)
199  {
200  tpoints1.push_back(tr1(points1[i]));
201  tpoints2.push_back(tr2(points2[i]));
202  }
203  for (int i=0; i<nl; ++i)
204  {
205  double a=lines1[i].a(), b=lines1[i].b(), c=lines1[i].c(), d=std::sqrt(a*a+b*b);
206  tpoints1.push_back(tr1(vgl_homg_point_2d<double>(-a*c,-b*c,d)));
207  a=lines2[i].a(), b=lines2[i].b(), c=lines2[i].c(), d = std::sqrt(a*a+b*b);
208  tpoints2.push_back(tr2(vgl_homg_point_2d<double>(-a*c,-b*c,d)));
209  }
210 
211  vgl_h_matrix_2d<double> tr1_inv = tr1.get_inverse();
212  vgl_h_matrix_2d<double> initial_h_norm = tr2*initial_h_*tr1_inv;
213 
214  //The Levenberg-Marquardt algorithm can now be applied
216  if (!optimize_h(tpoints1, tpoints2, initial_h_norm, hopt))
217  return false;
218 
219  // hopt has to be transformed back to the coordinate system of
220  // the original point sets, i.e.,
221  // p1' = tr1 p1 , p2' = tr2 p2
222  // hopt was determined from the transform relation
223  // p2' = hopt p1', thus
224  // (tr2 p2) = hopt (tr1 p1)
225  // p2 = (tr2^-1 hopt tr1) p1 = H p1
226 
227  vgl_h_matrix_2d<double> tr2_inv = tr2.get_inverse();
228  H = tr2_inv*hopt*tr1;
229  return true;
230 }
vnl_matrix_fixed< T, 3, 3 > const & get_matrix() const
Return the 3x3 homography matrix.
vgl_h_matrix_2d< double > initial_h_
contains class vgl_h_matrix_2d_optimize_lmq
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.
bool optimize_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.
vgl_h_matrix_2d_optimize_lmq(vgl_h_matrix_2d< double > const &initial_h)
Constructor from initial homography to be optimized.
The similarity transform that normalizes a point set.
bool optimize_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 optimize_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_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_from_points(std::vector< vgl_homg_point_2d< T > > const &points, bool isotropic=true)
compute the normalizing transform.
#define l
vgl_h_matrix_2d get_inverse() const
Return the inverse homography.
Represents a homogeneous 2D point.
Definition: vgl_fwd.h:8
bool optimize_h(std::vector< vgl_homg_point_2d< double > > const &points1, std::vector< vgl_homg_point_2d< double > > const &points2, vgl_h_matrix_2d< double > const &h_initial, vgl_h_matrix_2d< double > &h_optimized)
the main routine for carrying out the optimization. (used by the others).