xref: /aosp_15_r20/frameworks/av/media/libheadtracking/QuaternionUtil.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1*ec779b8eSAndroid Build Coastguard Worker /*
2*ec779b8eSAndroid Build Coastguard Worker  * Copyright (C) 2021 The Android Open Source Project
3*ec779b8eSAndroid Build Coastguard Worker  *
4*ec779b8eSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*ec779b8eSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*ec779b8eSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*ec779b8eSAndroid Build Coastguard Worker  *
8*ec779b8eSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*ec779b8eSAndroid Build Coastguard Worker  *
10*ec779b8eSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*ec779b8eSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*ec779b8eSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*ec779b8eSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*ec779b8eSAndroid Build Coastguard Worker  * limitations under the License.
15*ec779b8eSAndroid Build Coastguard Worker  */
16*ec779b8eSAndroid Build Coastguard Worker 
17*ec779b8eSAndroid Build Coastguard Worker #include "media/QuaternionUtil.h"
18*ec779b8eSAndroid Build Coastguard Worker 
19*ec779b8eSAndroid Build Coastguard Worker #include <cassert>
20*ec779b8eSAndroid Build Coastguard Worker 
21*ec779b8eSAndroid Build Coastguard Worker namespace android {
22*ec779b8eSAndroid Build Coastguard Worker namespace media {
23*ec779b8eSAndroid Build Coastguard Worker 
24*ec779b8eSAndroid Build Coastguard Worker using Eigen::NumTraits;
25*ec779b8eSAndroid Build Coastguard Worker using Eigen::Quaternionf;
26*ec779b8eSAndroid Build Coastguard Worker using Eigen::Vector3f;
27*ec779b8eSAndroid Build Coastguard Worker 
28*ec779b8eSAndroid Build Coastguard Worker namespace {
29*ec779b8eSAndroid Build Coastguard Worker 
LogSU2(const Quaternionf & q)30*ec779b8eSAndroid Build Coastguard Worker Vector3f LogSU2(const Quaternionf& q) {
31*ec779b8eSAndroid Build Coastguard Worker     // Implementation of the logarithmic map of SU(2) using atan.
32*ec779b8eSAndroid Build Coastguard Worker     // This follows Hertzberg et al. "Integrating Generic Sensor Fusion Algorithms
33*ec779b8eSAndroid Build Coastguard Worker     // with Sound State Representations through Encapsulation of Manifolds", Eq.
34*ec779b8eSAndroid Build Coastguard Worker     // (31)
35*ec779b8eSAndroid Build Coastguard Worker     // We use asin and acos instead of atan to enable the use of Eigen Autodiff
36*ec779b8eSAndroid Build Coastguard Worker     // with SU2.
37*ec779b8eSAndroid Build Coastguard Worker     const float sign_of_w = q.w() < 0.f ? -1.f : 1.f;
38*ec779b8eSAndroid Build Coastguard Worker     const float abs_w = sign_of_w * q.w();
39*ec779b8eSAndroid Build Coastguard Worker     const Vector3f v = sign_of_w * q.vec();
40*ec779b8eSAndroid Build Coastguard Worker     const float squared_norm_of_v = v.squaredNorm();
41*ec779b8eSAndroid Build Coastguard Worker 
42*ec779b8eSAndroid Build Coastguard Worker     assert(abs(1.f - abs_w * abs_w - squared_norm_of_v) < NumTraits<float>::dummy_precision());
43*ec779b8eSAndroid Build Coastguard Worker 
44*ec779b8eSAndroid Build Coastguard Worker     if (squared_norm_of_v > NumTraits<float>::dummy_precision()) {
45*ec779b8eSAndroid Build Coastguard Worker         const float norm_of_v = sqrt(squared_norm_of_v);
46*ec779b8eSAndroid Build Coastguard Worker         if (abs_w > NumTraits<float>::dummy_precision()) {
47*ec779b8eSAndroid Build Coastguard Worker             // asin(x) = acos(x) at x = 1/sqrt(2).
48*ec779b8eSAndroid Build Coastguard Worker             if (norm_of_v <= float(M_SQRT1_2)) {
49*ec779b8eSAndroid Build Coastguard Worker                 return (asin(norm_of_v) / norm_of_v) * v;
50*ec779b8eSAndroid Build Coastguard Worker             }
51*ec779b8eSAndroid Build Coastguard Worker             return (acos(abs_w) / norm_of_v) * v;
52*ec779b8eSAndroid Build Coastguard Worker         }
53*ec779b8eSAndroid Build Coastguard Worker         return (M_PI_2 / norm_of_v) * v;
54*ec779b8eSAndroid Build Coastguard Worker     }
55*ec779b8eSAndroid Build Coastguard Worker 
56*ec779b8eSAndroid Build Coastguard Worker     // Taylor expansion at squared_norm_of_v == 0
57*ec779b8eSAndroid Build Coastguard Worker     return (1.f / abs_w - squared_norm_of_v / (3.f * pow(abs_w, 3))) * v;
58*ec779b8eSAndroid Build Coastguard Worker }
59*ec779b8eSAndroid Build Coastguard Worker 
ExpSU2(const Vector3f & delta)60*ec779b8eSAndroid Build Coastguard Worker Quaternionf ExpSU2(const Vector3f& delta) {
61*ec779b8eSAndroid Build Coastguard Worker     Quaternionf q_delta;
62*ec779b8eSAndroid Build Coastguard Worker     const float theta_squared = delta.squaredNorm();
63*ec779b8eSAndroid Build Coastguard Worker     if (theta_squared > NumTraits<float>::dummy_precision()) {
64*ec779b8eSAndroid Build Coastguard Worker         const float theta = sqrt(theta_squared);
65*ec779b8eSAndroid Build Coastguard Worker         q_delta.w() = cos(theta);
66*ec779b8eSAndroid Build Coastguard Worker         q_delta.vec() = (sin(theta) / theta) * delta;
67*ec779b8eSAndroid Build Coastguard Worker     } else {
68*ec779b8eSAndroid Build Coastguard Worker         // taylor expansions around theta == 0
69*ec779b8eSAndroid Build Coastguard Worker         q_delta.w() = 1.f - 0.5f * theta_squared;
70*ec779b8eSAndroid Build Coastguard Worker         q_delta.vec() = (1.f - 1.f / 6.f * theta_squared) * delta;
71*ec779b8eSAndroid Build Coastguard Worker     }
72*ec779b8eSAndroid Build Coastguard Worker     return q_delta;
73*ec779b8eSAndroid Build Coastguard Worker }
74*ec779b8eSAndroid Build Coastguard Worker 
75*ec779b8eSAndroid Build Coastguard Worker }  // namespace
76*ec779b8eSAndroid Build Coastguard Worker 
rotationVectorToQuaternion(const Vector3f & rotationVector)77*ec779b8eSAndroid Build Coastguard Worker Quaternionf rotationVectorToQuaternion(const Vector3f& rotationVector) {
78*ec779b8eSAndroid Build Coastguard Worker     //  SU(2) is a double cover of SO(3), thus we have to half the tangent vector
79*ec779b8eSAndroid Build Coastguard Worker     //  delta
80*ec779b8eSAndroid Build Coastguard Worker     const Vector3f half_delta = 0.5f * rotationVector;
81*ec779b8eSAndroid Build Coastguard Worker     return ExpSU2(half_delta);
82*ec779b8eSAndroid Build Coastguard Worker }
83*ec779b8eSAndroid Build Coastguard Worker 
quaternionToRotationVector(const Quaternionf & quaternion)84*ec779b8eSAndroid Build Coastguard Worker Vector3f quaternionToRotationVector(const Quaternionf& quaternion) {
85*ec779b8eSAndroid Build Coastguard Worker     // SU(2) is a double cover of SO(3), thus we have to multiply the tangent
86*ec779b8eSAndroid Build Coastguard Worker     // vector delta by two
87*ec779b8eSAndroid Build Coastguard Worker     return 2.f * LogSU2(quaternion);
88*ec779b8eSAndroid Build Coastguard Worker }
89*ec779b8eSAndroid Build Coastguard Worker 
rotateX(float angle)90*ec779b8eSAndroid Build Coastguard Worker Quaternionf rotateX(float angle) {
91*ec779b8eSAndroid Build Coastguard Worker     return rotationVectorToQuaternion(Vector3f(1, 0, 0) * angle);
92*ec779b8eSAndroid Build Coastguard Worker }
93*ec779b8eSAndroid Build Coastguard Worker 
rotateY(float angle)94*ec779b8eSAndroid Build Coastguard Worker Quaternionf rotateY(float angle) {
95*ec779b8eSAndroid Build Coastguard Worker     return rotationVectorToQuaternion(Vector3f(0, 1, 0) * angle);
96*ec779b8eSAndroid Build Coastguard Worker }
97*ec779b8eSAndroid Build Coastguard Worker 
rotateZ(float angle)98*ec779b8eSAndroid Build Coastguard Worker Quaternionf rotateZ(float angle) {
99*ec779b8eSAndroid Build Coastguard Worker     return rotationVectorToQuaternion(Vector3f(0, 0, 1) * angle);
100*ec779b8eSAndroid Build Coastguard Worker }
101*ec779b8eSAndroid Build Coastguard Worker 
102*ec779b8eSAndroid Build Coastguard Worker }  // namespace media
103*ec779b8eSAndroid Build Coastguard Worker }  // namespace android
104