blob: f50603d5e1f4db54ea0ab3af61f20470601f5887 [file] [log] [blame]
Johannes Beck6bde61d2019-12-28 13:29:19 +01001// Ceres Solver - A fast non-linear least squares minimizer
Sameer Agarwal5a30cae2023-09-19 15:29:34 -07002// Copyright 2023 Google Inc. All rights reserved.
Johannes Beck6bde61d2019-12-28 13:29:19 +01003// http://ceres-solver.org/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are met:
7//
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.
16//
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.
28//
29// Author: jodebo_beck@gmx.de (Johannes Beck)
30//
31
32#ifndef CERES_PUBLIC_INTERNAL_LINE_PARAMETERIZATION_H_
33#define CERES_PUBLIC_INTERNAL_LINE_PARAMETERIZATION_H_
34
35#include "householder_vector.h"
36
37namespace ceres {
38
39template <int AmbientSpaceDimension>
40bool LineParameterization<AmbientSpaceDimension>::Plus(
41 const double* x_ptr,
42 const double* delta_ptr,
43 double* x_plus_delta_ptr) const {
44 // We seek a box plus operator of the form
45 //
46 // [o*, d*] = Plus([o, d], [delta_o, delta_d])
47 //
48 // where o is the origin point, d is the direction vector, delta_o is
49 // the delta of the origin point and delta_d the delta of the direction and
50 // o* and d* is the updated origin point and direction.
51 //
52 // We separate the Plus operator into the origin point and directional part
53 // d* = Plus_d(d, delta_d)
54 // o* = Plus_o(o, d, delta_o)
55 //
Sameer Agarwal41675682020-04-02 07:28:38 -070056 // The direction update function Plus_d is the same as for the homogeneous
57 // vector parameterization:
Johannes Beck6bde61d2019-12-28 13:29:19 +010058 //
59 // d* = H_{v(d)} [0.5 sinc(0.5 |delta_d|) delta_d, cos(0.5 |delta_d|)]^T
60 //
61 // where H is the householder matrix
62 // H_{v} = I - (2 / |v|^2) v v^T
63 // and
64 // v(d) = d - sign(d_n) |d| e_n.
65 //
66 // The origin point update function Plus_o is defined as
67 //
68 // o* = o + H_{v(d)} [0.5 delta_o, 0]^T.
69
70 static constexpr int kDim = AmbientSpaceDimension;
71 using AmbientVector = Eigen::Matrix<double, kDim, 1>;
72 using AmbientVectorRef = Eigen::Map<Eigen::Matrix<double, kDim, 1>>;
Sameer Agarwal41675682020-04-02 07:28:38 -070073 using ConstAmbientVectorRef =
74 Eigen::Map<const Eigen::Matrix<double, kDim, 1>>;
Johannes Beck6bde61d2019-12-28 13:29:19 +010075 using ConstTangentVectorRef =
76 Eigen::Map<const Eigen::Matrix<double, kDim - 1, 1>>;
Sameer Agarwal41675682020-04-02 07:28:38 -070077
Johannes Beck6bde61d2019-12-28 13:29:19 +010078 ConstAmbientVectorRef o(x_ptr);
79 ConstAmbientVectorRef d(x_ptr + kDim);
80
81 ConstTangentVectorRef delta_o(delta_ptr);
82 ConstTangentVectorRef delta_d(delta_ptr + kDim - 1);
83 AmbientVectorRef o_plus_delta(x_plus_delta_ptr);
84 AmbientVectorRef d_plus_delta(x_plus_delta_ptr + kDim);
85
86 const double norm_delta_d = delta_d.norm();
87
88 o_plus_delta = o;
89
90 // Shortcut for zero delta direction.
91 if (norm_delta_d == 0.0) {
92 d_plus_delta = d;
93
94 if (delta_o.isZero(0.0)) {
95 return true;
96 }
97 }
98
99 // Calculate the householder transformation which is needed for f_d and f_o.
100 AmbientVector v;
101 double beta;
Sameer Agarwal41675682020-04-02 07:28:38 -0700102
103 // NOTE: The explicit template arguments are needed here because
104 // ComputeHouseholderVector is templated and some versions of MSVC
105 // have trouble deducing the type of v automatically.
106 internal::ComputeHouseholderVector<ConstAmbientVectorRef, double, kDim>(
107 d, &v, &beta);
Johannes Beck6bde61d2019-12-28 13:29:19 +0100108
109 if (norm_delta_d != 0.0) {
110 // Map the delta from the minimum representation to the over parameterized
111 // homogeneous vector. See section A6.9.2 on page 624 of Hartley & Zisserman
112 // (2nd Edition) for a detailed description. Note there is a typo on Page
113 // 625, line 4 so check the book errata.
114 const double norm_delta_div_2 = 0.5 * norm_delta_d;
115 const double sin_delta_by_delta =
116 std::sin(norm_delta_div_2) / norm_delta_div_2;
117
118 // Apply the delta update to remain on the unit sphere. See section A6.9.3
119 // on page 625 of Hartley & Zisserman (2nd Edition) for a detailed
120 // description.
121 AmbientVector y;
122 y.template head<kDim - 1>() = 0.5 * sin_delta_by_delta * delta_d;
123 y[kDim - 1] = std::cos(norm_delta_div_2);
124
125 d_plus_delta = d.norm() * (y - v * (beta * (v.transpose() * y)));
126 }
127
128 // The null space is in the direction of the line, so the tangent space is
129 // perpendicular to the line direction. This is achieved by using the
130 // householder matrix of the direction and allow only movements
131 // perpendicular to e_n.
132 //
133 // The factor of 0.5 is used to be consistent with the line direction
134 // update.
135 AmbientVector y;
136 y << 0.5 * delta_o, 0;
137 o_plus_delta += y - v * (beta * (v.transpose() * y));
138
139 return true;
140}
141
142template <int AmbientSpaceDimension>
143bool LineParameterization<AmbientSpaceDimension>::ComputeJacobian(
144 const double* x_ptr, double* jacobian_ptr) const {
145 static constexpr int kDim = AmbientSpaceDimension;
146 using AmbientVector = Eigen::Matrix<double, kDim, 1>;
Sameer Agarwal41675682020-04-02 07:28:38 -0700147 using ConstAmbientVectorRef =
148 Eigen::Map<const Eigen::Matrix<double, kDim, 1>>;
Johannes Beck6bde61d2019-12-28 13:29:19 +0100149 using MatrixRef = Eigen::Map<
150 Eigen::Matrix<double, 2 * kDim, 2 * (kDim - 1), Eigen::RowMajor>>;
151
152 ConstAmbientVectorRef d(x_ptr + kDim);
153 MatrixRef jacobian(jacobian_ptr);
154
155 // Clear the Jacobian as only half of the matrix is not zero.
156 jacobian.setZero();
157
158 AmbientVector v;
159 double beta;
Sameer Agarwal41675682020-04-02 07:28:38 -0700160
161 // NOTE: The explicit template arguments are needed here because
162 // ComputeHouseholderVector is templated and some versions of MSVC
163 // have trouble deducing the type of v automatically.
164 internal::ComputeHouseholderVector<ConstAmbientVectorRef, double, kDim>(
165 d, &v, &beta);
Johannes Beck6bde61d2019-12-28 13:29:19 +0100166
167 // The Jacobian is equal to J = 0.5 * H.leftCols(kDim - 1) where H is
168 // the Householder matrix (H = I - beta * v * v') for the origin point. For
169 // the line direction part the Jacobian is scaled by the norm of the
170 // direction.
171 for (int i = 0; i < kDim - 1; ++i) {
172 jacobian.block(0, i, kDim, 1) = -0.5 * beta * v(i) * v;
173 jacobian.col(i)(i) += 0.5;
174 }
175
176 jacobian.template block<kDim, kDim - 1>(kDim, kDim - 1) =
177 jacobian.template block<kDim, kDim - 1>(0, 0) * d.norm();
178 return true;
179}
180
181} // namespace ceres
182
183#endif // CERES_PUBLIC_INTERNAL_LINE_PARAMETERIZATION_H_