1 // Ceres Solver - A fast non-linear least squares minimizer 2 // Copyright 2010, 2011, 2012 Google Inc. All rights reserved. 3 // http://code.google.com/p/ceres-solver/ 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: sameeragarwal (at) google.com (Sameer Agarwal) 30 31 #include <cmath> 32 #include "ceres/fpclassify.h" 33 #include "ceres/internal/autodiff.h" 34 #include "ceres/internal/eigen.h" 35 #include "ceres/local_parameterization.h" 36 #include "ceres/rotation.h" 37 #include "gtest/gtest.h" 38 39 namespace ceres { 40 namespace internal { 41 42 TEST(IdentityParameterization, EverythingTest) { 43 IdentityParameterization parameterization(3); 44 EXPECT_EQ(parameterization.GlobalSize(), 3); 45 EXPECT_EQ(parameterization.LocalSize(), 3); 46 47 double x[3] = {1.0, 2.0, 3.0}; 48 double delta[3] = {0.0, 1.0, 2.0}; 49 double x_plus_delta[3] = {0.0, 0.0, 0.0}; 50 parameterization.Plus(x, delta, x_plus_delta); 51 EXPECT_EQ(x_plus_delta[0], 1.0); 52 EXPECT_EQ(x_plus_delta[1], 3.0); 53 EXPECT_EQ(x_plus_delta[2], 5.0); 54 55 double jacobian[9]; 56 parameterization.ComputeJacobian(x, jacobian); 57 int k = 0; 58 for (int i = 0; i < 3; ++i) { 59 for (int j = 0; j < 3; ++j, ++k) { 60 EXPECT_EQ(jacobian[k], (i == j) ? 1.0 : 0.0); 61 } 62 } 63 } 64 65 TEST(SubsetParameterization, DeathTests) { 66 vector<int> constant_parameters; 67 EXPECT_DEATH_IF_SUPPORTED( 68 SubsetParameterization parameterization(1, constant_parameters), 69 "at least"); 70 71 constant_parameters.push_back(0); 72 EXPECT_DEATH_IF_SUPPORTED( 73 SubsetParameterization parameterization(1, constant_parameters), 74 "Number of parameters"); 75 76 constant_parameters.push_back(1); 77 EXPECT_DEATH_IF_SUPPORTED( 78 SubsetParameterization parameterization(2, constant_parameters), 79 "Number of parameters"); 80 81 constant_parameters.push_back(1); 82 EXPECT_DEATH_IF_SUPPORTED( 83 SubsetParameterization parameterization(2, constant_parameters), 84 "duplicates"); 85 } 86 87 TEST(SubsetParameterization, NormalFunctionTest) { 88 double x[4] = {1.0, 2.0, 3.0, 4.0}; 89 for (int i = 0; i < 4; ++i) { 90 vector<int> constant_parameters; 91 constant_parameters.push_back(i); 92 SubsetParameterization parameterization(4, constant_parameters); 93 double delta[3] = {1.0, 2.0, 3.0}; 94 double x_plus_delta[4] = {0.0, 0.0, 0.0}; 95 96 parameterization.Plus(x, delta, x_plus_delta); 97 int k = 0; 98 for (int j = 0; j < 4; ++j) { 99 if (j == i) { 100 EXPECT_EQ(x_plus_delta[j], x[j]); 101 } else { 102 EXPECT_EQ(x_plus_delta[j], x[j] + delta[k++]); 103 } 104 } 105 106 double jacobian[4 * 3]; 107 parameterization.ComputeJacobian(x, jacobian); 108 int delta_cursor = 0; 109 int jacobian_cursor = 0; 110 for (int j = 0; j < 4; ++j) { 111 if (j != i) { 112 for (int k = 0; k < 3; ++k, jacobian_cursor++) { 113 EXPECT_EQ(jacobian[jacobian_cursor], delta_cursor == k ? 1.0 : 0.0); 114 } 115 ++delta_cursor; 116 } else { 117 for (int k = 0; k < 3; ++k, jacobian_cursor++) { 118 EXPECT_EQ(jacobian[jacobian_cursor], 0.0); 119 } 120 } 121 } 122 }; 123 } 124 125 // Functor needed to implement automatically differentiated Plus for 126 // quaternions. 127 struct QuaternionPlus { 128 template<typename T> 129 bool operator()(const T* x, const T* delta, T* x_plus_delta) const { 130 const T squared_norm_delta = 131 delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]; 132 133 T q_delta[4]; 134 if (squared_norm_delta > T(0.0)) { 135 T norm_delta = sqrt(squared_norm_delta); 136 const T sin_delta_by_delta = sin(norm_delta) / norm_delta; 137 q_delta[0] = cos(norm_delta); 138 q_delta[1] = sin_delta_by_delta * delta[0]; 139 q_delta[2] = sin_delta_by_delta * delta[1]; 140 q_delta[3] = sin_delta_by_delta * delta[2]; 141 } else { 142 // We do not just use q_delta = [1,0,0,0] here because that is a 143 // constant and when used for automatic differentiation will 144 // lead to a zero derivative. Instead we take a first order 145 // approximation and evaluate it at zero. 146 q_delta[0] = T(1.0); 147 q_delta[1] = delta[0]; 148 q_delta[2] = delta[1]; 149 q_delta[3] = delta[2]; 150 } 151 152 QuaternionProduct(q_delta, x, x_plus_delta); 153 return true; 154 } 155 }; 156 157 void QuaternionParameterizationTestHelper(const double* x, 158 const double* delta, 159 const double* q_delta) { 160 const double kTolerance = 1e-14; 161 double x_plus_delta_ref[4] = {0.0, 0.0, 0.0, 0.0}; 162 QuaternionProduct(q_delta, x, x_plus_delta_ref); 163 164 double x_plus_delta[4] = {0.0, 0.0, 0.0, 0.0}; 165 QuaternionParameterization param; 166 param.Plus(x, delta, x_plus_delta); 167 for (int i = 0; i < 4; ++i) { 168 EXPECT_NEAR(x_plus_delta[i], x_plus_delta_ref[i], kTolerance); 169 } 170 171 const double x_plus_delta_norm = 172 sqrt(x_plus_delta[0] * x_plus_delta[0] + 173 x_plus_delta[1] * x_plus_delta[1] + 174 x_plus_delta[2] * x_plus_delta[2] + 175 x_plus_delta[3] * x_plus_delta[3]); 176 177 EXPECT_NEAR(x_plus_delta_norm, 1.0, kTolerance); 178 179 double jacobian_ref[12]; 180 double zero_delta[3] = {0.0, 0.0, 0.0}; 181 const double* parameters[2] = {x, zero_delta}; 182 double* jacobian_array[2] = { NULL, jacobian_ref }; 183 184 // Autodiff jacobian at delta_x = 0. 185 internal::AutoDiff<QuaternionPlus, double, 4, 3>::Differentiate( 186 QuaternionPlus(), parameters, 4, x_plus_delta, jacobian_array); 187 188 double jacobian[12]; 189 param.ComputeJacobian(x, jacobian); 190 for (int i = 0; i < 12; ++i) { 191 EXPECT_TRUE(IsFinite(jacobian[i])); 192 EXPECT_NEAR(jacobian[i], jacobian_ref[i], kTolerance) 193 << "Jacobian mismatch: i = " << i 194 << "\n Expected \n" << ConstMatrixRef(jacobian_ref, 4, 3) 195 << "\n Actual \n" << ConstMatrixRef(jacobian, 4, 3); 196 } 197 } 198 199 TEST(QuaternionParameterization, ZeroTest) { 200 double x[4] = {0.5, 0.5, 0.5, 0.5}; 201 double delta[3] = {0.0, 0.0, 0.0}; 202 double q_delta[4] = {1.0, 0.0, 0.0, 0.0}; 203 QuaternionParameterizationTestHelper(x, delta, q_delta); 204 } 205 206 207 TEST(QuaternionParameterization, NearZeroTest) { 208 double x[4] = {0.52, 0.25, 0.15, 0.45}; 209 double norm_x = sqrt(x[0] * x[0] + 210 x[1] * x[1] + 211 x[2] * x[2] + 212 x[3] * x[3]); 213 for (int i = 0; i < 4; ++i) { 214 x[i] = x[i] / norm_x; 215 } 216 217 double delta[3] = {0.24, 0.15, 0.10}; 218 for (int i = 0; i < 3; ++i) { 219 delta[i] = delta[i] * 1e-14; 220 } 221 222 double q_delta[4]; 223 q_delta[0] = 1.0; 224 q_delta[1] = delta[0]; 225 q_delta[2] = delta[1]; 226 q_delta[3] = delta[2]; 227 228 QuaternionParameterizationTestHelper(x, delta, q_delta); 229 } 230 231 TEST(QuaternionParameterization, AwayFromZeroTest) { 232 double x[4] = {0.52, 0.25, 0.15, 0.45}; 233 double norm_x = sqrt(x[0] * x[0] + 234 x[1] * x[1] + 235 x[2] * x[2] + 236 x[3] * x[3]); 237 238 for (int i = 0; i < 4; ++i) { 239 x[i] = x[i] / norm_x; 240 } 241 242 double delta[3] = {0.24, 0.15, 0.10}; 243 const double delta_norm = sqrt(delta[0] * delta[0] + 244 delta[1] * delta[1] + 245 delta[2] * delta[2]); 246 double q_delta[4]; 247 q_delta[0] = cos(delta_norm); 248 q_delta[1] = sin(delta_norm) / delta_norm * delta[0]; 249 q_delta[2] = sin(delta_norm) / delta_norm * delta[1]; 250 q_delta[3] = sin(delta_norm) / delta_norm * delta[2]; 251 252 QuaternionParameterizationTestHelper(x, delta, q_delta); 253 } 254 255 256 } // namespace internal 257 } // namespace ceres 258