blob: d980ba2b771b925f38fd9f34b2a8e967944d85a7 [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2015 Google Inc. All rights reserved.
3// 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: sameeragarwal@google.com (Sameer Agarwal)
30
31#include <cmath>
32#include <limits>
33#include <string>
34#include "ceres/internal/eigen.h"
35#include "ceres/is_close.h"
36#include "ceres/internal/port.h"
37#include "ceres/jet.h"
38#include "ceres/rotation.h"
39#include "ceres/stringprintf.h"
40#include "ceres/test_util.h"
41#include "glog/logging.h"
42#include "gmock/gmock.h"
43#include "gtest/gtest.h"
44
45namespace ceres {
46namespace internal {
47
48using std::min;
49using std::max;
50using std::numeric_limits;
51using std::string;
52using std::swap;
53
54const double kPi = 3.14159265358979323846;
55const double kHalfSqrt2 = 0.707106781186547524401;
56
57double RandDouble() {
58 double r = rand();
59 return r / RAND_MAX;
60}
61
62// A tolerance value for floating-point comparisons.
63static double const kTolerance = numeric_limits<double>::epsilon() * 10;
64
65// Looser tolerance used for numerically unstable conversions.
66static double const kLooseTolerance = 1e-9;
67
68// Use as:
69// double quaternion[4];
70// EXPECT_THAT(quaternion, IsNormalizedQuaternion());
71MATCHER(IsNormalizedQuaternion, "") {
72 if (arg == NULL) {
73 *result_listener << "Null quaternion";
74 return false;
75 }
76
77 double norm2 = arg[0] * arg[0] + arg[1] * arg[1] +
78 arg[2] * arg[2] + arg[3] * arg[3];
79 if (fabs(norm2 - 1.0) > kTolerance) {
80 *result_listener << "squared norm is " << norm2;
81 return false;
82 }
83
84 return true;
85}
86
87// Use as:
88// double expected_quaternion[4];
89// double actual_quaternion[4];
90// EXPECT_THAT(actual_quaternion, IsNearQuaternion(expected_quaternion));
91MATCHER_P(IsNearQuaternion, expected, "") {
92 if (arg == NULL) {
93 *result_listener << "Null quaternion";
94 return false;
95 }
96
97 // Quaternions are equivalent upto a sign change. So we will compare
98 // both signs before declaring failure.
99 bool near = true;
100 for (int i = 0; i < 4; i++) {
101 if (fabs(arg[i] - expected[i]) > kTolerance) {
102 near = false;
103 break;
104 }
105 }
106
107 if (near) {
108 return true;
109 }
110
111 near = true;
112 for (int i = 0; i < 4; i++) {
113 if (fabs(arg[i] + expected[i]) > kTolerance) {
114 near = false;
115 break;
116 }
117 }
118
119 if (near) {
120 return true;
121 }
122
123 *result_listener << "expected : "
124 << expected[0] << " "
125 << expected[1] << " "
126 << expected[2] << " "
127 << expected[3] << " "
128 << "actual : "
129 << arg[0] << " "
130 << arg[1] << " "
131 << arg[2] << " "
132 << arg[3];
133 return false;
134}
135
136// Use as:
137// double expected_axis_angle[3];
138// double actual_axis_angle[3];
139// EXPECT_THAT(actual_axis_angle, IsNearAngleAxis(expected_axis_angle));
140MATCHER_P(IsNearAngleAxis, expected, "") {
141 if (arg == NULL) {
142 *result_listener << "Null axis/angle";
143 return false;
144 }
145
146 Eigen::Vector3d a(arg[0], arg[1], arg[2]);
147 Eigen::Vector3d e(expected[0], expected[1], expected[2]);
148 const double e_norm = e.norm();
149
150 double delta_norm = numeric_limits<double>::max();
151 if (e_norm > 0) {
152 // Deal with the sign ambiguity near PI. Since the sign can flip,
153 // we take the smaller of the two differences.
154 if (fabs(e_norm - kPi) < kLooseTolerance) {
155 delta_norm = min((a - e).norm(), (a + e).norm()) / e_norm;
156 } else {
157 delta_norm = (a - e).norm() / e_norm;
158 }
159 } else {
160 delta_norm = a.norm();
161 }
162
163 if (delta_norm <= kLooseTolerance) {
164 return true;
165 }
166
167 *result_listener << " arg:"
168 << " " << arg[0]
169 << " " << arg[1]
170 << " " << arg[2]
171 << " was expected to be:"
172 << " " << expected[0]
173 << " " << expected[1]
174 << " " << expected[2];
175 return false;
176}
177
178// Use as:
179// double matrix[9];
180// EXPECT_THAT(matrix, IsOrthonormal());
181MATCHER(IsOrthonormal, "") {
182 if (arg == NULL) {
183 *result_listener << "Null matrix";
184 return false;
185 }
186
187 for (int c1 = 0; c1 < 3; c1++) {
188 for (int c2 = 0; c2 < 3; c2++) {
189 double v = 0;
190 for (int i = 0; i < 3; i++) {
191 v += arg[i + 3 * c1] * arg[i + 3 * c2];
192 }
193 double expected = (c1 == c2) ? 1 : 0;
194 if (fabs(expected - v) > kTolerance) {
195 *result_listener << "Columns " << c1 << " and " << c2
196 << " should have dot product " << expected
197 << " but have " << v;
198 return false;
199 }
200 }
201 }
202
203 return true;
204}
205
206// Use as:
207// double matrix1[9];
208// double matrix2[9];
209// EXPECT_THAT(matrix1, IsNear3x3Matrix(matrix2));
210MATCHER_P(IsNear3x3Matrix, expected, "") {
211 if (arg == NULL) {
212 *result_listener << "Null matrix";
213 return false;
214 }
215
216 for (int i = 0; i < 9; i++) {
217 if (fabs(arg[i] - expected[i]) > kTolerance) {
218 *result_listener << "component " << i << " should be " << expected[i];
219 return false;
220 }
221 }
222
223 return true;
224}
225
226// Transforms a zero axis/angle to a quaternion.
227TEST(Rotation, ZeroAngleAxisToQuaternion) {
228 double axis_angle[3] = { 0, 0, 0 };
229 double quaternion[4];
230 double expected[4] = { 1, 0, 0, 0 };
231 AngleAxisToQuaternion(axis_angle, quaternion);
232 EXPECT_THAT(quaternion, IsNormalizedQuaternion());
233 EXPECT_THAT(quaternion, IsNearQuaternion(expected));
234}
235
236// Test that exact conversion works for small angles.
237TEST(Rotation, SmallAngleAxisToQuaternion) {
238 // Small, finite value to test.
239 double theta = 1.0e-2;
240 double axis_angle[3] = { theta, 0, 0 };
241 double quaternion[4];
242 double expected[4] = { cos(theta/2), sin(theta/2.0), 0, 0 };
243 AngleAxisToQuaternion(axis_angle, quaternion);
244 EXPECT_THAT(quaternion, IsNormalizedQuaternion());
245 EXPECT_THAT(quaternion, IsNearQuaternion(expected));
246}
247
248// Test that approximate conversion works for very small angles.
249TEST(Rotation, TinyAngleAxisToQuaternion) {
250 // Very small value that could potentially cause underflow.
251 double theta = pow(numeric_limits<double>::min(), 0.75);
252 double axis_angle[3] = { theta, 0, 0 };
253 double quaternion[4];
254 double expected[4] = { cos(theta/2), sin(theta/2.0), 0, 0 };
255 AngleAxisToQuaternion(axis_angle, quaternion);
256 EXPECT_THAT(quaternion, IsNormalizedQuaternion());
257 EXPECT_THAT(quaternion, IsNearQuaternion(expected));
258}
259
260// Transforms a rotation by pi/2 around X to a quaternion.
261TEST(Rotation, XRotationToQuaternion) {
262 double axis_angle[3] = { kPi / 2, 0, 0 };
263 double quaternion[4];
264 double expected[4] = { kHalfSqrt2, kHalfSqrt2, 0, 0 };
265 AngleAxisToQuaternion(axis_angle, quaternion);
266 EXPECT_THAT(quaternion, IsNormalizedQuaternion());
267 EXPECT_THAT(quaternion, IsNearQuaternion(expected));
268}
269
270// Transforms a unit quaternion to an axis angle.
271TEST(Rotation, UnitQuaternionToAngleAxis) {
272 double quaternion[4] = { 1, 0, 0, 0 };
273 double axis_angle[3];
274 double expected[3] = { 0, 0, 0 };
275 QuaternionToAngleAxis(quaternion, axis_angle);
276 EXPECT_THAT(axis_angle, IsNearAngleAxis(expected));
277}
278
279// Transforms a quaternion that rotates by pi about the Y axis to an axis angle.
280TEST(Rotation, YRotationQuaternionToAngleAxis) {
281 double quaternion[4] = { 0, 0, 1, 0 };
282 double axis_angle[3];
283 double expected[3] = { 0, kPi, 0 };
284 QuaternionToAngleAxis(quaternion, axis_angle);
285 EXPECT_THAT(axis_angle, IsNearAngleAxis(expected));
286}
287
288// Transforms a quaternion that rotates by pi/3 about the Z axis to an axis
289// angle.
290TEST(Rotation, ZRotationQuaternionToAngleAxis) {
291 double quaternion[4] = { sqrt(3) / 2, 0, 0, 0.5 };
292 double axis_angle[3];
293 double expected[3] = { 0, 0, kPi / 3 };
294 QuaternionToAngleAxis(quaternion, axis_angle);
295 EXPECT_THAT(axis_angle, IsNearAngleAxis(expected));
296}
297
298// Test that exact conversion works for small angles.
299TEST(Rotation, SmallQuaternionToAngleAxis) {
300 // Small, finite value to test.
301 double theta = 1.0e-2;
302 double quaternion[4] = { cos(theta/2), sin(theta/2.0), 0, 0 };
303 double axis_angle[3];
304 double expected[3] = { theta, 0, 0 };
305 QuaternionToAngleAxis(quaternion, axis_angle);
306 EXPECT_THAT(axis_angle, IsNearAngleAxis(expected));
307}
308
309// Test that approximate conversion works for very small angles.
310TEST(Rotation, TinyQuaternionToAngleAxis) {
311 // Very small value that could potentially cause underflow.
312 double theta = pow(numeric_limits<double>::min(), 0.75);
313 double quaternion[4] = { cos(theta/2), sin(theta/2.0), 0, 0 };
314 double axis_angle[3];
315 double expected[3] = { theta, 0, 0 };
316 QuaternionToAngleAxis(quaternion, axis_angle);
317 EXPECT_THAT(axis_angle, IsNearAngleAxis(expected));
318}
319
320TEST(Rotation, QuaternionToAngleAxisAngleIsLessThanPi) {
321 double quaternion[4];
322 double angle_axis[3];
323
324 const double half_theta = 0.75 * kPi;
325
326 quaternion[0] = cos(half_theta);
327 quaternion[1] = 1.0 * sin(half_theta);
328 quaternion[2] = 0.0;
329 quaternion[3] = 0.0;
330 QuaternionToAngleAxis(quaternion, angle_axis);
331 const double angle = sqrt(angle_axis[0] * angle_axis[0] +
332 angle_axis[1] * angle_axis[1] +
333 angle_axis[2] * angle_axis[2]);
334 EXPECT_LE(angle, kPi);
335}
336
337static const int kNumTrials = 10000;
338
339// Takes a bunch of random axis/angle values, converts them to quaternions,
340// and back again.
341TEST(Rotation, AngleAxisToQuaterionAndBack) {
342 srand(5);
343 for (int i = 0; i < kNumTrials; i++) {
344 double axis_angle[3];
345 // Make an axis by choosing three random numbers in [-1, 1) and
346 // normalizing.
347 double norm = 0;
348 for (int i = 0; i < 3; i++) {
349 axis_angle[i] = RandDouble() * 2 - 1;
350 norm += axis_angle[i] * axis_angle[i];
351 }
352 norm = sqrt(norm);
353
354 // Angle in [-pi, pi).
355 double theta = kPi * 2 * RandDouble() - kPi;
356 for (int i = 0; i < 3; i++) {
357 axis_angle[i] = axis_angle[i] * theta / norm;
358 }
359
360 double quaternion[4];
361 double round_trip[3];
362 // We use ASSERTs here because if there's one failure, there are
363 // probably many and spewing a million failures doesn't make anyone's
364 // day.
365 AngleAxisToQuaternion(axis_angle, quaternion);
366 ASSERT_THAT(quaternion, IsNormalizedQuaternion());
367 QuaternionToAngleAxis(quaternion, round_trip);
368 ASSERT_THAT(round_trip, IsNearAngleAxis(axis_angle));
369 }
370}
371
372// Takes a bunch of random quaternions, converts them to axis/angle,
373// and back again.
374TEST(Rotation, QuaterionToAngleAxisAndBack) {
375 srand(5);
376 for (int i = 0; i < kNumTrials; i++) {
377 double quaternion[4];
378 // Choose four random numbers in [-1, 1) and normalize.
379 double norm = 0;
380 for (int i = 0; i < 4; i++) {
381 quaternion[i] = RandDouble() * 2 - 1;
382 norm += quaternion[i] * quaternion[i];
383 }
384 norm = sqrt(norm);
385
386 for (int i = 0; i < 4; i++) {
387 quaternion[i] = quaternion[i] / norm;
388 }
389
390 double axis_angle[3];
391 double round_trip[4];
392 QuaternionToAngleAxis(quaternion, axis_angle);
393 AngleAxisToQuaternion(axis_angle, round_trip);
394 ASSERT_THAT(round_trip, IsNormalizedQuaternion());
395 ASSERT_THAT(round_trip, IsNearQuaternion(quaternion));
396 }
397}
398
399// Transforms a zero axis/angle to a rotation matrix.
400TEST(Rotation, ZeroAngleAxisToRotationMatrix) {
401 double axis_angle[3] = { 0, 0, 0 };
402 double matrix[9];
403 double expected[9] = { 1, 0, 0, 0, 1, 0, 0, 0, 1 };
404 AngleAxisToRotationMatrix(axis_angle, matrix);
405 EXPECT_THAT(matrix, IsOrthonormal());
406 EXPECT_THAT(matrix, IsNear3x3Matrix(expected));
407}
408
409TEST(Rotation, NearZeroAngleAxisToRotationMatrix) {
410 double axis_angle[3] = { 1e-24, 2e-24, 3e-24 };
411 double matrix[9];
412 double expected[9] = { 1, 0, 0, 0, 1, 0, 0, 0, 1 };
413 AngleAxisToRotationMatrix(axis_angle, matrix);
414 EXPECT_THAT(matrix, IsOrthonormal());
415 EXPECT_THAT(matrix, IsNear3x3Matrix(expected));
416}
417
418// Transforms a rotation by pi/2 around X to a rotation matrix and back.
419TEST(Rotation, XRotationToRotationMatrix) {
420 double axis_angle[3] = { kPi / 2, 0, 0 };
421 double matrix[9];
422 // The rotation matrices are stored column-major.
423 double expected[9] = { 1, 0, 0, 0, 0, 1, 0, -1, 0 };
424 AngleAxisToRotationMatrix(axis_angle, matrix);
425 EXPECT_THAT(matrix, IsOrthonormal());
426 EXPECT_THAT(matrix, IsNear3x3Matrix(expected));
427 double round_trip[3];
428 RotationMatrixToAngleAxis(matrix, round_trip);
429 EXPECT_THAT(round_trip, IsNearAngleAxis(axis_angle));
430}
431
432// Transforms an axis angle that rotates by pi about the Y axis to a
433// rotation matrix and back.
434TEST(Rotation, YRotationToRotationMatrix) {
435 double axis_angle[3] = { 0, kPi, 0 };
436 double matrix[9];
437 double expected[9] = { -1, 0, 0, 0, 1, 0, 0, 0, -1 };
438 AngleAxisToRotationMatrix(axis_angle, matrix);
439 EXPECT_THAT(matrix, IsOrthonormal());
440 EXPECT_THAT(matrix, IsNear3x3Matrix(expected));
441
442 double round_trip[3];
443 RotationMatrixToAngleAxis(matrix, round_trip);
444 EXPECT_THAT(round_trip, IsNearAngleAxis(axis_angle));
445}
446
447TEST(Rotation, NearPiAngleAxisRoundTrip) {
448 double in_axis_angle[3];
449 double matrix[9];
450 double out_axis_angle[3];
451
452 srand(5);
453 for (int i = 0; i < kNumTrials; i++) {
454 // Make an axis by choosing three random numbers in [-1, 1) and
455 // normalizing.
456 double norm = 0;
457 for (int i = 0; i < 3; i++) {
458 in_axis_angle[i] = RandDouble() * 2 - 1;
459 norm += in_axis_angle[i] * in_axis_angle[i];
460 }
461 norm = sqrt(norm);
462
463 // Angle in [pi - kMaxSmallAngle, pi).
464 const double kMaxSmallAngle = 1e-8;
465 double theta = kPi - kMaxSmallAngle * RandDouble();
466
467 for (int i = 0; i < 3; i++) {
468 in_axis_angle[i] *= (theta / norm);
469 }
470 AngleAxisToRotationMatrix(in_axis_angle, matrix);
471 RotationMatrixToAngleAxis(matrix, out_axis_angle);
472 EXPECT_THAT(in_axis_angle, IsNearAngleAxis(out_axis_angle));
473 }
474}
475
476TEST(Rotation, AtPiAngleAxisRoundTrip) {
477 // A rotation of kPi about the X axis;
478 static const double kMatrix[3][3] = {
479 {1.0, 0.0, 0.0},
480 {0.0, -1.0, 0.0},
481 {0.0, 0.0, -1.0}
482 };
483
484 double in_matrix[9];
485 // Fill it from kMatrix in col-major order.
486 for (int j = 0, k = 0; j < 3; ++j) {
487 for (int i = 0; i < 3; ++i, ++k) {
488 in_matrix[k] = kMatrix[i][j];
489 }
490 }
491
492 const double expected_axis_angle[3] = { kPi, 0, 0 };
493
494 double out_matrix[9];
495 double axis_angle[3];
496 RotationMatrixToAngleAxis(in_matrix, axis_angle);
497 AngleAxisToRotationMatrix(axis_angle, out_matrix);
498
499 LOG(INFO) << "AngleAxis = " << axis_angle[0] << " " << axis_angle[1]
500 << " " << axis_angle[2];
501 LOG(INFO) << "Expected AngleAxis = " << kPi << " 0 0";
502 double out_rowmajor[3][3];
503 for (int j = 0, k = 0; j < 3; ++j) {
504 for (int i = 0; i < 3; ++i, ++k) {
505 out_rowmajor[i][j] = out_matrix[k];
506 }
507 }
508 LOG(INFO) << "Rotation:";
509 LOG(INFO) << "EXPECTED | ACTUAL";
510 for (int i = 0; i < 3; ++i) {
511 string line;
512 for (int j = 0; j < 3; ++j) {
513 StringAppendF(&line, "%g ", kMatrix[i][j]);
514 }
515 line += " | ";
516 for (int j = 0; j < 3; ++j) {
517 StringAppendF(&line, "%g ", out_rowmajor[i][j]);
518 }
519 LOG(INFO) << line;
520 }
521
522 EXPECT_THAT(axis_angle, IsNearAngleAxis(expected_axis_angle));
523 EXPECT_THAT(out_matrix, IsNear3x3Matrix(in_matrix));
524}
525
526// Transforms an axis angle that rotates by pi/3 about the Z axis to a
527// rotation matrix.
528TEST(Rotation, ZRotationToRotationMatrix) {
529 double axis_angle[3] = { 0, 0, kPi / 3 };
530 double matrix[9];
531 // This is laid-out row-major on the screen but is actually stored
532 // column-major.
533 double expected[9] = { 0.5, sqrt(3) / 2, 0, // Column 1
534 -sqrt(3) / 2, 0.5, 0, // Column 2
535 0, 0, 1 }; // Column 3
536 AngleAxisToRotationMatrix(axis_angle, matrix);
537 EXPECT_THAT(matrix, IsOrthonormal());
538 EXPECT_THAT(matrix, IsNear3x3Matrix(expected));
539 double round_trip[3];
540 RotationMatrixToAngleAxis(matrix, round_trip);
541 EXPECT_THAT(round_trip, IsNearAngleAxis(axis_angle));
542}
543
544// Takes a bunch of random axis/angle values, converts them to rotation
545// matrices, and back again.
546TEST(Rotation, AngleAxisToRotationMatrixAndBack) {
547 srand(5);
548 for (int i = 0; i < kNumTrials; i++) {
549 double axis_angle[3];
550 // Make an axis by choosing three random numbers in [-1, 1) and
551 // normalizing.
552 double norm = 0;
553 for (int i = 0; i < 3; i++) {
554 axis_angle[i] = RandDouble() * 2 - 1;
555 norm += axis_angle[i] * axis_angle[i];
556 }
557 norm = sqrt(norm);
558
559 // Angle in [-pi, pi).
560 double theta = kPi * 2 * RandDouble() - kPi;
561 for (int i = 0; i < 3; i++) {
562 axis_angle[i] = axis_angle[i] * theta / norm;
563 }
564
565 double matrix[9];
566 double round_trip[3];
567 AngleAxisToRotationMatrix(axis_angle, matrix);
568 ASSERT_THAT(matrix, IsOrthonormal());
569 RotationMatrixToAngleAxis(matrix, round_trip);
570
571 for (int i = 0; i < 3; ++i) {
572 EXPECT_NEAR(round_trip[i], axis_angle[i], kLooseTolerance);
573 }
574 }
575}
576
577// Takes a bunch of random axis/angle values near zero, converts them
578// to rotation matrices, and back again.
579TEST(Rotation, AngleAxisToRotationMatrixAndBackNearZero) {
580 srand(5);
581 for (int i = 0; i < kNumTrials; i++) {
582 double axis_angle[3];
583 // Make an axis by choosing three random numbers in [-1, 1) and
584 // normalizing.
585 double norm = 0;
586 for (int i = 0; i < 3; i++) {
587 axis_angle[i] = RandDouble() * 2 - 1;
588 norm += axis_angle[i] * axis_angle[i];
589 }
590 norm = sqrt(norm);
591
592 // Tiny theta.
593 double theta = 1e-16 * (kPi * 2 * RandDouble() - kPi);
594 for (int i = 0; i < 3; i++) {
595 axis_angle[i] = axis_angle[i] * theta / norm;
596 }
597
598 double matrix[9];
599 double round_trip[3];
600 AngleAxisToRotationMatrix(axis_angle, matrix);
601 ASSERT_THAT(matrix, IsOrthonormal());
602 RotationMatrixToAngleAxis(matrix, round_trip);
603
604 for (int i = 0; i < 3; ++i) {
605 EXPECT_NEAR(round_trip[i], axis_angle[i],
606 numeric_limits<double>::epsilon());
607 }
608 }
609}
610
611
612// Transposes a 3x3 matrix.
613static void Transpose3x3(double m[9]) {
614 swap(m[1], m[3]);
615 swap(m[2], m[6]);
616 swap(m[5], m[7]);
617}
618
619// Convert Euler angles from radians to degrees.
620static void ToDegrees(double euler_angles[3]) {
621 for (int i = 0; i < 3; ++i) {
622 euler_angles[i] *= 180.0 / kPi;
623 }
624}
625
626// Compare the 3x3 rotation matrices produced by the axis-angle
627// rotation 'aa' and the Euler angle rotation 'ea' (in radians).
628static void CompareEulerToAngleAxis(double aa[3], double ea[3]) {
629 double aa_matrix[9];
630 AngleAxisToRotationMatrix(aa, aa_matrix);
631 Transpose3x3(aa_matrix); // Column to row major order.
632
633 double ea_matrix[9];
634 ToDegrees(ea); // Radians to degrees.
635 const int kRowStride = 3;
636 EulerAnglesToRotationMatrix(ea, kRowStride, ea_matrix);
637
638 EXPECT_THAT(aa_matrix, IsOrthonormal());
639 EXPECT_THAT(ea_matrix, IsOrthonormal());
640 EXPECT_THAT(ea_matrix, IsNear3x3Matrix(aa_matrix));
641}
642
643// Test with rotation axis along the x/y/z axes.
644// Also test zero rotation.
645TEST(EulerAnglesToRotationMatrix, OnAxis) {
646 int n_tests = 0;
647 for (double x = -1.0; x <= 1.0; x += 1.0) {
648 for (double y = -1.0; y <= 1.0; y += 1.0) {
649 for (double z = -1.0; z <= 1.0; z += 1.0) {
650 if ((x != 0) + (y != 0) + (z != 0) > 1)
651 continue;
652 double axis_angle[3] = {x, y, z};
653 double euler_angles[3] = {x, y, z};
654 CompareEulerToAngleAxis(axis_angle, euler_angles);
655 ++n_tests;
656 }
657 }
658 }
659 CHECK_EQ(7, n_tests);
660}
661
662// Test that a random rotation produces an orthonormal rotation
663// matrix.
664TEST(EulerAnglesToRotationMatrix, IsOrthonormal) {
665 srand(5);
666 for (int trial = 0; trial < kNumTrials; ++trial) {
667 double euler_angles_degrees[3];
668 for (int i = 0; i < 3; ++i) {
669 euler_angles_degrees[i] = RandDouble() * 360.0 - 180.0;
670 }
671 double rotation_matrix[9];
672 EulerAnglesToRotationMatrix(euler_angles_degrees, 3, rotation_matrix);
673 EXPECT_THAT(rotation_matrix, IsOrthonormal());
674 }
675}
676
677// Tests using Jets for specific behavior involving auto differentiation
678// near singularity points.
679
680typedef Jet<double, 3> J3;
681typedef Jet<double, 4> J4;
682
683J3 MakeJ3(double a, double v0, double v1, double v2) {
684 J3 j;
685 j.a = a;
686 j.v[0] = v0;
687 j.v[1] = v1;
688 j.v[2] = v2;
689 return j;
690}
691
692J4 MakeJ4(double a, double v0, double v1, double v2, double v3) {
693 J4 j;
694 j.a = a;
695 j.v[0] = v0;
696 j.v[1] = v1;
697 j.v[2] = v2;
698 j.v[3] = v3;
699 return j;
700}
701
702bool IsClose(double x, double y) {
703 EXPECT_FALSE(IsNaN(x));
704 EXPECT_FALSE(IsNaN(y));
705 return internal::IsClose(x, y, kTolerance, NULL, NULL);
706}
707
708template <int N>
709bool IsClose(const Jet<double, N> &x, const Jet<double, N> &y) {
710 if (!IsClose(x.a, y.a)) {
711 return false;
712 }
713 for (int i = 0; i < N; i++) {
714 if (!IsClose(x.v[i], y.v[i])) {
715 return false;
716 }
717 }
718 return true;
719}
720
721template <int M, int N>
722void ExpectJetArraysClose(const Jet<double, N> *x, const Jet<double, N> *y) {
723 for (int i = 0; i < M; i++) {
724 if (!IsClose(x[i], y[i])) {
725 LOG(ERROR) << "Jet " << i << "/" << M << " not equal";
726 LOG(ERROR) << "x[" << i << "]: " << x[i];
727 LOG(ERROR) << "y[" << i << "]: " << y[i];
728 Jet<double, N> d, zero;
729 d.a = y[i].a - x[i].a;
730 for (int j = 0; j < N; j++) {
731 d.v[j] = y[i].v[j] - x[i].v[j];
732 }
733 LOG(ERROR) << "diff: " << d;
734 EXPECT_TRUE(IsClose(x[i], y[i]));
735 }
736 }
737}
738
739// Log-10 of a value well below machine precision.
740static const int kSmallTinyCutoff =
741 static_cast<int>(2 * log(numeric_limits<double>::epsilon())/log(10.0));
742
743// Log-10 of a value just below values representable by double.
744static const int kTinyZeroLimit =
745 static_cast<int>(1 + log(numeric_limits<double>::min())/log(10.0));
746
747// Test that exact conversion works for small angles when jets are used.
748TEST(Rotation, SmallAngleAxisToQuaternionForJets) {
749 // Examine small x rotations that are still large enough
750 // to be well within the range represented by doubles.
751 for (int i = -2; i >= kSmallTinyCutoff; i--) {
752 double theta = pow(10.0, i);
753 J3 axis_angle[3] = { J3(theta, 0), J3(0, 1), J3(0, 2) };
754 J3 quaternion[4];
755 J3 expected[4] = {
756 MakeJ3(cos(theta/2), -sin(theta/2)/2, 0, 0),
757 MakeJ3(sin(theta/2), cos(theta/2)/2, 0, 0),
758 MakeJ3(0, 0, sin(theta/2)/theta, 0),
759 MakeJ3(0, 0, 0, sin(theta/2)/theta),
760 };
761 AngleAxisToQuaternion(axis_angle, quaternion);
762 ExpectJetArraysClose<4, 3>(quaternion, expected);
763 }
764}
765
766
767// Test that conversion works for very small angles when jets are used.
768TEST(Rotation, TinyAngleAxisToQuaternionForJets) {
769 // Examine tiny x rotations that extend all the way to where
770 // underflow occurs.
771 for (int i = kSmallTinyCutoff; i >= kTinyZeroLimit; i--) {
772 double theta = pow(10.0, i);
773 J3 axis_angle[3] = { J3(theta, 0), J3(0, 1), J3(0, 2) };
774 J3 quaternion[4];
775 // To avoid loss of precision in the test itself,
776 // a finite expansion is used here, which will
777 // be exact up to machine precision for the test values used.
778 J3 expected[4] = {
779 MakeJ3(1.0, 0, 0, 0),
780 MakeJ3(0, 0.5, 0, 0),
781 MakeJ3(0, 0, 0.5, 0),
782 MakeJ3(0, 0, 0, 0.5),
783 };
784 AngleAxisToQuaternion(axis_angle, quaternion);
785 ExpectJetArraysClose<4, 3>(quaternion, expected);
786 }
787}
788
789// Test that derivatives are correct for zero rotation.
790TEST(Rotation, ZeroAngleAxisToQuaternionForJets) {
791 J3 axis_angle[3] = { J3(0, 0), J3(0, 1), J3(0, 2) };
792 J3 quaternion[4];
793 J3 expected[4] = {
794 MakeJ3(1.0, 0, 0, 0),
795 MakeJ3(0, 0.5, 0, 0),
796 MakeJ3(0, 0, 0.5, 0),
797 MakeJ3(0, 0, 0, 0.5),
798 };
799 AngleAxisToQuaternion(axis_angle, quaternion);
800 ExpectJetArraysClose<4, 3>(quaternion, expected);
801}
802
803// Test that exact conversion works for small angles.
804TEST(Rotation, SmallQuaternionToAngleAxisForJets) {
805 // Examine small x rotations that are still large enough
806 // to be well within the range represented by doubles.
807 for (int i = -2; i >= kSmallTinyCutoff; i--) {
808 double theta = pow(10.0, i);
809 double s = sin(theta);
810 double c = cos(theta);
811 J4 quaternion[4] = { J4(c, 0), J4(s, 1), J4(0, 2), J4(0, 3) };
812 J4 axis_angle[3];
813 J4 expected[3] = {
814 MakeJ4(2*theta, -2*s, 2*c, 0, 0),
815 MakeJ4(0, 0, 0, 2*theta/s, 0),
816 MakeJ4(0, 0, 0, 0, 2*theta/s),
817 };
818 QuaternionToAngleAxis(quaternion, axis_angle);
819 ExpectJetArraysClose<3, 4>(axis_angle, expected);
820 }
821}
822
823// Test that conversion works for very small angles.
824TEST(Rotation, TinyQuaternionToAngleAxisForJets) {
825 // Examine tiny x rotations that extend all the way to where
826 // underflow occurs.
827 for (int i = kSmallTinyCutoff; i >= kTinyZeroLimit; i--) {
828 double theta = pow(10.0, i);
829 double s = sin(theta);
830 double c = cos(theta);
831 J4 quaternion[4] = { J4(c, 0), J4(s, 1), J4(0, 2), J4(0, 3) };
832 J4 axis_angle[3];
833 // To avoid loss of precision in the test itself,
834 // a finite expansion is used here, which will
835 // be exact up to machine precision for the test values used.
836 J4 expected[3] = {
837 MakeJ4(2*theta, -2*s, 2.0, 0, 0),
838 MakeJ4(0, 0, 0, 2.0, 0),
839 MakeJ4(0, 0, 0, 0, 2.0),
840 };
841 QuaternionToAngleAxis(quaternion, axis_angle);
842 ExpectJetArraysClose<3, 4>(axis_angle, expected);
843 }
844}
845
846// Test that conversion works for no rotation.
847TEST(Rotation, ZeroQuaternionToAngleAxisForJets) {
848 J4 quaternion[4] = { J4(1, 0), J4(0, 1), J4(0, 2), J4(0, 3) };
849 J4 axis_angle[3];
850 J4 expected[3] = {
851 MakeJ4(0, 0, 2.0, 0, 0),
852 MakeJ4(0, 0, 0, 2.0, 0),
853 MakeJ4(0, 0, 0, 0, 2.0),
854 };
855 QuaternionToAngleAxis(quaternion, axis_angle);
856 ExpectJetArraysClose<3, 4>(axis_angle, expected);
857}
858
859TEST(Quaternion, RotatePointGivesSameAnswerAsRotationByMatrixCanned) {
860 // Canned data generated in octave.
861 double const q[4] = {
862 +0.1956830471754074,
863 -0.0150618562474847,
864 +0.7634572982788086,
865 -0.3019454777240753,
866 };
867 double const Q[3][3] = { // Scaled rotation matrix.
868 { -0.6355194033477252, 0.0951730541682254, 0.3078870197911186 },
869 { -0.1411693904792992, 0.5297609702153905, -0.4551502574482019 },
870 { -0.2896955822708862, -0.4669396571547050, -0.4536309793389248 },
871 };
872 double const R[3][3] = { // With unit rows and columns.
873 { -0.8918859164053080, 0.1335655625725649, 0.4320876677394745 },
874 { -0.1981166751680096, 0.7434648665444399, -0.6387564287225856 },
875 { -0.4065578619806013, -0.6553016349046693, -0.6366242786393164 },
876 };
877
878 // Compute R from q and compare to known answer.
879 double Rq[3][3];
880 QuaternionToScaledRotation<double>(q, Rq[0]);
881 ExpectArraysClose(9, Q[0], Rq[0], kTolerance);
882
883 // Now do the same but compute R with normalization.
884 QuaternionToRotation<double>(q, Rq[0]);
885 ExpectArraysClose(9, R[0], Rq[0], kTolerance);
886}
887
888
889TEST(Quaternion, RotatePointGivesSameAnswerAsRotationByMatrix) {
890 // Rotation defined by a unit quaternion.
891 double const q[4] = {
892 0.2318160216097109,
893 -0.0178430356832060,
894 0.9044300776717159,
895 -0.3576998641394597,
896 };
897 double const p[3] = {
898 +0.11,
899 -13.15,
900 1.17,
901 };
902
903 double R[3 * 3];
904 QuaternionToRotation(q, R);
905
906 double result1[3];
907 UnitQuaternionRotatePoint(q, p, result1);
908
909 double result2[3];
910 VectorRef(result2, 3) = ConstMatrixRef(R, 3, 3)* ConstVectorRef(p, 3);
911 ExpectArraysClose(3, result1, result2, kTolerance);
912}
913
914
915// Verify that (a * b) * c == a * (b * c).
916TEST(Quaternion, MultiplicationIsAssociative) {
917 double a[4];
918 double b[4];
919 double c[4];
920 for (int i = 0; i < 4; ++i) {
921 a[i] = 2 * RandDouble() - 1;
922 b[i] = 2 * RandDouble() - 1;
923 c[i] = 2 * RandDouble() - 1;
924 }
925
926 double ab[4];
927 double ab_c[4];
928 QuaternionProduct(a, b, ab);
929 QuaternionProduct(ab, c, ab_c);
930
931 double bc[4];
932 double a_bc[4];
933 QuaternionProduct(b, c, bc);
934 QuaternionProduct(a, bc, a_bc);
935
936 ASSERT_NEAR(ab_c[0], a_bc[0], kTolerance);
937 ASSERT_NEAR(ab_c[1], a_bc[1], kTolerance);
938 ASSERT_NEAR(ab_c[2], a_bc[2], kTolerance);
939 ASSERT_NEAR(ab_c[3], a_bc[3], kTolerance);
940}
941
942
943TEST(AngleAxis, RotatePointGivesSameAnswerAsRotationMatrix) {
944 double angle_axis[3];
945 double R[9];
946 double p[3];
947 double angle_axis_rotated_p[3];
948 double rotation_matrix_rotated_p[3];
949
950 for (int i = 0; i < 10000; ++i) {
951 double theta = (2.0 * i * 0.0011 - 1.0) * kPi;
952 for (int j = 0; j < 50; ++j) {
953 double norm2 = 0.0;
954 for (int k = 0; k < 3; ++k) {
955 angle_axis[k] = 2.0 * RandDouble() - 1.0;
956 p[k] = 2.0 * RandDouble() - 1.0;
957 norm2 = angle_axis[k] * angle_axis[k];
958 }
959
960 const double inv_norm = theta / sqrt(norm2);
961 for (int k = 0; k < 3; ++k) {
962 angle_axis[k] *= inv_norm;
963 }
964
965 AngleAxisToRotationMatrix(angle_axis, R);
966 rotation_matrix_rotated_p[0] = R[0] * p[0] + R[3] * p[1] + R[6] * p[2];
967 rotation_matrix_rotated_p[1] = R[1] * p[0] + R[4] * p[1] + R[7] * p[2];
968 rotation_matrix_rotated_p[2] = R[2] * p[0] + R[5] * p[1] + R[8] * p[2];
969
970 AngleAxisRotatePoint(angle_axis, p, angle_axis_rotated_p);
971 for (int k = 0; k < 3; ++k) {
972 EXPECT_NEAR(rotation_matrix_rotated_p[k],
973 angle_axis_rotated_p[k],
974 kTolerance) << "p: " << p[0]
975 << " " << p[1]
976 << " " << p[2]
977 << " angle_axis: " << angle_axis[0]
978 << " " << angle_axis[1]
979 << " " << angle_axis[2];
980 }
981 }
982 }
983}
984
985TEST(AngleAxis, NearZeroRotatePointGivesSameAnswerAsRotationMatrix) {
986 double angle_axis[3];
987 double R[9];
988 double p[3];
989 double angle_axis_rotated_p[3];
990 double rotation_matrix_rotated_p[3];
991
992 for (int i = 0; i < 10000; ++i) {
993 double norm2 = 0.0;
994 for (int k = 0; k < 3; ++k) {
995 angle_axis[k] = 2.0 * RandDouble() - 1.0;
996 p[k] = 2.0 * RandDouble() - 1.0;
997 norm2 = angle_axis[k] * angle_axis[k];
998 }
999
1000 double theta = (2.0 * i * 0.0001 - 1.0) * 1e-16;
1001 const double inv_norm = theta / sqrt(norm2);
1002 for (int k = 0; k < 3; ++k) {
1003 angle_axis[k] *= inv_norm;
1004 }
1005
1006 AngleAxisToRotationMatrix(angle_axis, R);
1007 rotation_matrix_rotated_p[0] = R[0] * p[0] + R[3] * p[1] + R[6] * p[2];
1008 rotation_matrix_rotated_p[1] = R[1] * p[0] + R[4] * p[1] + R[7] * p[2];
1009 rotation_matrix_rotated_p[2] = R[2] * p[0] + R[5] * p[1] + R[8] * p[2];
1010
1011 AngleAxisRotatePoint(angle_axis, p, angle_axis_rotated_p);
1012 for (int k = 0; k < 3; ++k) {
1013 EXPECT_NEAR(rotation_matrix_rotated_p[k],
1014 angle_axis_rotated_p[k],
1015 kTolerance) << "p: " << p[0]
1016 << " " << p[1]
1017 << " " << p[2]
1018 << " angle_axis: " << angle_axis[0]
1019 << " " << angle_axis[1]
1020 << " " << angle_axis[2];
1021 }
1022 }
1023}
1024
1025TEST(MatrixAdapter, RowMajor3x3ReturnTypeAndAccessIsCorrect) {
1026 double array[9] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 };
1027 const float const_array[9] =
1028 { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f };
1029 MatrixAdapter<double, 3, 1> A = RowMajorAdapter3x3(array);
1030 MatrixAdapter<const float, 3, 1> B = RowMajorAdapter3x3(const_array);
1031
1032 for (int i = 0; i < 3; ++i) {
1033 for (int j = 0; j < 3; ++j) {
1034 // The values are integers from 1 to 9, so equality tests are appropriate
1035 // even for float and double values.
1036 EXPECT_EQ(A(i, j), array[3*i+j]);
1037 EXPECT_EQ(B(i, j), const_array[3*i+j]);
1038 }
1039 }
1040}
1041
1042TEST(MatrixAdapter, ColumnMajor3x3ReturnTypeAndAccessIsCorrect) {
1043 double array[9] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 };
1044 const float const_array[9] =
1045 { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f };
1046 MatrixAdapter<double, 1, 3> A = ColumnMajorAdapter3x3(array);
1047 MatrixAdapter<const float, 1, 3> B = ColumnMajorAdapter3x3(const_array);
1048
1049 for (int i = 0; i < 3; ++i) {
1050 for (int j = 0; j < 3; ++j) {
1051 // The values are integers from 1 to 9, so equality tests are
1052 // appropriate even for float and double values.
1053 EXPECT_EQ(A(i, j), array[3*j+i]);
1054 EXPECT_EQ(B(i, j), const_array[3*j+i]);
1055 }
1056 }
1057}
1058
1059TEST(MatrixAdapter, RowMajor2x4IsCorrect) {
1060 const int expected[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
1061 int array[8];
1062 MatrixAdapter<int, 4, 1> M(array);
1063 M(0, 0) = 1; M(0, 1) = 2; M(0, 2) = 3; M(0, 3) = 4;
1064 M(1, 0) = 5; M(1, 1) = 6; M(1, 2) = 7; M(1, 3) = 8;
1065 for (int k = 0; k < 8; ++k) {
1066 EXPECT_EQ(array[k], expected[k]);
1067 }
1068}
1069
1070TEST(MatrixAdapter, ColumnMajor2x4IsCorrect) {
1071 const int expected[8] = { 1, 5, 2, 6, 3, 7, 4, 8 };
1072 int array[8];
1073 MatrixAdapter<int, 1, 2> M(array);
1074 M(0, 0) = 1; M(0, 1) = 2; M(0, 2) = 3; M(0, 3) = 4;
1075 M(1, 0) = 5; M(1, 1) = 6; M(1, 2) = 7; M(1, 3) = 8;
1076 for (int k = 0; k < 8; ++k) {
1077 EXPECT_EQ(array[k], expected[k]);
1078 }
1079}
1080
1081TEST(RotationMatrixToAngleAxis, NearPiExampleOneFromTobiasStrauss) {
1082 // Example from Tobias Strauss
1083 const double rotation_matrix[] = {
1084 -0.999807135425239, -0.0128154391194470, -0.0148814136745799,
1085 -0.0128154391194470, -0.148441438622958, 0.988838158557669,
1086 -0.0148814136745799, 0.988838158557669, 0.148248574048196
1087 };
1088
1089 double angle_axis[3];
1090 RotationMatrixToAngleAxis(RowMajorAdapter3x3(rotation_matrix), angle_axis);
1091 double round_trip[9];
1092 AngleAxisToRotationMatrix(angle_axis, RowMajorAdapter3x3(round_trip));
1093 EXPECT_THAT(rotation_matrix, IsNear3x3Matrix(round_trip));
1094}
1095
1096void CheckRotationMatrixToAngleAxisRoundTrip(const double theta,
1097 const double phi,
1098 const double angle) {
1099 double angle_axis[3];
1100 angle_axis[0] = angle * sin(phi) * cos(theta);
1101 angle_axis[1] = angle * sin(phi) * sin(theta);
1102 angle_axis[2] = angle * cos(phi);
1103
1104 double rotation_matrix[9];
1105 AngleAxisToRotationMatrix(angle_axis, rotation_matrix);
1106
1107 double angle_axis_round_trip[3];
1108 RotationMatrixToAngleAxis(rotation_matrix, angle_axis_round_trip);
1109 EXPECT_THAT(angle_axis_round_trip, IsNearAngleAxis(angle_axis));
1110}
1111
1112TEST(RotationMatrixToAngleAxis, ExhaustiveRoundTrip) {
1113 const double kMaxSmallAngle = 1e-8;
1114 const int kNumSteps = 1000;
1115 for (int i = 0; i < kNumSteps; ++i) {
1116 const double theta = static_cast<double>(i) / kNumSteps * 2.0 * kPi;
1117 for (int j = 0; j < kNumSteps; ++j) {
1118 const double phi = static_cast<double>(j) / kNumSteps * kPi;
1119 // Rotations of angle Pi.
1120 CheckRotationMatrixToAngleAxisRoundTrip(theta, phi, kPi);
1121 // Rotation of angle approximately Pi.
1122 CheckRotationMatrixToAngleAxisRoundTrip(
1123 theta, phi, kPi - kMaxSmallAngle * RandDouble());
1124 // Rotations of angle approximately zero.
1125 CheckRotationMatrixToAngleAxisRoundTrip(
1126 theta, phi, kMaxSmallAngle * 2.0 * RandDouble() - 1.0);
1127 }
1128 }
1129}
1130
1131} // namespace internal
1132} // namespace ceres