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