ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
bundle_adjustment.cc
Go to the documentation of this file.
1 // Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 //
14 // * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
15 // its contributors may be used to endorse or promote products derived
16 // from this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 // POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)
31 
33 
34 #include <iomanip>
35 
36 #ifdef OPENMP_ENABLED
37 #include <omp.h>
38 #endif
39 
40 #include "base/camera_models.h"
41 #include "base/cost_functions.h"
42 #include "base/projection.h"
43 #include "optim/manifold.h"
44 #include "util/cuda.h"
45 #include "util/misc.h"
46 #include "util/threading.h"
47 #include "util/timer.h"
48 
49 namespace colmap {
50 
52 // BundleAdjustmentConfig
54 
56 
57 size_t BundleAdjustmentConfig::NumImages() const { return image_ids_.size(); }
58 
60  return variable_point3D_ids_.size() + constant_point3D_ids_.size();
61 }
62 
64  return constant_camera_ids_.size();
65 }
66 
68  return constant_poses_.size();
69 }
70 
72  return constant_tvecs_.size();
73 }
74 
76  return variable_point3D_ids_.size();
77 }
78 
80  return constant_point3D_ids_.size();
81 }
82 
84  const Reconstruction& reconstruction) const {
85  // Count the number of observations for all added images.
86  size_t num_observations = 0;
87  for (const image_t image_id : image_ids_) {
88  num_observations += reconstruction.Image(image_id).NumPoints3D();
89  }
90 
91  // Count the number of observations for all added 3D points that are not
92  // already added as part of the images above.
93 
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;
101  }
102  }
103  return num_observations_for_point;
104  };
105 
106  for (const auto point3D_id : variable_point3D_ids_) {
107  num_observations += NumObservationsForPoint(point3D_id);
108  }
109  for (const auto point3D_id : constant_point3D_ids_) {
110  num_observations += NumObservationsForPoint(point3D_id);
111  }
112 
113  return 2 * num_observations;
114 }
115 
117  image_ids_.insert(image_id);
118 }
119 
120 bool BundleAdjustmentConfig::HasImage(const image_t image_id) const {
121  return image_ids_.find(image_id) != image_ids_.end();
122 }
123 
125  image_ids_.erase(image_id);
126 }
127 
129  constant_camera_ids_.insert(camera_id);
130 }
131 
133  constant_camera_ids_.erase(camera_id);
134 }
135 
137  return constant_camera_ids_.find(camera_id) != constant_camera_ids_.end();
138 }
139 
141  CHECK(HasImage(image_id));
142  CHECK(!HasConstantTvec(image_id));
143  constant_poses_.insert(image_id);
144 }
145 
147  constant_poses_.erase(image_id);
148 }
149 
151  return constant_poses_.find(image_id) != constant_poses_.end();
152 }
153 
155  const std::vector<int>& idxs) {
156  CHECK_GT(idxs.size(), 0);
157  CHECK_LE(idxs.size(), 3);
158  CHECK(HasImage(image_id));
159  CHECK(!HasConstantPose(image_id));
160  CHECK(!VectorContainsDuplicateValues(idxs))
161  << "Tvec indices must not contain duplicates";
162  constant_tvecs_.emplace(image_id, idxs);
163 }
164 
166  constant_tvecs_.erase(image_id);
167 }
168 
170  return constant_tvecs_.find(image_id) != constant_tvecs_.end();
171 }
172 
173 const std::unordered_set<image_t>& BundleAdjustmentConfig::Images() const {
174  return image_ids_;
175 }
176 
177 const std::unordered_set<point3D_t>& BundleAdjustmentConfig::VariablePoints()
178  const {
179  return variable_point3D_ids_;
180 }
181 
182 const std::unordered_set<point3D_t>& BundleAdjustmentConfig::ConstantPoints()
183  const {
184  return constant_point3D_ids_;
185 }
186 
187 const std::vector<int>& BundleAdjustmentConfig::ConstantTvec(
188  const image_t image_id) const {
189  return constant_tvecs_.at(image_id);
190 }
191 
193  CHECK(!HasConstantPoint(point3D_id));
194  variable_point3D_ids_.insert(point3D_id);
195 }
196 
198  CHECK(!HasVariablePoint(point3D_id));
199  constant_point3D_ids_.insert(point3D_id);
200 }
201 
202 bool BundleAdjustmentConfig::HasPoint(const point3D_t point3D_id) const {
203  return HasVariablePoint(point3D_id) || HasConstantPoint(point3D_id);
204 }
205 
207  const point3D_t point3D_id) const {
208  return variable_point3D_ids_.find(point3D_id) !=
209  variable_point3D_ids_.end();
210 }
211 
213  const point3D_t point3D_id) const {
214  return constant_point3D_ids_.find(point3D_id) !=
215  constant_point3D_ids_.end();
216 }
217 
219  variable_point3D_ids_.erase(point3D_id);
220 }
221 
223  constant_point3D_ids_.erase(point3D_id);
224 }
225 
227 // BundleAdjustmentOptions
229 
230 ceres::LossFunction* BundleAdjustmentOptions::CreateLossFunction() const {
231  ceres::LossFunction* loss_function = nullptr;
232  switch (loss_function_type) {
234  loss_function = new ceres::TrivialLoss();
235  break;
237  loss_function = new ceres::SoftLOneLoss(loss_function_scale);
238  break;
240  loss_function = new ceres::CauchyLoss(loss_function_scale);
241  break;
242  }
243  CHECK_NOTNULL(loss_function);
244  return loss_function;
245 }
246 
253  return true;
254 }
255 
257  const BundleAdjustmentConfig& config,
258  const ceres::Problem& problem) const {
259  ceres::Solver::Options custom_solver_options = solver_options;
260  if (VLOG_IS_ON(2)) {
261  custom_solver_options.minimizer_progress_to_stdout = true;
262  custom_solver_options.logging_type =
263  ceres::LoggingType::PER_MINIMIZER_ITERATION;
264  }
265 
266  const int num_images = config.NumImages();
267  const bool has_sparse =
268  custom_solver_options.sparse_linear_algebra_library_type !=
269  ceres::NO_SPARSE;
270 
271  int max_num_images_direct_dense_solver =
273  int max_num_images_direct_sparse_solver =
275 
276 #ifdef CUDA_ENABLED
277  bool cuda_solver_enabled = false;
278 
279 #if (CERES_VERSION_MAJOR >= 3 || \
280  (CERES_VERSION_MAJOR == 2 && CERES_VERSION_MINOR >= 2)) && \
281  !defined(CERES_NO_CUDA)
282  if (use_gpu && num_images >= min_num_images_gpu_solver) {
283  cuda_solver_enabled = true;
284  custom_solver_options.dense_linear_algebra_library_type = ceres::CUDA;
285  max_num_images_direct_dense_solver =
287  }
288 #else
289  if (use_gpu) {
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 "
293  "dense "
294  "solvers.";
295  }
296 #endif
297 
298 #if (CERES_VERSION_MAJOR >= 3 || \
299  (CERES_VERSION_MAJOR == 2 && CERES_VERSION_MINOR >= 3)) && \
300  !defined(CERES_NO_CUDSS)
301  if (use_gpu && num_images >= min_num_images_gpu_solver) {
302  cuda_solver_enabled = true;
303  custom_solver_options.sparse_linear_algebra_library_type =
304  ceres::CUDA_SPARSE;
305  max_num_images_direct_sparse_solver =
307  }
308 #else
309  if (use_gpu) {
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 "
313  "sparse "
314  "solvers.";
315  }
316 #endif
317 
318  if (cuda_solver_enabled) {
319  const std::vector<int> gpu_indices = CSVToVector<int>(gpu_index);
320  CHECK_GT(gpu_indices.size(), 0);
321  SetBestCudaDevice(gpu_indices[0]);
322  }
323 #else
324  if (use_gpu) {
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 "
328  "solvers.";
329  }
330 #endif // CUDA_ENABLED
331 
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;
337  } else { // Indirect sparse (preconditioned CG) solver.
338  custom_solver_options.linear_solver_type = ceres::ITERATIVE_SCHUR;
339  custom_solver_options.preconditioner_type = ceres::SCHUR_JACOBI;
340  }
341 
342  if (problem.NumResiduals() < min_num_residuals_for_cpu_multi_threading) {
343  custom_solver_options.num_threads = 1;
344 #if CERES_VERSION_MAJOR < 2
345  custom_solver_options.num_linear_solver_threads = 1;
346 #endif // CERES_VERSION_MAJOR
347  } else {
348  custom_solver_options.num_threads =
349  GetEffectiveNumThreads(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);
354 #endif // CERES_VERSION_MAJOR
355  }
356 
357  std::string solver_error;
358  CHECK(custom_solver_options.IsValid(&solver_error)) << solver_error;
359  return custom_solver_options;
360 }
361 
363 // BundleAdjuster
365 
367  const BundleAdjustmentConfig& config)
368  : options_(options), config_(config) {
369  CHECK(options_.Check());
370 }
371 
372 bool BundleAdjuster::Solve(Reconstruction* reconstruction) {
373  CHECK_NOTNULL(reconstruction);
374  CHECK(!problem_) << "Cannot use the same BundleAdjuster multiple times";
375 
376  problem_.reset(new ceres::Problem());
377 
378  ceres::LossFunction* loss_function = options_.CreateLossFunction();
379  SetUp(reconstruction, loss_function);
380 
381  if (problem_->NumResiduals() == 0) {
382  return false;
383  }
384 
385  const ceres::Solver::Options solver_options =
387 
388  ceres::Solve(solver_options, problem_.get(), &summary_);
389 
390  if (solver_options.minimizer_progress_to_stdout) {
391  std::cout << std::endl;
392  }
393 
394  if (options_.print_summary) {
395  PrintHeading2("Bundle adjustment report");
397  }
398 
399  TearDown(reconstruction);
400 
401  return true;
402 }
403 
404 const ceres::Solver::Summary& BundleAdjuster::Summary() const {
405  return summary_;
406 }
407 
408 void BundleAdjuster::SetUp(Reconstruction* reconstruction,
409  ceres::LossFunction* loss_function) {
410  // Warning: AddPointsToProblem assumes that AddImageToProblem is called
411  // first. Do not change order of instructions!
412  for (const image_t image_id : config_.Images()) {
413  AddImageToProblem(image_id, reconstruction, loss_function);
414  }
415  for (const auto point3D_id : config_.VariablePoints()) {
416  AddPointToProblem(point3D_id, reconstruction, loss_function);
417  }
418  for (const auto point3D_id : config_.ConstantPoints()) {
419  AddPointToProblem(point3D_id, reconstruction, loss_function);
420  }
421 
422  ParameterizeCameras(reconstruction);
423  ParameterizePoints(reconstruction);
424 }
425 
426 void BundleAdjuster::TearDown(Reconstruction*) {
427  // Nothing to do
428 }
429 
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());
435 
436  // CostFunction assumes unit quaternions.
437  image.NormalizeQvec();
438 
439  double* qvec_data = image.Qvec().data();
440  double* tvec_data = image.Tvec().data();
441  double* camera_params_data = camera.ParamsData();
442 
443  const bool constant_pose =
445 
446  // Add residuals to bundle adjustment problem.
447  size_t num_observations = 0;
448  for (const Point2D& point2D : image.Points2D()) {
449  if (!point2D.HasPoint3D()) {
450  continue;
451  }
452 
453  num_observations += 1;
454  point3D_num_observations_[point2D.Point3DId()] += 1;
455 
456  Point3D& point3D = reconstruction->Point3D(point2D.Point3DId());
457  assert(point3D.Track().Length() > 1);
458 
459  ceres::CostFunction* cost_function = nullptr;
460 
461  if (constant_pose) {
462  switch (camera.ModelId()) {
463 #define CAMERA_MODEL_CASE(CameraModel) \
464  case CameraModel::kModelId: \
465  cost_function = \
466  BundleAdjustmentConstantPoseCostFunction<CameraModel>::Create( \
467  image.Qvec(), image.Tvec(), point2D.XY()); \
468  break;
469 
471 
472 #undef CAMERA_MODEL_CASE
473  }
474 
475  problem_->AddResidualBlock(cost_function, loss_function,
476  point3D.XYZ().data(),
477  camera_params_data);
478  } else {
479  switch (camera.ModelId()) {
480 #define CAMERA_MODEL_CASE(CameraModel) \
481  case CameraModel::kModelId: \
482  cost_function = BundleAdjustmentCostFunction<CameraModel>::Create( \
483  point2D.XY()); \
484  break;
485 
487 
488 #undef CAMERA_MODEL_CASE
489  }
490 
491  problem_->AddResidualBlock(cost_function, loss_function, qvec_data,
492  tvec_data, point3D.XYZ().data(),
493  camera_params_data);
494  }
495  }
496 
497  if (num_observations > 0) {
498  camera_ids_.insert(image.CameraId());
499 
500  // Set pose parameterization.
501  if (!constant_pose) {
502  // ceres::LocalParameterization* quaternion_parameterization =
503  // new ceres::QuaternionParameterization;
504  // problem_->SetParameterization(qvec_data,
505  // quaternion_parameterization);
506  SetQuaternionManifold(problem_.get(), qvec_data);
507  if (config_.HasConstantTvec(image_id)) {
508  const std::vector<int>& constant_tvec_idxs =
509  config_.ConstantTvec(image_id);
510  // ceres::SubsetParameterization* tvec_parameterization =
511  // new ceres::SubsetParameterization(3, constant_tvec_idxs);
512  // problem_->SetParameterization(tvec_data,
513  // tvec_parameterization);
514  SetSubsetManifold(3, constant_tvec_idxs, problem_.get(),
515  tvec_data);
516  }
517  }
518  }
519 }
520 
521 void BundleAdjuster::AddPointToProblem(const point3D_t point3D_id,
522  Reconstruction* reconstruction,
523  ceres::LossFunction* loss_function) {
524  Point3D& point3D = reconstruction->Point3D(point3D_id);
525 
526  // Is 3D point already fully contained in the problem? I.e. its entire track
527  // is contained in `variable_image_ids`, `constant_image_ids`,
528  // `constant_x_image_ids`.
529  if (point3D_num_observations_[point3D_id] == point3D.Track().Length()) {
530  return;
531  }
532 
533  for (const auto& track_el : point3D.Track().Elements()) {
534  // Skip observations that were already added in `FillImages`.
535  if (config_.HasImage(track_el.image_id)) {
536  continue;
537  }
538 
539  point3D_num_observations_[point3D_id] += 1;
540 
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);
544 
545  // We do not want to refine the camera of images that are not
546  // part of `constant_image_ids_`, `constant_image_ids_`,
547  // `constant_x_image_ids_`.
548  if (camera_ids_.count(image.CameraId()) == 0) {
549  camera_ids_.insert(image.CameraId());
550  config_.SetConstantCamera(image.CameraId());
551  }
552 
553  ceres::CostFunction* cost_function = nullptr;
554 
555  switch (camera.ModelId()) {
556 #define CAMERA_MODEL_CASE(CameraModel) \
557  case CameraModel::kModelId: \
558  cost_function = \
559  BundleAdjustmentConstantPoseCostFunction<CameraModel>::Create( \
560  image.Qvec(), image.Tvec(), point2D.XY()); \
561  break;
562 
564 
565 #undef CAMERA_MODEL_CASE
566  }
567  problem_->AddResidualBlock(cost_function, loss_function,
568  point3D.XYZ().data(), camera.ParamsData());
569  }
570 }
571 
573  const bool constant_camera = !options_.refine_focal_length &&
576  for (const camera_t camera_id : camera_ids_) {
577  Camera& camera = reconstruction->Camera(camera_id);
578 
579  if (constant_camera || config_.IsConstantCamera(camera_id)) {
580  problem_->SetParameterBlockConstant(camera.ParamsData());
581  continue;
582  } else {
583  std::vector<int> const_camera_params;
584 
586  const std::vector<size_t>& params_idxs =
587  camera.FocalLengthIdxs();
588  const_camera_params.insert(const_camera_params.end(),
589  params_idxs.begin(),
590  params_idxs.end());
591  }
593  const std::vector<size_t>& params_idxs =
594  camera.PrincipalPointIdxs();
595  const_camera_params.insert(const_camera_params.end(),
596  params_idxs.begin(),
597  params_idxs.end());
598  }
600  const std::vector<size_t>& params_idxs =
601  camera.ExtraParamsIdxs();
602  const_camera_params.insert(const_camera_params.end(),
603  params_idxs.begin(),
604  params_idxs.end());
605  }
606 
607  if (const_camera_params.size() > 0) {
608  // ceres::SubsetParameterization* camera_params_parameterization
609  // =
610  // new ceres::SubsetParameterization(
611  // static_cast<int>(camera.NumParams()),
612  // const_camera_params);
613  // problem_->SetParameterization(camera.ParamsData(),
614  // camera_params_parameterization);
615  SetSubsetManifold(static_cast<int>(camera.NumParams()),
616  const_camera_params, problem_.get(),
617  camera.ParamsData());
618  }
619  }
620  }
621 }
622 
624  for (const auto elem : point3D_num_observations_) {
625  Point3D& point3D = reconstruction->Point3D(elem.first);
626  if (point3D.Track().Length() > elem.second) {
627  problem_->SetParameterBlockConstant(point3D.XYZ().data());
628  }
629  }
630 
631  for (const point3D_t point3D_id : config_.ConstantPoints()) {
632  Point3D& point3D = reconstruction->Point3D(point3D_id);
633  problem_->SetParameterBlockConstant(point3D.XYZ().data());
634  }
635 }
636 
637 #ifdef PBA_ENABLED
639 // ParallelBundleAdjuster
641 
642 bool ParallelBundleAdjuster::Options::Check() const {
643  CHECK_OPTION_GE(max_num_iterations, 0);
644  return true;
645 }
646 
647 ParallelBundleAdjuster::ParallelBundleAdjuster(
648  const Options& options,
649  const BundleAdjustmentOptions& ba_options,
650  const BundleAdjustmentConfig& config)
651  : options_(options),
652  ba_options_(ba_options),
653  config_(config),
654  num_measurements_(0) {
655  CHECK(options_.Check());
656  CHECK(ba_options_.Check());
657  CHECK_EQ(config_.NumConstantCameras(), 0)
658  << "PBA does not allow to set individual cameras constant";
659  CHECK_EQ(config_.NumConstantPoses(), 0)
660  << "PBA does not allow to set individual translational elements "
661  "constant";
662  CHECK_EQ(config_.NumConstantTvecs(), 0)
663  << "PBA does not allow to set individual translational elements "
664  "constant";
665  CHECK(config_.NumVariablePoints() == 0 && config_.NumConstantPoints() == 0)
666  << "PBA does not allow to parameterize individual 3D points";
667 }
668 
669 bool ParallelBundleAdjuster::Solve(Reconstruction* reconstruction) {
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);
675 
676  SetUp(reconstruction);
677 
678  const int num_residuals = static_cast<int>(2 * measurements_.size());
679 
680  size_t num_threads = options_.num_threads;
681  if (num_residuals < options_.min_num_residuals_for_cpu_multi_threading) {
682  num_threads = 1;
683  }
684 
685  pba::ParallelBA::DeviceT device;
686  const int kMaxNumResidualsFloat = 100 * 1000;
687  if (num_residuals > kMaxNumResidualsFloat) {
688  // The threshold for using double precision is empirically chosen and
689  // ensures that the system can be reliable solved.
690  device = pba::ParallelBA::PBA_CPU_DOUBLE;
691  } else {
692  if (options_.gpu_index < 0) {
693  device = pba::ParallelBA::PBA_CUDA_DEVICE_DEFAULT;
694  } else {
695  device = static_cast<pba::ParallelBA::DeviceT>(
696  pba::ParallelBA::PBA_CUDA_DEVICE0 + options_.gpu_index);
697  }
698  }
699 
700  pba::ParallelBA pba(device, num_threads);
701 
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);
706 
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;
714 
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());
719 
720  Timer timer;
721  timer.Start();
722  pba.RunBundleAdjustment();
723  timer.Pause();
724 
725  // Compose Ceres solver summary from PBA options.
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();
737 
738  TearDown(reconstruction);
739 
740  if (options_.print_summary) {
741  PrintHeading2("Bundle adjustment report");
742  PrintSolverSummary(summary_);
743  }
744 
745  return true;
746 }
747 
748 const ceres::Solver::Summary& ParallelBundleAdjuster::Summary() const {
749  return summary_;
750 }
751 
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) {
756  return false;
757  }
758 
759  // Check that all cameras are SIMPLE_RADIAL and that no intrinsics are
760  // shared.
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() !=
767  return false;
768  }
769  camera_ids.insert(image.second.CameraId());
770  }
771  }
772  return true;
773 }
774 
775 void ParallelBundleAdjuster::SetUp(Reconstruction* reconstruction) {
776  // Important: PBA requires the track of 3D points to be stored
777  // contiguously, i.e. the point3D_idxs_ vector contains consecutive indices.
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);
784 }
785 
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];
790 
791  // Note: Do not use PBA's quaternion methods as they seem to lead to
792  // numerical instability or other issues.
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());
797  image.Qvec() = RotationMatrixToQuaternion(rotation_matrix.transpose());
798 
799  Camera& camera = reconstruction->Camera(image.CameraId());
800  camera.Params(0) = pba_camera.GetFocalLength();
801  camera.Params(3) = pba_camera.GetProjectionDistortion();
802  }
803 
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());
807  }
808 }
809 
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";
816 
817  const Camera& camera = reconstruction->Camera(image.CameraId());
818  CHECK_EQ(camera.ModelId(), SimpleRadialCameraModel::model_id)
819  << "PBA only supports the SIMPLE_RADIAL camera model";
820 
821  // Note: Do not use PBA's quaternion methods as they seem to lead to
822  // numerical instability or other issues.
823  const Eigen::Matrix3d rotation_matrix =
824  QuaternionToRotationMatrix(image.Qvec()).transpose();
825 
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());
831 
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();
841  } else {
842  pba_camera.SetVariableCamera();
843  }
844 
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);
851 
852  for (const Point2D& point2D : image.Points2D()) {
853  if (point2D.HasPoint3D()) {
854  point3D_ids_.insert(point2D.Point3DId());
855  }
856  }
857  }
858 }
859 
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_);
867 
868  int point3D_idx = 0;
869  size_t measurement_idx = 0;
870 
871  for (const auto point3D_id : point3D_ids_) {
872  const Point3D& point3D = reconstruction->Point3D(point3D_id);
873  points3D_[point3D_idx].SetPoint(point3D.XYZ().data());
874  ordered_point3D_ids_[point3D_idx] = point3D_id;
875 
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;
888  }
889  }
890  point3D_idx += 1;
891  }
892 
893  CHECK_EQ(point3D_idx, points3D_.size());
894  CHECK_EQ(measurement_idx, measurements_.size());
895 }
896 #endif
897 
899 // RigBundleAdjuster
901 
903  const Options& rig_options,
904  const BundleAdjustmentConfig& config)
905  : BundleAdjuster(options, config), rig_options_(rig_options) {}
906 
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";
912 
913  // Check the validity of the provided camera rigs.
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);
921  }
922 
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);
928  }
929  }
930  }
931 
932  problem_.reset(new ceres::Problem());
933 
934  ceres::LossFunction* loss_function = options_.CreateLossFunction();
935  SetUp(reconstruction, camera_rigs, loss_function);
936 
937  if (problem_->NumResiduals() == 0) {
938  return false;
939  }
940 
941  const ceres::Solver::Options solver_options =
943 
944  ceres::Solve(solver_options, problem_.get(), &summary_);
945 
946  if (solver_options.minimizer_progress_to_stdout) {
947  std::cout << std::endl;
948  }
949 
950  if (options_.print_summary) {
951  PrintHeading2("Rig Bundle adjustment report");
953  }
954 
955  TearDown(reconstruction, *camera_rigs);
956 
957  return true;
958 }
959 
960 void RigBundleAdjuster::SetUp(Reconstruction* reconstruction,
961  std::vector<CameraRig>* camera_rigs,
962  ceres::LossFunction* loss_function) {
963  ComputeCameraRigPoses(*reconstruction, *camera_rigs);
964 
965  for (const image_t image_id : config_.Images()) {
966  AddImageToProblem(image_id, reconstruction, camera_rigs, loss_function);
967  }
968  for (const auto point3D_id : config_.VariablePoints()) {
969  AddPointToProblem(point3D_id, reconstruction, loss_function);
970  }
971  for (const auto point3D_id : config_.ConstantPoints()) {
972  AddPointToProblem(point3D_id, reconstruction, loss_function);
973  }
974 
975  ParameterizeCameras(reconstruction);
976  ParameterizePoints(reconstruction);
977  ParameterizeCameraRigs(reconstruction);
978 }
979 
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);
986  ConcatenatePoses(*image_id_to_rig_qvec_.at(image_id),
987  *image_id_to_rig_tvec_.at(image_id),
988  camera_rig.RelativeQvec(image.CameraId()),
989  camera_rig.RelativeTvec(image.CameraId()),
990  &image.Qvec(), &image.Tvec());
991  }
992 }
993 
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 =
999  rig_options_.max_reproj_error * rig_options_.max_reproj_error;
1000 
1001  Image& image = reconstruction->Image(image_id);
1002  Camera& camera = reconstruction->Camera(image.CameraId());
1003 
1004  const bool constant_pose = config_.HasConstantPose(image_id);
1005  const bool constant_tvec = config_.HasConstantTvec(image_id);
1006 
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;
1013  Eigen::Matrix3x4d rig_proj_matrix = Eigen::Matrix3x4d::Zero();
1014 
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();
1025 
1026  // Concatenate the absolute pose of the rig and the relative pose the
1027  // camera within the rig to detect outlier observations.
1028  Eigen::Vector4d rig_concat_qvec;
1029  Eigen::Vector3d rig_concat_tvec;
1030  ConcatenatePoses(*image_id_to_rig_qvec_.at(image_id),
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);
1035  rig_proj_matrix =
1036  ComposeProjectionMatrix(rig_concat_qvec, rig_concat_tvec);
1037  } else {
1038  // CostFunction assumes unit quaternions.
1039  image.NormalizeQvec();
1040  qvec_data = image.Qvec().data();
1041  tvec_data = image.Tvec().data();
1042  }
1043 
1044  // Collect cameras for final parameterization.
1045  CHECK(image.HasCamera());
1046  camera_ids_.insert(image.CameraId());
1047 
1048  // The number of added observations for the current image.
1049  size_t num_observations = 0;
1050 
1051  // Add residuals to bundle adjustment problem.
1052  for (const Point2D& point2D : image.Points2D()) {
1053  if (!point2D.HasPoint3D()) {
1054  continue;
1055  }
1056 
1057  Point3D& point3D = reconstruction->Point3D(point2D.Point3DId());
1058  assert(point3D.Track().Length() > 1);
1059 
1060  if (camera_rig != nullptr &&
1061  CalculateSquaredReprojectionError(point2D.XY(), point3D.XYZ(),
1062  rig_proj_matrix, camera) >
1063  max_squared_reproj_error) {
1064  continue;
1065  }
1066 
1067  num_observations += 1;
1068  point3D_num_observations_[point2D.Point3DId()] += 1;
1069 
1070  ceres::CostFunction* cost_function = nullptr;
1071 
1072  if (camera_rig == nullptr) {
1073  if (constant_pose) {
1074  switch (camera.ModelId()) {
1075 #define CAMERA_MODEL_CASE(CameraModel) \
1076  case CameraModel::kModelId: \
1077  cost_function = \
1078  BundleAdjustmentConstantPoseCostFunction<CameraModel>::Create( \
1079  image.Qvec(), image.Tvec(), point2D.XY()); \
1080  break;
1081 
1083 
1084 #undef CAMERA_MODEL_CASE
1085  }
1086 
1087  problem_->AddResidualBlock(cost_function, loss_function,
1088  point3D.XYZ().data(),
1089  camera_params_data);
1090  } else {
1091  switch (camera.ModelId()) {
1092 #define CAMERA_MODEL_CASE(CameraModel) \
1093  case CameraModel::kModelId: \
1094  cost_function = BundleAdjustmentCostFunction<CameraModel>::Create( \
1095  point2D.XY()); \
1096  break;
1097 
1099 
1100 #undef CAMERA_MODEL_CASE
1101  }
1102 
1103  problem_->AddResidualBlock(
1104  cost_function, loss_function, qvec_data, tvec_data,
1105  point3D.XYZ().data(), camera_params_data);
1106  }
1107  } else {
1108  switch (camera.ModelId()) {
1109 #define CAMERA_MODEL_CASE(CameraModel) \
1110  case CameraModel::kModelId: \
1111  cost_function = RigBundleAdjustmentCostFunction<CameraModel>::Create( \
1112  point2D.XY()); \
1113  \
1114  break;
1115 
1117 
1118 #undef CAMERA_MODEL_CASE
1119  }
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);
1124  }
1125  }
1126 
1127  if (num_observations > 0) {
1128  parameterized_qvec_data_.insert(qvec_data);
1129 
1130  if (camera_rig != nullptr) {
1131  parameterized_qvec_data_.insert(rig_qvec_data);
1132  // Set the relative pose of the camera constant if relative pose
1133  // refinement is disabled or if it is the reference camera to avoid
1134  // over- parameterization of the camera pose.
1135  if (!rig_options_.refine_relative_poses ||
1136  image.CameraId() == camera_rig->RefCameraId()) {
1137  problem_->SetParameterBlockConstant(qvec_data);
1138  problem_->SetParameterBlockConstant(tvec_data);
1139  }
1140  }
1141 
1142  // Set pose parameterization.
1143  if (!constant_pose && constant_tvec) {
1144  SetQuaternionManifold(problem_.get(), qvec_data);
1145  const std::vector<int>& constant_tvec_idxs =
1146  config_.ConstantTvec(image_id);
1147  // ceres::SubsetParameterization* tvec_parameterization =
1148  // new ceres::SubsetParameterization(3, constant_tvec_idxs);
1149  // problem_->SetParameterization(tvec_data, tvec_parameterization);
1150  SetSubsetManifold(3, constant_tvec_idxs, problem_.get(), tvec_data);
1151  }
1152  }
1153 }
1154 
1155 void RigBundleAdjuster::AddPointToProblem(const point3D_t point3D_id,
1156  Reconstruction* reconstruction,
1157  ceres::LossFunction* loss_function) {
1158  Point3D& point3D = reconstruction->Point3D(point3D_id);
1159 
1160  // Is 3D point already fully contained in the problem? I.e. its entire track
1161  // is contained in `variable_image_ids`, `constant_image_ids`,
1162  // `constant_x_image_ids`.
1163  if (point3D_num_observations_[point3D_id] == point3D.Track().Length()) {
1164  return;
1165  }
1166 
1167  for (const auto& track_el : point3D.Track().Elements()) {
1168  // Skip observations that were already added in `AddImageToProblem`.
1169  if (config_.HasImage(track_el.image_id)) {
1170  continue;
1171  }
1172 
1173  point3D_num_observations_[point3D_id] += 1;
1174 
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);
1178 
1179  // We do not want to refine the camera of images that are not
1180  // part of `constant_image_ids_`, `constant_image_ids_`,
1181  // `constant_x_image_ids_`.
1182  if (camera_ids_.count(image.CameraId()) == 0) {
1183  camera_ids_.insert(image.CameraId());
1184  config_.SetConstantCamera(image.CameraId());
1185  }
1186 
1187  ceres::CostFunction* cost_function = nullptr;
1188 
1189  switch (camera.ModelId()) {
1190 #define CAMERA_MODEL_CASE(CameraModel) \
1191  case CameraModel::kModelId: \
1192  cost_function = \
1193  BundleAdjustmentConstantPoseCostFunction<CameraModel>::Create( \
1194  image.Qvec(), image.Tvec(), point2D.XY()); \
1195  problem_->AddResidualBlock(cost_function, loss_function, \
1196  point3D.XYZ().data(), camera.ParamsData()); \
1197  break;
1198 
1200 
1201 #undef CAMERA_MODEL_CASE
1202  }
1203  }
1204 }
1205 
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();
1219  ++snapshot_idx) {
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]);
1228  }
1229  }
1230  }
1231 }
1232 
1233 void RigBundleAdjuster::ParameterizeCameraRigs(Reconstruction* reconstruction) {
1234  for (double* qvec_data : parameterized_qvec_data_) {
1235  // ceres::LocalParameterization* quaternion_parameterization =
1236  // new ceres::QuaternionParameterization;
1237  // problem_->SetParameterization(qvec_data,
1238  // quaternion_parameterization);
1239  SetQuaternionManifold(problem_.get(), qvec_data);
1240  }
1241 }
1242 
1243 void PrintSolverSummary(const ceres::Solver::Summary& summary) {
1244  std::cout << std::right << std::setw(16) << "Residuals : ";
1245  std::cout << std::left << summary.num_residuals_reduced << std::endl;
1246 
1247  std::cout << std::right << std::setw(16) << "Parameters : ";
1248  std::cout << std::left << summary.num_effective_parameters_reduced
1249  << std::endl;
1250 
1251  std::cout << std::right << std::setw(16) << "Iterations : ";
1252  std::cout << std::left
1253  << summary.num_successful_steps + summary.num_unsuccessful_steps
1254  << std::endl;
1255 
1256  std::cout << std::right << std::setw(16) << "Time : ";
1257  std::cout << std::left << summary.total_time_in_seconds << " [s]"
1258  << std::endl;
1259 
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)
1263  << " [px]" << std::endl;
1264 
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)
1268  << " [px]" << std::endl;
1269 
1270  std::cout << std::right << std::setw(16) << "Termination : ";
1271 
1272  std::string termination = "";
1273 
1274  switch (summary.termination_type) {
1275  case ceres::CONVERGENCE:
1276  termination = "Convergence";
1277  break;
1278  case ceres::NO_CONVERGENCE:
1279  termination = "No convergence";
1280  break;
1281  case ceres::FAILURE:
1282  termination = "Failure";
1283  break;
1284  case ceres::USER_SUCCESS:
1285  termination = "User success";
1286  break;
1287  case ceres::USER_FAILURE:
1288  termination = "User failure";
1289  break;
1290  default:
1291  termination = "Unknown";
1292  break;
1293  }
1294 
1295  std::cout << std::right << termination << std::endl;
1296  std::cout << std::endl;
1297 }
1298 
1299 } // namespace colmap
std::shared_ptr< core::Tensor > image
#define CAMERA_MODEL_SWITCH_CASES
Point3D(T x=0., T y=0., T z=0.)
Definition: Common.h:120
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)
bool HasConstantPose(const image_t image_id) 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)
void AddImage(const image_t image_id)
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)
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
Definition: camera.cc:67
size_t NumParams() const
Definition: camera.h:174
const std::vector< size_t > & ExtraParamsIdxs() const
Definition: camera.cc:71
const std::vector< size_t > & FocalLengthIdxs() const
Definition: camera.cc:63
const double * ParamsData() const
Definition: camera.h:184
point2D_t NumPoints3D() const
Definition: image.h:265
const Eigen::Vector3d & XYZ() const
Definition: point3d.h:74
const class Track & Track() const
Definition: point3d.h:106
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)
size_t Length() const
Definition: track.h:82
#define CHECK_OPTION_LT(val1, val2)
Definition: logging.h:32
#define CHECK_OPTION_GE(val1, val2)
Definition: logging.h:33
Matrix< double, 3, 4 > Matrix3x4d
Definition: types.h:39
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
void Solve(const Tensor &A, const Tensor &B, Tensor &X)
Solve AX = B with LU decomposition. A is a square matrix.
Definition: Solve.cpp:22
void SetBestCudaDevice(const int gpu_index)
Definition: cuda.cc:67
double CalculateSquaredReprojectionError(const Eigen::Vector2d &point2D, const Eigen::Vector3d &point3D, const Eigen::Vector4d &qvec, const Eigen::Vector3d &tvec, const Camera &camera)
Definition: projection.cc:111
void PrintHeading2(const std::string &heading)
Definition: misc.cc:231
uint64_t point3D_t
Definition: types.h:72
bool VectorContainsDuplicateValues(const std::vector< T > &vector)
Definition: misc.h:143
void ConcatenatePoses(const Eigen::Vector4d &qvec1, const Eigen::Vector3d &tvec1, const Eigen::Vector4d &qvec2, const Eigen::Vector3d &tvec2, Eigen::Vector4d *qvec12, Eigen::Vector3d *tvec12)
Definition: pose.cc:183
void PrintSolverSummary(const ceres::Solver::Summary &summary)
Eigen::Vector4d RotationMatrixToQuaternion(const Eigen::Matrix3d &rot_mat)
Definition: pose.cc:70
void SetQuaternionManifold(ceres::Problem *problem, double *quat_xyzw)
Definition: manifold.h:19
uint32_t image_t
Definition: types.h:61
uint32_t camera_t
Definition: types.h:58
Eigen::Matrix3x4d ComposeProjectionMatrix(const Eigen::Vector4d &qvec, const Eigen::Vector3d &tvec)
Definition: projection.cc:39
void SetSubsetManifold(int size, const std::vector< int > &constant_params, ceres::Problem *problem, double *params)
Definition: manifold.h:29
int GetEffectiveNumThreads(const int num_threads)
Definition: threading.cc:269
Eigen::Matrix3d QuaternionToRotationMatrix(const Eigen::Vector4d &qvec)
Definition: pose.cc:75
ceres::Solver::Options CreateSolverOptions(const BundleAdjustmentConfig &config, const ceres::Problem &problem) const
ceres::Solver::Options solver_options
ceres::LossFunction * CreateLossFunction() const