1 // Ceres Solver - A fast non-linear least squares minimizer
2 // Copyright 2015 Google Inc. All rights reserved.
3 // http://ceres-solver.org/
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
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.
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.
29 // Author: sameeragarwal@google.com (Sameer Agarwal)
31 #include "ceres/local_parameterization.h"
34 #include "Eigen/Geometry"
35 #include "ceres/householder_vector.h"
36 #include "ceres/internal/eigen.h"
37 #include "ceres/internal/fixed_array.h"
38 #include "ceres/rotation.h"
39 #include "glog/logging.h"
45 LocalParameterization::~LocalParameterization() {
48 bool LocalParameterization::MultiplyByJacobian(const double* x,
50 const double* global_matrix,
51 double* local_matrix) const {
52 Matrix jacobian(GlobalSize(), LocalSize());
53 if (!ComputeJacobian(x, jacobian.data())) {
57 MatrixRef(local_matrix, num_rows, LocalSize()) =
58 ConstMatrixRef(global_matrix, num_rows, GlobalSize()) * jacobian;
62 IdentityParameterization::IdentityParameterization(const int size)
67 bool IdentityParameterization::Plus(const double* x,
69 double* x_plus_delta) const {
70 VectorRef(x_plus_delta, size_) =
71 ConstVectorRef(x, size_) + ConstVectorRef(delta, size_);
75 bool IdentityParameterization::ComputeJacobian(const double* x,
76 double* jacobian) const {
77 MatrixRef(jacobian, size_, size_) = Matrix::Identity(size_, size_);
81 bool IdentityParameterization::MultiplyByJacobian(const double* x,
83 const double* global_matrix,
84 double* local_matrix) const {
85 std::copy(global_matrix,
86 global_matrix + num_cols * GlobalSize(),
91 SubsetParameterization::SubsetParameterization(
92 int size, const vector<int>& constant_parameters)
93 : local_size_(size - constant_parameters.size()), constancy_mask_(size, 0) {
94 vector<int> constant = constant_parameters;
95 std::sort(constant.begin(), constant.end());
96 CHECK_GE(constant.front(), 0)
97 << "Indices indicating constant parameter must be greater than zero.";
98 CHECK_LT(constant.back(), size)
99 << "Indices indicating constant parameter must be less than the size "
100 << "of the parameter block.";
101 CHECK(std::adjacent_find(constant.begin(), constant.end()) == constant.end())
102 << "The set of constant parameters cannot contain duplicates";
103 for (int i = 0; i < constant_parameters.size(); ++i) {
104 constancy_mask_[constant_parameters[i]] = 1;
108 bool SubsetParameterization::Plus(const double* x,
110 double* x_plus_delta) const {
111 for (int i = 0, j = 0; i < constancy_mask_.size(); ++i) {
112 if (constancy_mask_[i]) {
113 x_plus_delta[i] = x[i];
115 x_plus_delta[i] = x[i] + delta[j++];
121 bool SubsetParameterization::ComputeJacobian(const double* x,
122 double* jacobian) const {
123 if (local_size_ == 0) {
127 MatrixRef m(jacobian, constancy_mask_.size(), local_size_);
129 for (int i = 0, j = 0; i < constancy_mask_.size(); ++i) {
130 if (!constancy_mask_[i]) {
137 bool SubsetParameterization::MultiplyByJacobian(const double* x,
139 const double* global_matrix,
140 double* local_matrix) const {
141 if (local_size_ == 0) {
145 for (int row = 0; row < num_rows; ++row) {
146 for (int col = 0, j = 0; col < constancy_mask_.size(); ++col) {
147 if (!constancy_mask_[col]) {
148 local_matrix[row * LocalSize() + j++] =
149 global_matrix[row * GlobalSize() + col];
156 bool QuaternionParameterization::Plus(const double* x,
158 double* x_plus_delta) const {
159 const double norm_delta =
160 sqrt(delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]);
161 if (norm_delta > 0.0) {
162 const double sin_delta_by_delta = (sin(norm_delta) / norm_delta);
164 q_delta[0] = cos(norm_delta);
165 q_delta[1] = sin_delta_by_delta * delta[0];
166 q_delta[2] = sin_delta_by_delta * delta[1];
167 q_delta[3] = sin_delta_by_delta * delta[2];
168 QuaternionProduct(q_delta, x, x_plus_delta);
170 for (int i = 0; i < 4; ++i) {
171 x_plus_delta[i] = x[i];
177 bool QuaternionParameterization::ComputeJacobian(const double* x,
178 double* jacobian) const {
179 jacobian[0] = -x[1]; jacobian[1] = -x[2]; jacobian[2] = -x[3]; // NOLINT
180 jacobian[3] = x[0]; jacobian[4] = x[3]; jacobian[5] = -x[2]; // NOLINT
181 jacobian[6] = -x[3]; jacobian[7] = x[0]; jacobian[8] = x[1]; // NOLINT
182 jacobian[9] = x[2]; jacobian[10] = -x[1]; jacobian[11] = x[0]; // NOLINT
186 bool EigenQuaternionParameterization::Plus(const double* x_ptr,
188 double* x_plus_delta_ptr) const {
189 Eigen::Map<Eigen::Quaterniond> x_plus_delta(x_plus_delta_ptr);
190 Eigen::Map<const Eigen::Quaterniond> x(x_ptr);
192 const double norm_delta =
193 sqrt(delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]);
194 if (norm_delta > 0.0) {
195 const double sin_delta_by_delta = sin(norm_delta) / norm_delta;
197 // Note, in the constructor w is first.
198 Eigen::Quaterniond delta_q(cos(norm_delta),
199 sin_delta_by_delta * delta[0],
200 sin_delta_by_delta * delta[1],
201 sin_delta_by_delta * delta[2]);
202 x_plus_delta = delta_q * x;
210 bool EigenQuaternionParameterization::ComputeJacobian(const double* x,
211 double* jacobian) const {
212 jacobian[0] = x[3]; jacobian[1] = x[2]; jacobian[2] = -x[1]; // NOLINT
213 jacobian[3] = -x[2]; jacobian[4] = x[3]; jacobian[5] = x[0]; // NOLINT
214 jacobian[6] = x[1]; jacobian[7] = -x[0]; jacobian[8] = x[3]; // NOLINT
215 jacobian[9] = -x[0]; jacobian[10] = -x[1]; jacobian[11] = -x[2]; // NOLINT
219 HomogeneousVectorParameterization::HomogeneousVectorParameterization(int size)
221 CHECK_GT(size_, 1) << "The size of the homogeneous vector needs to be "
222 << "greater than 1.";
225 bool HomogeneousVectorParameterization::Plus(const double* x_ptr,
226 const double* delta_ptr,
227 double* x_plus_delta_ptr) const {
228 ConstVectorRef x(x_ptr, size_);
229 ConstVectorRef delta(delta_ptr, size_ - 1);
230 VectorRef x_plus_delta(x_plus_delta_ptr, size_);
232 const double norm_delta = delta.norm();
234 if (norm_delta == 0.0) {
239 // Map the delta from the minimum representation to the over parameterized
240 // homogeneous vector. See section A6.9.2 on page 624 of Hartley & Zisserman
241 // (2nd Edition) for a detailed description. Note there is a typo on Page
242 // 625, line 4 so check the book errata.
243 const double norm_delta_div_2 = 0.5 * norm_delta;
244 const double sin_delta_by_delta = sin(norm_delta_div_2) /
248 y.head(size_ - 1) = 0.5 * sin_delta_by_delta * delta;
249 y(size_ - 1) = cos(norm_delta_div_2);
253 internal::ComputeHouseholderVector<double>(x, &v, &beta);
255 // Apply the delta update to remain on the unit sphere. See section A6.9.3
256 // on page 625 of Hartley & Zisserman (2nd Edition) for a detailed
258 x_plus_delta = x.norm() * (y - v * (beta * (v.transpose() * y)));
263 bool HomogeneousVectorParameterization::ComputeJacobian(
264 const double* x_ptr, double* jacobian_ptr) const {
265 ConstVectorRef x(x_ptr, size_);
266 MatrixRef jacobian(jacobian_ptr, size_, size_ - 1);
270 internal::ComputeHouseholderVector<double>(x, &v, &beta);
272 // The Jacobian is equal to J = 0.5 * H.leftCols(size_ - 1) where H is the
273 // Householder matrix (H = I - beta * v * v').
274 for (int i = 0; i < size_ - 1; ++i) {
275 jacobian.col(i) = -0.5 * beta * v(i) * v;
276 jacobian.col(i)(i) += 0.5;
278 jacobian *= x.norm();
283 ProductParameterization::ProductParameterization(
284 LocalParameterization* local_param1,
285 LocalParameterization* local_param2) {
286 local_params_.push_back(local_param1);
287 local_params_.push_back(local_param2);
291 ProductParameterization::ProductParameterization(
292 LocalParameterization* local_param1,
293 LocalParameterization* local_param2,
294 LocalParameterization* local_param3) {
295 local_params_.push_back(local_param1);
296 local_params_.push_back(local_param2);
297 local_params_.push_back(local_param3);
301 ProductParameterization::ProductParameterization(
302 LocalParameterization* local_param1,
303 LocalParameterization* local_param2,
304 LocalParameterization* local_param3,
305 LocalParameterization* local_param4) {
306 local_params_.push_back(local_param1);
307 local_params_.push_back(local_param2);
308 local_params_.push_back(local_param3);
309 local_params_.push_back(local_param4);
313 ProductParameterization::~ProductParameterization() {
314 for (int i = 0; i < local_params_.size(); ++i) {
315 delete local_params_[i];
319 void ProductParameterization::Init() {
323 for (int i = 0; i < local_params_.size(); ++i) {
324 const LocalParameterization* param = local_params_[i];
325 buffer_size_ = std::max(buffer_size_,
326 param->LocalSize() * param->GlobalSize());
327 global_size_ += param->GlobalSize();
328 local_size_ += param->LocalSize();
332 bool ProductParameterization::Plus(const double* x,
334 double* x_plus_delta) const {
336 int delta_cursor = 0;
337 for (int i = 0; i < local_params_.size(); ++i) {
338 const LocalParameterization* param = local_params_[i];
339 if (!param->Plus(x + x_cursor,
340 delta + delta_cursor,
341 x_plus_delta + x_cursor)) {
344 delta_cursor += param->LocalSize();
345 x_cursor += param->GlobalSize();
351 bool ProductParameterization::ComputeJacobian(const double* x,
352 double* jacobian_ptr) const {
353 MatrixRef jacobian(jacobian_ptr, GlobalSize(), LocalSize());
355 internal::FixedArray<double> buffer(buffer_size_);
358 int delta_cursor = 0;
359 for (int i = 0; i < local_params_.size(); ++i) {
360 const LocalParameterization* param = local_params_[i];
361 const int local_size = param->LocalSize();
362 const int global_size = param->GlobalSize();
364 if (!param->ComputeJacobian(x + x_cursor, buffer.get())) {
367 jacobian.block(x_cursor, delta_cursor, global_size, local_size)
368 = MatrixRef(buffer.get(), global_size, local_size);
370 delta_cursor += local_size;
371 x_cursor += global_size;