1 // This file is part of OpenMVG, an Open Multiple View Geometry C++ library.
2 
3 // Copyright (c) 2015 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_CAMERAS_CAMERA_PINHOLE_RADIAL_HPP
10 #define OPENMVG_CAMERAS_CAMERA_PINHOLE_RADIAL_HPP
11 
12 #include <vector>
13 
14 #include "openMVG/cameras/Camera_Common.hpp"
15 #include "openMVG/cameras/Camera_Pinhole.hpp"
16 
17 namespace openMVG
18 {
19 namespace cameras
20 {
21 
22 /**
23 * @brief Namespace handling radial distortion computation
24 */
25 namespace radial_distortion
26 {
27 
28 
29 /**
30 * @brief Solve by bisection the p' radius such that Square(disto(radius(p'))) = r^2
31 * @param params Parameters of the distortion
32 * @param r2 Target radius
33 * @param functor Functor used to paramatrize the distortion
34 * @param epsilon Error driven threshold
35 * @return Best radius
36 */
37 template <class Disto_Functor>
bisection_Radius_Solve(const std::vector<double> & params,double r2,Disto_Functor & functor,double epsilon=1e-10)38 double bisection_Radius_Solve(
39   const std::vector<double> & params, // radial distortion parameters
40   double r2, // targeted radius
41   Disto_Functor & functor,
42   double epsilon = 1e-10 // criteria to stop the bisection
43 )
44 {
45   // Guess plausible upper and lower bound
46   double lowerbound = r2, upbound = r2;
47   while ( functor( params, lowerbound ) > r2 )
48   {
49     lowerbound /= 1.05;
50   }
51   while ( functor( params, upbound ) < r2 )
52   {
53     upbound *= 1.05;
54   }
55 
56   // Perform a bisection until epsilon accuracy is not reached
57   while ( epsilon < upbound - lowerbound )
58   {
59     const double mid = .5 * ( lowerbound + upbound );
60     if ( functor( params, mid ) > r2 )
61     {
62       upbound = mid;
63     }
64     else
65     {
66       lowerbound = mid;
67     }
68   }
69   return .5 * ( lowerbound + upbound );
70 }
71 
72 } // namespace radial_distortion
73 
74 /**
75  * @brief Implement a Pinhole camera with a 1 radial distortion coefficient.
76  * \f$ x_d = x_u (1 + K_1 r^2 ) \f$
77  */
78 class Pinhole_Intrinsic_Radial_K1 : public Pinhole_Intrinsic
79 {
80   using class_type = Pinhole_Intrinsic_Radial_K1;
81 
82   protected:
83     /// center of distortion is applied by the Intrinsics class
84     std::vector<double> params_; // K1
85 
86   public:
87 
88     /**
89     * @brief Constructor
90     * @param w Width of the image
91     * @param h Height of the image
92     * @param focal Focal (in pixel) of the camera
93     * @param ppx Principal point on X-Axis
94     * @param ppy Principal point on Y-Axis
95     * @param k1 Distortion coefficient
96     */
Pinhole_Intrinsic_Radial_K1(int w=0,int h=0,double focal=0.0,double ppx=0,double ppy=0,double k1=0.0)97     Pinhole_Intrinsic_Radial_K1(
98       int w = 0, int h = 0,
99       double focal = 0.0, double ppx = 0, double ppy = 0,
100       double k1 = 0.0 )
101       : Pinhole_Intrinsic( w, h, focal, ppx, ppy ),
102         params_({k1})
103     {
104 
105     }
106 
107     ~Pinhole_Intrinsic_Radial_K1() override = default;
108 
109     /**
110     * @brief Tell from which type the embed camera is
111     * @retval PINHOLE_CAMERA_RADIAL1
112     */
getType() const113     EINTRINSIC getType() const override
114     {
115       return PINHOLE_CAMERA_RADIAL1;
116     }
117 
118     /**
119     * @brief Does the camera model handle a distortion field?
120     * @retval true if intrinsic holds distortion
121     * @retval false if intrinsic does not hold distortion
122     */
have_disto() const123     bool have_disto() const override
124     {
125       return true;
126     }
127 
128     /**
129     * @brief Add the distortion field to a point (that is in normalized camera frame)
130     * @param p Point before distortion computation (in normalized camera frame)
131     * @return point with distortion
132     */
add_disto(const Vec2 & p) const133     Vec2 add_disto( const Vec2 & p ) const override
134     {
135       const double k1 = params_[0];
136 
137       const double r2 = p( 0 ) * p( 0 ) + p( 1 ) * p( 1 );
138       const double r_coeff = ( 1. + k1 * r2 );
139 
140       return ( p * r_coeff );
141     }
142 
143     /**
144     * @brief Remove the distortion to a camera point (that is in normalized camera frame)
145     * @param p Point with distortion
146     * @return Point without distortion
147     */
remove_disto(const Vec2 & p) const148     Vec2 remove_disto( const Vec2& p ) const override
149     {
150       // Compute the radius from which the point p comes from thanks to a bisection
151       // Minimize disto(radius(p')^2) == actual Squared(radius(p))
152 
153       const double r2 = p( 0 ) * p( 0 ) + p( 1 ) * p( 1 );
154       const double radius = ( r2 == 0 ) ?
155                             1. :
156                             ::sqrt( radial_distortion::bisection_Radius_Solve( params_, r2, distoFunctor ) / r2 );
157       return radius * p;
158     }
159 
160     /**
161     * @brief Data wrapper for non linear optimization (get data)
162     * @return vector of parameter of this intrinsic
163     */
getParams() const164     std::vector<double> getParams() const override
165     {
166       std::vector<double> params = Pinhole_Intrinsic::getParams();
167       params.insert(params.end(), std::begin(params_), std::end(params_));
168       return params;
169     }
170 
171     /**
172     * @brief Data wrapper for non linear optimization (update from data)
173     * @param params List of params used to update this intrinsic
174     * @retval true if update is correct
175     * @retval false if there was an error during update
176     */
updateFromParams(const std::vector<double> & params)177     bool updateFromParams( const std::vector<double> & params ) override
178     {
179       if ( params.size() == 4 )
180       {
181         *this = Pinhole_Intrinsic_Radial_K1(
182                   w_, h_,
183                   params[0], params[1], params[2], // focal, ppx, ppy
184                   params[3] ); //K1
185         return true;
186       }
187       else
188       {
189         return false;
190       }
191     }
192 
193     /**
194     * @brief Return the list of parameter indexes that must be held constant
195     * @param parametrization The given parametrization
196     */
subsetParameterization(const Intrinsic_Parameter_Type & parametrization) const197     std::vector<int> subsetParameterization
198     (
199       const Intrinsic_Parameter_Type & parametrization) const override
200     {
201       std::vector<int> constant_index;
202       const int param = static_cast<int>(parametrization);
203       if ( !(param & (int)Intrinsic_Parameter_Type::ADJUST_FOCAL_LENGTH)
204           || param & (int)Intrinsic_Parameter_Type::NONE )
205       {
206         constant_index.insert(constant_index.end(), 0);
207       }
208       if ( !(param & (int)Intrinsic_Parameter_Type::ADJUST_PRINCIPAL_POINT)
209           || param & (int)Intrinsic_Parameter_Type::NONE )
210       {
211         constant_index.insert(constant_index.end(), {1, 2});
212       }
213       if ( !(param & (int)Intrinsic_Parameter_Type::ADJUST_DISTORTION)
214           || param & (int)Intrinsic_Parameter_Type::NONE )
215       {
216         constant_index.insert(constant_index.end(), 3);
217       }
218       return constant_index;
219     }
220 
221     /**
222     * @brief Return the un-distorted pixel (with removed distortion)
223     * @param p Input distorted pixel
224     * @return Point without distortion
225     */
get_ud_pixel(const Vec2 & p) const226     Vec2 get_ud_pixel( const Vec2& p ) const override
227     {
228       return cam2ima( remove_disto( ima2cam( p ) ) );
229     }
230 
231     /**
232     * @brief Return the distorted pixel (with added distortion)
233     * @param p Input pixel
234     * @return Distorted pixel
235     */
get_d_pixel(const Vec2 & p) const236     Vec2 get_d_pixel( const Vec2& p ) const override
237     {
238       return cam2ima( add_disto( ima2cam( p ) ) );
239     }
240 
241     /**
242     * @brief Serialization out
243     * @param ar Archive
244     */
245     template <class Archive>
246     inline void save( Archive & ar ) const;
247 
248     /**
249     * @brief  Serialization in
250     * @param ar Archive
251     */
252     template <class Archive>
253     inline void load( Archive & ar );
254 
255     /**
256     * @brief Clone the object
257     * @return A clone (copy of the stored object)
258     */
clone(void) const259     IntrinsicBase * clone( void ) const override
260     {
261       return new class_type( *this );
262     }
263 
264   private:
265 
266 
267     /**
268     * @brief Functor to solve Square(disto(radius(p'))) = r^2
269     * @param params List of parameters (only the first one is used)
270     * @param r2 square distance (relative to center)
271     * @return distance
272     */
distoFunctor(const std::vector<double> & params,double r2)273     static inline double distoFunctor( const std::vector<double> & params, double r2 )
274     {
275       const double & k1 = params[0];
276       return r2 * Square( 1. + r2 * k1 );
277     }
278 };
279 
280 /**
281 * @brief Implement a Pinhole camera with a 3 radial distortion coefficients.
282 * \f$ x_d = x_u (1 + K_1 r^2 + K_2 r^4 + K_3 r^6) \f$
283 */
284 class Pinhole_Intrinsic_Radial_K3 : public Pinhole_Intrinsic
285 {
286   using class_type = Pinhole_Intrinsic_Radial_K3;
287 
288   protected:
289     // center of distortion is applied by the Intrinsics class
290     /// K1, K2, K3
291     std::vector<double> params_;
292 
293   public:
294 
295     /**
296     * @brief Constructor
297     * @param w Width of image
298     * @param h Height of image
299     * @param focal Focal (in pixel) of the camera
300     * @param ppx Principal point on X-Axis
301     * @param ppy Principal point on Y-Axis
302     * @param k1 First radial distortion coefficient
303     * @param k2 Second radial distortion coefficient
304     * @param k3 Third radial distortion coefficient
305     */
Pinhole_Intrinsic_Radial_K3(int w=0,int h=0,double focal=0.0,double ppx=0,double ppy=0,double k1=0.0,double k2=0.0,double k3=0.0)306     Pinhole_Intrinsic_Radial_K3(
307       int w = 0, int h = 0,
308       double focal = 0.0, double ppx = 0, double ppy = 0,
309       double k1 = 0.0, double k2 = 0.0, double k3 = 0.0 )
310       : Pinhole_Intrinsic( w, h, focal, ppx, ppy ),
311         params_({k1, k2, k3})
312     {
313     }
314 
315     ~Pinhole_Intrinsic_Radial_K3() override = default;
316 
317     /**
318     * @brief Tell from which type the embed camera is
319     * @retval PINHOLE_CAMERA_RADIAL3
320     */
getType() const321     EINTRINSIC getType() const override
322     {
323       return PINHOLE_CAMERA_RADIAL3;
324     }
325 
326     /**
327     * @brief Does the camera model handle a distortion field?
328     * @retval true
329     */
have_disto() const330     bool have_disto() const override
331     {
332       return true;
333     }
334 
335     /**
336     * @brief Add the distortion field to a point (that is in normalized camera frame)
337     * @param p Point before distortion computation (in normalized camera frame)
338     * @return point with distortion
339     */
add_disto(const Vec2 & p) const340     Vec2 add_disto( const Vec2 & p ) const override
341     {
342       const double & k1 = params_[0], & k2 = params_[1], & k3 = params_[2];
343 
344       const double r2 = p( 0 ) * p( 0 ) + p( 1 ) * p( 1 );
345       const double r4 = r2 * r2;
346       const double r6 = r4 * r2;
347       const double r_coeff = ( 1. + k1 * r2 + k2 * r4 + k3 * r6 );
348 
349       return ( p * r_coeff );
350     }
351 
352     /**
353     * @brief Remove the distortion to a camera point (that is in normalized camera frame)
354     * @param p Point with distortion
355     * @return Point without distortion
356     */
remove_disto(const Vec2 & p) const357     Vec2 remove_disto( const Vec2& p ) const override
358     {
359       // Compute the radius from which the point p comes from thanks to a bisection
360       // Minimize disto(radius(p')^2) == actual Squared(radius(p))
361 
362       const double r2 = p( 0 ) * p( 0 ) + p( 1 ) * p( 1 );
363       const double radius = ( r2 == 0 ) ? //1. : ::sqrt(bisectionSolve(_params, r2) / r2);
364                             1. :
365                             ::sqrt( radial_distortion::bisection_Radius_Solve( params_, r2, distoFunctor ) / r2 );
366       return radius * p;
367     }
368 
369     /**
370     * @brief Data wrapper for non linear optimization (get data)
371     * @return vector of parameter of this intrinsic
372     */
getParams() const373     std::vector<double> getParams() const override
374     {
375       std::vector<double> params = Pinhole_Intrinsic::getParams();
376       params.insert( params.end(), std::begin(params_), std::end(params_));
377       return params;
378     }
379 
380     /**
381     * @brief Data wrapper for non linear optimization (update from data)
382     * @param params List of params used to update this intrinsic
383     * @retval true if update is correct
384     * @retval false if there was an error during update
385     */
updateFromParams(const std::vector<double> & params)386     bool updateFromParams( const std::vector<double> & params ) override
387     {
388       if ( params.size() == 6 )
389       {
390         *this = Pinhole_Intrinsic_Radial_K3(
391                   w_, h_,
392                   params[0], params[1], params[2], // focal, ppx, ppy
393                   params[3], params[4], params[5] ); // K1, K2, K3
394         return true;
395       }
396       else
397       {
398         return false;
399       }
400     }
401 
402     /**
403     * @brief Return the list of parameter indexes that must be held constant
404     * @param parametrization The given parametrization
405     */
subsetParameterization(const Intrinsic_Parameter_Type & parametrization) const406     std::vector<int> subsetParameterization
407     (
408       const Intrinsic_Parameter_Type & parametrization) const override
409     {
410       std::vector<int> constant_index;
411       const int param = static_cast<int>(parametrization);
412       if ( !(param & (int)Intrinsic_Parameter_Type::ADJUST_FOCAL_LENGTH)
413           || param & (int)Intrinsic_Parameter_Type::NONE )
414       {
415         constant_index.insert(constant_index.end(), 0);
416       }
417       if ( !(param & (int)Intrinsic_Parameter_Type::ADJUST_PRINCIPAL_POINT)
418           || param & (int)Intrinsic_Parameter_Type::NONE )
419       {
420         constant_index.insert(constant_index.end(), {1, 2});
421       }
422       if ( !(param & (int)Intrinsic_Parameter_Type::ADJUST_DISTORTION)
423           || param & (int)Intrinsic_Parameter_Type::NONE )
424       {
425         constant_index.insert(constant_index.end(), {3, 4, 5});
426       }
427       return constant_index;
428     }
429 
430     /**
431     * @brief Return the un-distorted pixel (with removed distortion)
432     * @param p Input distorted pixel
433     * @return Point without distortion
434     */
get_ud_pixel(const Vec2 & p) const435     Vec2 get_ud_pixel( const Vec2& p ) const override
436     {
437       return cam2ima( remove_disto( ima2cam( p ) ) );
438     }
439 
440     /**
441     * @brief Return the distorted pixel (with added distortion)
442     * @param p Input pixel
443     * @return Distorted pixel
444     */
get_d_pixel(const Vec2 & p) const445     Vec2 get_d_pixel( const Vec2& p ) const override
446     {
447       return cam2ima( add_disto( ima2cam( p ) ) );
448     }
449 
450     /**
451     * @brief Serialization out
452     * @param ar Archive
453     */
454     template <class Archive>
455     inline void save( Archive & ar ) const;
456 
457     /**
458     * @brief  Serialization in
459     * @param ar Archive
460     */
461     template <class Archive>
462     inline void load( Archive & ar );
463 
464     /**
465     * @brief Clone the object
466     * @return A clone (copy of the stored object)
467     */
clone(void) const468     IntrinsicBase * clone( void ) const override
469     {
470       return new class_type( *this );
471     }
472 
473   private:
474 
475 
476     /**
477     * @brief Functor to solve Square(disto(radius(p'))) = r^2
478     * @param params List of the radial factors {k1, k2, k3}
479     * @param r2 square distance (relative to center)
480     * @return distance
481     */
distoFunctor(const std::vector<double> & params,double r2)482     static inline double distoFunctor( const std::vector<double> & params, double r2 )
483     {
484       const double & k1 = params[0], & k2 = params[1], & k3 = params[2];
485       return r2 * Square( 1. + r2 * ( k1 + r2 * ( k2 + r2 * k3 ) ) );
486     }
487 };
488 
489 } // namespace cameras
490 } // namespace openMVG
491 
492 #endif // #ifndef OPENMVG_CAMERAS_CAMERA_PINHOLE_RADIAL_K_HPP
493