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 #include "openMVG/sfm/pipelines/localization/SfM_Localizer.hpp" 10 11 #include "openMVG/cameras/Camera_Common.hpp" 12 #include "openMVG/cameras/Camera_Intrinsics.hpp" 13 #include "openMVG/cameras/Camera_Pinhole.hpp" 14 #include "openMVG/multiview/solver_resection_kernel.hpp" 15 #include "openMVG/multiview/solver_resection_p3p.hpp" 16 #include "openMVG/multiview/solver_resection_up2p_kukelova.hpp" 17 #include "openMVG/sfm/sfm_data.hpp" 18 #include "openMVG/sfm/sfm_data_BA.hpp" 19 #include "openMVG/sfm/sfm_data_BA_ceres.hpp" 20 #include "openMVG/sfm/sfm_landmark.hpp" 21 #include "openMVG/robust_estimation/robust_estimator_ACRansac.hpp" 22 #include "openMVG/robust_estimation/robust_estimator_ACRansacKernelAdaptator.hpp" 23 #include "openMVG/system/logger.hpp" 24 25 #include <memory> 26 #include <utility> 27 28 namespace openMVG 29 { 30 /// Pose/Resection Kernel adapter for the A contrario model estimator with 31 /// known camera intrinsics. 32 template <typename SolverArg, 33 typename ModelArg = Mat34> 34 class ACKernelAdaptorResection_Intrinsics 35 { 36 public: 37 using Solver = SolverArg; 38 using Model = ModelArg; 39 ACKernelAdaptorResection_Intrinsics(const Mat & x2d,const Mat & x3D,const cameras::IntrinsicBase * camera)40 ACKernelAdaptorResection_Intrinsics 41 ( 42 const Mat & x2d, // Undistorted 2d feature_point location 43 const Mat & x3D, // 3D corresponding points 44 const cameras::IntrinsicBase * camera 45 ):x2d_(x2d), 46 x3D_(x3D), 47 logalpha0_(log10(M_PI)), 48 N1_(Mat3::Identity()), 49 camera_(camera) 50 { 51 N1_.diagonal().head(2) *= camera->imagePlane_toCameraPlaneError(1.0); 52 assert(2 == x2d_.rows()); 53 assert(3 == x3D_.rows()); 54 assert(x2d_.cols() == x3D_.cols()); 55 bearing_vectors_= camera->operator()(x2d_); 56 } 57 58 enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES }; 59 enum { MAX_MODELS = Solver::MAX_MODELS }; 60 Fit(const std::vector<uint32_t> & samples,std::vector<Model> * models) const61 void Fit(const std::vector<uint32_t> &samples, std::vector<Model> *models) const { 62 Solver::Solve(ExtractColumns(bearing_vectors_, samples), // bearing vectors 63 ExtractColumns(x3D_, samples), // 3D points 64 models); // Found model hypothesis 65 } 66 Errors(const Model & model,std::vector<double> & vec_errors) const67 void Errors(const Model & model, std::vector<double> & vec_errors) const 68 { 69 // Convert the found model into a Pose3 70 const Vec3 t = model.block(0, 3, 3, 1); 71 const geometry::Pose3 pose(model.block(0, 0, 3, 3), 72 - model.block(0, 0, 3, 3).transpose() * t); 73 74 vec_errors.resize(x2d_.cols()); 75 76 const bool ignore_distortion = true; // We ignore distortion since we are using undistorted bearing vector as input 77 78 for (Mat::Index sample = 0; sample < x2d_.cols(); ++sample) 79 { 80 vec_errors[sample] = (camera_->residual(pose(x3D_.col(sample)), 81 x2d_.col(sample), 82 ignore_distortion) * N1_(0,0)).squaredNorm(); 83 } 84 } 85 NumSamples() const86 size_t NumSamples() const { return x2d_.cols(); } 87 Unnormalize(Model * model) const88 void Unnormalize(Model * model) const { 89 } 90 logalpha0() const91 double logalpha0() const {return logalpha0_;} multError() const92 double multError() const {return 1.0;} // point to point error normalizer1() const93 Mat3 normalizer1() const {return Mat3::Identity();} normalizer2() const94 Mat3 normalizer2() const {return N1_;} unormalizeError(double val) const95 double unormalizeError(double val) const {return sqrt(val) / N1_(0,0);} 96 97 private: 98 Mat x2d_, bearing_vectors_; 99 const Mat & x3D_; 100 Mat3 N1_; 101 double logalpha0_; // Alpha0 is used to make the error adaptive to the image size 102 const cameras::IntrinsicBase * camera_; // Intrinsic camera parameter 103 }; 104 } // namespace openMVG 105 106 namespace openMVG { 107 namespace sfm { 108 Localize(const resection::SolverType & solver_type,const Pair & image_size,const cameras::IntrinsicBase * optional_intrinsics,Image_Localizer_Match_Data & resection_data,geometry::Pose3 & pose)109 bool SfM_Localizer::Localize 110 ( 111 const resection::SolverType & solver_type, 112 const Pair & image_size, 113 const cameras::IntrinsicBase * optional_intrinsics, 114 Image_Localizer_Match_Data & resection_data, 115 geometry::Pose3 & pose 116 ) 117 { 118 // -- 119 // Compute the camera pose (resectioning) 120 // -- 121 Mat34 P; 122 resection_data.vec_inliers.clear(); 123 124 // Setup the admissible upper bound residual error 125 const double dPrecision = 126 resection_data.error_max == std::numeric_limits<double>::infinity() ? 127 std::numeric_limits<double>::infinity() : 128 Square(resection_data.error_max); 129 130 size_t MINIMUM_SAMPLES = 0; 131 132 switch (solver_type) 133 { 134 case resection::SolverType::DLT_6POINTS: 135 { 136 //-- 137 // Classic resection (try to compute the entire P matrix) 138 using SolverType = openMVG::resection::kernel::SixPointResectionSolver; 139 MINIMUM_SAMPLES = SolverType::MINIMUM_SAMPLES; 140 141 using KernelType = 142 openMVG::robust::ACKernelAdaptorResection< 143 SolverType, 144 resection::SquaredPixelReprojectionError, 145 openMVG::robust::UnnormalizerResection, 146 Mat34>; 147 148 KernelType kernel(resection_data.pt2D, image_size.first, image_size.second, 149 resection_data.pt3D); 150 // Robust estimation of the pose and its precision 151 const std::pair<double,double> ACRansacOut = 152 openMVG::robust::ACRANSAC(kernel, 153 resection_data.vec_inliers, 154 resection_data.max_iteration, 155 &P, 156 dPrecision, 157 true); 158 // Update the upper bound precision of the model found by AC-RANSAC 159 resection_data.error_max = ACRansacOut.first; 160 } 161 break; 162 case resection::SolverType::P3P_NORDBERG_ECCV18: 163 { 164 if (!optional_intrinsics) 165 { 166 std::cerr << "Intrinsic data is required for P3P solvers." << std::endl; 167 return false; 168 } 169 //-- 170 // Since the intrinsic data is known, compute only the pose 171 using SolverType = openMVG::euclidean_resection::P3PSolver_Nordberg; 172 MINIMUM_SAMPLES = SolverType::MINIMUM_SAMPLES; 173 174 using KernelType = 175 ACKernelAdaptorResection_Intrinsics< 176 SolverType, 177 Mat34>; 178 179 KernelType kernel(resection_data.pt2D, resection_data.pt3D, optional_intrinsics); 180 // Robust estimation of the pose matrix and its precision 181 const auto ACRansacOut = 182 openMVG::robust::ACRANSAC(kernel, 183 resection_data.vec_inliers, 184 resection_data.max_iteration, 185 &P, 186 dPrecision, 187 true); 188 // Update the upper bound precision of the model found by AC-RANSAC 189 resection_data.error_max = ACRansacOut.first; 190 } 191 break; 192 case resection::SolverType::P3P_KE_CVPR17: 193 { 194 if (!optional_intrinsics) 195 { 196 OPENMVG_LOG_ERROR << "Intrinsic data is required for P3P solvers."; 197 return false; 198 } 199 //-- 200 // Since the intrinsic data is known, compute only the pose 201 using SolverType = openMVG::euclidean_resection::P3PSolver_Ke; 202 MINIMUM_SAMPLES = SolverType::MINIMUM_SAMPLES; 203 204 using KernelType = 205 ACKernelAdaptorResection_Intrinsics< 206 SolverType, 207 Mat34>; 208 209 KernelType kernel(resection_data.pt2D, resection_data.pt3D, optional_intrinsics); 210 // Robust estimation of the pose matrix and its precision 211 const auto ACRansacOut = 212 openMVG::robust::ACRANSAC(kernel, 213 resection_data.vec_inliers, 214 resection_data.max_iteration, 215 &P, 216 dPrecision, 217 true); 218 // Update the upper bound precision of the model found by AC-RANSAC 219 resection_data.error_max = ACRansacOut.first; 220 } 221 break; 222 case resection::SolverType::P3P_KNEIP_CVPR11: 223 { 224 if (!optional_intrinsics) 225 { 226 OPENMVG_LOG_ERROR << "Intrinsic data is required for P3P solvers."; 227 return false; 228 } 229 //-- 230 // Since the intrinsic data is known, compute only the pose 231 using SolverType = openMVG::euclidean_resection::P3PSolver_Kneip; 232 MINIMUM_SAMPLES = SolverType::MINIMUM_SAMPLES; 233 234 using KernelType = 235 ACKernelAdaptorResection_Intrinsics< 236 SolverType, 237 Mat34>; 238 239 KernelType kernel(resection_data.pt2D, resection_data.pt3D, optional_intrinsics); 240 241 // Robust estimation of the pose matrix and its precision 242 const auto ACRansacOut = 243 openMVG::robust::ACRANSAC(kernel, 244 resection_data.vec_inliers, 245 resection_data.max_iteration, 246 &P, 247 dPrecision, 248 true); 249 // Update the upper bound precision of the model found by AC-RANSAC 250 resection_data.error_max = ACRansacOut.first; 251 } 252 break; 253 case resection::SolverType::UP2P_KUKELOVA_ACCV10: 254 { 255 if (!optional_intrinsics) 256 { 257 std::cerr << "Intrinsic data is required for P3P solvers." << std::endl; 258 return false; 259 } 260 //-- 261 // Since the intrinsic data is known, compute only the pose 262 using SolverType = openMVG::euclidean_resection::UP2PSolver_Kukelova; 263 MINIMUM_SAMPLES = SolverType::MINIMUM_SAMPLES; 264 265 using KernelType = 266 ACKernelAdaptorResection_Intrinsics< 267 SolverType, 268 Mat34>; 269 270 KernelType kernel(resection_data.pt2D, resection_data.pt3D, optional_intrinsics); 271 272 // Robust estimation of the pose matrix and its precision 273 const auto ACRansacOut = 274 openMVG::robust::ACRANSAC(kernel, 275 resection_data.vec_inliers, 276 resection_data.max_iteration, 277 &P, 278 dPrecision, 279 true); 280 // Update the upper bound precision of the model found by AC-RANSAC 281 resection_data.error_max = ACRansacOut.first; 282 } 283 break; 284 default: 285 { 286 OPENMVG_LOG_ERROR << "Unknown absolute pose solver type."; 287 return false; 288 } 289 } 290 291 // Test if the mode support some points (more than those required for estimation) 292 const bool bResection = (resection_data.vec_inliers.size() > 2.5 * MINIMUM_SAMPLES); 293 294 if (bResection) 295 { 296 resection_data.projection_matrix = P; 297 Mat3 K, R; 298 Vec3 t; 299 KRt_From_P(P, &K, &R, &t); 300 pose = geometry::Pose3(R, -R.transpose() * t); 301 } 302 303 OPENMVG_LOG_INFO << "\n" 304 << "-------------------------------" << "\n" 305 << "-- Robust Resection statistics: " << "\n" 306 << "-- Resection status: " << bResection << "\n" 307 << "-- #Points used for Resection: " << resection_data.pt2D.cols() << "\n" 308 << "-- #Points validated by robust Resection: " << resection_data.vec_inliers.size() << "\n" 309 << "-- Threshold: " << resection_data.error_max << "\n" 310 << "-------------------------------"; 311 312 return bResection; 313 } 314 RefinePose(cameras::IntrinsicBase * intrinsics,geometry::Pose3 & pose,Image_Localizer_Match_Data & matching_data,bool b_refine_pose,bool b_refine_intrinsic)315 bool SfM_Localizer::RefinePose 316 ( 317 cameras::IntrinsicBase * intrinsics, 318 geometry::Pose3 & pose, 319 Image_Localizer_Match_Data & matching_data, 320 bool b_refine_pose, 321 bool b_refine_intrinsic 322 ) 323 { 324 if (!b_refine_pose && !b_refine_intrinsic) 325 { 326 // Nothing to do (There is no parameter to refine) 327 return false; 328 } 329 330 // Setup a tiny SfM scene with the corresponding 2D-3D data 331 SfM_Data sfm_data; 332 // view 333 sfm_data.views.insert({0, std::make_shared<View>("",0, 0, 0)}); 334 // pose 335 sfm_data.poses[0] = pose; 336 // intrinsic 337 std::shared_ptr<cameras::IntrinsicBase> shared_intrinsics(intrinsics->clone()); 338 sfm_data.intrinsics[0] = shared_intrinsics; 339 // structure data (2D-3D correspondences) 340 for (size_t i = 0; i < matching_data.vec_inliers.size(); ++i) 341 { 342 const size_t idx = matching_data.vec_inliers[i]; 343 Landmark landmark; 344 landmark.X = matching_data.pt3D.col(idx); 345 landmark.obs[0] = Observation(matching_data.pt2D.col(idx), UndefinedIndexT); 346 sfm_data.structure[i] = std::move(landmark); 347 } 348 349 // Configure BA options (refine the intrinsic and the pose parameter only if requested) 350 const Optimize_Options ba_refine_options 351 ( 352 (b_refine_intrinsic) ? cameras::Intrinsic_Parameter_Type::ADJUST_ALL : cameras::Intrinsic_Parameter_Type::NONE, 353 (b_refine_pose) ? Extrinsic_Parameter_Type::ADJUST_ALL : Extrinsic_Parameter_Type::NONE, 354 Structure_Parameter_Type::NONE // STRUCTURE must remain constant 355 ); 356 Bundle_Adjustment_Ceres bundle_adjustment_obj; 357 const bool b_BA_Status = bundle_adjustment_obj.Adjust( 358 sfm_data, 359 ba_refine_options); 360 if (b_BA_Status) 361 { 362 pose = sfm_data.poses[0]; 363 if (b_refine_intrinsic) 364 intrinsics->updateFromParams(shared_intrinsics->getParams()); 365 } 366 367 return b_BA_Status; 368 } 369 370 } // namespace sfm 371 } // namespace openMVG 372