Squashed 'third_party/ceres/' content from commit e51e9b4
Change-Id: I763587619d57e594d3fa158dc3a7fe0b89a1743b
git-subtree-dir: third_party/ceres
git-subtree-split: e51e9b46f6ca88ab8b2266d0e362771db6d98067
diff --git a/internal/ceres/local_parameterization.cc b/internal/ceres/local_parameterization.cc
new file mode 100644
index 0000000..a7fe4a1
--- /dev/null
+++ b/internal/ceres/local_parameterization.cc
@@ -0,0 +1,384 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2015 Google Inc. All rights reserved.
+// http://ceres-solver.org/
+//
+// 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 "ceres/local_parameterization.h"
+
+#include <algorithm>
+#include "Eigen/Geometry"
+#include "ceres/householder_vector.h"
+#include "ceres/internal/eigen.h"
+#include "ceres/internal/fixed_array.h"
+#include "ceres/rotation.h"
+#include "glog/logging.h"
+
+namespace ceres {
+
+using std::vector;
+
+LocalParameterization::~LocalParameterization() {
+}
+
+bool LocalParameterization::MultiplyByJacobian(const double* x,
+ const int num_rows,
+ const double* global_matrix,
+ double* local_matrix) const {
+ if (LocalSize() == 0) {
+ return true;
+ }
+
+ Matrix jacobian(GlobalSize(), LocalSize());
+ if (!ComputeJacobian(x, jacobian.data())) {
+ return false;
+ }
+
+ MatrixRef(local_matrix, num_rows, LocalSize()) =
+ ConstMatrixRef(global_matrix, num_rows, GlobalSize()) * jacobian;
+ return true;
+}
+
+IdentityParameterization::IdentityParameterization(const int size)
+ : size_(size) {
+ CHECK_GT(size, 0);
+}
+
+bool IdentityParameterization::Plus(const double* x,
+ const double* delta,
+ double* x_plus_delta) const {
+ VectorRef(x_plus_delta, size_) =
+ ConstVectorRef(x, size_) + ConstVectorRef(delta, size_);
+ return true;
+}
+
+bool IdentityParameterization::ComputeJacobian(const double* x,
+ double* jacobian) const {
+ MatrixRef(jacobian, size_, size_).setIdentity();
+ return true;
+}
+
+bool IdentityParameterization::MultiplyByJacobian(const double* x,
+ const int num_cols,
+ const double* global_matrix,
+ double* local_matrix) const {
+ std::copy(global_matrix,
+ global_matrix + num_cols * GlobalSize(),
+ local_matrix);
+ return true;
+}
+
+SubsetParameterization::SubsetParameterization(
+ int size, const vector<int>& constant_parameters)
+ : local_size_(size - constant_parameters.size()), constancy_mask_(size, 0) {
+ vector<int> constant = constant_parameters;
+ std::sort(constant.begin(), constant.end());
+ CHECK_GE(constant.front(), 0) << "Indices indicating constant parameter must "
+ "be greater than equal to zero.";
+ CHECK_LT(constant.back(), size)
+ << "Indices indicating constant parameter must be less than the size "
+ << "of the parameter block.";
+ CHECK(std::adjacent_find(constant.begin(), constant.end()) == constant.end())
+ << "The set of constant parameters cannot contain duplicates";
+ for (int i = 0; i < constant_parameters.size(); ++i) {
+ constancy_mask_[constant_parameters[i]] = 1;
+ }
+}
+
+bool SubsetParameterization::Plus(const double* x,
+ const double* delta,
+ double* x_plus_delta) const {
+ const int global_size = GlobalSize();
+ for (int i = 0, j = 0; i < global_size; ++i) {
+ if (constancy_mask_[i]) {
+ x_plus_delta[i] = x[i];
+ } else {
+ x_plus_delta[i] = x[i] + delta[j++];
+ }
+ }
+ return true;
+}
+
+bool SubsetParameterization::ComputeJacobian(const double* x,
+ double* jacobian) const {
+ if (local_size_ == 0) {
+ return true;
+ }
+
+ const int global_size = GlobalSize();
+ MatrixRef m(jacobian, global_size, local_size_);
+ m.setZero();
+ for (int i = 0, j = 0; i < global_size; ++i) {
+ if (!constancy_mask_[i]) {
+ m(i, j++) = 1.0;
+ }
+ }
+ return true;
+}
+
+bool SubsetParameterization::MultiplyByJacobian(const double* x,
+ const int num_rows,
+ const double* global_matrix,
+ double* local_matrix) const {
+ if (local_size_ == 0) {
+ return true;
+ }
+
+ const int global_size = GlobalSize();
+ for (int row = 0; row < num_rows; ++row) {
+ for (int col = 0, j = 0; col < global_size; ++col) {
+ if (!constancy_mask_[col]) {
+ local_matrix[row * local_size_ + j++] =
+ global_matrix[row * global_size + col];
+ }
+ }
+ }
+ return true;
+}
+
+bool QuaternionParameterization::Plus(const double* x,
+ const double* delta,
+ double* x_plus_delta) const {
+ const double norm_delta =
+ sqrt(delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]);
+ if (norm_delta > 0.0) {
+ const double sin_delta_by_delta = (sin(norm_delta) / norm_delta);
+ double q_delta[4];
+ q_delta[0] = cos(norm_delta);
+ q_delta[1] = sin_delta_by_delta * delta[0];
+ q_delta[2] = sin_delta_by_delta * delta[1];
+ q_delta[3] = sin_delta_by_delta * delta[2];
+ QuaternionProduct(q_delta, x, x_plus_delta);
+ } else {
+ for (int i = 0; i < 4; ++i) {
+ x_plus_delta[i] = x[i];
+ }
+ }
+ return true;
+}
+
+bool QuaternionParameterization::ComputeJacobian(const double* x,
+ double* jacobian) const {
+ jacobian[0] = -x[1]; jacobian[1] = -x[2]; jacobian[2] = -x[3]; // NOLINT
+ jacobian[3] = x[0]; jacobian[4] = x[3]; jacobian[5] = -x[2]; // NOLINT
+ jacobian[6] = -x[3]; jacobian[7] = x[0]; jacobian[8] = x[1]; // NOLINT
+ jacobian[9] = x[2]; jacobian[10] = -x[1]; jacobian[11] = x[0]; // NOLINT
+ return true;
+}
+
+bool EigenQuaternionParameterization::Plus(const double* x_ptr,
+ const double* delta,
+ double* x_plus_delta_ptr) const {
+ Eigen::Map<Eigen::Quaterniond> x_plus_delta(x_plus_delta_ptr);
+ Eigen::Map<const Eigen::Quaterniond> x(x_ptr);
+
+ const double norm_delta =
+ sqrt(delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]);
+ if (norm_delta > 0.0) {
+ const double sin_delta_by_delta = sin(norm_delta) / norm_delta;
+
+ // Note, in the constructor w is first.
+ Eigen::Quaterniond delta_q(cos(norm_delta),
+ sin_delta_by_delta * delta[0],
+ sin_delta_by_delta * delta[1],
+ sin_delta_by_delta * delta[2]);
+ x_plus_delta = delta_q * x;
+ } else {
+ x_plus_delta = x;
+ }
+
+ return true;
+}
+
+bool EigenQuaternionParameterization::ComputeJacobian(const double* x,
+ double* jacobian) const {
+ jacobian[0] = x[3]; jacobian[1] = x[2]; jacobian[2] = -x[1]; // NOLINT
+ jacobian[3] = -x[2]; jacobian[4] = x[3]; jacobian[5] = x[0]; // NOLINT
+ jacobian[6] = x[1]; jacobian[7] = -x[0]; jacobian[8] = x[3]; // NOLINT
+ jacobian[9] = -x[0]; jacobian[10] = -x[1]; jacobian[11] = -x[2]; // NOLINT
+ return true;
+}
+
+HomogeneousVectorParameterization::HomogeneousVectorParameterization(int size)
+ : size_(size) {
+ CHECK_GT(size_, 1) << "The size of the homogeneous vector needs to be "
+ << "greater than 1.";
+}
+
+bool HomogeneousVectorParameterization::Plus(const double* x_ptr,
+ const double* delta_ptr,
+ double* x_plus_delta_ptr) const {
+ ConstVectorRef x(x_ptr, size_);
+ ConstVectorRef delta(delta_ptr, size_ - 1);
+ VectorRef x_plus_delta(x_plus_delta_ptr, size_);
+
+ const double norm_delta = delta.norm();
+
+ if (norm_delta == 0.0) {
+ x_plus_delta = x;
+ return true;
+ }
+
+ // Map the delta from the minimum representation to the over parameterized
+ // homogeneous vector. See section A6.9.2 on page 624 of Hartley & Zisserman
+ // (2nd Edition) for a detailed description. Note there is a typo on Page
+ // 625, line 4 so check the book errata.
+ const double norm_delta_div_2 = 0.5 * norm_delta;
+ const double sin_delta_by_delta = sin(norm_delta_div_2) /
+ norm_delta_div_2;
+
+ Vector y(size_);
+ y.head(size_ - 1) = 0.5 * sin_delta_by_delta * delta;
+ y(size_ - 1) = cos(norm_delta_div_2);
+
+ Vector v(size_);
+ double beta;
+ internal::ComputeHouseholderVector<double>(x, &v, &beta);
+
+ // Apply the delta update to remain on the unit sphere. See section A6.9.3
+ // on page 625 of Hartley & Zisserman (2nd Edition) for a detailed
+ // description.
+ x_plus_delta = x.norm() * (y - v * (beta * (v.transpose() * y)));
+
+ return true;
+}
+
+bool HomogeneousVectorParameterization::ComputeJacobian(
+ const double* x_ptr, double* jacobian_ptr) const {
+ ConstVectorRef x(x_ptr, size_);
+ MatrixRef jacobian(jacobian_ptr, size_, size_ - 1);
+
+ Vector v(size_);
+ double beta;
+ internal::ComputeHouseholderVector<double>(x, &v, &beta);
+
+ // The Jacobian is equal to J = 0.5 * H.leftCols(size_ - 1) where H is the
+ // Householder matrix (H = I - beta * v * v').
+ for (int i = 0; i < size_ - 1; ++i) {
+ jacobian.col(i) = -0.5 * beta * v(i) * v;
+ jacobian.col(i)(i) += 0.5;
+ }
+ jacobian *= x.norm();
+
+ return true;
+}
+
+ProductParameterization::ProductParameterization(
+ LocalParameterization* local_param1,
+ LocalParameterization* local_param2) {
+ local_params_.push_back(local_param1);
+ local_params_.push_back(local_param2);
+ Init();
+}
+
+ProductParameterization::ProductParameterization(
+ LocalParameterization* local_param1,
+ LocalParameterization* local_param2,
+ LocalParameterization* local_param3) {
+ local_params_.push_back(local_param1);
+ local_params_.push_back(local_param2);
+ local_params_.push_back(local_param3);
+ Init();
+}
+
+ProductParameterization::ProductParameterization(
+ LocalParameterization* local_param1,
+ LocalParameterization* local_param2,
+ LocalParameterization* local_param3,
+ LocalParameterization* local_param4) {
+ local_params_.push_back(local_param1);
+ local_params_.push_back(local_param2);
+ local_params_.push_back(local_param3);
+ local_params_.push_back(local_param4);
+ Init();
+}
+
+ProductParameterization::~ProductParameterization() {
+ for (int i = 0; i < local_params_.size(); ++i) {
+ delete local_params_[i];
+ }
+}
+
+void ProductParameterization::Init() {
+ global_size_ = 0;
+ local_size_ = 0;
+ buffer_size_ = 0;
+ for (int i = 0; i < local_params_.size(); ++i) {
+ const LocalParameterization* param = local_params_[i];
+ buffer_size_ = std::max(buffer_size_,
+ param->LocalSize() * param->GlobalSize());
+ global_size_ += param->GlobalSize();
+ local_size_ += param->LocalSize();
+ }
+}
+
+bool ProductParameterization::Plus(const double* x,
+ const double* delta,
+ double* x_plus_delta) const {
+ int x_cursor = 0;
+ int delta_cursor = 0;
+ for (int i = 0; i < local_params_.size(); ++i) {
+ const LocalParameterization* param = local_params_[i];
+ if (!param->Plus(x + x_cursor,
+ delta + delta_cursor,
+ x_plus_delta + x_cursor)) {
+ return false;
+ }
+ delta_cursor += param->LocalSize();
+ x_cursor += param->GlobalSize();
+ }
+
+ return true;
+}
+
+bool ProductParameterization::ComputeJacobian(const double* x,
+ double* jacobian_ptr) const {
+ MatrixRef jacobian(jacobian_ptr, GlobalSize(), LocalSize());
+ jacobian.setZero();
+ internal::FixedArray<double> buffer(buffer_size_);
+
+ int x_cursor = 0;
+ int delta_cursor = 0;
+ for (int i = 0; i < local_params_.size(); ++i) {
+ const LocalParameterization* param = local_params_[i];
+ const int local_size = param->LocalSize();
+ const int global_size = param->GlobalSize();
+
+ if (!param->ComputeJacobian(x + x_cursor, buffer.get())) {
+ return false;
+ }
+ jacobian.block(x_cursor, delta_cursor, global_size, local_size)
+ = MatrixRef(buffer.get(), global_size, local_size);
+
+ delta_cursor += local_size;
+ x_cursor += global_size;
+ }
+
+ return true;
+}
+
+} // namespace ceres