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