|  | // Ceres Solver - A fast non-linear least squares minimizer | 
|  | // Copyright 2022 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/internal/eigen.h" | 
|  | #include "ceres/internal/fixed_array.h" | 
|  | #include "ceres/internal/householder_vector.h" | 
|  | #include "ceres/rotation.h" | 
|  | #include "glog/logging.h" | 
|  |  | 
|  | namespace ceres { | 
|  |  | 
|  | using std::vector; | 
|  |  | 
|  | LocalParameterization::~LocalParameterization() = default; | 
|  |  | 
|  | 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) { | 
|  | if (constant_parameters.empty()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | 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 parameter : constant_parameters) { | 
|  | constancy_mask_[parameter] = 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_cols, | 
|  | const double* global_matrix, | 
|  | double* local_matrix) const { | 
|  | if (local_size_ == 0) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | const int global_size = GlobalSize(); | 
|  | for (int col = 0; col < num_cols; ++col) { | 
|  | for (int i = 0, j = 0; i < global_size; ++i) { | 
|  | if (!constancy_mask_[i]) { | 
|  | local_matrix[col * local_size_ + j++] = | 
|  | global_matrix[col * global_size + i]; | 
|  | } | 
|  | } | 
|  | } | 
|  | 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 { | 
|  | // clang-format off | 
|  | jacobian[0] = -x[1];  jacobian[1]  = -x[2];   jacobian[2]  = -x[3]; | 
|  | jacobian[3] =  x[0];  jacobian[4]  =  x[3];   jacobian[5]  = -x[2]; | 
|  | jacobian[6] = -x[3];  jacobian[7]  =  x[0];   jacobian[8]  =  x[1]; | 
|  | jacobian[9] =  x[2];  jacobian[10] = -x[1];   jacobian[11] =  x[0]; | 
|  | // clang-format on | 
|  | 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 { | 
|  | // clang-format off | 
|  | jacobian[0] =  x[3];  jacobian[1]  =  x[2];  jacobian[2]  = -x[1]; | 
|  | jacobian[3] = -x[2];  jacobian[4]  =  x[3];  jacobian[5]  =  x[0]; | 
|  | jacobian[6] =  x[1];  jacobian[7]  = -x[0];  jacobian[8]  =  x[3]; | 
|  | jacobian[9] = -x[0];  jacobian[10] = -x[1];  jacobian[11] = -x[2]; | 
|  | // clang-format on | 
|  | 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 = | 
|  | std::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) = std::cos(norm_delta_div_2); | 
|  |  | 
|  | Vector v(size_); | 
|  | double beta; | 
|  |  | 
|  | // NOTE: The explicit template arguments are needed here because | 
|  | // ComputeHouseholderVector is templated and some versions of MSVC | 
|  | // have trouble deducing the type of v automatically. | 
|  | internal::ComputeHouseholderVector<ConstVectorRef, double, Eigen::Dynamic>( | 
|  | 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; | 
|  |  | 
|  | // NOTE: The explicit template arguments are needed here because | 
|  | // ComputeHouseholderVector is templated and some versions of MSVC | 
|  | // have trouble deducing the type of v automatically. | 
|  | internal::ComputeHouseholderVector<ConstVectorRef, double, Eigen::Dynamic>( | 
|  | 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; | 
|  | } | 
|  |  | 
|  | bool ProductParameterization::Plus(const double* x, | 
|  | const double* delta, | 
|  | double* x_plus_delta) const { | 
|  | int x_cursor = 0; | 
|  | int delta_cursor = 0; | 
|  | for (const auto& param : local_params_) { | 
|  | 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 (const auto& param : local_params_) { | 
|  | const int local_size = param->LocalSize(); | 
|  | const int global_size = param->GlobalSize(); | 
|  |  | 
|  | if (!param->ComputeJacobian(x + x_cursor, buffer.data())) { | 
|  | return false; | 
|  | } | 
|  | jacobian.block(x_cursor, delta_cursor, global_size, local_size) = | 
|  | MatrixRef(buffer.data(), global_size, local_size); | 
|  |  | 
|  | delta_cursor += local_size; | 
|  | x_cursor += global_size; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace ceres |