ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
bundle_adjustment.h
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #pragma once
9 
10 #include <ceres/ceres.h>
11 
12 #include <Eigen/Core>
13 #include <memory>
14 #include <unordered_set>
15 
16 #ifdef PBA_ENABLED
17 #include "PBA/pba.h"
18 #endif
19 #include "base/camera_rig.h"
20 #include "base/reconstruction.h"
21 #include "optim/manifold.h"
22 #include "util/alignment.h"
23 
24 namespace colmap {
25 
26 // Configuration container to setup bundle adjustment problems.
28 public:
30 
31  size_t NumImages() const;
32  size_t NumPoints() const;
33  size_t NumConstantCameras() const;
34  size_t NumConstantPoses() const;
35  size_t NumConstantTvecs() const;
36  size_t NumVariablePoints() const;
37  size_t NumConstantPoints() const;
38 
39  // Determine the number of residuals for the given reconstruction. The
40  // number of residuals equals the number of observations times two.
41  size_t NumResiduals(const Reconstruction& reconstruction) const;
42 
43  // Add / remove images from the configuration.
44  void AddImage(const image_t image_id);
45  bool HasImage(const image_t image_id) const;
46  void RemoveImage(const image_t image_id);
47 
48  // Set cameras of added images as constant or variable. By default all
49  // cameras of added images are variable. Note that the corresponding images
50  // have to be added prior to calling these methods.
51  void SetConstantCamera(const camera_t camera_id);
52  void SetVariableCamera(const camera_t camera_id);
53  bool IsConstantCamera(const camera_t camera_id) const;
54 
55  // Set the pose of added images as constant. The pose is defined as the
56  // rotational and translational part of the projection matrix.
57  void SetConstantPose(const image_t image_id);
58  void SetVariablePose(const image_t image_id);
59  bool HasConstantPose(const image_t image_id) const;
60 
61  // Set the translational part of the pose, hence the constant pose
62  // indices may be in [0, 1, 2] and must be unique. Note that the
63  // corresponding images have to be added prior to calling these methods.
64  void SetConstantTvec(const image_t image_id, const std::vector<int>& idxs);
65  void RemoveConstantTvec(const image_t image_id);
66  bool HasConstantTvec(const image_t image_id) const;
67 
68  // Add / remove points from the configuration. Note that points can either
69  // be variable or constant but not both at the same time.
70  void AddVariablePoint(const point3D_t point3D_id);
71  void AddConstantPoint(const point3D_t point3D_id);
72  bool HasPoint(const point3D_t point3D_id) const;
73  bool HasVariablePoint(const point3D_t point3D_id) const;
74  bool HasConstantPoint(const point3D_t point3D_id) const;
75  void RemoveVariablePoint(const point3D_t point3D_id);
76  void RemoveConstantPoint(const point3D_t point3D_id);
77 
78  // Access configuration data.
79  const std::unordered_set<image_t>& Images() const;
80  const std::unordered_set<point3D_t>& VariablePoints() const;
81  const std::unordered_set<point3D_t>& ConstantPoints() const;
82  const std::vector<int>& ConstantTvec(const image_t image_id) const;
83 
84 private:
85  std::unordered_set<camera_t> constant_camera_ids_;
86  std::unordered_set<image_t> image_ids_;
87  std::unordered_set<point3D_t> variable_point3D_ids_;
88  std::unordered_set<point3D_t> constant_point3D_ids_;
89  std::unordered_set<image_t> constant_poses_;
90  std::unordered_map<image_t, std::vector<int>> constant_tvecs_;
91 };
92 
94  // Loss function types: Trivial (non-robust) and Cauchy (robust) loss.
97 
98  // Scaling factor determines residual at which robustification takes place.
99  double loss_function_scale = 1.0;
100 
101  // Whether to refine the focal length parameter group.
102  bool refine_focal_length = true;
103 
104  // Whether to refine the principal point parameter group.
106 
107  // Whether to refine the extra parameter group.
108  bool refine_extra_params = true;
109 
110  // Whether to refine the extrinsic parameter group.
111  bool refine_extrinsics = true;
112 
113  // Whether to print a final summary.
114  bool print_summary = true;
115 
116  // Whether to use Ceres' CUDA linear algebra library, if available.
117  bool use_gpu = false;
118  std::string gpu_index = "-1";
119 
120  // Heuristic threshold to switch from CPU to GPU based solvers.
121  // Typically, the GPU is faster for large problems but the overhead of
122  // transferring memory from the CPU to the GPU leads to better CPU
123  // performance for small problems. This depends on the specific problem and
124  // hardware.
126 
127  // Heuristic threshold on the minimum number of residuals to enable
128  // multi-threading. Note that single-threaded is typically better for small
129  // bundle adjustment problems due to the overhead of threading.
131 
132  // Heuristic thresholds to switch between direct, sparse, and iterative
133  // solvers. These thresholds may not be optimal for all types of problems.
138 
139  // Ceres-Solver options.
140  ceres::Solver::Options solver_options;
141 
143  solver_options.function_tolerance = 0.0;
144  solver_options.gradient_tolerance = 1e-4;
145  solver_options.parameter_tolerance = 0.0;
146  solver_options.logging_type = ceres::LoggingType::SILENT;
147  solver_options.minimizer_progress_to_stdout = false;
148  solver_options.max_num_iterations = 100;
149  solver_options.max_linear_solver_iterations = 200;
150  solver_options.max_num_consecutive_invalid_steps = 10;
151  solver_options.max_consecutive_nonmonotonic_steps = 10;
152  solver_options.num_threads = -1;
153 #if CERES_VERSION_MAJOR < 2
154  solver_options.num_linear_solver_threads = -1;
155 #endif // CERES_VERSION_MAJOR
156  }
157 
158  // Create a new loss function based on the specified options. The caller
159  // takes ownership of the loss function.
160  ceres::LossFunction* CreateLossFunction() const;
161 
162  // Create options tailored for given bundle adjustment config and problem.
163  ceres::Solver::Options CreateSolverOptions(
164  const BundleAdjustmentConfig& config,
165  const ceres::Problem& problem) const;
166 
167  bool Check() const;
168 };
169 
170 // Bundle adjustment based on Ceres-Solver. Enables most flexible configurations
171 // and provides best solution quality.
173 public:
175  const BundleAdjustmentConfig& config);
176 
177  bool Solve(Reconstruction* reconstruction);
178 
179  // Get the Ceres solver summary for the last call to `Solve`.
180  const ceres::Solver::Summary& Summary() const;
181 
182 private:
183  void SetUp(Reconstruction* reconstruction,
184  ceres::LossFunction* loss_function);
185  void TearDown(Reconstruction* reconstruction);
186 
187  void AddImageToProblem(const image_t image_id,
188  Reconstruction* reconstruction,
189  ceres::LossFunction* loss_function);
190 
191  void AddPointToProblem(const point3D_t point3D_id,
192  Reconstruction* reconstruction,
193  ceres::LossFunction* loss_function);
194 
195 protected:
196  void ParameterizeCameras(Reconstruction* reconstruction);
197  void ParameterizePoints(Reconstruction* reconstruction);
198 
201  std::unique_ptr<ceres::Problem> problem_;
202  ceres::Solver::Summary summary_;
203  std::unordered_set<camera_t> camera_ids_;
204  std::unordered_map<point3D_t, size_t> point3D_num_observations_;
205 };
206 
207 #ifdef PBA_ENABLED
208 // Bundle adjustment using PBA (GPU or CPU). Less flexible and accurate than
209 // Ceres-Solver bundle adjustment but much faster. Only supports SimpleRadial
210 // camera model.
211 class ParallelBundleAdjuster {
212 public:
213  struct Options {
214  // Whether to print a final summary.
215  bool print_summary = true;
216 
217  // Maximum number of iterations.
218  int max_num_iterations = 50;
219 
220  // Index of the GPU used for bundle adjustment.
221  int gpu_index = -1;
222 
223  // Number of threads for CPU based bundle adjustment.
224  int num_threads = -1;
225 
226  // Minimum number of residuals to enable multi-threading. Note that
227  // single-threaded is typically better for small bundle adjustment
228  // problems due to the overhead of threading.
229  int min_num_residuals_for_cpu_multi_threading = 50000;
230 
231  bool Check() const;
232  };
233 
234  ParallelBundleAdjuster(const Options& options,
235  const BundleAdjustmentOptions& ba_options,
236  const BundleAdjustmentConfig& config);
237 
238  bool Solve(Reconstruction* reconstruction);
239 
240  // Get the Ceres solver summary for the last call to `Solve`.
241  const ceres::Solver::Summary& Summary() const;
242 
243  // Check whether PBA is supported for the given reconstruction. If the
244  // reconstruction is not supported, the PBA solver will exit ungracefully.
245  static bool IsSupported(const BundleAdjustmentOptions& options,
246  const Reconstruction& reconstruction);
247 
248 private:
249  void SetUp(Reconstruction* reconstruction);
250  void TearDown(Reconstruction* reconstruction);
251 
252  void AddImagesToProblem(Reconstruction* reconstruction);
253  void AddPointsToProblem(Reconstruction* reconstruction);
254 
255  const Options options_;
256  const BundleAdjustmentOptions ba_options_;
257  BundleAdjustmentConfig config_;
258  ceres::Solver::Summary summary_;
259 
260  size_t num_measurements_;
261  std::vector<pba::CameraT> cameras_;
262  std::vector<pba::Point3D> points3D_;
263  std::vector<pba::Point2D> measurements_;
264  std::unordered_set<camera_t> camera_ids_;
265  std::unordered_set<point3D_t> point3D_ids_;
266  std::vector<int> camera_idxs_;
267  std::vector<int> point3D_idxs_;
268  std::vector<image_t> ordered_image_ids_;
269  std::vector<point3D_t> ordered_point3D_ids_;
270  std::unordered_map<image_t, int> image_id_to_camera_idx_;
271 };
272 #endif
273 
275 public:
276  struct Options {
277  // Whether to optimize the relative poses of the camera rigs.
279 
280  // The maximum allowed reprojection error for an observation to be
281  // considered in the bundle adjustment. Some observations might have
282  // large reprojection errors due to the concatenation of the absolute
283  // and relative rig poses, which might be different from the absolute
284  // pose of the image in the reconstruction.
285  double max_reproj_error = 1000.0;
286  };
287 
289  const Options& rig_options,
290  const BundleAdjustmentConfig& config);
291 
292  bool Solve(Reconstruction* reconstruction,
293  std::vector<CameraRig>* camera_rigs);
294 
295 private:
296  void SetUp(Reconstruction* reconstruction,
297  std::vector<CameraRig>* camera_rigs,
298  ceres::LossFunction* loss_function);
299  void TearDown(Reconstruction* reconstruction,
300  const std::vector<CameraRig>& camera_rigs);
301 
302  void AddImageToProblem(const image_t image_id,
303  Reconstruction* reconstruction,
304  std::vector<CameraRig>* camera_rigs,
305  ceres::LossFunction* loss_function);
306 
307  void AddPointToProblem(const point3D_t point3D_id,
308  Reconstruction* reconstruction,
309  ceres::LossFunction* loss_function);
310 
311  void ComputeCameraRigPoses(const Reconstruction& reconstruction,
312  const std::vector<CameraRig>& camera_rigs);
313 
314  void ParameterizeCameraRigs(Reconstruction* reconstruction);
315 
316  const Options rig_options_;
317 
318  // Mapping from images to camera rigs.
319  std::unordered_map<image_t, CameraRig*> image_id_to_camera_rig_;
320 
321  // Mapping from images to the absolute camera rig poses.
322  std::unordered_map<image_t, Eigen::Vector4d*> image_id_to_rig_qvec_;
323  std::unordered_map<image_t, Eigen::Vector3d*> image_id_to_rig_tvec_;
324 
325  // For each camera rig, the absolute camera rig poses.
326  std::vector<std::vector<Eigen::Vector4d>> camera_rig_qvecs_;
327  std::vector<std::vector<Eigen::Vector3d>> camera_rig_tvecs_;
328 
329  // The Quaternions added to the problem, used to set the local
330  // parameterization once after setting up the problem.
331  std::unordered_set<double*> parameterized_qvec_data_;
332 };
333 
334 void PrintSolverSummary(const ceres::Solver::Summary& summary);
335 
336 } // namespace colmap
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
RigBundleAdjuster(const BundleAdjustmentOptions &options, const Options &rig_options, const BundleAdjustmentConfig &config)
bool Solve(Reconstruction *reconstruction, std::vector< CameraRig > *camera_rigs)
const double * e
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
uint64_t point3D_t
Definition: types.h:72
void PrintSolverSummary(const ceres::Solver::Summary &summary)
uint32_t image_t
Definition: types.h:61
uint32_t camera_t
Definition: types.h:58
ceres::Solver::Options CreateSolverOptions(const BundleAdjustmentConfig &config, const ceres::Problem &problem) const
ceres::Solver::Options solver_options
ceres::LossFunction * CreateLossFunction() const