Berkeley SfM
bundle_adjuster.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015, The Regents of the University of California (Regents).
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above
13  * copyright notice, this list of conditions and the following
14  * disclaimer in the documentation and/or other materials provided
15  * with the distribution.
16  *
17  * 3. Neither the name of the copyright holder nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  *
33  * Please contact the author(s) of this library if you have any questions.
34  * Authors: Erik Nelson ( eanelson@eecs.berkeley.edu )
35  * David Fridovich-Keil ( dfk@eecs.berkeley.edu )
36  */
37 
38 #include "bundle_adjuster.h"
39 
40 #include <glog/logging.h>
41 
42 #include "../optimization/cost_functors.h"
43 
44 namespace bsfm {
45 
46 // Solve the bundle adjustment problem, internally updating the positions of all
47 // views in 'view_indices', as well as all landmarks that they jointly observe
48 // (any landmark seen by at least 2 views).
50  const std::vector<ViewIndex>& view_indices) const {
51  // Create a ceres optimization problem.
52  ceres::Problem problem;
53 
54  // Create storage containers for optimization variables. We only do this
55  // because camera extrinsics by default are represented as a 4x4 homogeneous
56  // matrix, and we would prefer to optimize 6 camera variables (rotation and
57  // translation) instead of 16.
58  std::vector<Vector3d> rotations(view_indices.size());
59  std::vector<Vector3d> translations(view_indices.size());
60 
61  for (size_t ii = 0; ii < view_indices.size(); ++ii) {
62  View::Ptr view = View::GetView(view_indices[ii]);
63  if (view == nullptr) {
64  LOG(WARNING) << "View is null. Cannot perform bundle adjustment.";
65  return false;
66  }
67 
68  // Get camera extrinsics matrix for optimization.
69  rotations[ii] = view->Camera().AxisAngleRotation();
70  translations[ii] = view->Camera().Translation();
71 
72  // Get static camera intrinsics for evaluating cost function.
73  Matrix3d K = view->Camera().K();
74 
75  // Make a new residual block on the problem for every 3D point that this
76  // view sees.
77  const std::vector<Observation::Ptr> observations = view->Observations();
78  for (size_t jj = 0; jj < observations.size(); ++jj) {
79  CHECK_NOTNULL(observations[jj].get());
80  if (!observations[jj]->IsIncorporated())
81  continue;
82 
83  Landmark::Ptr landmark = observations[jj]->GetLandmark();
84  if (landmark == nullptr) {
85  LOG(WARNING) << "Landmark is null. Cannot perform bundle adjustment.";
86  return false;
87  }
88 
89  // Make sure the landmark has been seen by at least two of the views we
90  // are doing bundle adjustment over.
91  if (!landmark->SeenByAtLeastTwoViews(view_indices))
92  continue;
93 
94  // Add a residual block to the cost function.
95  problem.AddResidualBlock(
96  BundleAdjustmentError::Create(observations[jj]->Feature(), K),
97  NULL, /* squared loss */
98  rotations[ii].data(),
99  translations[ii].data(),
100  landmark->PositionData());
101  }
102  }
103 
104  // Solve the bundle adjustment problem.
105  ceres::Solver::Options ceres_options;
106  if (!ConvertOptionsToCeresOptions(options, &ceres_options)) {
107  LOG(WARNING) << "Bundle adjustment options are not valid.";
108  }
109 
110  ceres::Solver::Summary summary;
111  ceres::Solve(ceres_options, &problem, &summary);
112 
113  // Print a summary of the optimization.
114  if (options.print_summary) {
115  std::cout << summary.FullReport() << std::endl;
116  }
117 
118  // Assign optimized camera parameters back into views.
119  for (size_t ii = 0; ii < view_indices.size(); ++ii) {
120  Pose pose;
121  pose.FromAxisAngle(rotations[ii]);
122 
123  // We already know this view is not null.
124  View::Ptr view = View::GetView(view_indices[ii]);
125  view->MutableCamera().MutableExtrinsics().SetWorldToCamera(pose);
126  view->MutableCamera().MutableExtrinsics().SetTranslation(translations[ii]);
127  }
128 
129  return summary.IsSolutionUsable();
130 }
131 
133  const BundleAdjustmentOptions& options,
134  ceres::Solver::Options* ceres_options) const {
135  CHECK_NOTNULL(ceres_options);
136 
137  // Trust region strategy must be LM.
138  ceres_options->trust_region_strategy_type = ceres::LEVENBERG_MARQUARDT;
139 
140  // Set linear solver type.
141  if (options.solver_type.compare("DENSE_QR")==0) {
142  ceres_options->linear_solver_type = ceres::DENSE_QR;
143  } else if (options.solver_type.compare("DENSE_NORMAL_CHOLESKY")==0) {
144  ceres_options->linear_solver_type = ceres::DENSE_NORMAL_CHOLESKY;
145  } else if (options.solver_type.compare("SPARSE_NORMAL_CHOLESKY")==0) {
146  ceres_options->linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY;
147  } else if (options.solver_type.compare("CGNR")==0) {
148  ceres_options->linear_solver_type = ceres::CGNR;
149  } else if (options.solver_type.compare("DENSE_SCHUR")==0) {
150  ceres_options->linear_solver_type = ceres::DENSE_SCHUR;
151  } else if (options.solver_type.compare("SPARSE_SCHUR")==0) {
152  ceres_options->linear_solver_type = ceres::SPARSE_SCHUR;
153  } else if (options.solver_type.compare("ITERATIVE_SCHUR")==0) {
154  ceres_options->linear_solver_type = ceres::ITERATIVE_SCHUR;
155  } else {
156  ceres_options->linear_solver_type = ceres::SPARSE_SCHUR;
157  }
158 
159  // Set print progress.
160  if (options.print_progress) {
161  ceres_options->logging_type = ceres::PER_MINIMIZER_ITERATION;
162  ceres_options->minimizer_progress_to_stdout = true;
163  } else {
164  ceres_options->logging_type = ceres::SILENT;
165  ceres_options->minimizer_progress_to_stdout = false;
166  }
167 
168  // Set gradient and function tolerance.
169  ceres_options->gradient_tolerance = options.gradient_tolerance;
170  ceres_options->function_tolerance = options.function_tolerance;
171 
172  // Make sure the options we set are valid.
173  std::string error_message;
174  bool valid_options = ceres_options->IsValid(&error_message);
175  if (!valid_options) {
176  LOG(WARNING) << "Ceres options error: " << error_message;
177  }
178  return valid_options;
179 }
180 
181 } //\namespace bsfm
bool Solve(const BundleAdjustmentOptions &options, const std::vector< ViewIndex > &view_indices) const
static ceres::CostFunction * Create(const Feature &x, const Matrix3d &K)
static View::Ptr GetView(ViewIndex view_index)
Definition: view.cpp:60
Definition: camera.cpp:50
std::shared_ptr< Landmark > Ptr
Definition: landmark.h:66
std::shared_ptr< View > Ptr
Definition: view.h:66
Matrix4d FromAxisAngle(const Vector3d &aa)
Definition: pose.cpp:174
bool ConvertOptionsToCeresOptions(const BundleAdjustmentOptions &options, ceres::Solver::Options *ceres_options) const