1 // This is oxl/mvl/FMatrixComputeNonLinear.cxx
2 //:
3 // \file
4
5 #include <iostream>
6 #include "FMatrixComputeNonLinear.h"
7
8 #ifdef _MSC_VER
9 # include "vcl_msvc_warnings.h"
10 #endif
11
12 #include "vnl/vnl_matrix.h"
13 #include <vnl/algo/vnl_levenberg_marquardt.h>
14 #include "vnl/vnl_double_2.h"
15 #include "vnl/vnl_double_3.h"
16 #include "vgl/vgl_homg_line_2d.h"
17 #include <mvl/HomgNorm2D.h>
18 #include <mvl/HomgPoint2D.h>
19 #include <mvl/HomgLine2D.h>
20 #include <mvl/HomgInterestPointSet.h>
21 #include <mvl/FMatrix.h>
22 #include <mvl/FManifoldProject.h>
23 #include <mvl/FMatrixCompute7Point.h>
24
25
26 // Seven parameters for a minimal parametrization of the F matrix
27 constexpr int FMatrixComputeNonLinear_nparams = 7;
28
29 //-----------------------------------------------------------------------------
30 //: Constructor
31 //
FMatrixComputeNonLinear(PairMatchSetCorner * matches)32 FMatrixComputeNonLinear::FMatrixComputeNonLinear(PairMatchSetCorner* matches)
33 : vnl_least_squares_function(FMatrixComputeNonLinear_nparams, matches->compute_match_count(), no_gradient),
34 data_size_(matches->compute_match_count()),
35 matches_(*matches),
36 one_(true)
37 {
38 // Copy matching points from matchset.
39 // Set up some initial variables
40 HomgInterestPointSet const* points1 = matches_.get_corners1();
41 HomgInterestPointSet const* points2 = matches_.get_corners2();
42 std::vector<HomgPoint2D> dead1, dead2;
43 std::vector<int> point1_int, point2_int;
44 matches_.extract_matches(dead1, point1_int, dead2, point2_int);
45 data_size_ = matches_.count();
46 points1_.resize(points1->size());
47 points2_.resize(points2->size());
48
49 // Get the actual image points
50 for (int a = 0; a < data_size_; a++) {
51 vnl_double_2 temp1;
52 temp1 = points1->get_2d(point1_int[a]);
53 points1_[a] = vgl_homg_point_2d<double>(temp1[0], temp1[1], 1.0);
54 }
55
56 for (int a = 0; a < data_size_; a++) {
57 vnl_double_2 temp2;
58 temp2 = points2->get_2d(point2_int[a]);
59 points2_[a] = vgl_homg_point_2d<double>(temp2[0], temp2[1], 1.0);
60 }
61 }
62
63 //-----------------------------------------------------------------------------
64 //: Compute the F Matrix by augmenting a 7 point basis
65
compute_basis(FMatrix * F,std::vector<int> basis)66 bool FMatrixComputeNonLinear::compute_basis(FMatrix* F, std::vector<int> basis)
67 {
68 one_ = false;
69 std::vector<vgl_homg_point_2d<double> > basis1(7), basis2(7);
70 for (int i = 0; i < 7; i++) {
71 int other = matches_.get_match_12(basis[i]);
72 if (other == -1)
73 std::cerr << "The basis index doesn't include a match for " << i << ".\n";
74 else {
75 vnl_double_2 p1 = matches_.get_corners1()->get_2d(basis[i]);
76 vnl_double_2 p2 = matches_.get_corners2()->get_2d(other);
77 basis1[i] = vgl_homg_point_2d<double>(p1[0], p1[1], 1.0);
78 basis2[i] = vgl_homg_point_2d<double>(p2[0], p2[1], 1.0);
79 }
80 }
81 basis1_ = basis1;
82 basis2_ = basis2;
83 return compute(F);
84 }
85
86
87 // General compute method, will alternate between either of the
88 // selected options
compute(FMatrix * F)89 bool FMatrixComputeNonLinear::compute(FMatrix* F)
90 {
91 FMatrix F_final;
92 // fm_fmatrix_nagmin
93 std::cerr << "FMatrixComputeNonLinear: matches = "<< data_size_ <<", using "<< FMatrixComputeNonLinear_nparams <<" parameters\n";
94 double so_far = 1e+8;
95 FMatrix norm_F = *F;
96 if (one_)
97 {
98 for (p_ = 0; p_ < 3; p_++)
99 {
100 for (q_ = 0; q_ < 3; q_++)
101 {
102 for (r_ = 0; r_ < 4; r_++)
103 {
104 FMatrix norm_F = *F;
105 int r1 = 0, c1 = 0, r2 = 0, c2 = 0;
106 get_plan(r1, c1, r2, c2);
107 vnl_matrix<double> mat;
108 norm_F.get(&mat);
109
110 switch (r_) {
111 case 0 :
112 mat /= mat.get(r1, c1);
113 break;
114 case 1 :
115 mat /= mat.get(r1, c2);
116 break;
117 case 2 :
118 mat /= mat.get(r2, c1);
119 break;
120 case 3 :
121 mat /= mat.get(r2, c2);
122 break;
123 default: // this cannot happen
124 break;
125 }
126
127 vnl_vector<double> f_params(FMatrixComputeNonLinear_nparams);
128 fmatrix_to_params(FMatrix(mat), f_params);
129 //FMatrix res = params_to_fmatrix(f_params);
130 vnl_levenberg_marquardt lm(*this);
131
132 // Adjusting these parameters will alter the
133 // convergence properties of the minimisation
134 lm.set_max_function_evals(200);
135 lm.set_f_tolerance(1e-6);
136 lm.set_x_tolerance(1e-6);
137 // lm.set_epsilon_function(1e-6);
138 lm.minimize(f_params);
139
140 if (lm.get_end_error() < so_far) {
141 so_far = lm.get_end_error();
142 std::cerr << "so_far : " << so_far << std::endl;
143 norm_F = params_to_fmatrix(f_params);
144 F_final = norm_F;
145 vgl_homg_point_2d<double> e1, e2;
146 F_final.get_epipoles(e1, e2);
147 std::cerr << "Epipole locations 1 : " << e1 << " 2 : " << e2 << '\n';
148 }
149 }
150 }
151 }
152 }
153 else
154 {
155 F_orig_ = norm_F;
156 vnl_vector<double> f_params(FMatrixComputeNonLinear_nparams, 0.0);
157 FMatrix res = params_to_fmatrix(f_params);
158 vnl_double_3x3 mat1 = norm_F.get_matrix();
159 vnl_double_3x3 mat2 = res.get_matrix();
160 mat1 /= mat1(2, 2);
161 mat2 /= mat2(2, 2);
162
163 vnl_levenberg_marquardt lm(*this);
164
165 // Adjusting these parameters will alter the
166 // convergence properties of the minimisation
167 lm.set_max_function_evals(100);
168 lm.set_f_tolerance(1e-4);
169 lm.set_x_tolerance(1e-3);
170 lm.set_epsilon_function(1e-4);
171 lm.minimize(f_params);
172
173 if (lm.get_end_error() < so_far) {
174 so_far = lm.get_end_error();
175 std::cerr << "so_far : " << so_far << std::endl;
176 for (int l = 0; l < 7; l++)
177 std::cerr << f_params(l) << std::endl;
178 norm_F = params_to_fmatrix(f_params);
179 F_final = norm_F;
180 lm.diagnose_outcome();
181 vgl_homg_point_2d<double> e1, e2;
182 F_final.get_epipoles(e1, e2);
183 std::cerr << "Epipole locations 1 : " << e1 << " 2 : " << e2 << '\n';
184 }
185 }
186 *F = F_final;
187 return true;
188 }
189
190 //-----------------------------------------------------------------------------
191 //: The virtual function from vnl_levenberg_marquardt which returns the RMS epipolar error and a vector of residuals.
f(const vnl_vector<double> & f_params,vnl_vector<double> & fx)192 void FMatrixComputeNonLinear::f(const vnl_vector<double>& f_params, vnl_vector<double>& fx)
193 {
194 FMatrix F = params_to_fmatrix(f_params);
195
196 fx = calculate_residuals(&F);
197 }
198
199 // Convert an F Matrix to the parameters to be minimised
fmatrix_to_params(const FMatrix & F,vnl_vector<double> & params)200 void FMatrixComputeNonLinear::fmatrix_to_params(const FMatrix& F, vnl_vector<double>& params)
201 {
202 // Leaving d as 1.0 on all the rotations
203 int c1= 0, r1 = 0, c2 = 0, r2 = 0;
204 get_plan(r1, c1, r2, c2);
205 double b = 0.0, a = 0.0, c = 0.0;
206 switch (r_) {
207 case 0 :
208 a = F.get(r1, c2);
209 b = -F.get(r2, c1);
210 c = -F.get(r2, c2);
211 break;
212 case 1 :
213 a = F.get(r1, c1);
214 b = -F.get(r2 ,c1);
215 c = -F.get(r2, c2);
216 break;
217 case 2 :
218 a = F.get(r1, c1);
219 b = F.get(r1, c2);
220 c = -F.get(r2, c2);
221 break;
222 case 3 :
223 a = F.get(r1, c1);
224 b = F.get(r1, c2);
225 c = -F.get(r2, c1);
226 break;
227 default: // this cannot happen
228 break;
229 }
230 vgl_homg_point_2d<double> one, two;
231 vnl_double_2 e1, e2;
232 F.get_epipoles(one, two);
233 vnl_double_3 e1h(one.x(), one.y(), one.w());
234 vnl_double_3 e2h(two.x(), two.y(), two.w());
235 e1h = e1h.normalize();
236 e2h = e2h.normalize();
237
238 switch (p_) {
239 case 0 :
240 e1 = vnl_double_2(e1h[1]/e1h[0], e1h[2]/e1h[0]);
241 break;
242 case 1 :
243 e1 = vnl_double_2(e1h[0]/e1h[1], e1h[2]/e1h[1]);
244 break;
245 case 2 :
246 e1 = vnl_double_2(e1h[0]/e1h[2], e1h[1]/e1h[2]);
247 break;
248 default: // this cannot happen
249 break;
250 }
251 switch (q_) {
252 case 0 :
253 e2 = vnl_double_2(e2h[1]/e2h[0], e2h[2]/e2h[0]);
254 break;
255 case 1 :
256 e2 = vnl_double_2(e2h[0]/e2h[1], e2h[2]/e2h[1]);
257 break;
258 case 2 :
259 e2 = vnl_double_2(e2h[0]/e2h[2], e2h[1]/e2h[2]);
260 break;
261 default: // this cannot happen
262 break;
263 }
264 params.put(0, a);
265 params.put(1, b);
266 params.put(2, c);
267 params.put(3, e1[0]);
268 params.put(4, e1[1]);
269 params.put(5, e2[0]);
270 params.put(6, e2[1]);
271 }
272
273
274 // Convert the parameters to an F Matrix
params_to_fmatrix(const vnl_vector<double> & params)275 FMatrix FMatrixComputeNonLinear::params_to_fmatrix(const vnl_vector<double>& params)
276 {
277 FMatrix ret;
278
279 if (one_)
280 {
281 vnl_double_3x3 ref = ret.get_matrix();
282 // Again the d is moved about through the different parametrizations
283 int c1= 0, r1 = 0, c2 = 0, r2 = 0;
284 get_plan(r1, c1, r2, c2);
285 double a = params.get(0);
286 double b = params.get(1);
287 double c = params.get(2);
288 double x = -params.get(3);
289 double y = -params.get(4);
290 double xd = -params.get(5);
291 double yd = -params.get(6);
292 double d;
293 switch (r_)
294 {
295 case 0 :
296 d = 1.0;
297 ref.put(r1, c2, a);
298 ref.put(r2, c1, -b);
299 ref.put(r2, c2, -c);
300 ref.put(r1, c1, d);
301 ref.put(r1, p_, d*x + a*y);
302 ref.put(r2, p_, -b*x - c*y);
303 ref.put(q_, c1, d*xd - b*yd);
304 ref.put(q_, c2, a*xd - c*yd);
305 ref.put(q_, p_, xd*(d*x + a*y) - yd*(b*x + c*y));
306 break;
307 case 1 :
308 d = 1.0;
309 ref.put(r1, c1, a);
310 ref.put(r2, c1, -b);
311 ref.put(r2, c2, -c);
312 ref.put(r1, c2, d);
313 ref.put(r1, p_, a*x + d*y);
314 ref.put(r2, p_, -b*x - c*y);
315 ref.put(q_, c1, a*xd - b*yd);
316 ref.put(q_, c2, d*xd - c*yd);
317 ref.put(q_, p_, xd*(a*x + d*y) - yd*(b*x + c*y));
318 break;
319 case 2 :
320 d = -1.0;
321 ref.put(r1, c1, a);
322 ref.put(r1, c2, b);
323 ref.put(r2, c2, -c);
324 ref.put(r2, c1, -d);
325 ref.put(r1, p_, a*x + b*y);
326 ref.put(r2, p_, -d*x - c*y);
327 ref.put(q_, c1, a*xd - d*yd);
328 ref.put(q_, c2, b*xd - c*yd);
329 ref.put(q_, p_, xd*(a*x + b*y) - yd*(d*x + c*y));
330 break;
331 case 3 :
332 d = -1.0;
333 ref.put(r1, c1, a);
334 ref.put(r1, c2, b);
335 ref.put(r2, c1, -c);
336 ref.put(r2, c2, -d);
337 ref.put(r1, p_, a*x + b*y);
338 ref.put(r2, p_, -c*x - d*y);
339 ref.put(q_, c1, a*xd - c*yd);
340 ref.put(q_, c2, b*xd - d*yd);
341 ref.put(q_, p_, xd*(a*x + b*y) - yd*(c*x + d*y));
342 break;
343 default: // this cannot happen
344 break;
345 }
346 ret.set(ref);
347 return ret;
348 }
349 else
350 {
351 std::vector<vgl_homg_point_2d<double> > new_points1(7);
352 vgl_homg_point_2d<double> e1, e2;
353 F_orig_.get_epipoles(e1, e2);
354 double e1nx = e1.x()/e1.w(), e1ny = e1.y()/e1.w();
355 // double e2nx = e2.x()/e2.w(), e2ny = e2.y()/e2.w();
356 for (int i = 0; i < 7; i++) {
357 double t1x = basis1_[i].x()/basis1_[i].w(), t1y = basis1_[i].y()/basis1_[i].w();
358 // double t2x = basis2_[i].x()/basis2_[i].w(), t2y = basis2_[i].y()/basis2_[i].w();
359 double grads1 = -(e1ny - t1y)/(e1nx - t1x);
360 // double grads2 = -(e2ny - t2y)/(e2nx - t2x);
361 new_points1[i] = vgl_homg_point_2d<double>(params[i]/grads1 + t1x, params[i]*grads1 + t1y, 1.0);
362 // new_points2[i] = vgl_homg_point_2d<double>(params[i]/grads2 + t2x, params[i]*grads2 + t2y, 1.0);
363 }
364 FMatrixCompute7Point computor(true, true);
365 std::vector<FMatrix*> ref;
366 if (!computor.compute(new_points1, basis2_, ref))
367 std::cerr << "FMatrixCompute7Point Failure\n";
368 double final = 0.0;
369 unsigned int num = 0;
370 for (unsigned int l = 0; l < ref.size(); l++) {
371 vnl_vector<double> res = calculate_residuals(ref[l]);
372 double so_far = 0.0;
373 for (double re : res)
374 so_far += re;
375 // std::cerr << "so_far : " << so_far << std::endl;
376 if (so_far < final) {
377 final = so_far;
378 num = l;
379 }
380 }
381 ret = *ref[num];
382
383 for (auto & l : ref)
384 delete l;
385
386 return ret;
387 }
388 }
389
390 // Forms a map of the different rank-2 structures for the F Matrix
get_plan(int & r1,int & c1,int & r2,int & c2) const391 void FMatrixComputeNonLinear::get_plan(int &r1, int &c1, int &r2, int &c2) const
392 {
393 switch (p_) {
394 case 0 :
395 c1 = 1;
396 c2 = 2;
397 break;
398 case 1 :
399 c1 = 0;
400 c2 = 2;
401 break;
402 case 2 :
403 c1 = 0;
404 c2 = 1;
405 break;
406 default: // this cannot happen
407 break;
408 }
409 switch (q_) {
410 case 0 :
411 r1 = 1;
412 r2 = 2;
413 break;
414 case 1 :
415 r1 = 0;
416 r2 = 2;
417 break;
418 case 2 :
419 r1 = 0;
420 r2 = 1;
421 break;
422 default: // this cannot happen
423 break;
424 }
425 }
426
perp_dist_squared(vgl_homg_point_2d<double> const & p,vgl_homg_line_2d<double> const & l)427 static double perp_dist_squared(vgl_homg_point_2d<double> const& p,
428 vgl_homg_line_2d <double> const& l)
429 {
430 double r = l.a()*p.x() + l.b()*p.y() + l.c()*p.w();
431 if (r == 0) return 0.0;
432 r /= p.w();
433 return r * r / (l.a()*l.a() + l.b()*l.b());
434 }
435
calculate_residuals(FMatrix * F)436 vnl_vector<double> FMatrixComputeNonLinear::calculate_residuals(FMatrix* F)
437 {
438 vnl_vector<double> fx(data_size_);
439 vnl_matrix<double> f(3, 3, 0.0);
440 F->get(&f);
441 f /= f.rms();
442 #if 0
443 vnl_matrix<double> ft = f.transpose();
444 vnl_matrix<double> z(3, 3, 0.0);
445 z.put(0, 0, 1.0);z.put(1, 1, 1.0);
446 vnl_matrix<double> pre1 = ft*z*f;
447 vnl_matrix<double> pre2 = f*z*ft;
448 #endif
449
450 for (int i = 0; i < data_size_; i++)
451 {
452 vgl_homg_point_2d<double> one = points1_[i], two = points2_[i];
453 double t = 0.0;
454 vgl_homg_line_2d<double> l1 = F->image2_epipolar_line(one);
455 vgl_homg_line_2d<double> l2 = F->image1_epipolar_line(two);
456 t += perp_dist_squared(two, l1);
457 t += perp_dist_squared(one, l2);
458 #if 0
459 vnl_double_3 oi(1.0); oi[0]=one.x()/one.w(); oi[1]=one.y()/one.w();
460 vnl_double_3 ti(1.0); ti[0]=two.x()/two.w(); ti[1]=two.x()/two.w();
461 vnl_double_3 p1 = ti*pre1;
462 vnl_double_3 p2 = oi*pre2;
463 double p11 = p1[0]*oi[0] + p1[1]*oi[1] + p1[2]*oi[2];
464 double p21 = p2[0]*ti[0] + p2[1]*ti[1] + p2[2]*ti[2];
465 double factor = 1.0/ p11 + 1.0/ p21;
466 #endif
467 fx[i] = t;//*factor;
468 }
469 return fx;
470 }
471