60 return variable_point3D_ids_.size() + constant_point3D_ids_.size();
64 return constant_camera_ids_.size();
68 return constant_poses_.size();
72 return constant_tvecs_.size();
76 return variable_point3D_ids_.size();
80 return constant_point3D_ids_.size();
86 size_t num_observations = 0;
87 for (
const image_t image_id : image_ids_) {
94 auto NumObservationsForPoint =
95 [
this, &reconstruction](
const point3D_t point3D_id) {
96 size_t num_observations_for_point = 0;
97 const auto& point3D = reconstruction.
Point3D(point3D_id);
98 for (
const auto& track_el : point3D.Track().Elements()) {
99 if (image_ids_.count(track_el.image_id) == 0) {
100 num_observations_for_point += 1;
103 return num_observations_for_point;
106 for (
const auto point3D_id : variable_point3D_ids_) {
107 num_observations += NumObservationsForPoint(point3D_id);
109 for (
const auto point3D_id : constant_point3D_ids_) {
110 num_observations += NumObservationsForPoint(point3D_id);
113 return 2 * num_observations;
117 image_ids_.insert(image_id);
121 return image_ids_.find(image_id) != image_ids_.end();
125 image_ids_.erase(image_id);
129 constant_camera_ids_.insert(camera_id);
133 constant_camera_ids_.erase(camera_id);
137 return constant_camera_ids_.find(camera_id) != constant_camera_ids_.end();
143 constant_poses_.insert(image_id);
147 constant_poses_.erase(image_id);
151 return constant_poses_.find(image_id) != constant_poses_.end();
155 const std::vector<int>& idxs) {
156 CHECK_GT(idxs.size(), 0);
157 CHECK_LE(idxs.size(), 3);
161 <<
"Tvec indices must not contain duplicates";
162 constant_tvecs_.emplace(image_id, idxs);
166 constant_tvecs_.erase(image_id);
170 return constant_tvecs_.find(image_id) != constant_tvecs_.end();
179 return variable_point3D_ids_;
184 return constant_point3D_ids_;
188 const image_t image_id)
const {
189 return constant_tvecs_.at(image_id);
194 variable_point3D_ids_.insert(point3D_id);
199 constant_point3D_ids_.insert(point3D_id);
208 return variable_point3D_ids_.find(point3D_id) !=
209 variable_point3D_ids_.end();
214 return constant_point3D_ids_.find(point3D_id) !=
215 constant_point3D_ids_.end();
219 variable_point3D_ids_.erase(point3D_id);
223 constant_point3D_ids_.erase(point3D_id);
231 ceres::LossFunction* loss_function =
nullptr;
234 loss_function =
new ceres::TrivialLoss();
243 CHECK_NOTNULL(loss_function);
244 return loss_function;
258 const ceres::Problem& problem)
const {
261 custom_solver_options.minimizer_progress_to_stdout =
true;
262 custom_solver_options.logging_type =
263 ceres::LoggingType::PER_MINIMIZER_ITERATION;
266 const int num_images = config.
NumImages();
267 const bool has_sparse =
268 custom_solver_options.sparse_linear_algebra_library_type !=
271 int max_num_images_direct_dense_solver =
273 int max_num_images_direct_sparse_solver =
277 bool cuda_solver_enabled =
false;
279 #if (CERES_VERSION_MAJOR >= 3 || \
280 (CERES_VERSION_MAJOR == 2 && CERES_VERSION_MINOR >= 2)) && \
281 !defined(CERES_NO_CUDA)
283 cuda_solver_enabled =
true;
284 custom_solver_options.dense_linear_algebra_library_type = ceres::CUDA;
285 max_num_images_direct_dense_solver =
290 LOG_FIRST_N(WARNING, 1)
291 <<
"Requested to use GPU for bundle adjustment, but Ceres was "
292 "compiled without CUDA support. Falling back to CPU-based "
298 #if (CERES_VERSION_MAJOR >= 3 || \
299 (CERES_VERSION_MAJOR == 2 && CERES_VERSION_MINOR >= 3)) && \
300 !defined(CERES_NO_CUDSS)
302 cuda_solver_enabled =
true;
303 custom_solver_options.sparse_linear_algebra_library_type =
305 max_num_images_direct_sparse_solver =
310 LOG_FIRST_N(WARNING, 1)
311 <<
"Requested to use GPU for bundle adjustment, but Ceres was "
312 "compiled without cuDSS support. Falling back to CPU-based "
318 if (cuda_solver_enabled) {
319 const std::vector<int> gpu_indices = CSVToVector<int>(
gpu_index);
320 CHECK_GT(gpu_indices.size(), 0);
325 LOG_FIRST_N(WARNING, 1)
326 <<
"Requested to use GPU for bundle adjustment, but COLMAP was "
327 "compiled without CUDA support. Falling back to CPU-based "
332 if (num_images <= max_num_images_direct_dense_solver) {
333 custom_solver_options.linear_solver_type = ceres::DENSE_SCHUR;
334 }
else if (has_sparse &&
335 num_images <= max_num_images_direct_sparse_solver) {
336 custom_solver_options.linear_solver_type = ceres::SPARSE_SCHUR;
338 custom_solver_options.linear_solver_type = ceres::ITERATIVE_SCHUR;
339 custom_solver_options.preconditioner_type = ceres::SCHUR_JACOBI;
343 custom_solver_options.num_threads = 1;
344 #if CERES_VERSION_MAJOR < 2
345 custom_solver_options.num_linear_solver_threads = 1;
348 custom_solver_options.num_threads =
350 #if CERES_VERSION_MAJOR < 2
351 custom_solver_options.num_linear_solver_threads =
353 custom_solver_options.num_linear_solver_threads);
357 std::string solver_error;
358 CHECK(custom_solver_options.IsValid(&solver_error)) << solver_error;
359 return custom_solver_options;
368 : options_(options), config_(config) {
373 CHECK_NOTNULL(reconstruction);
374 CHECK(!
problem_) <<
"Cannot use the same BundleAdjuster multiple times";
376 problem_.reset(
new ceres::Problem());
379 SetUp(reconstruction, loss_function);
381 if (
problem_->NumResiduals() == 0) {
385 const ceres::Solver::Options solver_options =
390 if (solver_options.minimizer_progress_to_stdout) {
399 TearDown(reconstruction);
409 ceres::LossFunction* loss_function) {
413 AddImageToProblem(image_id, reconstruction, loss_function);
416 AddPointToProblem(point3D_id, reconstruction, loss_function);
419 AddPointToProblem(point3D_id, reconstruction, loss_function);
426 void BundleAdjuster::TearDown(Reconstruction*) {
430 void BundleAdjuster::AddImageToProblem(
const image_t image_id,
431 Reconstruction* reconstruction,
432 ceres::LossFunction* loss_function) {
433 Image&
image = reconstruction->Image(image_id);
434 Camera& camera = reconstruction->Camera(
image.CameraId());
437 image.NormalizeQvec();
439 double* qvec_data =
image.Qvec().data();
440 double* tvec_data =
image.Tvec().data();
441 double* camera_params_data = camera.ParamsData();
443 const bool constant_pose =
447 size_t num_observations = 0;
448 for (
const Point2D& point2D :
image.Points2D()) {
449 if (!point2D.HasPoint3D()) {
453 num_observations += 1;
457 assert(point3D.Track().Length() > 1);
459 ceres::CostFunction* cost_function =
nullptr;
462 switch (camera.ModelId()) {
463 #define CAMERA_MODEL_CASE(CameraModel) \
464 case CameraModel::kModelId: \
466 BundleAdjustmentConstantPoseCostFunction<CameraModel>::Create( \
467 image.Qvec(), image.Tvec(), point2D.XY()); \
472 #undef CAMERA_MODEL_CASE
475 problem_->AddResidualBlock(cost_function, loss_function,
476 point3D.XYZ().data(),
479 switch (camera.ModelId()) {
480 #define CAMERA_MODEL_CASE(CameraModel) \
481 case CameraModel::kModelId: \
482 cost_function = BundleAdjustmentCostFunction<CameraModel>::Create( \
488 #undef CAMERA_MODEL_CASE
491 problem_->AddResidualBlock(cost_function, loss_function, qvec_data,
492 tvec_data, point3D.XYZ().data(),
497 if (num_observations > 0) {
501 if (!constant_pose) {
508 const std::vector<int>& constant_tvec_idxs =
521 void BundleAdjuster::AddPointToProblem(
const point3D_t point3D_id,
522 Reconstruction* reconstruction,
523 ceres::LossFunction* loss_function) {
533 for (
const auto& track_el : point3D.Track().Elements()) {
541 Image&
image = reconstruction->Image(track_el.image_id);
542 Camera& camera = reconstruction->Camera(
image.CameraId());
543 const Point2D& point2D =
image.Point2D(track_el.point2D_idx);
553 ceres::CostFunction* cost_function =
nullptr;
555 switch (camera.ModelId()) {
556 #define CAMERA_MODEL_CASE(CameraModel) \
557 case CameraModel::kModelId: \
559 BundleAdjustmentConstantPoseCostFunction<CameraModel>::Create( \
560 image.Qvec(), image.Tvec(), point2D.XY()); \
565 #undef CAMERA_MODEL_CASE
567 problem_->AddResidualBlock(cost_function, loss_function,
568 point3D.XYZ().data(), camera.ParamsData());
583 std::vector<int> const_camera_params;
586 const std::vector<size_t>& params_idxs =
588 const_camera_params.insert(const_camera_params.end(),
593 const std::vector<size_t>& params_idxs =
595 const_camera_params.insert(const_camera_params.end(),
600 const std::vector<size_t>& params_idxs =
602 const_camera_params.insert(const_camera_params.end(),
607 if (const_camera_params.size() > 0) {
616 const_camera_params,
problem_.get(),
627 problem_->SetParameterBlockConstant(point3D.
XYZ().data());
633 problem_->SetParameterBlockConstant(point3D.
XYZ().data());
642 bool ParallelBundleAdjuster::Options::Check()
const {
647 ParallelBundleAdjuster::ParallelBundleAdjuster(
648 const Options& options,
649 const BundleAdjustmentOptions& ba_options,
650 const BundleAdjustmentConfig& config)
652 ba_options_(ba_options),
654 num_measurements_(0) {
656 CHECK(ba_options_.Check());
658 <<
"PBA does not allow to set individual cameras constant";
660 <<
"PBA does not allow to set individual translational elements "
663 <<
"PBA does not allow to set individual translational elements "
666 <<
"PBA does not allow to parameterize individual 3D points";
670 CHECK_NOTNULL(reconstruction);
671 CHECK_EQ(num_measurements_, 0)
672 <<
"Cannot use the same ParallelBundleAdjuster multiple times";
673 CHECK(!ba_options_.refine_principal_point);
674 CHECK_EQ(ba_options_.refine_focal_length, ba_options_.refine_extra_params);
676 SetUp(reconstruction);
678 const int num_residuals =
static_cast<int>(2 * measurements_.size());
680 size_t num_threads = options_.num_threads;
681 if (num_residuals < options_.min_num_residuals_for_cpu_multi_threading) {
685 pba::ParallelBA::DeviceT device;
686 const int kMaxNumResidualsFloat = 100 * 1000;
687 if (num_residuals > kMaxNumResidualsFloat) {
690 device = pba::ParallelBA::PBA_CPU_DOUBLE;
692 if (options_.gpu_index < 0) {
693 device = pba::ParallelBA::PBA_CUDA_DEVICE_DEFAULT;
695 device =
static_cast<pba::ParallelBA::DeviceT
>(
696 pba::ParallelBA::PBA_CUDA_DEVICE0 + options_.gpu_index);
700 pba::ParallelBA pba(device, num_threads);
702 pba.SetNextBundleMode(pba::ParallelBA::BUNDLE_FULL);
703 pba.EnableRadialDistortion(pba::ParallelBA::PBA_PROJECTION_DISTORTION);
704 pba.SetFixedIntrinsics(!ba_options_.refine_focal_length &&
705 !ba_options_.refine_extra_params);
707 pba::ConfigBA* pba_config = pba.GetInternalConfig();
708 pba_config->__lm_delta_threshold /= 100.0f;
709 pba_config->__lm_gradient_threshold /= 100.0f;
710 pba_config->__lm_mse_threshold = 0.0f;
711 pba_config->__cg_min_iteration = 10;
712 pba_config->__verbose_level = 2;
713 pba_config->__lm_max_iteration = options_.max_num_iterations;
715 pba.SetCameraData(cameras_.size(), cameras_.data());
716 pba.SetPointData(points3D_.size(), points3D_.data());
717 pba.SetProjection(measurements_.size(), measurements_.data(),
718 point3D_idxs_.data(), camera_idxs_.data());
722 pba.RunBundleAdjustment();
726 summary_.num_residuals_reduced = num_residuals;
727 summary_.num_effective_parameters_reduced =
static_cast<int>(
728 8 * config_.NumImages() - 2 * config_.NumConstantCameras() +
729 3 * points3D_.size());
730 summary_.num_successful_steps = pba_config->GetIterationsLM() + 1;
731 summary_.termination_type = ceres::TerminationType::USER_SUCCESS;
732 summary_.initial_cost =
733 pba_config->GetInitialMSE() * summary_.num_residuals_reduced / 4;
734 summary_.final_cost =
735 pba_config->GetFinalMSE() * summary_.num_residuals_reduced / 4;
736 summary_.total_time_in_seconds = timer.ElapsedSeconds();
738 TearDown(reconstruction);
740 if (options_.print_summary) {
748 const ceres::Solver::Summary& ParallelBundleAdjuster::Summary()
const {
752 bool ParallelBundleAdjuster::IsSupported(
const BundleAdjustmentOptions& options,
753 const Reconstruction& reconstruction) {
754 if (options.refine_principal_point ||
755 options.refine_focal_length != options.refine_extra_params) {
761 std::set<camera_t> camera_ids;
762 for (
const auto&
image : reconstruction.Images()) {
763 if (
image.second.IsRegistered()) {
764 if (camera_ids.count(
image.second.CameraId()) != 0 ||
765 reconstruction.Camera(
image.second.CameraId()).ModelId() !=
769 camera_ids.insert(
image.second.CameraId());
775 void ParallelBundleAdjuster::SetUp(Reconstruction* reconstruction) {
778 cameras_.reserve(config_.NumImages());
779 camera_ids_.reserve(config_.NumImages());
780 ordered_image_ids_.reserve(config_.NumImages());
781 image_id_to_camera_idx_.reserve(config_.NumImages());
782 AddImagesToProblem(reconstruction);
783 AddPointsToProblem(reconstruction);
786 void ParallelBundleAdjuster::TearDown(Reconstruction* reconstruction) {
787 for (
size_t i = 0; i < cameras_.size(); ++i) {
788 const image_t image_id = ordered_image_ids_[i];
789 const pba::CameraT& pba_camera = cameras_[i];
793 Image&
image = reconstruction->Image(image_id);
794 Eigen::Matrix3d rotation_matrix;
795 pba_camera.GetMatrixRotation(rotation_matrix.data());
796 pba_camera.GetTranslation(
image.Tvec().data());
799 Camera& camera = reconstruction->Camera(
image.CameraId());
800 camera.Params(0) = pba_camera.GetFocalLength();
801 camera.Params(3) = pba_camera.GetProjectionDistortion();
804 for (
size_t i = 0; i < points3D_.size(); ++i) {
805 Point3D& point3D = reconstruction->
Point3D(ordered_point3D_ids_[i]);
806 points3D_[i].GetPoint(point3D.XYZ().data());
810 void ParallelBundleAdjuster::AddImagesToProblem(
811 Reconstruction* reconstruction) {
812 for (
const image_t image_id : config_.Images()) {
813 const Image&
image = reconstruction->Image(image_id);
814 CHECK_EQ(camera_ids_.count(
image.CameraId()), 0)
815 <<
"PBA does not support shared intrinsics";
817 const Camera& camera = reconstruction->Camera(
image.CameraId());
819 <<
"PBA only supports the SIMPLE_RADIAL camera model";
823 const Eigen::Matrix3d rotation_matrix =
826 pba::CameraT pba_camera;
827 pba_camera.SetFocalLength(camera.Params(0));
828 pba_camera.SetProjectionDistortion(camera.Params(3));
829 pba_camera.SetMatrixRotation(rotation_matrix.data());
830 pba_camera.SetTranslation(
image.Tvec().data());
832 CHECK(!config_.HasConstantTvec(image_id))
833 <<
"PBA cannot fix partial extrinsics";
834 if (!ba_options_.refine_extrinsics ||
835 config_.HasConstantPose(image_id)) {
836 CHECK(config_.IsConstantCamera(
image.CameraId()))
837 <<
"PBA cannot fix extrinsics only";
838 pba_camera.SetConstantCamera();
839 }
else if (config_.IsConstantCamera(
image.CameraId())) {
840 pba_camera.SetFixedIntrinsic();
842 pba_camera.SetVariableCamera();
845 num_measurements_ +=
image.NumPoints3D();
846 cameras_.push_back(pba_camera);
847 camera_ids_.insert(
image.CameraId());
848 ordered_image_ids_.push_back(image_id);
849 image_id_to_camera_idx_.emplace(image_id,
850 static_cast<int>(cameras_.size()) - 1);
852 for (
const Point2D& point2D :
image.Points2D()) {
853 if (point2D.HasPoint3D()) {
854 point3D_ids_.insert(point2D.Point3DId());
860 void ParallelBundleAdjuster::AddPointsToProblem(
861 Reconstruction* reconstruction) {
862 points3D_.resize(point3D_ids_.size());
863 ordered_point3D_ids_.resize(point3D_ids_.size());
864 measurements_.resize(num_measurements_);
865 camera_idxs_.resize(num_measurements_);
866 point3D_idxs_.resize(num_measurements_);
869 size_t measurement_idx = 0;
871 for (
const auto point3D_id : point3D_ids_) {
873 points3D_[point3D_idx].SetPoint(point3D.XYZ().data());
874 ordered_point3D_ids_[point3D_idx] = point3D_id;
876 for (
const auto track_el : point3D.Track().Elements()) {
877 if (image_id_to_camera_idx_.count(track_el.image_id) > 0) {
878 const Image&
image = reconstruction->Image(track_el.image_id);
879 const Camera& camera = reconstruction->Camera(
image.CameraId());
880 const Point2D& point2D =
image.Point2D(track_el.point2D_idx);
881 measurements_[measurement_idx].SetPoint2D(
882 point2D.X() - camera.Params(1),
883 point2D.Y() - camera.Params(2));
884 camera_idxs_[measurement_idx] =
885 image_id_to_camera_idx_.at(track_el.image_id);
886 point3D_idxs_[measurement_idx] = point3D_idx;
887 measurement_idx += 1;
893 CHECK_EQ(point3D_idx, points3D_.size());
894 CHECK_EQ(measurement_idx, measurements_.size());
908 std::vector<CameraRig>* camera_rigs) {
909 CHECK_NOTNULL(reconstruction);
910 CHECK_NOTNULL(camera_rigs);
911 CHECK(!
problem_) <<
"Cannot use the same BundleAdjuster multiple times";
914 std::unordered_set<camera_t> rig_camera_ids;
915 for (
auto& camera_rig : *camera_rigs) {
916 camera_rig.Check(*reconstruction);
917 for (
const auto& camera_id : camera_rig.GetCameraIds()) {
918 CHECK_EQ(rig_camera_ids.count(camera_id), 0)
919 <<
"Camera must not be part of multiple camera rigs";
920 rig_camera_ids.insert(camera_id);
923 for (
const auto& snapshot : camera_rig.Snapshots()) {
924 for (
const auto& image_id : snapshot) {
925 CHECK_EQ(image_id_to_camera_rig_.count(image_id), 0)
926 <<
"Image must not be part of multiple camera rigs";
927 image_id_to_camera_rig_.emplace(image_id, &camera_rig);
932 problem_.reset(
new ceres::Problem());
935 SetUp(reconstruction, camera_rigs, loss_function);
937 if (
problem_->NumResiduals() == 0) {
941 const ceres::Solver::Options solver_options =
946 if (solver_options.minimizer_progress_to_stdout) {
955 TearDown(reconstruction, *camera_rigs);
961 std::vector<CameraRig>* camera_rigs,
962 ceres::LossFunction* loss_function) {
963 ComputeCameraRigPoses(*reconstruction, *camera_rigs);
966 AddImageToProblem(image_id, reconstruction, camera_rigs, loss_function);
969 AddPointToProblem(point3D_id, reconstruction, loss_function);
972 AddPointToProblem(point3D_id, reconstruction, loss_function);
977 ParameterizeCameraRigs(reconstruction);
980 void RigBundleAdjuster::TearDown(Reconstruction* reconstruction,
981 const std::vector<CameraRig>& camera_rigs) {
982 for (
const auto& elem : image_id_to_camera_rig_) {
983 const auto image_id = elem.first;
984 const auto& camera_rig = *elem.second;
985 auto&
image = reconstruction->Image(image_id);
987 *image_id_to_rig_tvec_.at(image_id),
988 camera_rig.RelativeQvec(
image.CameraId()),
989 camera_rig.RelativeTvec(
image.CameraId()),
994 void RigBundleAdjuster::AddImageToProblem(
const image_t image_id,
995 Reconstruction* reconstruction,
996 std::vector<CameraRig>* camera_rigs,
997 ceres::LossFunction* loss_function) {
998 const double max_squared_reproj_error =
1001 Image&
image = reconstruction->Image(image_id);
1002 Camera& camera = reconstruction->Camera(
image.CameraId());
1007 double* qvec_data =
nullptr;
1008 double* tvec_data =
nullptr;
1009 double* rig_qvec_data =
nullptr;
1010 double* rig_tvec_data =
nullptr;
1011 double* camera_params_data = camera.ParamsData();
1012 CameraRig* camera_rig =
nullptr;
1015 if (image_id_to_camera_rig_.count(image_id) > 0) {
1016 CHECK(!constant_pose) <<
"Images contained in a camera rig must not "
1017 "have constant pose";
1018 CHECK(!constant_tvec) <<
"Images contained in a camera rig must not "
1019 "have constant tvec";
1020 camera_rig = image_id_to_camera_rig_.at(image_id);
1021 rig_qvec_data = image_id_to_rig_qvec_.at(image_id)->data();
1022 rig_tvec_data = image_id_to_rig_tvec_.at(image_id)->data();
1023 qvec_data = camera_rig->RelativeQvec(
image.CameraId()).data();
1024 tvec_data = camera_rig->RelativeTvec(
image.CameraId()).data();
1028 Eigen::Vector4d rig_concat_qvec;
1029 Eigen::Vector3d rig_concat_tvec;
1031 *image_id_to_rig_tvec_.at(image_id),
1032 camera_rig->RelativeQvec(
image.CameraId()),
1033 camera_rig->RelativeTvec(
image.CameraId()),
1034 &rig_concat_qvec, &rig_concat_tvec);
1039 image.NormalizeQvec();
1040 qvec_data =
image.Qvec().data();
1041 tvec_data =
image.Tvec().data();
1045 CHECK(
image.HasCamera());
1049 size_t num_observations = 0;
1052 for (
const Point2D& point2D :
image.Points2D()) {
1053 if (!point2D.HasPoint3D()) {
1058 assert(point3D.Track().Length() > 1);
1060 if (camera_rig !=
nullptr &&
1062 rig_proj_matrix, camera) >
1063 max_squared_reproj_error) {
1067 num_observations += 1;
1070 ceres::CostFunction* cost_function =
nullptr;
1072 if (camera_rig ==
nullptr) {
1073 if (constant_pose) {
1074 switch (camera.ModelId()) {
1075 #define CAMERA_MODEL_CASE(CameraModel) \
1076 case CameraModel::kModelId: \
1078 BundleAdjustmentConstantPoseCostFunction<CameraModel>::Create( \
1079 image.Qvec(), image.Tvec(), point2D.XY()); \
1084 #undef CAMERA_MODEL_CASE
1087 problem_->AddResidualBlock(cost_function, loss_function,
1088 point3D.XYZ().data(),
1089 camera_params_data);
1091 switch (camera.ModelId()) {
1092 #define CAMERA_MODEL_CASE(CameraModel) \
1093 case CameraModel::kModelId: \
1094 cost_function = BundleAdjustmentCostFunction<CameraModel>::Create( \
1100 #undef CAMERA_MODEL_CASE
1104 cost_function, loss_function, qvec_data, tvec_data,
1105 point3D.XYZ().data(), camera_params_data);
1108 switch (camera.ModelId()) {
1109 #define CAMERA_MODEL_CASE(CameraModel) \
1110 case CameraModel::kModelId: \
1111 cost_function = RigBundleAdjustmentCostFunction<CameraModel>::Create( \
1118 #undef CAMERA_MODEL_CASE
1120 problem_->AddResidualBlock(cost_function, loss_function,
1121 rig_qvec_data, rig_tvec_data, qvec_data,
1122 tvec_data, point3D.XYZ().data(),
1123 camera_params_data);
1127 if (num_observations > 0) {
1128 parameterized_qvec_data_.insert(qvec_data);
1130 if (camera_rig !=
nullptr) {
1131 parameterized_qvec_data_.insert(rig_qvec_data);
1136 image.CameraId() == camera_rig->RefCameraId()) {
1137 problem_->SetParameterBlockConstant(qvec_data);
1138 problem_->SetParameterBlockConstant(tvec_data);
1143 if (!constant_pose && constant_tvec) {
1145 const std::vector<int>& constant_tvec_idxs =
1155 void RigBundleAdjuster::AddPointToProblem(
const point3D_t point3D_id,
1156 Reconstruction* reconstruction,
1157 ceres::LossFunction* loss_function) {
1167 for (
const auto& track_el : point3D.Track().Elements()) {
1175 Image&
image = reconstruction->Image(track_el.image_id);
1176 Camera& camera = reconstruction->Camera(
image.CameraId());
1177 const Point2D& point2D =
image.Point2D(track_el.point2D_idx);
1187 ceres::CostFunction* cost_function =
nullptr;
1189 switch (camera.ModelId()) {
1190 #define CAMERA_MODEL_CASE(CameraModel) \
1191 case CameraModel::kModelId: \
1193 BundleAdjustmentConstantPoseCostFunction<CameraModel>::Create( \
1194 image.Qvec(), image.Tvec(), point2D.XY()); \
1195 problem_->AddResidualBlock(cost_function, loss_function, \
1196 point3D.XYZ().data(), camera.ParamsData()); \
1201 #undef CAMERA_MODEL_CASE
1206 void RigBundleAdjuster::ComputeCameraRigPoses(
1207 const Reconstruction& reconstruction,
1208 const std::vector<CameraRig>& camera_rigs) {
1209 camera_rig_qvecs_.reserve(camera_rigs.size());
1210 camera_rig_tvecs_.reserve(camera_rigs.size());
1211 for (
const auto& camera_rig : camera_rigs) {
1212 camera_rig_qvecs_.emplace_back();
1213 camera_rig_tvecs_.emplace_back();
1214 auto& rig_qvecs = camera_rig_qvecs_.back();
1215 auto& rig_tvecs = camera_rig_tvecs_.back();
1216 rig_qvecs.resize(camera_rig.NumSnapshots());
1217 rig_tvecs.resize(camera_rig.NumSnapshots());
1218 for (
size_t snapshot_idx = 0; snapshot_idx < camera_rig.NumSnapshots();
1220 camera_rig.ComputeAbsolutePose(snapshot_idx, reconstruction,
1221 &rig_qvecs[snapshot_idx],
1222 &rig_tvecs[snapshot_idx]);
1223 for (
const auto image_id : camera_rig.Snapshots()[snapshot_idx]) {
1224 image_id_to_rig_qvec_.emplace(image_id,
1225 &rig_qvecs[snapshot_idx]);
1226 image_id_to_rig_tvec_.emplace(image_id,
1227 &rig_tvecs[snapshot_idx]);
1233 void RigBundleAdjuster::ParameterizeCameraRigs(Reconstruction* reconstruction) {
1234 for (
double* qvec_data : parameterized_qvec_data_) {
1244 std::cout << std::right << std::setw(16) <<
"Residuals : ";
1245 std::cout << std::left << summary.num_residuals_reduced <<
std::endl;
1247 std::cout << std::right << std::setw(16) <<
"Parameters : ";
1248 std::cout << std::left << summary.num_effective_parameters_reduced
1251 std::cout << std::right << std::setw(16) <<
"Iterations : ";
1252 std::cout << std::left
1253 << summary.num_successful_steps + summary.num_unsuccessful_steps
1256 std::cout << std::right << std::setw(16) <<
"Time : ";
1257 std::cout << std::left << summary.total_time_in_seconds <<
" [s]"
1260 std::cout << std::right << std::setw(16) <<
"Initial cost : ";
1261 std::cout << std::right << std::setprecision(6)
1262 << std::sqrt(summary.initial_cost / summary.num_residuals_reduced)
1265 std::cout << std::right << std::setw(16) <<
"Final cost : ";
1266 std::cout << std::right << std::setprecision(6)
1267 << std::sqrt(summary.final_cost / summary.num_residuals_reduced)
1270 std::cout << std::right << std::setw(16) <<
"Termination : ";
1272 std::string termination =
"";
1274 switch (summary.termination_type) {
1275 case ceres::CONVERGENCE:
1276 termination =
"Convergence";
1278 case ceres::NO_CONVERGENCE:
1279 termination =
"No convergence";
1281 case ceres::FAILURE:
1282 termination =
"Failure";
1284 case ceres::USER_SUCCESS:
1285 termination =
"User success";
1287 case ceres::USER_FAILURE:
1288 termination =
"User failure";
1291 termination =
"Unknown";
1295 std::cout << std::right << termination <<
std::endl;
std::shared_ptr< core::Tensor > image
#define CAMERA_MODEL_SWITCH_CASES
Point3D(T x=0., T y=0., T z=0.)
std::unique_ptr< ceres::Problem > problem_
const BundleAdjustmentOptions options_
BundleAdjustmentConfig config_
std::unordered_set< camera_t > camera_ids_
const ceres::Solver::Summary & Summary() const
bool Solve(Reconstruction *reconstruction)
std::unordered_map< point3D_t, size_t > point3D_num_observations_
ceres::Solver::Summary summary_
void ParameterizeCameras(Reconstruction *reconstruction)
BundleAdjuster(const BundleAdjustmentOptions &options, const BundleAdjustmentConfig &config)
void ParameterizePoints(Reconstruction *reconstruction)
void RemoveConstantPoint(const point3D_t point3D_id)
void AddVariablePoint(const point3D_t point3D_id)
size_t NumConstantPoses() const
bool HasConstantPose(const image_t image_id) const
size_t NumConstantTvecs() const
void SetVariableCamera(const camera_t camera_id)
void SetVariablePose(const image_t image_id)
bool IsConstantCamera(const camera_t camera_id) const
const std::vector< int > & ConstantTvec(const image_t image_id) const
bool HasConstantTvec(const image_t image_id) const
const std::unordered_set< point3D_t > & VariablePoints() const
bool HasVariablePoint(const point3D_t point3D_id) const
const std::unordered_set< image_t > & Images() const
void RemoveImage(const image_t image_id)
void SetConstantCamera(const camera_t camera_id)
void SetConstantPose(const image_t image_id)
const std::unordered_set< point3D_t > & ConstantPoints() const
bool HasPoint(const point3D_t point3D_id) const
void RemoveConstantTvec(const image_t image_id)
size_t NumConstantPoints() const
void AddImage(const image_t image_id)
size_t NumConstantCameras() const
void SetConstantTvec(const image_t image_id, const std::vector< int > &idxs)
void RemoveVariablePoint(const point3D_t point3D_id)
void AddConstantPoint(const point3D_t point3D_id)
size_t NumVariablePoints() const
bool HasConstantPoint(const point3D_t point3D_id) const
size_t NumResiduals(const Reconstruction &reconstruction) const
bool HasImage(const image_t image_id) const
const std::vector< size_t > & PrincipalPointIdxs() const
const std::vector< size_t > & ExtraParamsIdxs() const
const std::vector< size_t > & FocalLengthIdxs() const
const double * ParamsData() const
point2D_t NumPoints3D() const
const Eigen::Vector3d & XYZ() const
const class Track & Track() const
const class Image & Image(const image_t image_id) const
const class Camera & Camera(const camera_t camera_id) const
const class Point3D & Point3D(const point3D_t point3D_id) const
RigBundleAdjuster(const BundleAdjustmentOptions &options, const Options &rig_options, const BundleAdjustmentConfig &config)
bool Solve(Reconstruction *reconstruction, std::vector< CameraRig > *camera_rigs)
#define CHECK_OPTION_LT(val1, val2)
#define CHECK_OPTION_GE(val1, val2)
Matrix< double, 3, 4 > Matrix3x4d
QTextStream & endl(QTextStream &stream)
void Solve(const Tensor &A, const Tensor &B, Tensor &X)
Solve AX = B with LU decomposition. A is a square matrix.
void SetBestCudaDevice(const int gpu_index)
double CalculateSquaredReprojectionError(const Eigen::Vector2d &point2D, const Eigen::Vector3d &point3D, const Eigen::Vector4d &qvec, const Eigen::Vector3d &tvec, const Camera &camera)
void PrintHeading2(const std::string &heading)
bool VectorContainsDuplicateValues(const std::vector< T > &vector)
void ConcatenatePoses(const Eigen::Vector4d &qvec1, const Eigen::Vector3d &tvec1, const Eigen::Vector4d &qvec2, const Eigen::Vector3d &tvec2, Eigen::Vector4d *qvec12, Eigen::Vector3d *tvec12)
void PrintSolverSummary(const ceres::Solver::Summary &summary)
Eigen::Vector4d RotationMatrixToQuaternion(const Eigen::Matrix3d &rot_mat)
void SetQuaternionManifold(ceres::Problem *problem, double *quat_xyzw)
Eigen::Matrix3x4d ComposeProjectionMatrix(const Eigen::Vector4d &qvec, const Eigen::Vector3d &tvec)
void SetSubsetManifold(int size, const std::vector< int > &constant_params, ceres::Problem *problem, double *params)
int GetEffectiveNumThreads(const int num_threads)
Eigen::Matrix3d QuaternionToRotationMatrix(const Eigen::Vector4d &qvec)
ceres::Solver::Options CreateSolverOptions(const BundleAdjustmentConfig &config, const ceres::Problem &problem) const
LossFunctionType loss_function_type
int min_num_images_gpu_solver
bool refine_principal_point
int max_num_images_direct_dense_cpu_solver
int max_num_images_direct_sparse_gpu_solver
int max_num_images_direct_dense_gpu_solver
double loss_function_scale
int min_num_residuals_for_cpu_multi_threading
ceres::Solver::Options solver_options
int max_num_images_direct_sparse_cpu_solver
ceres::LossFunction * CreateLossFunction() const
bool refine_relative_poses
static const int model_id