1 // This file is part of OpenMVG, an Open Multiple View Geometry C++ library.
2
3 // Copyright (c) 2012, 2013 Pierre MOULON.
4
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
9 #ifndef OPENMVG_ROBUST_ESTIMATOR_ACRANSAC_KERNEL_ADAPTATOR_HPP
10 #define OPENMVG_ROBUST_ESTIMATOR_ACRANSAC_KERNEL_ADAPTATOR_HPP
11
12 // Here a collection of A contrario Kernel adaptor.
13 // - See // [1] "Robust and accurate calibration of camera networks". PhD.
14 // - Authors: Pierre MOULON
15 //
16 //ACKernelAdaptor
17 // i.e. general two view estimation (affine, homography, fundamental)
18 //
19 // For pose/resection estimation:
20 // - ACKernelAdaptorResection
21 // For essential matrix estimation
22 // - ACKernelAdaptorEssential
23 // - ACKernelAdaptor_AngularRadianError
24 //
25 // Mainly it add correct data normalization and define the required functions
26 // by the ACRANSAC algorithm.
27 //
28
29 #include <vector>
30
31 #include "openMVG/multiview/conditioning.hpp"
32 #include "openMVG/multiview/essential.hpp"
33 #include "openMVG/numeric/extract_columns.hpp"
34
35 namespace openMVG {
36 namespace robust{
37
38 enum AContrarioParametrizationType
39 {
40 POINT_TO_LINE = 0,
41 POINT_TO_POINT = 1,
42 RADIAN_ANGLE = 2
43 };
44
45 template <int PARAMETRIZATION = AContrarioParametrizationType::POINT_TO_LINE>
46 struct ACParametrizationHelper
47 {
LogAlpha0openMVG::robust::ACParametrizationHelper48 static double LogAlpha0
49 (
50 const int w = 1,
51 const int h = 1,
52 const double scaling_factor = 1.
53 )
54 {
55 // Ratio of containing diagonal image rectangle over image area
56 const double D = std::hypot(w, h); // diameter
57 const double A = w * static_cast<double>(h); // area
58 return log10(2. * D / A / scaling_factor);
59 }
60
MultErroropenMVG::robust::ACParametrizationHelper61 static constexpr double MultError()
62 {
63 return .5;
64 }
65 };
66
67 template<>
LogAlpha0(const int w,const int h,const double scaling_factor)68 inline double ACParametrizationHelper<AContrarioParametrizationType::POINT_TO_POINT>::LogAlpha0
69 (
70 const int w,
71 const int h,
72 const double scaling_factor
73 )
74 {
75 // ratio of area: unit circle over image area
76 return log10(M_PI / (w*static_cast<double>(h)) / (scaling_factor*scaling_factor));
77 }
78
79 template<>
MultError()80 constexpr double ACParametrizationHelper<AContrarioParametrizationType::POINT_TO_POINT>::MultError()
81 {
82 return 1.;
83 }
84
85 template<>
LogAlpha0(const int w,const int h,const double scaling_factor)86 inline double ACParametrizationHelper<AContrarioParametrizationType::RADIAN_ANGLE>::LogAlpha0
87 (
88 const int w,
89 const int h,
90 const double scaling_factor
91 )
92 {
93 return log10(1. / 2.);
94 }
95
96 template<>
MultError()97 constexpr double ACParametrizationHelper<AContrarioParametrizationType::RADIAN_ANGLE>::MultError()
98 {
99 return 1. / 4.;
100 }
101
102 /// Two view Kernel adapter for the A contrario model estimator
103 /// Handle data normalization and compute the corresponding logalpha 0
104 /// that depends of the error model (point to line, or point to point)
105 /// This kernel adapter is working for affine, homography, fundamental matrix
106 /// estimation.
107 template <typename SolverArg,
108 typename ErrorArg,
109 typename UnnormalizerArg,
110 typename ModelArg = Mat3>
111 class ACKernelAdaptor
112 {
113 public:
114 using Solver = SolverArg;
115 using Model = ModelArg;
116 using ErrorT = ErrorArg;
117
ACKernelAdaptor(const Mat & x1,int w1,int h1,const Mat & x2,int w2,int h2,bool bPointToLine=true)118 ACKernelAdaptor(
119 const Mat &x1, int w1, int h1,
120 const Mat &x2, int w2, int h2, bool bPointToLine = true)
121 : x1_(x1.rows(), x1.cols()), x2_(x2.rows(), x2.cols()),
122 N1_(3,3), N2_(3,3), logalpha0_(0.0), bPointToLine_(bPointToLine)
123 {
124 assert(2 == x1_.rows());
125 assert(x1_.rows() == x2_.rows());
126 assert(x1_.cols() == x2_.cols());
127
128 NormalizePoints(x1, &x1_, &N1_, w1, h1);
129 NormalizePoints(x2, &x2_, &N2_, w2, h2);
130
131 // LogAlpha0 is used to make error data scale invariant
132 logalpha0_ =
133 (bPointToLine) ?
134 ACParametrizationHelper<AContrarioParametrizationType::POINT_TO_LINE>::LogAlpha0(w2, h2, N2_(0,0)) :
135 ACParametrizationHelper<AContrarioParametrizationType::POINT_TO_POINT>::LogAlpha0(w2, h2, N2_(0,0));
136 }
137
138 enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES };
139 enum { MAX_MODELS = Solver::MAX_MODELS };
140
Fit(const std::vector<uint32_t> & samples,std::vector<Model> * models) const141 void Fit
142 (
143 const std::vector<uint32_t> &samples,
144 std::vector<Model> *models
145 ) const
146 {
147 const auto x1 = ExtractColumns(x1_, samples);
148 const auto x2 = ExtractColumns(x2_, samples);
149 Solver::Solve(x1, x2, models);
150 }
151
Error(uint32_t sample,const Model & model) const152 double Error
153 (
154 uint32_t sample,
155 const Model &model
156 ) const
157 {
158 return ErrorT::Error(model, x1_.col(sample), x2_.col(sample));
159 }
160
Errors(const Model & model,std::vector<double> & vec_errors) const161 void Errors
162 (
163 const Model & model,
164 std::vector<double> & vec_errors
165 ) const
166 {
167 vec_errors.resize(x1_.cols());
168 for (uint32_t sample = 0; sample < x1_.cols(); ++sample)
169 vec_errors[sample] = ErrorT::Error(model, x1_.col(sample), x2_.col(sample));
170 }
171
NumSamples() const172 size_t NumSamples() const
173 {
174 return static_cast<size_t>(x1_.cols());
175 }
176
Unnormalize(Model * model) const177 void Unnormalize(Model * model) const
178 {
179 // Unnormalize model from the computed conditioning.
180 UnnormalizerArg::Unnormalize(N1_, N2_, model);
181 }
182
logalpha0() const183 double logalpha0() const {return logalpha0_;}
184
multError() const185 double multError() const
186 {
187 return (bPointToLine_) ?
188 ACParametrizationHelper<AContrarioParametrizationType::POINT_TO_LINE>::MultError() :
189 ACParametrizationHelper<AContrarioParametrizationType::POINT_TO_POINT>::MultError();
190 }
191
normalizer1() const192 Mat3 normalizer1() const {return N1_;}
normalizer2() const193 Mat3 normalizer2() const {return N2_;}
unormalizeError(double val) const194 double unormalizeError(double val) const {return sqrt(val) / N2_(0,0);}
195
196 private:
197 Mat x1_, x2_; // Normalized input data
198 Mat3 N1_, N2_; // Matrix used to normalize data
199 double logalpha0_; // Alpha0 is used to make the error adaptive to the image size
200 bool bPointToLine_; // Store if error model is pointToLine or point to point
201 };
202
203 struct UnnormalizerResection {
UnnormalizeopenMVG::robust::UnnormalizerResection204 static void Unnormalize(const Mat &T, const Mat &U, Mat34 *P){
205 *P = T.inverse() * (*P);
206 }
207 };
208
209 /// Pose/Resection Kernel adapter for the A contrario model estimator
210 template <typename SolverArg,
211 typename ErrorArg,
212 typename UnnormalizerArg,
213 typename ModelArg = Mat34>
214 class ACKernelAdaptorResection
215 {
216 public:
217 using Solver = SolverArg;
218 using Model = ModelArg;
219 using ErrorT = ErrorArg;
220
ACKernelAdaptorResection(const Mat & x2d,int w,int h,const Mat & x3D)221 ACKernelAdaptorResection
222 (
223 const Mat &x2d,
224 int w,
225 int h,
226 const Mat &x3D
227 ):
228 x2d_(x2d.rows(), x2d.cols()),
229 x3D_(x3D),
230 N1_(3,3),
231 logalpha0_(ACParametrizationHelper<AContrarioParametrizationType::POINT_TO_POINT>::LogAlpha0())
232 {
233 assert(2 == x2d_.rows());
234 assert(3 == x3D_.rows());
235 assert(x2d_.cols() == x3D_.cols());
236
237 NormalizePoints(x2d, &x2d_, &N1_, w, h);
238 }
239
240 enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES };
241 enum { MAX_MODELS = Solver::MAX_MODELS };
242
Fit(const std::vector<uint32_t> & samples,std::vector<Model> * models) const243 void Fit
244 (
245 const std::vector<uint32_t> &samples,
246 std::vector<Model> *models
247 ) const
248 {
249 const auto x1 = ExtractColumns(x2d_, samples);
250 const auto x2 = ExtractColumns(x3D_, samples);
251 Solver::Solve(x1, x2, models);
252 }
253
Error(uint32_t sample,const Model & model) const254 double Error(uint32_t sample, const Model &model) const
255 {
256 return ErrorT::Error(model, x2d_.col(sample), x3D_.col(sample));
257 }
258
Errors(const Model & model,std::vector<double> & vec_errors) const259 void Errors
260 (
261 const Model & model,
262 std::vector<double> & vec_errors
263 ) const
264 {
265 vec_errors.resize(x2d_.cols());
266 for (uint32_t sample = 0; sample < x2d_.cols(); ++sample)
267 vec_errors[sample] = ErrorT::Error(model, x2d_.col(sample), x3D_.col(sample));
268 }
269
NumSamples() const270 size_t NumSamples() const { return x2d_.cols(); }
271
Unnormalize(Model * model) const272 void Unnormalize(Model * model) const {
273 // Unnormalize model from the computed conditioning.
274 UnnormalizerArg::Unnormalize(N1_, Mat3::Identity(), model);
275 }
276
logalpha0() const277 double logalpha0() const {return logalpha0_;}
multError() const278 double multError() const {return ACParametrizationHelper<AContrarioParametrizationType::POINT_TO_POINT>::MultError();}
normalizer1() const279 Mat3 normalizer1() const {return Mat3::Identity();}
normalizer2() const280 Mat3 normalizer2() const {return N1_;}
unormalizeError(double val) const281 double unormalizeError(double val) const {return sqrt(val) / N1_(0,0);}
282
283 private:
284 Mat x2d_;
285 const Mat & x3D_;
286 Mat3 N1_; // Matrix used to normalize data
287 double logalpha0_; // Alpha0 is used to make the error adaptive to the image size
288 };
289
290 /// Essential matrix Kernel adaptor for the A contrario model estimator
291 template <typename SolverArg,
292 typename ErrorArg,
293 typename ModelArg = Mat3>
294 class ACKernelAdaptorEssential
295 {
296 public:
297 using Solver = SolverArg;
298 using Model = ModelArg;
299 using ErrorT = ErrorArg;
300
ACKernelAdaptorEssential(const Mat2X & x1,const Mat3X & bearing1,int w1,int h1,const Mat2X & x2,const Mat3X & bearing2,int w2,int h2,const Mat3 & K1,const Mat3 & K2)301 ACKernelAdaptorEssential
302 (
303 const Mat2X &x1, const Mat3X & bearing1, int w1, int h1,
304 const Mat2X &x2, const Mat3X & bearing2, int w2, int h2,
305 const Mat3 & K1, const Mat3 & K2
306 ):x1_(x1),
307 x2_(x2),
308 bearing1_(bearing1),
309 bearing2_(bearing2),
310 N1_(Mat3::Identity()),
311 N2_(Mat3::Identity()), logalpha0_(0.0),
312 K1_(K1), K2_(K2)
313 {
314 assert(2 == x1_.rows());
315 assert(x1_.rows() == x2_.rows());
316 assert(x1_.cols() == x2_.cols());
317
318 assert(3 == bearing1_.rows());
319 assert(bearing1_.rows() == bearing2_.rows());
320 assert(bearing1_.cols() == bearing2_.cols());
321
322 logalpha0_ = ACParametrizationHelper<AContrarioParametrizationType::POINT_TO_LINE>::LogAlpha0(w2, h2, 0.5);
323 }
324
325 enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES };
326 enum { MAX_MODELS = Solver::MAX_MODELS };
327
Fit(const std::vector<uint32_t> & samples,std::vector<Model> * models) const328 void Fit
329 (
330 const std::vector<uint32_t> &samples,
331 std::vector<Model> *models
332 ) const
333 {
334 const auto x1 = ExtractColumns(bearing1_, samples);
335 const auto x2 = ExtractColumns(bearing2_, samples);
336 Solver::Solve(x1, x2, models);
337 }
338
Error(uint32_t sample,const Model & model) const339 double Error
340 (
341 uint32_t sample,
342 const Model &model
343 ) const
344 {
345 Mat3 F;
346 FundamentalFromEssential(model, K1_, K2_, &F);
347 return ErrorT::Error(F, this->x1_.col(sample), this->x2_.col(sample));
348 }
349
Errors(const Model & model,std::vector<double> & vec_errors) const350 void Errors
351 (
352 const Model & model,
353 std::vector<double> & vec_errors
354 ) const
355 {
356 Mat3 F;
357 FundamentalFromEssential(model, K1_, K2_, &F);
358 vec_errors.resize(x1_.cols());
359 for (uint32_t sample = 0; sample < x1_.cols(); ++sample)
360 vec_errors[sample] = ErrorT::Error(F, this->x1_.col(sample), this->x2_.col(sample));
361 }
362
NumSamples() const363 size_t NumSamples() const { return x1_.cols(); }
Unnormalize(Model * model) const364 void Unnormalize(Model * model) const {}
logalpha0() const365 double logalpha0() const {return logalpha0_;}
multError() const366 double multError() const {return ACParametrizationHelper<AContrarioParametrizationType::POINT_TO_LINE>::MultError();}
normalizer1() const367 Mat3 normalizer1() const {return N1_;}
normalizer2() const368 Mat3 normalizer2() const {return N2_;}
unormalizeError(double val) const369 double unormalizeError(double val) const { return val; }
370
371 private:
372 Mat2X x1_, x2_; // image points
373 Mat3X bearing1_, bearing2_; // bearing vectors
374 Mat3 N1_, N2_; // Matrix used to normalize data
375 double logalpha0_; // Alpha0 is used to make the error adaptive to the image size
376 Mat3 K1_, K2_; // Intrinsic camera parameter
377 };
378
379 /// Essential Ortho matrix Kernel adaptor for the A contrario model estimator
380 template <
381 typename SolverArg,
382 typename ErrorArg,
383 typename ModelArg = Mat3>
384 class ACKernelAdaptorEssentialOrtho
385 {
386 public:
387 using Solver = SolverArg;
388 using Model = ModelArg;
389 using ErrorT = ErrorArg;
390
ACKernelAdaptorEssentialOrtho(const Mat3X & bearing1,int w1,int h1,const Mat3X & bearing2,int w2,int h2)391 ACKernelAdaptorEssentialOrtho
392 (
393 const Mat3X & bearing1, int w1, int h1,
394 const Mat3X & bearing2, int w2, int h2
395 ):
396 bearing1_(bearing1.colwise().hnormalized()),
397 bearing2_(bearing2.colwise().hnormalized()),
398 N1_(Mat3::Identity()),
399 N2_(Mat3::Identity()),
400 logalpha0_(0.0)
401 {
402
403 assert(2 == bearing1_.rows());
404 assert(bearing1_.rows() == bearing2_.rows());
405 assert(bearing1_.cols() == bearing2_.cols());
406
407 logalpha0_ = ACParametrizationHelper<AContrarioParametrizationType::POINT_TO_LINE>::LogAlpha0(w2, h2, 0.5);
408 }
409
410 enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES };
411 enum { MAX_MODELS = Solver::MAX_MODELS };
412
Fit(const std::vector<uint32_t> & samples,std::vector<Model> * models) const413 void Fit
414 (
415 const std::vector<uint32_t> &samples,
416 std::vector<Model> *models
417 ) const
418 {
419 const auto x1 = ExtractColumns(bearing1_, samples);
420 const auto x2 = ExtractColumns(bearing2_, samples);
421 Solver::Solve(x1, x2, models);
422 }
423
Error(uint32_t sample,const Model & model) const424 double Error
425 (
426 uint32_t sample,
427 const Model &model
428 ) const
429 {
430 return ErrorT::Error(model, bearing1_.col(sample), bearing2_.col(sample));
431 }
432
Errors(const Model & model,std::vector<double> & vec_errors) const433 void Errors
434 (
435 const Model & model,
436 std::vector<double> & vec_errors
437 ) const
438 {
439 vec_errors.resize(bearing1_.cols());
440 for (uint32_t sample = 0; sample < bearing1_.cols(); ++sample)
441 vec_errors[sample] = ErrorT::Error(model, bearing1_.col(sample), bearing2_.col(sample));
442 }
443
NumSamples() const444 size_t NumSamples() const { return bearing1_.cols(); }
Unnormalize(Model * model) const445 void Unnormalize(Model * model) const {}
logalpha0() const446 double logalpha0() const {return logalpha0_;}
multError() const447 double multError() const {return ACParametrizationHelper<AContrarioParametrizationType::POINT_TO_LINE>::MultError();}
normalizer1() const448 Mat3 normalizer1() const {return N1_;}
normalizer2() const449 Mat3 normalizer2() const {return N2_;}
unormalizeError(double val) const450 double unormalizeError(double val) const { return val; }
451
452 private:
453 Mat2X bearing1_, bearing2_; // hnormalized bearing vectors
454 Mat3 N1_, N2_; // Matrix used to normalize data
455 double logalpha0_; // Alpha0 is used to make the error adaptive to the image size
456 };
457
458
459 /// Two view Kernel adapter for the A contrario model estimator.
460 /// Specialization to handle radian angular residual error.
461 template <
462 typename SolverArg,
463 typename ErrorArg,
464 typename ModelArg = Mat3>
465 class ACKernelAdaptor_AngularRadianError
466 {
467 public:
468 using Solver = SolverArg;
469 using Model = ModelArg;
470 using ErrorT = ErrorArg;
471
ACKernelAdaptor_AngularRadianError(const Mat & xA,const Mat & xB)472 ACKernelAdaptor_AngularRadianError
473 (
474 const Mat & xA,
475 const Mat & xB
476 ):
477 x1_(xA), x2_(xB),
478 logalpha0_(ACParametrizationHelper<AContrarioParametrizationType::RADIAN_ANGLE>::LogAlpha0())
479 {
480 assert(3 == x1_.rows());
481 assert(x1_.rows() == x2_.rows());
482 assert(x1_.cols() == x2_.cols());
483 }
484
485 enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES };
486 enum { MAX_MODELS = Solver::MAX_MODELS };
487
Fit(const std::vector<uint32_t> & samples,std::vector<Model> * models) const488 void Fit
489 (
490 const std::vector<uint32_t> &samples,
491 std::vector<Model> *models
492 ) const
493 {
494 const Mat x1 = ExtractColumns(x1_, samples);
495 const Mat x2 = ExtractColumns(x2_, samples);
496 Solver::Solve(x1, x2, models);
497 }
498
Error(uint32_t sample,const Model & model) const499 double Error
500 (
501 uint32_t sample,
502 const Model &model
503 ) const
504 {
505 return Square(ErrorT::Error(model, x1_.col(sample), x2_.col(sample)));
506 }
507
Errors(const Model & model,std::vector<double> & vec_errors) const508 void Errors
509 (
510 const Model & model,
511 std::vector<double> & vec_errors
512 ) const
513 {
514 vec_errors.resize(x1_.cols());
515 for (uint32_t sample = 0; sample < x1_.cols(); ++sample)
516 vec_errors[sample] = Square(ErrorT::Error(model, x1_.col(sample), x2_.col(sample)));
517 }
518
NumSamples() const519 size_t NumSamples() const
520 {
521 return static_cast<size_t>(x1_.cols());
522 }
523
Unnormalize(Model * model) const524 void Unnormalize(Model * model) const {
525 //-- Do nothing, no normalization in the angular case
526 }
527
logalpha0() const528 double logalpha0() const {return logalpha0_;}
529
multError() const530 double multError() const {return ACParametrizationHelper<AContrarioParametrizationType::RADIAN_ANGLE>::MultError();}
531
normalizer1() const532 Mat3 normalizer1() const {return Mat3::Identity();}
normalizer2() const533 Mat3 normalizer2() const {return Mat3::Identity();}
unormalizeError(double val) const534 double unormalizeError(double val) const {return sqrt(val);}
535
536 private:
537 Mat x1_, x2_; // Normalized input data
538 double logalpha0_; // Alpha0 is used to make the error scale invariant
539 };
540
541 } // namespace robust
542 } // namespace openMVG
543
544 #endif // OPENMVG_ROBUST_ESTIMATOR_ACRANSAC_KERNEL_ADAPTATOR_HPP
545