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