Points&Forces (survey)
Software tools facilitating the task of surveying architecture
h_compute.cxx
Go to the documentation of this file.
1 /*
2  Copyright 2008-2019 Pierre SMARS (smars@yuntech.edu.tw)
3  This program is free software: you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation, either version 2 of the License, or
6  (at your option) any later version.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16 #include <iostream>
17 #include <vector>
18 #include <mvl/HMatrix2D.h>
19 #include <mvl/HMatrix2DComputeLinear.h>
20 #include <vnl/vnl_least_squares_function.h>
21 #include <vnl/algo/vnl_levenberg_marquardt.h>
22 #include <vgl/vgl_distance.h>
23 #include "a_point.h"
24 #include "SimpleOpt.h"
25 
27 
28 CSimpleOpt::SOption g_rgOptions[] = {
29  { OPT_NONLIN, _T("-nl"), SO_NONE },
30  { OPT_NONLIN, _T("--non-linear"), SO_NONE },
31  { OPT_HOMG, _T("-hg"), SO_NONE },
32  { OPT_HOMG, _T("--homogeneous"), SO_NONE },
33  { OPT_VERB, _T("-v"), SO_NONE },
34  { OPT_VERB, _T("--verbose"), SO_NONE },
35  { OPT_HELP, _T("-?"), SO_NONE },
36  { OPT_HELP, _T("-h"), SO_NONE },
37  { OPT_HELP, _T("--help"), SO_NONE },
38  SO_END_OF_OPTIONS
39 };
40 bool verbose = false;
41 //---------------------------------------------------------------------------
42 int error(int val)
43 {
44 #include "h_compute.help"
45  return val;
46 }
47 //***************************************************************************
50 {
51  public:
52  a_homography_function( std::vector<vgl_homg_point_2d<double> > & points_im1,
53  std::vector<vgl_homg_point_2d<double> > & points_im2) :
54  vnl_least_squares_function(9, points_im1.size()*2, no_gradient),
55  points_im1_(points_im1),
56  points_im2_(points_im2)
57  {};
58  void f(const vnl_vector<double>& x, vnl_vector<double>& fx);
59  void scale1(const double s) {scale1_=s;}
60  protected:
61  std::vector<vgl_homg_point_2d<double> > & points_im1_;
62  std::vector<vgl_homg_point_2d<double> > & points_im2_;
63  double scale1_;
64 };
65 //---------------------------------------------------------------------------
66 void normalise_h(vnl_double_3x3 & h)
67 {
68  double no = 0.;
69  for (int i=0;i<3;i++)
70  no += h(i,0)*h(i,0)+h(i,1)*h(i,1)+h(i,2)*h(i,2);
71  h /= sqrt(no);
72 }
73 //---------------------------------------------------------------------------
74 void a_homography_function::f(const vnl_vector<double>& x, vnl_vector<double>& fx)
75 {
76  vnl_double_3x3 h0;
77  h0(0,0) = x[0]; h0(0,1) = x[1]; h0(0,2) = x[2];
78  h0(1,0) = x[3]; h0(1,1) = x[4]; h0(1,2) = x[5];
79  h0(2,0) = x[6]; h0(2,1) = x[7]; h0(2,2) = x[8];
80  normalise_h(h0);
81  HMatrix2D H(h0);
82  double d = 0;
83  for (int i = 0; i< points_im1_.size(); i++)
84  {
85  vgl_homg_point_2d<double> pt_project = H.transform_to_plane2(points_im1_[i]);
86  vgl_homg_point_2d<double> pt_true = points_im2_[i];
87  fx[i*2] = pt_project.x()/pt_project.w()-pt_true.x();
88  fx[i*2+1] = pt_project.y()/pt_project.w()-pt_true.y();
89  if (verbose)
90  {
91  double dist = vgl_distance(pt_project,pt_true);
92  d += dist*dist;
93  }
94  }
95  if (verbose)
96  std::cerr << sqrt(d/points_im1_.size())*scale1_ << " ";
97 }
98 //---------------------------------------------------------------------------
99 int main(int argc, char**argv)
100 {
101  //parse the options
102  bool nonlinear = false;
103  bool homogeneous = false;
104  CSimpleOpt args(argc, argv, g_rgOptions);
105  while (args.Next())
106  {
107  if (args.LastError() == SO_SUCCESS)
108  {
109  if (args.OptionId() == OPT_HELP)
110  return error(0);
111  else if (args.OptionId() == OPT_NONLIN)
112  nonlinear = true;
113  else if (args.OptionId() == OPT_HOMG)
114  homogeneous = true;
115  else if (args.OptionId() == OPT_VERB)
116  verbose = true;
117  else
118  return error(-1);
119  // handle option, using OptionId(), OptionText() and OptionArg()
120  } else {
121  std::cerr << "Invalid argument: " << args.OptionText() << std::endl;
122  return error(args.LastError());
123  // handle error, one of: SO_OPT_INVALID, SO_OPT_MULTIPLE,
124  // SO_ARG_INVALID, SO_ARG_INVALID_TYPE, SO_ARG_MISSING
125  }
126  }
127  if (args.FileCount() != 0) return error(-2);
128 
129  //read the data from the standard input
130  std::vector<HomgPoint2D > points1;
131  std::vector<HomgPoint2D > points2;
132  while (std::cin.good())
133  {
134  double x,y,w=1;
135  std::cin >> x >> y;
136  if (homogeneous)
137  std::cin >> w;
138  points1.push_back(HomgPoint2D(x,y,w));
139  std::cin >> x >> y;
140  if (homogeneous)
141  std::cin >> w;
142  points2.push_back(HomgPoint2D(x,y,w));
143  std::cin >> std::ws; // Eat whitespace
144  }
145  if (nonlinear && points1.size()<5)
146  {
147  std::cerr << "option nonlinear is only active when the number of points is higher than 4" << std::endl;
148  nonlinear = false;
149  }
150  //normalisation
151  int npts = points1.size();
152  //1. calculate centre of point cloud
153  double um1 = 0; double vm1 = 0;
154  double um2 = 0; double vm2 = 0;
155  for (int i=0; i< npts; i++)
156  {
157  um1 += points1[i].x();
158  vm1 += points1[i].y();
159  um2 += points2[i].x();
160  vm2 += points2[i].y();
161  }
162  um1 /= npts; vm1 /= npts;
163  um2 /= npts; vm2 /= npts;
164  //2. compute average size of point cloud
165  a_point im10(um1,vm1,0.);
166  a_point im20(um2,vm2,0.);
167  double di1m = 0.;
168  double di2m = 0.;
169  for (int i=0; i< npts; i++)
170  {
171  a_point im1(points1[i].x(),points1[i].y(),0.);
172  double di = dist(im1,im10);
173  di1m += di*di;
174  a_point im2(points2[i].x(),points2[i].y(),0.);
175  di = dist(im2,im20);
176  di2m += di*di;
177  }
178  di1m = sqrt(di1m/npts);
179  di2m = sqrt(di2m/npts);
180  //3. scale and translate point cloud
181  std::vector<vgl_homg_point_2d<double> > points_im1;
182  std::vector<vgl_homg_point_2d<double> > points_im2;
183  for (int i=0; i< npts; i++)
184  {
185  double u1 = (points1[i].x()-um1)/di1m;
186  double v1 = (points1[i].y()-vm1)/di1m;
187  points_im1.push_back(vgl_homg_point_2d<double>(u1,v1,1.0));
188  points1[i].set(points_im1[i].x(),points_im1[i].y(),points_im1[i].w());
189  double u2 = (points2[i].x()-um2)/di2m;
190  double v2 = (points2[i].y()-vm2)/di2m;
191  points_im2.push_back(vgl_homg_point_2d<double>(u2,v2,1.0));
192  points2[i].set(points_im2[i].x(),points_im2[i].y(),points_im2[i].w());
193  }
194  //4. prepare the transformation matrices
195  vnl_double_3x3 hk1(0.);
196  hk1.fill_diagonal(1./di1m);
197  hk1(0,2) = -um1/di1m;
198  hk1(1,2) = -vm1/di1m;
199  hk1(2,2) = 1.;
200  vnl_double_3x3 hk2(0.);
201  hk2.fill_diagonal(di2m);
202  hk2(0,2) = um2;
203  hk2(1,2) = vm2;
204  hk2(2,2) = 1.;
205  //end of normalisation
206 
207  //compute linear estimate
208  HMatrix2DComputeLinear computor;
209  HMatrix2D h = computor.compute(points1, points2);
210  //normalise matrix ||h|| = 1
211  vnl_double_3x3 h0 = h.get_matrix();
212  normalise_h(h0);
213  if (nonlinear)
214  {
215  vnl_vector<double> x(9);
216  unsigned int vv = 0;
217  for (int i = 0; i< 3; i++)
218  {
219  for (int j = 0; j< 3; j++)
220  {
221  x[vv] = h0(i,j);
222  vv++;
223  }
224  }
225  a_homography_function f(points_im1,points_im2);
226  f.scale1(di1m);
227  vnl_levenberg_marquardt levmarq(f);
228  if (verbose)
229  std::cerr << "Errors (in the unit of ur, vr, wr)" << std::endl;
230  levmarq.minimize(x);
231  if (verbose)
232  std::cerr << std::endl;
233  h0(0,0) = x[0]; h0(0,1) = x[1]; h0(0,2) = x[2];
234  h0(1,0) = x[3]; h0(1,1) = x[4]; h0(1,2) = x[5];
235  h0(2,0) = x[6]; h0(2,1) = x[7]; h0(2,2) = x[8];
236  if (verbose)
237  {
238  std::cerr << "Levenberg Marquardt" << std::endl
239  << "min: " << levmarq.get_end_error() << " at " << x << " (" << levmarq.get_end_error()*di1m*sqrt(2.) << " in the unit of ur, vr, wr)" << std::endl
240  << "iterations: " << levmarq.get_num_iterations() << " "
241  << "evaluations: " << levmarq.get_num_evaluations() << std::endl;
242  levmarq.diagnose_outcome();
243  }
244  }
245  //denormalisation
246  vnl_double_3x3 hmat2 = hk2*h0*hk1;
247  normalise_h(hmat2);
248  //output results
249  std::cout << "3 3" << std::endl;
250  std::cout << hmat2 << std::endl;
251  return 0;
252 }
253 
homography solver
Definition: h_compute.cxx:50
std::vector< vgl_homg_point_2d< double > > & points_im2_
Definition: h_compute.cxx:62
void scale1(const double s)
Definition: h_compute.cxx:59
a_homography_function(std::vector< vgl_homg_point_2d< double > > &points_im1, std::vector< vgl_homg_point_2d< double > > &points_im2)
Definition: h_compute.cxx:52
std::vector< vgl_homg_point_2d< double > > & points_im1_
Definition: h_compute.cxx:61
void f(const vnl_vector< double > &x, vnl_vector< double > &fx)
Definition: h_compute.cxx:74
int error(int val)
Definition: h_compute.cxx:42
CSimpleOpt::SOption g_rgOptions[]
Definition: h_compute.cxx:28
int main(int argc, char **argv)
Definition: h_compute.cxx:99
void normalise_h(vnl_double_3x3 &h)
Definition: h_compute.cxx:66
@ OPT_HELP
Definition: h_compute.cxx:26
@ OPT_VERB
Definition: h_compute.cxx:26
@ OPT_HOMG
Definition: h_compute.cxx:26
@ OPT_NONLIN
Definition: h_compute.cxx:26
bool verbose
Definition: h_compute.cxx:40