Berkeley SfM
cost_functors.h
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 ///////////////////////////////////////////////////////////////////////////////
39 //
40 // This file defines cost functors that can be used in conjunction with Google
41 // Ceres solver to solve non-linear least-squares problems. Each functor must
42 // define a public templated operator() method that takes in arguments const T*
43 // const INPUT_VARIABLE, and T* OUTPUT_RESIDUAL, and returns a bool.
44 // 'INPUT_VARIABLE' will be the optimization variable, and 'OUTPUT_RESIDUAL'
45 // will be the cost associated with the input.
46 //
47 // One can define more specific cost functions by adding other structure to the
48 // functor, e.g. by passing in other parameters of the cost function to the
49 // functor's constructor.
50 //
51 ///////////////////////////////////////////////////////////////////////////////
52 
53 #ifndef BSFM_OPTIMIZATION_COST_FUNCTORS_H
54 #define BSFM_OPTIMIZATION_COST_FUNCTORS_H
55 
56 #include <ceres/rotation.h>
57 #include <Eigen/Core>
58 #include <glog/logging.h>
59 
60 #include "../geometry/point_3d.h"
61 #include "../matching/feature.h"
62 
63 namespace bsfm {
64 
65 // Geometric error is the distance in image space between a point x, and a
66 // projected point PX, where X is a 3D homogeneous point (4x1), P is a camera
67 // projection matrix (3x4), and x is the corresponding image-space point
68 // expressed in homogeneous coordinates (3x1). The geometric error can be
69 // expressed as sum_i d(x_i, PX_i)^2, where d( , ) is the Euclidean distance
70 // metric.
72  // We are trying to adjust our camera projection matrix, P, to satisfy
73  // x - PX = 0. Input is a 2D point in image space ('x'), and a 3D point in
74  // world space ('X').
77  GeometricError(const Feature& x, const Point3D& X) : x_(x), X_(X) {}
78 
79  // Residual is 2-dimensional, P is 12-dimensional (number of elements in a
80  // camera projection matrix).
81  template <typename T>
82  bool operator()(const T* const P, T* geometric_error) const {
83 
84  // Matrix multiplication: x - PX. Assume homogeneous coordinates are 1.0.
85  // Matrix P is stored in column-major order.
86  T scale = P[2] * X_.X() + P[5] * X_.Y() + P[8] * X_.Z() + P[11];
87  geometric_error[0] =
88  x_.u_ - (P[0] * X_.X() + P[3] * X_.Y() + P[6] * X_.Z() + P[9]) / scale;
89  geometric_error[1] =
90  x_.v_ - (P[1] * X_.X() + P[4] * X_.Y() + P[7] * X_.Z() + P[10]) / scale;
91 
92  return true;
93  }
94 
95  // Factory method.
96  static ceres::CostFunction* Create(const Feature& x, const Point3D& X) {
97  static const int kNumResiduals = 2;
98  static const int kNumCameraParameters = 12;
99  return new ceres::AutoDiffCostFunction<GeometricError,
100  kNumResiduals,
101  kNumCameraParameters>(new GeometricError(x, X));
102  }
103 }; //\struct GeometricError
104 
105 
106 // Bundle adjustment error is similar to geometric error, but the position of
107 // the 3D point is also optimized. The error is defined as the image-space
108 // geometric distance between an image point x, and a projected 3D landmark X
109 // projected according to x = K [R | t] X, where K and [R | t] are camera
110 // intrinsic and extrinsic parameter matrices, respectively. K is held constant
111 // during the optimization, and only the camera pose matrix [R | t] is optimized
112 // over.
113 //
114 // For speed, we optimize over an axis-angle parameterization of R (3
115 // varibales), rather than the 9 variables in a rotation matrix, in addition,
116 // for readability/cleanliness, we optimize over c, the camera center in world
117 // space, rather than t. These are related by c = -R'*t.
119  // Inputs are the image space point x and the intrinsics matrix K.
120  // Optimization variables are the 3D landmark position X, and the camera
121  // extrinsics matrix [R | t], expressed as an axis-angle rotation and a 3D
122  // translation vector, and as the camera origin expressed in world frame
123  // coordinates, c.
125  Matrix3d K_;
126  BundleAdjustmentError(const Feature& x, const Matrix3d& K) : x_(x), K_(K) {}
127 
128  template <typename T>
129  bool operator()(const T* const rotation, const T* const translation,
130  const T* const point, T* bundle_adjustment_error) const {
131  // Normally one would compute x = K * [R | t] X. Instead we have an
132  // axis-angle version of R, and the camera position c. To put the
133  // point X in camera frame, we need to compute R*X+t. Note that t = -R'*c,
134  // so R*X+t = R*(X+R'*t) = R*(X-c). Hence we can first subtract out the given
135  // camera position, and then perform the axis-angle rotation.
136 
137  // Remove camera translation.
138  T origin_point[3];
139  origin_point[0] = point[0] - translation[0];
140  origin_point[1] = point[1] - translation[1];
141  origin_point[2] = point[2] - translation[2];
142 
143  // Rotate point to camera frame.
144  T cam_point[3];
145  ceres::AngleAxisRotatePoint(rotation, origin_point, cam_point);
146 
147  // Get normalized pixel projection.
148  const T& depth = cam_point[2];
149  const T normalized_point[2] = {cam_point[0] / depth, cam_point[1] / depth};
150 
151  // Project normalized point into image using intrinsic parameters.
152  const T u = K_(0, 0) * normalized_point[0] +
153  K_(0, 1) * normalized_point[1] + K_(0, 2);
154  const T v = K_(1, 1) * normalized_point[1] + K_(1, 2);
155 
156  // Error is computed in image space.
157  bundle_adjustment_error[0] = x_.u_ - u;
158  bundle_adjustment_error[1] = x_.v_ - v;
159 
160  return true;
161  }
162 
163  // Factory method.
164  static ceres::CostFunction* Create(const Feature& x, const Matrix3d& K) {
165  // 2 residuals: image space u and v coordinates.
166  static const int kNumResiduals = 2;
167 
168  // 3 parameters, axis-angle representation.
169  static const int kNumRotationParameters = 3;
170 
171  // 3 parameters for camera translation.
172  static const int kNumTranslationParameters = 3;
173 
174  // 3 landmark position parameters: world space x, y, z.
175  static const int kNumLandmarkParameters = 3;
176 
177  return new ceres::AutoDiffCostFunction<BundleAdjustmentError,
178  kNumResiduals,
179  kNumRotationParameters,
180  kNumTranslationParameters,
181  kNumLandmarkParameters>(new BundleAdjustmentError(x, K));
182  }
183 }; //\BundleAdjustmentError
184 
185 } //\namespace bsfm
186 
187 #endif
BundleAdjustmentError(const Feature &x, const Matrix3d &K)
bool operator()(const T *const rotation, const T *const translation, const T *const point, T *bundle_adjustment_error) const
double v_
Definition: feature.h:62
GeometricError(const Feature &x, const Point3D &X)
Definition: cost_functors.h:77
static ceres::CostFunction * Create(const Feature &x, const Matrix3d &K)
static ceres::CostFunction * Create(const Feature &x, const Point3D &X)
Definition: cost_functors.h:96
double Y() const
Definition: point_3d.cpp:94
double u_
Definition: feature.h:62
double X() const
Definition: point_3d.cpp:92
Definition: camera.cpp:50
bool operator()(const T *const P, T *geometric_error) const
Definition: cost_functors.h:82
double Z() const
Definition: point_3d.cpp:96