Rewrite of the tutorial.
1. Quicker starting point.
2. Better discussion of derivatives.
3. Better hyperlinking to code and class documentation.
4. New robust estimation example.
5. Better naming of example code.
6. Removed dependency on gflags in all the core examples covered
in the tutorial.
Change-Id: Ibf3c7fe946fa2b4d22f8916a9366df267d34ca26
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 2307a03..94132be 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -28,28 +28,35 @@
#
# Author: keir@google.com (Keir Mierle)
-IF (${GFLAGS})
- ADD_EXECUTABLE(quadratic quadratic.cc)
- TARGET_LINK_LIBRARIES(quadratic ceres)
+ADD_EXECUTABLE(helloworld helloworld.cc)
+TARGET_LINK_LIBRARIES(helloworld ceres)
+ADD_EXECUTABLE(helloworld_numeric_diff helloworld_numeric_diff.cc)
+TARGET_LINK_LIBRARIES(helloworld_numeric_diff ceres)
+
+ADD_EXECUTABLE(helloworld_analytic_diff helloworld_analytic_diff.cc)
+TARGET_LINK_LIBRARIES(helloworld_analytic_diff ceres)
+
+ADD_EXECUTABLE(powell powell.cc)
+TARGET_LINK_LIBRARIES(powell ceres)
+
+ADD_EXECUTABLE(curve_fitting curve_fitting.cc)
+TARGET_LINK_LIBRARIES(curve_fitting ceres)
+
+ADD_EXECUTABLE(robust_curve_fitting robust_curve_fitting.cc)
+TARGET_LINK_LIBRARIES(robust_curve_fitting ceres)
+
+ADD_EXECUTABLE(simple_bundle_adjuster
+ simple_bundle_adjuster.cc)
+TARGET_LINK_LIBRARIES(simple_bundle_adjuster ceres)
+
+IF (${GFLAGS})
ADD_EXECUTABLE(nist nist.cc)
TARGET_LINK_LIBRARIES(nist ceres)
- ADD_EXECUTABLE(quadratic_auto_diff quadratic_auto_diff.cc)
- TARGET_LINK_LIBRARIES(quadratic_auto_diff ceres)
-
- ADD_EXECUTABLE(quadratic_numeric_diff quadratic_numeric_diff.cc)
- TARGET_LINK_LIBRARIES(quadratic_numeric_diff ceres)
-
- ADD_EXECUTABLE(powell powell.cc)
- TARGET_LINK_LIBRARIES(powell ceres)
-
ADD_EXECUTABLE(circle_fit circle_fit.cc)
TARGET_LINK_LIBRARIES(circle_fit ceres)
- ADD_EXECUTABLE(data_fitting data_fitting.cc)
- TARGET_LINK_LIBRARIES(data_fitting ceres)
-
ADD_EXECUTABLE(bundle_adjuster
bundle_adjuster.cc
bal_problem.cc)
@@ -61,6 +68,3 @@
TARGET_LINK_LIBRARIES(denoising ceres)
ENDIF (${GFLAGS})
-ADD_EXECUTABLE(simple_bundle_adjuster
- simple_bundle_adjuster.cc)
-TARGET_LINK_LIBRARIES(simple_bundle_adjuster ceres)
diff --git a/examples/data_fitting.cc b/examples/curve_fitting.cc
similarity index 97%
rename from examples/data_fitting.cc
rename to examples/curve_fitting.cc
index 5d54123..e77e177 100644
--- a/examples/data_fitting.cc
+++ b/examples/curve_fitting.cc
@@ -28,8 +28,8 @@
//
// Author: sameeragarwal@google.com (Sameer Agarwal)
+#include <glog/logging.h>
#include "ceres/ceres.h"
-#include "gflags/gflags.h"
using ceres::AutoDiffCostFunction;
using ceres::CostFunction;
@@ -118,8 +118,7 @@
4.950000e+00, 4.669206e+00,
};
-class ExponentialResidual {
- public:
+struct ExponentialResidual {
ExponentialResidual(double x, double y)
: x_(x), y_(y) {}
@@ -136,7 +135,6 @@
};
int main(int argc, char** argv) {
- google::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
double m = 0.0;
diff --git a/examples/quadratic_auto_diff.cc b/examples/helloworld.cc
similarity index 85%
rename from examples/quadratic_auto_diff.cc
rename to examples/helloworld.cc
index 1e2f3ef..6341918 100644
--- a/examples/quadratic_auto_diff.cc
+++ b/examples/helloworld.cc
@@ -33,9 +33,7 @@
// Minimize 0.5 (10 - x)^2 using jacobian matrix computed using
// automatic differentiation.
-#include <vector>
#include "ceres/ceres.h"
-#include "gflags/gflags.h"
#include "glog/logging.h"
using ceres::AutoDiffCostFunction;
@@ -48,8 +46,7 @@
// x. The method operator() is templated so that we can then use an
// automatic differentiation wrapper around it to generate its
// derivatives.
-class QuadraticCostFunctor {
- public:
+struct CostFunctor {
template <typename T> bool operator()(const T* const x, T* residual) const {
residual[0] = T(10.0) - x[0];
return true;
@@ -57,31 +54,28 @@
};
int main(int argc, char** argv) {
- google::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
- // The variable to solve for with its initial value.
- double initial_x = 5.0;
- double x = initial_x;
+ // The variable to solve for with its initial value. It will be
+ // mutated in place by the solver.
+ double x = 0.5;
+ const double initial_x = x;
// Build the problem.
Problem problem;
// Set up the only cost function (also known as residual). This uses
// auto-differentiation to obtain the derivative (jacobian).
- problem.AddResidualBlock(
- new AutoDiffCostFunction<QuadraticCostFunctor, 1, 1>(
- new QuadraticCostFunctor),
- NULL,
- &x);
+ CostFunction* cost_function =
+ new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);
+ problem.AddResidualBlock(cost_function, NULL, &x);
// Run the solver!
Solver::Options options;
- options.max_num_iterations = 10;
- options.linear_solver_type = ceres::DENSE_QR;
options.minimizer_progress_to_stdout = true;
Solver::Summary summary;
Solve(options, &problem, &summary);
+
std::cout << summary.BriefReport() << "\n";
std::cout << "x : " << initial_x
<< " -> " << x << "\n";
diff --git a/examples/quadratic.cc b/examples/helloworld_analytic_diff.cc
similarity index 75%
rename from examples/quadratic.cc
rename to examples/helloworld_analytic_diff.cc
index 8527af3..bff4804 100644
--- a/examples/quadratic.cc
+++ b/examples/helloworld_analytic_diff.cc
@@ -34,19 +34,22 @@
#include <vector>
#include "ceres/ceres.h"
-#include "gflags/gflags.h"
#include "glog/logging.h"
+using ceres::CostFunction;
using ceres::SizedCostFunction;
using ceres::Problem;
using ceres::Solver;
using ceres::Solve;
-class SimpleCostFunction
+// A CostFunction implementing analytically derivatives for the
+// function f(x) = 10 - x.
+class QuadraticCostFunction
: public SizedCostFunction<1 /* number of residuals */,
1 /* size of first parameter */> {
public:
- virtual ~SimpleCostFunction() {}
+ virtual ~QuadraticCostFunction() {}
+
virtual bool Evaluate(double const* const* parameters,
double* residuals,
double** jacobians) const {
@@ -58,33 +61,47 @@
// f'(x) = -1. Since there's only 1 parameter and that parameter
// has 1 dimension, there is only 1 element to fill in the
// jacobians.
+ //
+ // Since the Evaluate function can be called with the jacobians
+ // pointer equal to NULL, the Evaluate function must check to see
+ // if jacobians need to be computed.
+ //
+ // For this simple problem it is overkill to check if jacobians[0]
+ // is NULL, but in general when writing more complex
+ // CostFunctions, it is possible that Ceres may only demand the
+ // derivatives w.r.t. a subset of the parameter blocks.
if (jacobians != NULL && jacobians[0] != NULL) {
jacobians[0][0] = -1;
}
+
return true;
}
};
int main(int argc, char** argv) {
- google::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
- // The variable with its initial value that we will be solving for.
- double x = 5.0;
+ // The variable to solve for with its initial value. It will be
+ // mutated in place by the solver.
+ double x = 0.5;
+ const double initial_x = x;
// Build the problem.
Problem problem;
+
// Set up the only cost function (also known as residual).
- problem.AddResidualBlock(new SimpleCostFunction, NULL, &x);
+ CostFunction* cost_function = new QuadraticCostFunction;
+ problem.AddResidualBlock(cost_function, NULL, &x);
// Run the solver!
Solver::Options options;
- options.max_num_iterations = 10;
- options.linear_solver_type = ceres::DENSE_QR;
options.minimizer_progress_to_stdout = true;
Solver::Summary summary;
Solve(options, &problem, &summary);
+
std::cout << summary.BriefReport() << "\n";
- std::cout << "x : 5.0 -> " << x << "\n";
+ std::cout << "x : " << initial_x
+ << " -> " << x << "\n";
+
return 0;
}
diff --git a/examples/quadratic_numeric_diff.cc b/examples/helloworld_numeric_diff.cc
similarity index 84%
rename from examples/quadratic_numeric_diff.cc
rename to examples/helloworld_numeric_diff.cc
index 1082616..026b155 100644
--- a/examples/quadratic_numeric_diff.cc
+++ b/examples/helloworld_numeric_diff.cc
@@ -31,9 +31,7 @@
// Minimize 0.5 (10 - x)^2 using jacobian matrix computed using
// numeric differentiation.
-#include <vector>
#include "ceres/ceres.h"
-#include "gflags/gflags.h"
#include "glog/logging.h"
using ceres::NumericDiffCostFunction;
@@ -44,8 +42,7 @@
using ceres::Solve;
// A cost functor that implements the residual r = 10 - x.
-class QuadraticCostFunctor {
- public:
+struct CostFunctor {
bool operator()(const double* const x, double* residual) const {
residual[0] = 10.0 - x[0];
return true;
@@ -53,30 +50,28 @@
};
int main(int argc, char** argv) {
- google::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
- // The variable to solve for with its initial value.
- double initial_x = 5.0;
- double x = initial_x;
-
- // Set up the only cost function (also known as residual). This uses
- // numeric differentiation to obtain the derivative (jacobian).
- CostFunction* cost =
- new NumericDiffCostFunction<QuadraticCostFunctor, CENTRAL, 1, 1> (
- new QuadraticCostFunctor);
+ // The variable to solve for with its initial value. It will be
+ // mutated in place by the solver.
+ double x = 0.5;
+ const double initial_x = x;
// Build the problem.
Problem problem;
- problem.AddResidualBlock(cost, NULL, &x);
+
+ // Set up the only cost function (also known as residual). This uses
+ // numeric differentiation to obtain the derivative (jacobian).
+ CostFunction* cost_function =
+ new NumericDiffCostFunction<CostFunctor, CENTRAL, 1, 1> (new CostFunctor);
+ problem.AddResidualBlock(cost_function, NULL, &x);
// Run the solver!
Solver::Options options;
- options.max_num_iterations = 10;
- options.linear_solver_type = ceres::DENSE_QR;
options.minimizer_progress_to_stdout = true;
Solver::Summary summary;
Solve(options, &problem, &summary);
+
std::cout << summary.BriefReport() << "\n";
std::cout << "x : " << initial_x
<< " -> " << x << "\n";
diff --git a/examples/powell.cc b/examples/powell.cc
index 6cd3611..4a41728 100644
--- a/examples/powell.cc
+++ b/examples/powell.cc
@@ -46,7 +46,6 @@
#include <vector>
#include "ceres/ceres.h"
-#include "gflags/gflags.h"
#include "glog/logging.h"
using ceres::AutoDiffCostFunction;
@@ -55,8 +54,7 @@
using ceres::Solver;
using ceres::Solve;
-class F1 {
- public:
+struct F1 {
template <typename T> bool operator()(const T* const x1,
const T* const x2,
T* residual) const {
@@ -66,8 +64,7 @@
}
};
-class F2 {
- public:
+struct F2 {
template <typename T> bool operator()(const T* const x3,
const T* const x4,
T* residual) const {
@@ -77,8 +74,7 @@
}
};
-class F3 {
- public:
+struct F3 {
template <typename T> bool operator()(const T* const x2,
const T* const x4,
T* residual) const {
@@ -88,8 +84,7 @@
}
};
-class F4 {
- public:
+struct F4 {
template <typename T> bool operator()(const T* const x1,
const T* const x4,
T* residual) const {
@@ -100,7 +95,6 @@
};
int main(int argc, char** argv) {
- google::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
double x1 = 3.0;
diff --git a/examples/robust_curve_fitting.cc b/examples/robust_curve_fitting.cc
new file mode 100644
index 0000000..01cbbb2
--- /dev/null
+++ b/examples/robust_curve_fitting.cc
@@ -0,0 +1,163 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2010, 2011, 2012 Google Inc. All rights reserved.
+// http://code.google.com/p/ceres-solver/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: sameeragarwal@google.com (Sameer Agarwal)
+
+#include <glog/logging.h>
+#include "ceres/ceres.h"
+
+// Data generated using the following octave code.
+// randn('seed', 23497);
+// m = 0.3;
+// c = 0.1;
+// x=[0:0.075:5];
+// y = exp(m * x + c);
+// noise = randn(size(x)) * 0.2;
+// outlier_noise = rand(size(x)) < 0.05;
+// y_observed = y + noise + outlier_noise;
+// data = [x', y_observed'];
+
+const int kNumObservations = 67;
+const double data[] = {
+0.000000e+00, 1.133898e+00,
+7.500000e-02, 1.334902e+00,
+1.500000e-01, 1.213546e+00,
+2.250000e-01, 1.252016e+00,
+3.000000e-01, 1.392265e+00,
+3.750000e-01, 1.314458e+00,
+4.500000e-01, 1.472541e+00,
+5.250000e-01, 1.536218e+00,
+6.000000e-01, 1.355679e+00,
+6.750000e-01, 1.463566e+00,
+7.500000e-01, 1.490201e+00,
+8.250000e-01, 1.658699e+00,
+9.000000e-01, 1.067574e+00,
+9.750000e-01, 1.464629e+00,
+1.050000e+00, 1.402653e+00,
+1.125000e+00, 1.713141e+00,
+1.200000e+00, 1.527021e+00,
+1.275000e+00, 1.702632e+00,
+1.350000e+00, 1.423899e+00,
+1.425000e+00, 5.543078e+00, // Outlier point
+1.500000e+00, 5.664015e+00, // Outlier point
+1.575000e+00, 1.732484e+00,
+1.650000e+00, 1.543296e+00,
+1.725000e+00, 1.959523e+00,
+1.800000e+00, 1.685132e+00,
+1.875000e+00, 1.951791e+00,
+1.950000e+00, 2.095346e+00,
+2.025000e+00, 2.361460e+00,
+2.100000e+00, 2.169119e+00,
+2.175000e+00, 2.061745e+00,
+2.250000e+00, 2.178641e+00,
+2.325000e+00, 2.104346e+00,
+2.400000e+00, 2.584470e+00,
+2.475000e+00, 1.914158e+00,
+2.550000e+00, 2.368375e+00,
+2.625000e+00, 2.686125e+00,
+2.700000e+00, 2.712395e+00,
+2.775000e+00, 2.499511e+00,
+2.850000e+00, 2.558897e+00,
+2.925000e+00, 2.309154e+00,
+3.000000e+00, 2.869503e+00,
+3.075000e+00, 3.116645e+00,
+3.150000e+00, 3.094907e+00,
+3.225000e+00, 2.471759e+00,
+3.300000e+00, 3.017131e+00,
+3.375000e+00, 3.232381e+00,
+3.450000e+00, 2.944596e+00,
+3.525000e+00, 3.385343e+00,
+3.600000e+00, 3.199826e+00,
+3.675000e+00, 3.423039e+00,
+3.750000e+00, 3.621552e+00,
+3.825000e+00, 3.559255e+00,
+3.900000e+00, 3.530713e+00,
+3.975000e+00, 3.561766e+00,
+4.050000e+00, 3.544574e+00,
+4.125000e+00, 3.867945e+00,
+4.200000e+00, 4.049776e+00,
+4.275000e+00, 3.885601e+00,
+4.350000e+00, 4.110505e+00,
+4.425000e+00, 4.345320e+00,
+4.500000e+00, 4.161241e+00,
+4.575000e+00, 4.363407e+00,
+4.650000e+00, 4.161576e+00,
+4.725000e+00, 4.619728e+00,
+4.800000e+00, 4.737410e+00,
+4.875000e+00, 4.727863e+00,
+4.950000e+00, 4.669206e+00
+};
+
+using ceres::AutoDiffCostFunction;
+using ceres::CostFunction;
+using ceres::CauchyLoss;
+using ceres::Problem;
+using ceres::Solve;
+using ceres::Solver;
+
+struct ExponentialResidual {
+ ExponentialResidual(double x, double y)
+ : x_(x), y_(y) {}
+
+ template <typename T> bool operator()(const T* const m,
+ const T* const c,
+ T* residual) const {
+ residual[0] = T(y_) - exp(m[0] * T(x_) + c[0]);
+ return true;
+ }
+
+ private:
+ const double x_;
+ const double y_;
+};
+
+int main(int argc, char** argv) {
+ google::InitGoogleLogging(argv[0]);
+
+ double m = 0.0;
+ double c = 0.0;
+
+ Problem problem;
+ for (int i = 0; i < kNumObservations; ++i) {
+ CostFunction* cost_function =
+ new AutoDiffCostFunction<ExponentialResidual, 1, 1, 1>(
+ new ExponentialResidual(data[2 * i], data[2 * i + 1]));
+ problem.AddResidualBlock(cost_function, NULL, &m, &c);
+ }
+
+ Solver::Options options;
+ options.linear_solver_type = ceres::DENSE_QR;
+ options.minimizer_progress_to_stdout = true;
+
+ Solver::Summary summary;
+ Solve(options, &problem, &summary);
+ std::cout << summary.BriefReport() << "\n";
+ std::cout << "Initial m: " << 0.0 << " c: " << 0.0 << "\n";
+ std::cout << "Final m: " << m << " c: " << c << "\n";
+ return 0;
+}
diff --git a/examples/simple_bundle_adjuster.cc b/examples/simple_bundle_adjuster.cc
index cc6f04a..736d4a2 100644
--- a/examples/simple_bundle_adjuster.cc
+++ b/examples/simple_bundle_adjuster.cc
@@ -160,6 +160,14 @@
return true;
}
+ // Factory to hide the construction of the CostFunction object from
+ // the client code.
+ static ceres::CostFunction* Create(const double observed_x,
+ const double observed_y) {
+ return (new ceres::AutoDiffCostFunction<SnavelyReprojectionError, 2, 9, 3>(
+ new SnavelyReprojectionError(observed_x, observed_y)));
+ }
+
double observed_x;
double observed_y;
};
@@ -177,6 +185,8 @@
return 1;
}
+ const double* observations = bal_problem.observations();
+
// Create residuals for each observation in the bundle adjustment problem. The
// parameters for cameras and points are added automatically.
ceres::Problem problem;
@@ -184,12 +194,10 @@
// Each Residual block takes a point and a camera as input and outputs a 2
// dimensional residual. Internally, the cost function stores the observed
// image location and compares the reprojection against the observation.
- ceres::CostFunction* cost_function =
- new ceres::AutoDiffCostFunction<SnavelyReprojectionError, 2, 9, 3>(
- new SnavelyReprojectionError(
- bal_problem.observations()[2 * i + 0],
- bal_problem.observations()[2 * i + 1]));
+ ceres::CostFunction* cost_function =
+ SnavelyReprojectionError::Create(observations[2 * i + 0],
+ observations[2 * i + 1]);
problem.AddResidualBlock(cost_function,
NULL /* squared loss */,
bal_problem.mutable_camera_for_observation(i),