blob: 1216a82e57c8d104cb39054490970da276774ce8 [file] [log] [blame]
Keir Mierle8ebb0732012-04-30 23:09:08 -07001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2010, 2011, 2012 Google Inc. All rights reserved.
3// http://code.google.com/p/ceres-solver/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are met:
7//
8// * Redistributions of source code must retain the above copyright notice,
9// this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above copyright notice,
11// this list of conditions and the following disclaimer in the documentation
12// and/or other materials provided with the distribution.
13// * Neither the name of Google Inc. nor the names of its contributors may be
14// used to endorse or promote products derived from this software without
15// specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27// POSSIBILITY OF SUCH DAMAGE.
28//
29// Author: sameeragarwal@google.com (Sameer Agarwal)
30//
31// An example of solving a dynamically sized problem with various
32// solvers and loss functions.
33//
34// For a simpler bare bones example of doing bundle adjustment with
35// Ceres, please see simple_bundle_adjuster.cc.
36//
37// NOTE: This example will not compile without gflags and SuiteSparse.
38//
39// The problem being solved here is known as a Bundle Adjustment
40// problem in computer vision. Given a set of 3d points X_1, ..., X_n,
41// a set of cameras P_1, ..., P_m. If the point X_i is visible in
42// image j, then there is a 2D observation u_ij that is the expected
43// projection of X_i using P_j. The aim of this optimization is to
44// find values of X_i and P_j such that the reprojection error
45//
46// E(X,P) = sum_ij |u_ij - P_j X_i|^2
47//
48// is minimized.
49//
50// The problem used here comes from a collection of bundle adjustment
51// problems published at University of Washington.
52// http://grail.cs.washington.edu/projects/bal
53
54#include <algorithm>
55#include <cmath>
56#include <cstdio>
57#include <string>
58#include <vector>
59
60#include <gflags/gflags.h>
61#include <glog/logging.h>
62#include "bal_problem.h"
63#include "snavely_reprojection_error.h"
64#include "ceres/ceres.h"
65
66DEFINE_string(input, "", "Input File name");
Keir Mierle8ebb0732012-04-30 23:09:08 -070067DEFINE_string(solver_type, "sparse_schur", "Options are: "
Keir Mierlef7898fb2012-05-05 20:55:08 -070068 "sparse_schur, dense_schur, iterative_schur, cholesky, "
69 "dense_qr, and conjugate_gradients");
Keir Mierle8ebb0732012-04-30 23:09:08 -070070DEFINE_string(preconditioner_type, "jacobi", "Options are: "
71 "identity, jacobi, schur_jacobi, cluster_jacobi, "
72 "cluster_tridiagonal");
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -070073DEFINE_string(sparse_linear_algebra_library, "suitesparse",
74 "Options are: suitesparse and cxsparse");
Keir Mierle8ebb0732012-04-30 23:09:08 -070075DEFINE_int32(num_iterations, 5, "Number of iterations");
76DEFINE_int32(num_threads, 1, "Number of threads");
Keir Mierle211812a2012-05-07 04:33:50 -070077DEFINE_double(eta, 1e-2, "Default value for eta. Eta determines the "
78 "accuracy of each linear solve of the truncated newton step. "
79 "Changing this parameter can affect solve performance ");
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -070080DEFINE_string(ordering_type, "schur", "Options are: schur, user, natural");
Keir Mierle8ebb0732012-04-30 23:09:08 -070081DEFINE_bool(use_quaternions, false, "If true, uses quaternions to represent "
82 "rotations. If false, angle axis is used");
83DEFINE_bool(use_local_parameterization, false, "For quaternions, use a local "
84 "parameterization.");
85DEFINE_bool(robustify, false, "Use a robust loss function");
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -070086DEFINE_bool(use_block_amd, true, "Use a block oriented fill reducing ordering.");
Keir Mierle8ebb0732012-04-30 23:09:08 -070087
88namespace ceres {
89namespace examples {
90
91void SetLinearSolver(Solver::Options* options) {
92 if (FLAGS_solver_type == "sparse_schur") {
93 options->linear_solver_type = ceres::SPARSE_SCHUR;
94 } else if (FLAGS_solver_type == "dense_schur") {
95 options->linear_solver_type = ceres::DENSE_SCHUR;
96 } else if (FLAGS_solver_type == "iterative_schur") {
97 options->linear_solver_type = ceres::ITERATIVE_SCHUR;
98 } else if (FLAGS_solver_type == "cholesky") {
99 options->linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY;
Keir Mierlee2a6cdc2012-05-07 06:39:56 -0700100 } else if (FLAGS_solver_type == "cgnr") {
101 options->linear_solver_type = ceres::CGNR;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700102 } else if (FLAGS_solver_type == "dense_qr") {
103 // DENSE_QR is included here for completeness, but actually using
Keir Mierle211812a2012-05-07 04:33:50 -0700104 // this option is a bad idea due to the amount of memory needed
Keir Mierle8ebb0732012-04-30 23:09:08 -0700105 // to store even the smallest of the bundle adjustment jacobian
106 // arrays
107 options->linear_solver_type = ceres::DENSE_QR;
108 } else {
109 LOG(FATAL) << "Unknown ceres solver type: "
110 << FLAGS_solver_type;
111 }
112
Keir Mierlee2a6cdc2012-05-07 06:39:56 -0700113 if (options->linear_solver_type == ceres::CGNR) {
Keir Mierle211812a2012-05-07 04:33:50 -0700114 options->linear_solver_min_num_iterations = 5;
115 if (FLAGS_preconditioner_type == "identity") {
116 options->preconditioner_type = ceres::IDENTITY;
117 } else if (FLAGS_preconditioner_type == "jacobi") {
118 options->preconditioner_type = ceres::JACOBI;
119 } else {
Keir Mierlee2a6cdc2012-05-07 06:39:56 -0700120 LOG(FATAL) << "For CGNR, only identity and jacobian "
Keir Mierle211812a2012-05-07 04:33:50 -0700121 << "preconditioners are supported. Got: "
122 << FLAGS_preconditioner_type;
123 }
124 }
125
126 if (options->linear_solver_type == ceres::ITERATIVE_SCHUR) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700127 options->linear_solver_min_num_iterations = 5;
128 if (FLAGS_preconditioner_type == "identity") {
129 options->preconditioner_type = ceres::IDENTITY;
130 } else if (FLAGS_preconditioner_type == "jacobi") {
131 options->preconditioner_type = ceres::JACOBI;
132 } else if (FLAGS_preconditioner_type == "schur_jacobi") {
133 options->preconditioner_type = ceres::SCHUR_JACOBI;
134 } else if (FLAGS_preconditioner_type == "cluster_jacobi") {
135 options->preconditioner_type = ceres::CLUSTER_JACOBI;
136 } else if (FLAGS_preconditioner_type == "cluster_tridiagonal") {
137 options->preconditioner_type = ceres::CLUSTER_TRIDIAGONAL;
138 } else {
139 LOG(FATAL) << "Unknown ceres preconditioner type: "
Keir Mierle211812a2012-05-07 04:33:50 -0700140 << FLAGS_preconditioner_type;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700141 }
142 }
143
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700144 if (FLAGS_sparse_linear_algebra_library == "suitesparse") {
145 options->sparse_linear_algebra_library = SUITE_SPARSE;
146 } else if (FLAGS_sparse_linear_algebra_library == "cxsparse") {
147 options->sparse_linear_algebra_library = CX_SPARSE;
148 } else {
149 LOG(FATAL) << "Unknown sparse linear algebra library type.";
150 }
151
Keir Mierle8ebb0732012-04-30 23:09:08 -0700152 options->num_linear_solver_threads = FLAGS_num_threads;
153}
154
155void SetOrdering(BALProblem* bal_problem, Solver::Options* options) {
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700156 options->use_block_amd = FLAGS_use_block_amd;
157
158 // Only non-Schur solvers support the natural ordering for this
159 // problem.
160 if (FLAGS_ordering_type == "natural") {
161 if (options->linear_solver_type == SPARSE_SCHUR ||
162 options->linear_solver_type == DENSE_SCHUR ||
163 options->linear_solver_type == ITERATIVE_SCHUR) {
164 LOG(FATAL) << "Natural ordering with Schur type solver does not work.";
165 }
166 return;
167 }
168
Keir Mierle8ebb0732012-04-30 23:09:08 -0700169 // Bundle adjustment problems have a sparsity structure that makes
170 // them amenable to more specialized and much more efficient
171 // solution strategies. The SPARSE_SCHUR, DENSE_SCHUR and
172 // ITERATIVE_SCHUR solvers make use of this specialized
173 // structure. Using them however requires that the ParameterBlocks
174 // are in a particular order (points before cameras) and
175 // Solver::Options::num_eliminate_blocks is set to the number of
176 // points.
177 //
178 // This can either be done by specifying Options::ordering_type =
179 // ceres::SCHUR, in which case Ceres will automatically determine
180 // the right ParameterBlock ordering, or by manually specifying a
181 // suitable ordering vector and defining
182 // Options::num_eliminate_blocks.
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700183 if (FLAGS_ordering_type == "schur") {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700184 options->ordering_type = ceres::SCHUR;
185 return;
186 }
187
188 options->ordering_type = ceres::USER;
189 const int num_points = bal_problem->num_points();
190 const int point_block_size = bal_problem->point_block_size();
191 double* points = bal_problem->mutable_points();
192 const int num_cameras = bal_problem->num_cameras();
193 const int camera_block_size = bal_problem->camera_block_size();
194 double* cameras = bal_problem->mutable_cameras();
195
196 // The points come before the cameras.
197 for (int i = 0; i < num_points; ++i) {
198 options->ordering.push_back(points + point_block_size * i);
199 }
200
201 for (int i = 0; i < num_cameras; ++i) {
202 // When using axis-angle, there is a single parameter block for
203 // the entire camera.
204 options->ordering.push_back(cameras + camera_block_size * i);
205
206 // If quaternions are used, there are two blocks, so add the
207 // second block to the ordering.
208 if (FLAGS_use_quaternions) {
209 options->ordering.push_back(cameras + camera_block_size * i + 4);
210 }
211 }
212
213 options->num_eliminate_blocks = num_points;
214}
215
216void SetMinimizerOptions(Solver::Options* options) {
217 options->max_num_iterations = FLAGS_num_iterations;
218 options->minimizer_progress_to_stdout = true;
219 options->num_threads = FLAGS_num_threads;
Keir Mierlef7898fb2012-05-05 20:55:08 -0700220 options->eta = FLAGS_eta;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700221}
222
223void SetSolverOptionsFromFlags(BALProblem* bal_problem,
224 Solver::Options* options) {
Sameer Agarwalaa9a83c2012-05-29 17:40:17 -0700225 SetMinimizerOptions(options);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700226 SetLinearSolver(options);
227 SetOrdering(bal_problem, options);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700228}
229
230void BuildProblem(BALProblem* bal_problem, Problem* problem) {
231 const int point_block_size = bal_problem->point_block_size();
232 const int camera_block_size = bal_problem->camera_block_size();
233 double* points = bal_problem->mutable_points();
234 double* cameras = bal_problem->mutable_cameras();
235
236 // Observations is 2*num_observations long array observations =
237 // [u_1, u_2, ... , u_n], where each u_i is two dimensional, the x
238 // and y positions of the observation.
239 const double* observations = bal_problem->observations();
240
241 for (int i = 0; i < bal_problem->num_observations(); ++i) {
242 CostFunction* cost_function;
243 // Each Residual block takes a point and a camera as input and
244 // outputs a 2 dimensional residual.
245 if (FLAGS_use_quaternions) {
246 cost_function = new AutoDiffCostFunction<
247 SnavelyReprojectionErrorWitQuaternions, 2, 4, 6, 3>(
248 new SnavelyReprojectionErrorWitQuaternions(
249 observations[2 * i + 0],
250 observations[2 * i + 1]));
251 } else {
252 cost_function =
253 new AutoDiffCostFunction<SnavelyReprojectionError, 2, 9, 3>(
254 new SnavelyReprojectionError(observations[2 * i + 0],
255 observations[2 * i + 1]));
256 }
257
258 // If enabled use Huber's loss function.
259 LossFunction* loss_function = FLAGS_robustify ? new HuberLoss(1.0) : NULL;
260
261 // Each observation correponds to a pair of a camera and a point
262 // which are identified by camera_index()[i] and point_index()[i]
263 // respectively.
264 double* camera =
265 cameras + camera_block_size * bal_problem->camera_index()[i];
266 double* point = points + point_block_size * bal_problem->point_index()[i];
267
268 if (FLAGS_use_quaternions) {
269 // When using quaternions, we split the camera into two
270 // parameter blocks. One of size 4 for the quaternion and the
271 // other of size 6 containing the translation, focal length and
272 // the radial distortion parameters.
273 problem->AddResidualBlock(cost_function,
274 loss_function,
275 camera,
276 camera + 4,
277 point);
278 } else {
279 problem->AddResidualBlock(cost_function, loss_function, camera, point);
280 }
281 }
282
283 if (FLAGS_use_quaternions && FLAGS_use_local_parameterization) {
284 LocalParameterization* quaternion_parameterization =
285 new QuaternionParameterization;
286 for (int i = 0; i < bal_problem->num_cameras(); ++i) {
287 problem->SetParameterization(cameras + camera_block_size * i,
288 quaternion_parameterization);
289 }
290 }
291}
292
293void SolveProblem(const char* filename) {
294 BALProblem bal_problem(filename, FLAGS_use_quaternions);
295 Problem problem;
296 BuildProblem(&bal_problem, &problem);
297 Solver::Options options;
298 SetSolverOptionsFromFlags(&bal_problem, &options);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700299 Solver::Summary summary;
300 Solve(options, &problem, &summary);
301 std::cout << summary.FullReport() << "\n";
302}
303
304} // namespace examples
305} // namespace ceres
306
307int main(int argc, char** argv) {
308 google::ParseCommandLineFlags(&argc, &argv, true);
309 google::InitGoogleLogging(argv[0]);
310 if (FLAGS_input.empty()) {
311 LOG(ERROR) << "Usage: bundle_adjustment_example --input=bal_problem";
312 return 1;
313 }
314
315 CHECK(FLAGS_use_quaternions || !FLAGS_use_local_parameterization)
316 << "--use_local_parameterization can only be used with "
317 << "--use_quaternions.";
318 ceres::examples::SolveProblem(FLAGS_input.c_str());
319 return 0;
320}