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_SFM_SFM_DATA_BA_CERES_CAMERA_FUNCTOR_HPP
10 #define OPENMVG_SFM_SFM_DATA_BA_CERES_CAMERA_FUNCTOR_HPP
11 
12 #include <memory>
13 
14 #include <ceres/ceres.h>
15 #include <ceres/rotation.h>
16 
17 #include "openMVG/cameras/Camera_Intrinsics.hpp"
18 #include "openMVG/cameras/Camera_Pinhole.hpp"
19 #include "openMVG/cameras/Camera_Pinhole_Radial.hpp"
20 #include "openMVG/cameras/Camera_Pinhole_Brown.hpp"
21 #include "openMVG/cameras/Camera_Pinhole_Fisheye.hpp"
22 #include "openMVG/cameras/Camera_Spherical.hpp"
23 
24 //--
25 //- Define ceres Cost_functor for each OpenMVG camera model
26 //--
27 
28 namespace openMVG {
29 namespace sfm {
30 
31 
32 /// Decorator used to Weight a given cost camera functor
33 /// i.e useful to weight GCP (Ground Control Points)
34 template <typename CostFunctor>
35 struct WeightedCostFunction
36 {
WeightedCostFunctionopenMVG::sfm::WeightedCostFunction37   WeightedCostFunction(): weight_(1.0) {}
38 
WeightedCostFunctionopenMVG::sfm::WeightedCostFunction39   explicit WeightedCostFunction
40   (
41     CostFunctor * func,
42     const double weight
43   ):
44     functor_(func), weight_(weight)
45   {}
46 
47   template <typename T>
operator ()openMVG::sfm::WeightedCostFunction48   bool operator()
49   (
50     const T* const cam_intrinsic,
51     const T* const cam_extrinsics,
52     const T* const pos_3dpoint,
53     T* out_residuals
54   ) const
55   {
56     if (functor_->operator()(cam_intrinsic, cam_extrinsics, pos_3dpoint, out_residuals))
57     {
58       // Reweight the residual values
59       for (int i = 0; i < CostFunctor::num_residuals(); ++i)
60       {
61         out_residuals[i] *= T(weight_);
62       }
63       return true;
64     }
65     return false;
66   }
67 
68   template <typename T>
operator ()openMVG::sfm::WeightedCostFunction69   bool operator()
70   (
71     const T* const cam_extrinsics,
72     const T* const pos_3dpoint,
73     T* out_residuals
74   ) const
75   {
76     if (functor_->operator()(cam_extrinsics, pos_3dpoint, out_residuals))
77     {
78       // Reweight the residual values
79       for (int i = 0; i < CostFunctor::num_residuals(); ++i)
80       {
81         out_residuals[i] *= T(weight_);
82       }
83       return true;
84     }
85     return false;
86   }
87 
88   std::unique_ptr<CostFunctor> functor_;
89   const double weight_;
90 };
91 
92 /**
93  * @brief Ceres functor to use a Pinhole_Intrinsic (pinhole camera model K[R[t]) and a 3D point.
94  *
95  *  Data parameter blocks are the following <2,3,6,3>
96  *  - 2 => dimension of the residuals,
97  *  - 3 => the intrinsic data block [focal, principal point x, principal point y],
98  *  - 6 => the camera extrinsic data block (camera orientation and position) [R;t],
99  *         - rotation(angle axis), and translation [rX,rY,rZ,tx,ty,tz].
100  *  - 3 => a 3D point data block.
101  *
102  */
103 struct ResidualErrorFunctor_Pinhole_Intrinsic
104 {
ResidualErrorFunctor_Pinhole_IntrinsicopenMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic105   explicit ResidualErrorFunctor_Pinhole_Intrinsic(const double* const pos_2dpoint)
106   :m_pos_2dpoint(pos_2dpoint)
107   {
108   }
109 
110   // Enum to map intrinsics parameters between openMVG & ceres camera data parameter block.
111   enum : uint8_t {
112     OFFSET_FOCAL_LENGTH = 0,
113     OFFSET_PRINCIPAL_POINT_X = 1,
114     OFFSET_PRINCIPAL_POINT_Y = 2
115   };
116 
117   /**
118    * @param[in] cam_intrinsics: Camera intrinsics( focal, principal point [x,y] )
119    * @param[in] cam_extrinsics: Camera parameterized using one block of 6 parameters [R;t]:
120    *   - 3 for rotation(angle axis), 3 for translation
121    * @param[in] pos_3dpoint
122    * @param[out] out_residuals
123    */
124   template <typename T>
operator ()openMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic125   bool operator()(
126     const T* const cam_intrinsics,
127     const T* const cam_extrinsics,
128     const T* const pos_3dpoint,
129     T* out_residuals) const
130   {
131     //--
132     // Apply external parameters (Pose)
133     //--
134 
135     const T * cam_R = cam_extrinsics;
136     Eigen::Map<const Eigen::Matrix<T, 3, 1>> cam_t(&cam_extrinsics[3]);
137 
138     Eigen::Matrix<T, 3, 1> transformed_point;
139     // Rotate the point according the camera rotation
140     ceres::AngleAxisRotatePoint(cam_R, pos_3dpoint, transformed_point.data());
141 
142     // Apply the camera translation
143     transformed_point += cam_t;
144 
145     // Transform the point from homogeneous to euclidean (undistorted point)
146     const Eigen::Matrix<T, 2, 1> projected_point = transformed_point.hnormalized();
147 
148     //--
149     // Apply intrinsic parameters
150     //--
151 
152     const T& focal = cam_intrinsics[OFFSET_FOCAL_LENGTH];
153     const T& principal_point_x = cam_intrinsics[OFFSET_PRINCIPAL_POINT_X];
154     const T& principal_point_y = cam_intrinsics[OFFSET_PRINCIPAL_POINT_Y];
155 
156     // Apply focal length and principal point to get the final image coordinates
157 
158     // Compute and return the error is the difference between the predicted
159     //  and observed position
160     Eigen::Map<Eigen::Matrix<T, 2, 1>> residuals(out_residuals);
161     residuals << principal_point_x + projected_point.x() * focal - m_pos_2dpoint[0],
162                  principal_point_y + projected_point.y() * focal - m_pos_2dpoint[1];
163     return true;
164   }
165 
num_residualsopenMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic166   static int num_residuals() { return 2; }
167 
168   // Factory to hide the construction of the CostFunction object from
169   // the client code.
CreateopenMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic170   static ceres::CostFunction* Create
171   (
172     const Vec2 & observation,
173     const double weight = 0.0
174   )
175   {
176     if (weight == 0.0)
177     {
178       return
179         (new ceres::AutoDiffCostFunction
180           <ResidualErrorFunctor_Pinhole_Intrinsic, 2, 3, 6, 3>(
181             new ResidualErrorFunctor_Pinhole_Intrinsic(observation.data())));
182     }
183     else
184     {
185       return
186         (new ceres::AutoDiffCostFunction
187           <WeightedCostFunction<ResidualErrorFunctor_Pinhole_Intrinsic>, 2, 3, 6, 3>
188           (new WeightedCostFunction<ResidualErrorFunctor_Pinhole_Intrinsic>
189             (new ResidualErrorFunctor_Pinhole_Intrinsic(observation.data()), weight)));
190     }
191   }
192 
193   const double * m_pos_2dpoint; // The 2D observation
194 };
195 
196 /**
197  * @brief Ceres functor to use a Pinhole_Intrinsic_Radial_K1
198  *
199  *  Data parameter blocks are the following <2,4,6,3>
200  *  - 2 => dimension of the residuals,
201  *  - 4 => the intrinsic data block [focal, principal point x, principal point y, K1],
202  *  - 6 => the camera extrinsic data block (camera orientation and position) [R;t],
203  *         - rotation(angle axis), and translation [rX,rY,rZ,tx,ty,tz].
204  *  - 3 => a 3D point data block.
205  *
206  */
207 struct ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K1
208 {
ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K1openMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K1209   explicit ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K1(const double* const pos_2dpoint)
210   :m_pos_2dpoint(pos_2dpoint)
211   {
212   }
213 
214   // Enum to map intrinsics parameters between openMVG & ceres camera data parameter block.
215   enum : uint8_t {
216     OFFSET_FOCAL_LENGTH = 0,
217     OFFSET_PRINCIPAL_POINT_X = 1,
218     OFFSET_PRINCIPAL_POINT_Y = 2,
219     OFFSET_DISTO_K1 = 3
220   };
221 
222   /**
223    * @param[in] cam_intrinsics: Camera intrinsics( focal, principal point [x,y], K1 )
224    * @param[in] cam_extrinsics: Camera parameterized using one block of 6 parameters [R;t]:
225    *   - 3 for rotation(angle axis), 3 for translation
226    * @param[in] pos_3dpoint
227    * @param[out] out_residuals
228    */
229   template <typename T>
operator ()openMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K1230   bool operator()(
231     const T* const cam_intrinsics,
232     const T* const cam_extrinsics,
233     const T* const pos_3dpoint,
234     T* out_residuals) const
235   {
236     //--
237     // Apply external parameters (Pose)
238     //--
239 
240     const T * cam_R = cam_extrinsics;
241     Eigen::Map<const Eigen::Matrix<T, 3, 1>> cam_t(&cam_extrinsics[3]);
242 
243     Eigen::Matrix<T, 3, 1> transformed_point;
244     // Rotate the point according the camera rotation
245     ceres::AngleAxisRotatePoint(cam_R, pos_3dpoint, transformed_point.data());
246 
247     // Apply the camera translation
248     transformed_point += cam_t;
249 
250     // Transform the point from homogeneous to euclidean (undistorted point)
251     const Eigen::Matrix<T, 2, 1> projected_point = transformed_point.hnormalized();
252 
253     //--
254     // Apply intrinsic parameters
255     //--
256 
257     const T& focal = cam_intrinsics[OFFSET_FOCAL_LENGTH];
258     const T& principal_point_x = cam_intrinsics[OFFSET_PRINCIPAL_POINT_X];
259     const T& principal_point_y = cam_intrinsics[OFFSET_PRINCIPAL_POINT_Y];
260     const T& k1 = cam_intrinsics[OFFSET_DISTO_K1];
261 
262     const T r2 = projected_point.squaredNorm();
263     const T r_coeff = 1.0 + k1 * r2;
264 
265     Eigen::Map<Eigen::Matrix<T, 2, 1>> residuals(out_residuals);
266     residuals << principal_point_x + (projected_point.x() * r_coeff) * focal - m_pos_2dpoint[0],
267                  principal_point_y + (projected_point.y() * r_coeff) * focal - m_pos_2dpoint[1];
268 
269     return true;
270   }
271 
num_residualsopenMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K1272   static int num_residuals() { return 2; }
273 
274   // Factory to hide the construction of the CostFunction object from
275   // the client code.
CreateopenMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K1276   static ceres::CostFunction* Create
277   (
278     const Vec2 & observation,
279     const double weight = 0.0
280   )
281   {
282     if (weight == 0.0)
283     {
284       return
285         (new ceres::AutoDiffCostFunction
286           <ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K1, 2, 4, 6, 3>(
287             new ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K1(observation.data())));
288     }
289     else
290     {
291       return
292         (new ceres::AutoDiffCostFunction
293           <WeightedCostFunction<ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K1>, 2, 4, 6, 3>
294           (new WeightedCostFunction<ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K1>
295             (new ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K1(observation.data()), weight)));
296     }
297   }
298 
299   const double * m_pos_2dpoint; // The 2D observation
300 };
301 
302 /**
303  * @brief Ceres functor to use a Pinhole_Intrinsic_Radial_K3
304  *
305  *  Data parameter blocks are the following <2,6,6,3>
306  *  - 2 => dimension of the residuals,
307  *  - 6 => the intrinsic data block [focal, principal point x, principal point y, K1, K2, K3],
308  *  - 6 => the camera extrinsic data block (camera orientation and position) [R;t],
309  *         - rotation(angle axis), and translation [rX,rY,rZ,tx,ty,tz].
310  *  - 3 => a 3D point data block.
311  *
312  */
313 struct ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K3
314 {
ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K3openMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K3315   explicit ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K3(const double* const pos_2dpoint)
316   :m_pos_2dpoint(pos_2dpoint)
317   {
318   }
319 
320   // Enum to map intrinsics parameters between openMVG & ceres camera data parameter block.
321   enum : uint8_t {
322     OFFSET_FOCAL_LENGTH = 0,
323     OFFSET_PRINCIPAL_POINT_X = 1,
324     OFFSET_PRINCIPAL_POINT_Y = 2,
325     OFFSET_DISTO_K1 = 3,
326     OFFSET_DISTO_K2 = 4,
327     OFFSET_DISTO_K3 = 5,
328   };
329 
330   /**
331    * @param[in] cam_intrinsics: Camera intrinsics( focal, principal point [x,y], k1, k2, k3 )
332    * @param[in] cam_extrinsics: Camera parameterized using one block of 6 parameters [R;t]:
333    *   - 3 for rotation(angle axis), 3 for translation
334    * @param[in] pos_3dpoint
335    * @param[out] out_residuals
336    */
337   template <typename T>
operator ()openMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K3338   bool operator()(
339     const T* const cam_intrinsics,
340     const T* const cam_extrinsics,
341     const T* const pos_3dpoint,
342     T* out_residuals) const
343   {
344     //--
345     // Apply external parameters (Pose)
346     //--
347 
348     const T * cam_R = cam_extrinsics;
349     Eigen::Map<const Eigen::Matrix<T, 3, 1>> cam_t(&cam_extrinsics[3]);
350 
351     Eigen::Matrix<T, 3, 1> transformed_point;
352     // Rotate the point according the camera rotation
353     ceres::AngleAxisRotatePoint(cam_R, pos_3dpoint, transformed_point.data());
354 
355     // Apply the camera translation
356     transformed_point += cam_t;
357 
358     // Transform the point from homogeneous to euclidean (undistorted point)
359     const Eigen::Matrix<T, 2, 1> projected_point = transformed_point.hnormalized();
360     //--
361     // Apply intrinsic parameters
362     //--
363 
364     const T& focal = cam_intrinsics[OFFSET_FOCAL_LENGTH];
365     const T& principal_point_x = cam_intrinsics[OFFSET_PRINCIPAL_POINT_X];
366     const T& principal_point_y = cam_intrinsics[OFFSET_PRINCIPAL_POINT_Y];
367     const T& k1 = cam_intrinsics[OFFSET_DISTO_K1];
368     const T& k2 = cam_intrinsics[OFFSET_DISTO_K2];
369     const T& k3 = cam_intrinsics[OFFSET_DISTO_K3];
370 
371     // Apply distortion (xd,yd) = disto(x_u,y_u)
372     const T r2 = projected_point.squaredNorm();
373     const T r4 = r2 * r2;
374     const T r6 = r4 * r2;
375     const T r_coeff = (1.0 + k1 * r2 + k2 * r4 + k3 * r6);
376 
377     Eigen::Map<Eigen::Matrix<T, 2, 1>> residuals(out_residuals);
378     residuals << principal_point_x + (projected_point.x() * r_coeff) * focal - m_pos_2dpoint[0],
379                  principal_point_y + (projected_point.y() * r_coeff) * focal - m_pos_2dpoint[1];
380 
381     return true;
382   }
383 
num_residualsopenMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K3384   static int num_residuals() { return 2; }
385 
386   // Factory to hide the construction of the CostFunction object from
387   // the client code.
CreateopenMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K3388   static ceres::CostFunction* Create
389   (
390     const Vec2 & observation,
391     const double weight = 0.0
392   )
393   {
394     if (weight == 0.0)
395     {
396       return
397         (new ceres::AutoDiffCostFunction
398           <ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K3, 2, 6, 6, 3>(
399             new ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K3(observation.data())));
400     }
401     else
402     {
403       return
404         (new ceres::AutoDiffCostFunction
405           <WeightedCostFunction<ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K3>, 2, 6, 6, 3>
406           (new WeightedCostFunction<ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K3>
407             (new ResidualErrorFunctor_Pinhole_Intrinsic_Radial_K3(observation.data()), weight)));
408     }
409   }
410 
411   const double * m_pos_2dpoint; // The 2D observation
412 };
413 
414 /**
415  * @brief Ceres functor with constrained 3D points to use a Pinhole_Intrinsic_Brown_T2
416  *
417  *  Data parameter blocks are the following <2,8,6,3>
418  *  - 2 => dimension of the residuals,
419  *  - 8 => the intrinsic data block [focal, principal point x, principal point y, K1, K2, K3, T1, T2],
420  *  - 6 => the camera extrinsic data block (camera orientation and position) [R;t],
421  *         - rotation(angle axis), and translation [rX,rY,rZ,tx,ty,tz].
422  *  - 3 => a 3D point data block.
423  *
424  */
425 struct ResidualErrorFunctor_Pinhole_Intrinsic_Brown_T2
426 {
ResidualErrorFunctor_Pinhole_Intrinsic_Brown_T2openMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic_Brown_T2427   explicit ResidualErrorFunctor_Pinhole_Intrinsic_Brown_T2(const double* const pos_2dpoint)
428   :m_pos_2dpoint(pos_2dpoint)
429   {
430   }
431 
432   // Enum to map intrinsics parameters between openMVG & ceres camera data parameter block.
433   enum : uint8_t {
434     OFFSET_FOCAL_LENGTH = 0,
435     OFFSET_PRINCIPAL_POINT_X = 1,
436     OFFSET_PRINCIPAL_POINT_Y = 2,
437     OFFSET_DISTO_K1 = 3,
438     OFFSET_DISTO_K2 = 4,
439     OFFSET_DISTO_K3 = 5,
440     OFFSET_DISTO_T1 = 6,
441     OFFSET_DISTO_T2 = 7,
442   };
443 
444   /**
445    * @param[in] cam_intrinsics: Camera intrinsics( focal, principal point [x,y], k1, k2, k3, t1, t2 )
446    * @param[in] cam_extrinsics: Camera parameterized using one block of 6 parameters [R;t]:
447    *   - 3 for rotation(angle axis), 3 for translation
448    * @param[in] pos_3dpoint
449    * @param[out] out_residuals
450    */
451   template <typename T>
operator ()openMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic_Brown_T2452   bool operator()(
453     const T* const cam_intrinsics,
454     const T* const cam_extrinsics,
455     const T* const pos_3dpoint,
456     T* out_residuals) const
457   {
458     //--
459     // Apply external parameters (Pose)
460     //--
461 
462     const T * cam_R = cam_extrinsics;
463     Eigen::Map<const Eigen::Matrix<T, 3, 1>> cam_t(&cam_extrinsics[3]);
464 
465     Eigen::Matrix<T, 3, 1> transformed_point;
466     // Rotate the point according the camera rotation
467     ceres::AngleAxisRotatePoint(cam_R, pos_3dpoint, transformed_point.data());
468 
469     // Apply the camera translation
470     transformed_point += cam_t;
471 
472     // Transform the point from homogeneous to euclidean (undistorted point)
473     const Eigen::Matrix<T, 2, 1> projected_point = transformed_point.hnormalized();
474 
475     //--
476     // Apply intrinsic parameters
477     //--
478 
479     const T& focal = cam_intrinsics[OFFSET_FOCAL_LENGTH];
480     const T& principal_point_x = cam_intrinsics[OFFSET_PRINCIPAL_POINT_X];
481     const T& principal_point_y = cam_intrinsics[OFFSET_PRINCIPAL_POINT_Y];
482     const T& k1 = cam_intrinsics[OFFSET_DISTO_K1];
483     const T& k2 = cam_intrinsics[OFFSET_DISTO_K2];
484     const T& k3 = cam_intrinsics[OFFSET_DISTO_K3];
485     const T& t1 = cam_intrinsics[OFFSET_DISTO_T1];
486     const T& t2 = cam_intrinsics[OFFSET_DISTO_T2];
487 
488     // Apply distortion (xd,yd) = disto(x_u,y_u)
489     const T x_u = projected_point.x();
490     const T y_u = projected_point.y();
491     const T r2 = projected_point.squaredNorm();
492     const T r4 = r2 * r2;
493     const T r6 = r4 * r2;
494     const T r_coeff = (1.0 + k1 * r2 + k2 * r4 + k3 * r6);
495     const T t_x = t2 * (r2 + 2.0 * x_u * x_u) + 2.0 * t1 * x_u * y_u;
496     const T t_y = t1 * (r2 + 2.0 * y_u * y_u) + 2.0 * t2 * x_u * y_u;
497 
498     Eigen::Map<Eigen::Matrix<T, 2, 1>> residuals(out_residuals);
499     residuals << principal_point_x + (projected_point.x() * r_coeff + t_x) * focal - m_pos_2dpoint[0],
500                  principal_point_y + (projected_point.y() * r_coeff + t_y) * focal - m_pos_2dpoint[1];
501 
502     return true;
503   }
504 
num_residualsopenMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic_Brown_T2505   static int num_residuals() { return 2; }
506 
507   // Factory to hide the construction of the CostFunction object from
508   // the client code.
CreateopenMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic_Brown_T2509   static ceres::CostFunction* Create
510   (
511     const Vec2 & observation,
512     const double weight = 0.0
513   )
514   {
515     if (weight == 0.0)
516     {
517       return
518         (new ceres::AutoDiffCostFunction
519           <ResidualErrorFunctor_Pinhole_Intrinsic_Brown_T2, 2, 8, 6, 3>(
520             new ResidualErrorFunctor_Pinhole_Intrinsic_Brown_T2(observation.data())));
521     }
522     else
523     {
524       return
525         (new ceres::AutoDiffCostFunction
526           <WeightedCostFunction<ResidualErrorFunctor_Pinhole_Intrinsic_Brown_T2>, 2, 8, 6, 3>
527           (new WeightedCostFunction<ResidualErrorFunctor_Pinhole_Intrinsic_Brown_T2>
528             (new ResidualErrorFunctor_Pinhole_Intrinsic_Brown_T2(observation.data()), weight)));
529     }
530   }
531 
532   const double * m_pos_2dpoint; // The 2D observation
533 };
534 
535 
536 /**
537  * @brief Ceres functor with constrained 3D points to use a Pinhole_Intrinsic_Fisheye
538  *
539  *  Data parameter blocks are the following <2,8,6,3>
540  *  - 2 => dimension of the residuals,
541  *  - 7 => the intrinsic data block [focal, principal point x, principal point y, K1, K2, K3, K4],
542  *  - 6 => the camera extrinsic data block (camera orientation and position) [R;t],
543  *         - rotation(angle axis), and translation [rX,rY,rZ,tx,ty,tz].
544  *  - 3 => a 3D point data block.
545  *
546  */
547 
548 struct ResidualErrorFunctor_Pinhole_Intrinsic_Fisheye
549 {
ResidualErrorFunctor_Pinhole_Intrinsic_FisheyeopenMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic_Fisheye550   explicit ResidualErrorFunctor_Pinhole_Intrinsic_Fisheye(const double* const pos_2dpoint)
551   :m_pos_2dpoint(pos_2dpoint)
552   {
553   }
554 
555   // Enum to map intrinsics parameters between openMVG & ceres camera data parameter block.
556   enum : uint8_t {
557     OFFSET_FOCAL_LENGTH = 0,
558     OFFSET_PRINCIPAL_POINT_X = 1,
559     OFFSET_PRINCIPAL_POINT_Y = 2,
560     OFFSET_DISTO_K1 = 3,
561     OFFSET_DISTO_K2 = 4,
562     OFFSET_DISTO_K3 = 5,
563     OFFSET_DISTO_K4 = 6,
564   };
565 
566   /**
567    * @param[in] cam_intrinsics: Camera intrinsics( focal, principal point [x,y], k1, k2, k3, k4 )
568    * @param[in] cam_extrinsics: Camera parameterized using one block of 6 parameters [R;t]:
569    *   - 3 for rotation(angle axis), 3 for translation
570    * @param[in] pos_3dpoint
571    * @param[out] out_residuals
572    */
573   template <typename T>
operator ()openMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic_Fisheye574   bool operator()(
575     const T* const cam_intrinsics,
576     const T* const cam_extrinsics,
577     const T* const pos_3dpoint,
578     T* out_residuals) const
579   {
580     //--
581     // Apply external parameters (Pose)
582     //--
583 
584     const T * cam_R = cam_extrinsics;
585     Eigen::Map<const Eigen::Matrix<T, 3, 1>> cam_t(&cam_extrinsics[3]);
586 
587     Eigen::Matrix<T, 3, 1> transformed_point;
588     // Rotate the point according the camera rotation
589     ceres::AngleAxisRotatePoint(cam_R, pos_3dpoint, transformed_point.data());
590 
591     // Apply the camera translation
592     transformed_point += cam_t;
593 
594     // Transform the point from homogeneous to euclidean (undistorted point)
595     const Eigen::Matrix<T, 2, 1> projected_point = transformed_point.hnormalized();
596 
597     //--
598     // Apply intrinsic parameters
599     //--
600     const T& focal = cam_intrinsics[OFFSET_FOCAL_LENGTH];
601     const T& principal_point_x = cam_intrinsics[OFFSET_PRINCIPAL_POINT_X];
602     const T& principal_point_y = cam_intrinsics[OFFSET_PRINCIPAL_POINT_Y];
603     const T& k1 = cam_intrinsics[OFFSET_DISTO_K1];
604     const T& k2 = cam_intrinsics[OFFSET_DISTO_K2];
605     const T& k3 = cam_intrinsics[OFFSET_DISTO_K3];
606     const T& k4 = cam_intrinsics[OFFSET_DISTO_K4];
607 
608     // Apply distortion (xd,yd) = disto(x_u,y_u)
609     const T r2 = projected_point.squaredNorm();
610     const T r = sqrt(r2);
611     const T
612       theta = atan(r),
613       theta2 = theta*theta,
614       theta3 = theta2*theta,
615       theta4 = theta2*theta2,
616       theta5 = theta4*theta,
617       theta7 = theta3*theta3*theta, //thetha6*theta
618       theta8 = theta4*theta4,
619       theta9 = theta8*theta;
620     const T theta_dist = theta + k1*theta3 + k2*theta5 + k3*theta7 + k4*theta9;
621     const T inv_r = r > T(1e-8) ? T(1.0)/r : T(1.0);
622     const T cdist = r > T(1e-8) ? theta_dist * inv_r : T(1.0);
623 
624     Eigen::Map<Eigen::Matrix<T, 2, 1>> residuals(out_residuals);
625     residuals << principal_point_x + (projected_point.x() * cdist) * focal - m_pos_2dpoint[0],
626                  principal_point_y + (projected_point.y() * cdist) * focal - m_pos_2dpoint[1];
627 
628 
629     return true;
630   }
631 
num_residualsopenMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic_Fisheye632   static int num_residuals() { return 2; }
633 
634   // Factory to hide the construction of the CostFunction object from
635   // the client code.
CreateopenMVG::sfm::ResidualErrorFunctor_Pinhole_Intrinsic_Fisheye636   static ceres::CostFunction* Create
637   (
638     const Vec2 & observation,
639     const double weight = 0.0
640   )
641   {
642     if (weight == 0.0)
643     {
644       return
645         (new ceres::AutoDiffCostFunction
646           <ResidualErrorFunctor_Pinhole_Intrinsic_Fisheye, 2, 7, 6, 3>(
647             new ResidualErrorFunctor_Pinhole_Intrinsic_Fisheye(observation.data())));
648     }
649     else
650     {
651       return
652         (new ceres::AutoDiffCostFunction
653           <WeightedCostFunction<ResidualErrorFunctor_Pinhole_Intrinsic_Fisheye>, 2, 7, 6, 3>
654           (new WeightedCostFunction<ResidualErrorFunctor_Pinhole_Intrinsic_Fisheye>
655             (new ResidualErrorFunctor_Pinhole_Intrinsic_Fisheye(observation.data()), weight)));
656     }
657   }
658 
659   const double * m_pos_2dpoint; // The 2D observation
660 };
661 
662 struct ResidualErrorFunctor_Intrinsic_Spherical
663 {
ResidualErrorFunctor_Intrinsic_SphericalopenMVG::sfm::ResidualErrorFunctor_Intrinsic_Spherical664   explicit ResidualErrorFunctor_Intrinsic_Spherical
665   (
666     const double* const pos_2dpoint,
667     const uint32_t imageSize_w,
668     const uint32_t imageSize_h
669   )
670   : m_pos_2dpoint(pos_2dpoint),
671     m_imageSize{imageSize_w, imageSize_h}
672   {
673   }
674 
675   /**
676   * @param[in] cam_extrinsics: Camera parameterized using one block of 6 parameters [R;t]:
677   *   - 3 for rotation(angle axis), 3 for translation
678   * @param[in] pos_3dpoint
679   * @param[out] out_residuals
680   */
681   template <typename T>
operator ()openMVG::sfm::ResidualErrorFunctor_Intrinsic_Spherical682   bool operator()
683   (
684     const T* const cam_extrinsics,
685     const T* const pos_3dpoint,
686     T* out_residuals
687   )
688   const
689   {
690     //--
691     // Apply external parameters (Pose)
692     //--
693 
694     const T * cam_R = cam_extrinsics;
695     Eigen::Map<const Eigen::Matrix<T, 3, 1>> cam_t(&cam_extrinsics[3]);
696 
697     Eigen::Matrix<T, 3, 1> transformed_point;
698     // Rotate the point according the camera rotation
699     ceres::AngleAxisRotatePoint(cam_R, pos_3dpoint, transformed_point.data());
700 
701     // Apply the camera translation
702     transformed_point += cam_t;
703 
704     // Transform the coord in is Image space
705     const T lon = ceres::atan2(transformed_point.x(), transformed_point.z()); // Horizontal normalization of the  X-Z component
706     const T lat = ceres::atan2(-transformed_point.y(),
707                                Eigen::Matrix<T, 2, 1>(transformed_point.x(), transformed_point.z()).norm()); // Tilt angle
708     const T coord[] = {lon / (2 * M_PI), - lat / (2 * M_PI)}; // normalization
709 
710     const T size ( std::max(m_imageSize[0], m_imageSize[1]) );
711     const T projected_x = coord[0] * size - 0.5 + m_imageSize[0] / 2.0;
712     const T projected_y = coord[1] * size - 0.5 + m_imageSize[1] / 2.0;
713 
714     out_residuals[0] = projected_x - m_pos_2dpoint[0];
715     out_residuals[1] = projected_y - m_pos_2dpoint[1];
716 
717     return true;
718   }
719 
num_residualsopenMVG::sfm::ResidualErrorFunctor_Intrinsic_Spherical720   static int num_residuals() { return 2; }
721 
722   // Factory to hide the construction of the CostFunction object from
723   // the client code.
CreateopenMVG::sfm::ResidualErrorFunctor_Intrinsic_Spherical724   static ceres::CostFunction* Create
725   (
726     const cameras::IntrinsicBase * cameraInterface,
727     const Vec2 & observation,
728     const double weight = 0.0
729   )
730   {
731     if (weight == 0.0)
732     {
733       return
734           new ceres::AutoDiffCostFunction
735             <ResidualErrorFunctor_Intrinsic_Spherical, 2, 6, 3>(
736               new ResidualErrorFunctor_Intrinsic_Spherical(
737                 observation.data(),
738                 cameraInterface->w(),
739                 cameraInterface->h()
740               )
741             );
742     }
743     else
744     {
745       return
746         new ceres::AutoDiffCostFunction
747           <WeightedCostFunction<ResidualErrorFunctor_Intrinsic_Spherical>, 2, 6, 3>
748             (new WeightedCostFunction<ResidualErrorFunctor_Intrinsic_Spherical>
749               (new ResidualErrorFunctor_Intrinsic_Spherical(
750                 observation.data(),
751                 cameraInterface->w(),
752                 cameraInterface->h()),
753               weight)
754             );
755     }
756   }
757 
758   const double * m_pos_2dpoint;  // The 2D observation
759   size_t         m_imageSize[2]; // The image width and height
760 };
761 
762 } // namespace sfm
763 } // namespace openMVG
764 
765 #endif // OPENMVG_SFM_SFM_DATA_BA_CERES_CAMERA_FUNCTOR_HPP
766