Points&Forces (survey)
Software tools facilitating the task of surveying architecture
radial.cxx
Go to the documentation of this file.
1 /*
2  Copyright 2002-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 <osl/osl_fit_circle.h>
17 #include <osl/osl_easy_canny.h>
18 #include <osl/osl_save_topology.h>
19 #include <vil1/vil1_load.h>
20 #include <list>
21 #include <vector>
22 #include <iostream>
23 #include <vnl/vnl_least_squares_function.h>
24 #include <vnl/algo/vnl_levenberg_marquardt.h>
25 #include <mvl/HMatrix2D.h>
26 #include <mvl/HMatrix2DComputeLinear.h>
27 #include <math.h>
28 #include <stdlib.h>
29 
30 #include "a_point.h"
31 #include "SimpleOpt.h"
32 
34 
35 CSimpleOpt::SOption g_rgOptions[] = {
36  { OPT_NX, _T("-nx"), SO_REQ_SEP },
37  { OPT_NY, _T("-ny"), SO_REQ_SEP },
38  { OPT_RATIO, _T("-r"), SO_REQ_SEP },
39  { OPT_RATIO, _T("--ratio"), SO_REQ_SEP },
40  { OPT_TOL, _T("-t"), SO_REQ_SEP },
41  { OPT_TOL, _T("--tolerance"), SO_REQ_SEP },
42  { OPT_DATA, _T("-d"), SO_NONE },
43  { OPT_DATA, _T("--data"), SO_NONE },
44  { OPT_HELP, _T("-?"), SO_NONE },
45  { OPT_HELP, _T("-h"), SO_NONE },
46  { OPT_HELP, _T("--help"), SO_NONE },
47  SO_END_OF_OPTIONS
48 };
49 
50 //***************************************************************************
53 {
54  public:
55  a_radial_function( std::vector<HomgPoint2D> & points_image,
56  std::vector<HomgPoint2D> & points_world) :
57  vnl_least_squares_function(12, points_image.size()*2, no_gradient),
58  points_image_(points_image),
59  points_world_(points_world)
60  {};
61  void f(const vnl_vector<double>& x, vnl_vector<double>& fx);
62  protected:
63  std::vector<HomgPoint2D> & points_image_;
64  std::vector<HomgPoint2D> & points_world_;
65 };
66 //---------------------------------------------------------------------------
67 void a_radial_function::f(const vnl_vector<double>& x, vnl_vector<double>& fx)
68 {
69  vnl_double_3x3 h0;
70  h0(0,0) = x[0]; h0(0,1) = x[1]; h0(0,2) = x[2];
71  h0(1,0) = x[3]; h0(1,1) = x[4]; h0(1,2) = x[5];
72  h0(2,0) = x[6]; h0(2,1) = x[7]; h0(2,2) = x[8];
73  HMatrix2D H(h0);
74  double k1 = x[9];
75  double k2 = x[10];
76  double k3 = x[11];
77  double k0 = 1.-k1-k2-k3;
78  double d = 0.;
79  double u0 = 0.;
80  double v0 = 0.;
81  for (int i = 0; i< points_image_.size(); i++)
82  {
83  HomgPoint2D im = H.transform_to_plane2(points_world_[i]);
84  double u = points_image_[i].x();
85  double v = points_image_[i].y();
86  double r = sqrt((u-u0)*(u-u0)+(v-v0)*(v-v0));
87  double L = k0+k1*r+k2*r*r+k3*r*r*r;
88  double un = u0+L*(u-u0);
89  double vn = v0+L*(v-v0);
90  fx[i*2] = un-im.x()/im.w();
91  fx[i*2+1] = vn-im.y()/im.w();
92  // double dn = sqrt(fx[i*2]*fx[i*2]+fx[i*2+1]*fx[i*2+1]);
93  // d += dn;
94  }
95  // std::cerr << "error: " << d/points_image_.size()*999.3 << std::endl;
96 }
98 //---------------------------------------------------------------------------
99 int error(int val)
100 {
101 #include "radial.help"
102  return val;
103 }
104 //---------------------------------------------------------------------------
105 int main(int argc, char *argv[])
106 {
107  int nrow = 10;
108  int ncol = 14;
109  double poss_var = 0.25;
110  bool with_data = false;
111  double ratio = 1.;
112  CSimpleOpt args(argc, argv, g_rgOptions);
113  while (args.Next())
114  {
115  if (args.LastError() == SO_SUCCESS)
116  {
117  if (args.OptionId() == OPT_HELP)
118  return error(0);
119  else if (args.OptionId() == OPT_DATA)
120  with_data = true;
121  else if (args.OptionId() == OPT_NX)
122  {
123  std::ostringstream o;
124  o << args.OptionArg();
125  std::istringstream in(o.str().c_str());
126  in >> ncol;
127  } else if (args.OptionId() == OPT_NY)
128  {
129  std::ostringstream o;
130  o << args.OptionArg();
131  std::istringstream in(o.str().c_str());
132  in >> nrow;
133  } else if (args.OptionId() == OPT_RATIO)
134  {
135  std::ostringstream o;
136  o << args.OptionArg();
137  std::istringstream in(o.str().c_str());
138  in >> ratio;
139  } else if (args.OptionId() == OPT_TOL)
140  {
141  std::ostringstream o;
142  o << args.OptionArg();
143  std::istringstream in(o.str().c_str());
144  in >> poss_var;
145  } else
146  return error(-1);
147  // handle option, using OptionId(), OptionText() and OptionArg()
148  } else {
149  std::cerr << "Invalid argument: " << args.OptionText() << std::endl;
150  return error(args.LastError());
151  // handle error, one of: SO_OPT_INVALID, SO_OPT_MULTIPLE,
152  // SO_ARG_INVALID, SO_ARG_INVALID_TYPE, SO_ARG_MISSING
153  }
154  }
155 
156  if (args.FileCount() != 1) return error(-2);
157 
158  const int npts = nrow*ncol;
159  const double cx_w = (ncol-1)/2.*ratio;
160  const double cy_w = (nrow-1)/2.;
161  const double s_w = sqrt(cx_w*cx_w+cy_w*cy_w);
162  std::vector<a_point> centers_w;
163  std::vector<HomgPoint2D> centers_w2;
164  for (int i=0; i< nrow; i++)
165  {
166  for (int j=0; j<ncol; j++)
167  {
168  double x = ratio*(j-cx_w)/s_w;
169  double y = (i-cy_w)/s_w;
170  centers_w.push_back(a_point(x,y,0.));
171  centers_w2.push_back(HomgPoint2D(x,y,1.));
172  }
173  }
174  // input image
175  vil1_image image = vil1_load(args.File(0));
176  if (!image) return 2;
177  double width_i = image.width();
178  double height_i = image.height();
179  double cx_i = (width_i-1)/2.;
180  double cy_i = (height_i-1)/2.;
181  double s_i = sqrt(cx_i*cx_i+cy_i*cy_i);
182  // Canny edgel extraction
183  std::list<osl_edge*> edgels;
184  osl_easy_canny(0, image, &edgels);
185  double rm=0;
186  for (std::list<osl_edge*>::iterator it= edgels.begin(); it!= edgels.end(); it++)
187  {
188  osl_edge const *e = *it;
189  osl_fit_circle fit(*e);
190  rm += fit.radius();
191  }
192  rm /= edgels.size();
193  std::vector<a_point> centers_i;
194  if (with_data)
195  std::cout << npts << std::endl;
196  for (std::list<osl_edge*>::iterator it= edgels.begin(); it!= edgels.end(); it++)
197  {
198  osl_edge const *e = *it;
199  osl_fit_circle fit(*e);
200  double rf = fit.radius()/rm;
201  if ((rf>1.-poss_var)&&(rf<1.+poss_var))
202  {
203  double x = (fit.center().y()-cx_i)/s_i;
204  double y = (height_i-1-fit.center().x()-cy_i)/s_i;
205  a_point p(x,y,0.);
206  //std::cerr << p << std::endl;
207  centers_i.push_back(p);
208  }
209  }
210  if (centers_i.size() != npts)
211  {
212  std::cerr << "Problem" << std::endl;
213  std::cerr << "Number of circle identified is " << centers_i.size() << " and should be " << npts << std::endl;
214  std::cerr << "Did you try to blur the image? Is contrast high enough? Do you have borders?" << std::endl;
215  exit(1);
216  }
217  //1. estimate the transformation H between image and world
218  //1.1 estimate the position of 4 corners...
219  //1.1.1 estimate the extremes on the diagonal
220  int i_h[4];
221  double pm = 100000;
222  double pM = -100000;
223  for (int i =0; i< centers_i.size(); i++)
224  {
225  a_point p = centers_i[i];
226  double ptax = p.x()+p.y();
227  if (ptax<pm)
228  {
229  pm = ptax;
230  i_h[0] = i;
231  }
232  if (ptax>pM)
233  {
234  pM = ptax;
235  i_h[3] = i;
236  }
237  }
238  double x_h[4], y_h[4];
239  a_point p_h[4];
240  p_h[0] = centers_i[i_h[0]];
241  p_h[3] = centers_i[i_h[3]];
242  //1.1.2 estimate the other 2 extremes (maximising the surface of triangles formed with diagonal)
243  pm = 100000;
244  pM = -100000;
245  for (int i =0; i< centers_i.size(); i++)
246  {
247  a_point p = centers_i[i];
248  a_point pn = cross(p_h[3]-p_h[0],p-p_h[0]);
249  double ptax = pn.norm();
250  if (pn.z() < 0) ptax = -ptax;
251  if (ptax<pm)
252  {
253  pm = ptax;
254  i_h[1] = i;
255  }
256  if (ptax>pM)
257  {
258  pM = ptax;
259  i_h[2] = i;
260  }
261  }
262  p_h[1] = centers_i[i_h[1]];
263  p_h[2] = centers_i[i_h[2]];
264  std::cerr << p_h[0] << " " << p_h[1] << " " << p_h[2] << " " << p_h[3] << std::endl;
265  //1.2 estimate H
266  //HMatrix2DCompute4Point computor;
267  HMatrix2DComputeLinear computor(false);
268  std::vector<HomgPoint2D> corners_w,corners_i;
269  a_point pp = centers_w[0];
270  corners_w.push_back(HomgPoint2D(pp.x(),pp.y(),1.));
271  pp = centers_w[ncol-1];
272  corners_w.push_back(HomgPoint2D(pp.x(),pp.y(),1.));
273  pp = centers_w[ncol*(nrow-1)];
274  corners_w.push_back(HomgPoint2D(pp.x(),pp.y(),1.));
275  pp = centers_w[ncol*nrow-1];
276  corners_w.push_back(HomgPoint2D(pp.x(),pp.y(),1.));
277  for (int i=0;i<4;i++)
278  {
279  pp = centers_i[i_h[i]];
280  corners_i.push_back(HomgPoint2D(pp.x(),pp.y(),1.));
281  std::cerr << corners_w[i] << " " << corners_i[i] << std::endl;
282  }
283  HMatrix2D H = computor.compute(corners_w,corners_i);
284  std::cerr << H << std::endl;
285  //find the corresponding points
286  std::vector<HomgPoint2D> centers_i2;
287  for (int i =0; i< centers_i.size(); i++)
288  {
289  HomgPoint2D p_w(centers_w[i].x(),centers_w[i].y(),1.);
290  HomgPoint2D p_ie = H.transform_to_plane2(p_w);
291  double d0 = 1.e30;
292  int j0;
293  for (int j=0; j<centers_i.size(); j++)
294  {
295  HomgPoint2D p_i(centers_i[j].x(),centers_i[j].y(),1.);
296  double d = sqrt((p_i.x()/p_i.w()-p_ie.x()/p_ie.w())*(p_i.x()/p_i.w()-p_ie.x()/p_ie.w())+
297  (p_i.y()/p_i.w()-p_ie.y()/p_ie.w())*(p_i.y()/p_i.w()-p_ie.y()/p_ie.w()));
298  if (d<d0)
299  {
300  d0 = d;
301  j0 = j;
302  }
303  }
304  centers_i2.push_back(HomgPoint2D(centers_i[j0].x(),centers_i[j0].y(),1.));
305  }
306  HMatrix2D H2 = computor.compute(centers_w2,centers_i2);
307  std::cerr << H2 << std::endl;
308  for (int i =0; i< centers_i.size(); i++)
309  {
310  HomgPoint2D pi = centers_i2[i];
311  HomgPoint2D pw = centers_w2[i];
312  HomgPoint2D pi2 = H2.transform_to_plane2(pw);
313  double d = sqrt((pi.x()/pi.w()-pi2.x()/pi2.w())*(pi.x()/pi.w()-pi2.x()/pi2.w())+
314  (pi.y()/pi.w()-pi2.y()/pi2.w())*(pi.y()/pi.w()-pi2.y()/pi2.w()));
315  // std::cerr << "i: " << i << " d: " << d*s_i << std::endl;
316  if (with_data)
317  std::cout << centers_i2[i].x() << " " << centers_i2[i].y() << " " << d*10 << std::endl;
318  }
319  vnl_vector<double> x(12);
320  int vv = 0;
321  for (int i = 0; i< 3; i++)
322  {
323  for (int j = 0; j< 3; j++)
324  {
325  x[vv] = H2.get(i,j);
326  vv++;
327  }
328  }
329  x[9] = 0.; x[10] = 0.; x[11] = 0.;
330  a_radial_function f(centers_i2,centers_w2);
331  vnl_levenberg_marquardt levmarq(f);
332  // levmarq.set_max_function_evals(10000);
333  // levmarq.set_g_tolerance(levmarq.get_g_tolerance()/10.);
334  levmarq.minimize(x);
335  std::cerr << "Levenberg Marquardt" << std::endl
336  << "min: " << levmarq.get_end_error() << " at " << x << std::endl
337  << "iterations: " << levmarq.get_num_iterations() << " "
338  << "evaluations: " << levmarq.get_num_evaluations() << std::endl;
339  levmarq.diagnose_outcome();
340  if (!with_data)
341  std::cout << " -k1=" << x[9] << " -k2=" << x[10] << " -k3=" << x[11] << " -s" << std::endl;
342  return 0;
343 }
camera solver with radial distortions
Definition: radial.cxx:53
std::vector< HomgPoint2D > & points_image_
Definition: radial.cxx:63
a_radial_function(std::vector< HomgPoint2D > &points_image, std::vector< HomgPoint2D > &points_world)
Definition: radial.cxx:55
std::vector< HomgPoint2D > & points_world_
Definition: radial.cxx:64
void f(const vnl_vector< double > &x, vnl_vector< double > &fx)
Definition: radial.cxx:67
e_point cross(e_point &a, e_point &b)
Definition: e_point.cxx:105
double v(const uint32_t step, const uint32_t n)
Definition: generate.cxx:42
const double pi
Definition: orient.cxx:49
std::istringstream in
Definition: ply2tri.cxx:32
int main(int argc, char *argv[])
Definition: radial.cxx:105
@ OPT_NY
Definition: radial.cxx:33
@ OPT_TOL
Definition: radial.cxx:33
@ OPT_NX
Definition: radial.cxx:33
@ OPT_HELP
Definition: radial.cxx:33
@ OPT_DATA
Definition: radial.cxx:33
@ OPT_RATIO
Definition: radial.cxx:33
int error(int val)
camera solver with radial distortions
Definition: radial.cxx:99
CSimpleOpt::SOption g_rgOptions[]
Definition: radial.cxx:35