vnl_quaternion.hxx
Go to the documentation of this file.
1 // This is core/vnl/vnl_quaternion.hxx
2 #ifndef vnl_quaternion_hxx_
3 #define vnl_quaternion_hxx_
4 //:
5 // \file
6 //
7 // Copyright (C) 1992 General Electric Company.
8 //
9 // Permission is granted to any individual or institution to use, copy, modify,
10 // and distribute this software, provided that this complete copyright and
11 // permission notice is maintained, intact, in all copies and supporting
12 // documentation.
13 //
14 // General Electric Company,
15 // provides this software "as is" without express or implied warranty.
16 //
17 // Created: VDN 06/23/92 design and implementation
18 //
19 // Quaternion IS-A vector, and is a special case of general n-dimensional space.
20 // The IS-A relationship is enforced with public inheritance.
21 // All member functions on vectors are applicable to quaternions.
22 //
23 // Rep Invariant:
24 // - norm = 1, for a rotation.
25 // - position vector represented by imaginary quaternion.
26 // References:
27 // - Horn, B.K.P. (1987) Closed-form solution of absolute orientation using
28 // unit quaternions. J. Opt. Soc. Am. Vol 4, No 4, April.
29 // - Horn, B.K.P. (1987) Robot Vision. MIT Press. pp. 437-551.
30 //
31 
32 
33 #include <cmath>
34 #include <limits>
35 #include <iostream>
36 #include "vnl_quaternion.h"
37 
38 #ifdef _MSC_VER
39 # include <vcl_msvc_warnings.h>
40 #endif
41 
42 #include <vnl/vnl_cross.h>
43 #include <vnl/vnl_math.h>
44 
45 //: Creates a quaternion from its ordered components.
46 // x, y, z denote the imaginary part, which are the coordinates
47 // of the rotation axis multiplied by the sine of half the
48 // angle of rotation. r denotes the real part, or the
49 // cosine of half the angle of rotation. Default is to
50 // create a null quaternion, corresponding to a null rotation
51 // or an identity transform, which has undefined
52 // rotation axis.
53 
54 template <class T>
55 vnl_quaternion<T>::vnl_quaternion (T tx, T ty, T tz, T rea)
56 {
57  this->operator[](0) = tx; // 3 first elements are
58  this->operator[](1) = ty; // imaginary parts
59  this->operator[](2) = tz;
60  this->operator[](3) = rea; // last element is real part
61 }
62 
63 //: Creates a quaternion from the normalized axis direction and the angle of rotation in radians.
64 
65 template <class T>
67 {
68  double a = Angle * 0.5; // half angle
69  T s = T(std::sin(a));
70  for (int i = 0; i < 3; i++) // imaginary vector is sine of
71  this->operator[](i) = T(s * Axis(i));// half angle multiplied with axis
72  this->operator[](3) = T(std::cos(a)); // real part is cosine of half angle
73 }
74 
75 //: Creates a quaternion from a vector.
76 // 3D vector is converted into an imaginary quaternion with same
77 // (x, y, z) components.
78 
79 template <class T>
81 {
82  for (unsigned int i = 0; i < 3; ++i)
83  this->operator[](i) = vec(i);
84  this->operator[](3) = T(0);
85 }
86 
87 //: Creates a quaternion from a vector.
88 // 4D vector is assumed to be a 4-element quaternion, to
89 // provide casting between vector and quaternion
90 
91 template <class T>
93 {
94  for (unsigned int i = 0; i < 4; ++i) // 1-1 layout between vector & quaternion
95  this->operator[](i) = vec[i];
96 }
97 
98 
99 //: Creates a quaternion from a rotation matrix.
100 // Its orthonormal basis vectors are the matrix rows.
101 // \note this matrix \e must have determinant +1; this is not verified!
102 // \warning Takes the transpose of the rotation matrix, i.e.,
103 // the orthonormal vectors must be the rows of the matrix, not the columns.
104 template <class T>
106 {
107  double d0 = rot(0,0), d1 = rot(1,1), d2 = rot(2,2);
108  double xx = 1.0 + d0 - d1 - d2; // from the diagonal of the rotation
109  double yy = 1.0 - d0 + d1 - d2; // matrix, find the terms in
110  double zz = 1.0 - d0 - d1 + d2; // each Quaternion component
111  double rr = 1.0 + d0 + d1 + d2; // (using the fact that rr+xx+yy+zz=4)
112 
113  double max = rr; // find the maximum of all terms;
114  if (xx > max) max = xx; // dividing by the maximum makes
115  if (yy > max) max = yy; // the computations more stable
116  if (zz > max) max = zz; // and avoid division by zero
117 
118  if (rr == max) {
119  T r4 = T(std::sqrt(rr)*2);
120  this->r() = r4 / 4;
121  r4 = T(1) / r4;
122  this->x() = (rot(1,2) - rot(2,1)) * r4; // find other components from
123  this->y() = (rot(2,0) - rot(0,2)) * r4; // off diagonal terms of
124  this->z() = (rot(0,1) - rot(1,0)) * r4; // rotation matrix.
125  }
126  else if (xx == max) {
127  T x4 = T(std::sqrt(xx)*2);
128  this->x() = x4 / 4;
129  x4 = T(1) / x4;
130  this->y() = (rot(0,1) + rot(1,0)) * x4;
131  this->z() = (rot(0,2) + rot(2,0)) * x4;
132  this->r() = (rot(1,2) - rot(2,1)) * x4;
133  }
134  else if (yy == max) {
135  T y4 = T(std::sqrt(yy)*2);
136  this->y() = y4 / 4;
137  y4 = T(1) / y4;
138  this->x() = (rot(0,1) + rot(1,0)) * y4;
139  this->z() = (rot(1,2) + rot(2,1)) * y4;
140  this->r() = (rot(2,0) - rot(0,2)) * y4;
141  }
142  else {
143  T z4 = T(std::sqrt(zz)*2);
144  this->z() = z4 / 4;
145  z4 = T(1) / z4;
146  this->x() = (rot(0,2) + rot(2,0)) * z4;
147  this->y() = (rot(1,2) + rot(2,1)) * z4;
148  this->r() = (rot(0,1) - rot(1,0)) * z4;
149  }
150 }
151 
152 
153 //: Construct quaternion from Euler Angles
154 // That is a rotation about the X axis, followed by Y, followed by
155 // the Z axis, using a fixed reference frame.
156 template <class T>
157 vnl_quaternion<T>::vnl_quaternion(T theta_X, T theta_Y, T theta_Z)
158 {
159  vnl_quaternion<T> Rx(T(std::sin(double(theta_X)*0.5)), 0, 0, T(std::cos(double(theta_X)*0.5)));
160  vnl_quaternion<T> Ry(0, T(std::sin(double(theta_Y)*0.5)), 0, T(std::cos(double(theta_Y)*0.5)));
161  vnl_quaternion<T> Rz(0, 0, T(std::sin(double(theta_Z)*0.5)), T(std::cos(double(theta_Z)*0.5)));
162  *this = Rz * Ry * Rx;
163 }
164 
165 //: Rotation representation in Euler angles.
166 // The angles returned will be [theta_X,theta_Y,theta_Z]
167 // where the final rotation is found be first applying theta_X radians
168 // about the X axis, then theta_Y about the Y-axis, etc.
169 // The axes stay in a fixed reference frame.
170 template <class T>
172 {
173  vnl_vector_fixed<T,3> angles;
174 
175  vnl_matrix_fixed<T,4,4> rotM = rotation_matrix_transpose_4();
176  T xy = T(std::sqrt(double(vnl_math::sqr(rotM(0,0)) + vnl_math::sqr(rotM(0,1)))));
177  if (xy > std::numeric_limits<T>::epsilon() * T(8))
178  {
179  angles(0) = T(std::atan2(double(rotM(1,2)), double(rotM(2,2))));
180  angles(1) = T(std::atan2(double(-rotM(0,2)), double(xy)));
181  angles(2) = T(std::atan2(double(rotM(0,1)), double(rotM(0,0))));
182  }
183  else
184  {
185  angles(0) = T(std::atan2(double(-rotM(2,1)), double(rotM(1,1))));
186  angles(1) = T(std::atan2(double(-rotM(0,2)), double(xy)));
187  angles(2) = T(0);
188  }
189  return angles;
190 }
191 
192 
193 //: Queries the rotation angle of the quaternion.
194 // Returned angle lies in [0, 2*pi]
195 template <class T>
197 {
198  return 2 * std::atan2(double(this->imaginary().magnitude()),
199  double(this->real())); // angle is always positive
200 }
201 
202 //: Queries the direction of the rotation axis of the quaternion.
203 // A null quaternion will return zero for angle and k direction for axis.
204 template <class T>
206 {
207  vnl_vector_fixed<T,3> direc = this->imaginary(); // direc parallel to imag. part
208  T mag = direc.magnitude();
209  if (mag == T(0)) {
210  std::cout << "Axis not well defined for zero Quaternion. Using (0,0,1) instead.\n";
211  direc[2] = T(1); // or signal exception here.
212  }
213  else
214  direc /= mag; // normalize direction vector
215  return direc;
216 }
217 
218 
219 //: Converts a normalized quaternion into a square rotation matrix with dimension dim.
220 // This is the reverse counterpart of constructing a quaternion from a transformation matrix.
221 // WARNING this is inconsistent with the quaternion docs and q.rotate()
222 
223 template <class T>
225 {
226  T x2 = x() * x(), xy = x() * y(), rx = r() * x(),
227  y2 = y() * y(), yz = y() * z(), ry = r() * y(),
228  z2 = z() * z(), zx = z() * x(), rz = r() * z(),
229  r2 = r() * r();
231  rot(0,0) = r2 + x2 - y2 - z2; // fill diagonal terms
232  rot(1,1) = r2 - x2 + y2 - z2;
233  rot(2,2) = r2 - x2 - y2 + z2;
234  rot(0,1) = 2 * (xy + rz); // fill off diagonal terms
235  rot(0,2) = 2 * (zx - ry);
236  rot(1,2) = 2 * (yz + rx);
237  rot(1,0) = 2 * (xy - rz);
238  rot(2,0) = 2 * (zx + ry);
239  rot(2,1) = 2 * (yz - rx);
240 
241  return rot;
242 }
243 
244 
245 template <class T>
247 {
249  return rot.set_identity().update(this->rotation_matrix_transpose().as_ref());
250 }
251 
252 //: Returns the conjugate of given quaternion, having same real and opposite imaginary parts.
253 
254 template <class T>
256 {
257  return vnl_quaternion<T> (-x(), -y(), -z(), r());
258 }
259 
260 //: Returns the inverse of given quaternion.
261 // For unit quaternion representing rotation, the inverse is the
262 // same as the conjugate.
263 
264 template <class T>
266 {
267  vnl_quaternion<T> inv = this->conjugate();
268  inv /= vnl_c_vector<T>::dot_product(this->data_, this->data_, 4);
269  return inv;
270 }
271 
272 //: Returns the product of two quaternions.
273 // Multiplication of two quaternions is not symmetric and has
274 // fewer operations than multiplication of orthonormal
275 // matrices. If object is rotated by r1, then by r2, then
276 // the composed rotation (r2 o r1) is represented by the
277 // quaternion (q2 * q1), or by the matrix (m1 * m2). Note
278 // that matrix composition is reversed because matrices
279 // and vectors are represented row-wise.
280 
281 template <class T>
283 {
284  T r1 = this->real(); // real and img parts of args
285  T r2 = rhs.real();
286  vnl_vector_fixed<T,3> i1 = this->imaginary();
287  vnl_vector_fixed<T,3> i2 = rhs.imaginary();
288  T real_v = (r1 * r2) - ::dot_product(i1, i2); // real&img of product q1*q2
289  vnl_vector_fixed<T,3> img = vnl_cross_3d(i1, i2);
290  img += (i2 * r1) + (i1 * r2);
291  return vnl_quaternion<T>(img[0], img[1], img[2], real_v);
292 }
293 
294 //: Rotates 3D vector v with source quaternion and stores the rotated vector back into v.
295 // For speed and greater accuracy, first convert quaternion into an orthonormal
296 // matrix, then use matrix multiplication to rotate many vectors.
297 
298 template <class T>
300 {
301  T rea = this->real();
302  vnl_vector_fixed<T,3> i = this->imaginary();
304  return v + i_x_v * T(2*rea) - vnl_cross_3d(i_x_v, i) * T(2);
305 }
306 
307 #undef VNL_QUATERNION_INSTANTIATE
308 #define VNL_QUATERNION_INSTANTIATE(T) \
309 template class VNL_EXPORT vnl_quaternion<T >;\
310 /*template VNL_EXPORT std::ostream& operator<< (std::ostream&, vnl_quaternion<T > const&) */
311 
312 #endif // vnl_quaternion_hxx_
abs_t magnitude() const
Return magnitude (length) of vector.
vnl_matrix_fixed< T, 4, 4 > rotation_matrix_transpose_4() const
4x4 rotation matrix.
vnl_vector_fixed< T, 3 > imaginary() const
Copies and returns the imaginary part.
T real() const
Copies and returns the real part.
4-element vector that represents rotation in 3D.
Definition: vnl_fwd.h:29
vnl_matrix_fixed & update(vnl_matrix< T > const &, unsigned top=0, unsigned left=0)
Set values of this matrix to those of M, starting at [top,left].
Namespace with standard math functions.
Implements cross product for vectors.
vnl_vector_fixed< T, 3 > axis() const
Axis of rotation.
#define v
Definition: vnl_vector.h:42
vnl_quaternion< T > conjugate() const
Same real, opposite img part.
vnl_matrix_fixed & set_identity()
Sets this matrix to an identity matrix, then returns "*this".
double angle() const
Angle of rotation.
T dot_product(const vnl_vector_fixed< T, n > &a, const vnl_vector_fixed< T, n > &b)
vnl_quaternion< T > operator *(vnl_quaternion< T > const &) const
Returns the product of two quaternions.
vnl_decnum max(vnl_decnum const &x, vnl_decnum const &y)
Definition: vnl_decnum.h:412
vnl_vector_fixed< T, 3 > rotate(vnl_vector_fixed< T, 3 > const &) const
Rotate 3D v.
vnl_vector_fixed< T, 3 > rotation_euler_angles() const
Rotation representation in Euler angles.
vnl_quaternion< T > inverse() const
Inverse for nonzero quat.
vnl_matrix_fixed< T, 3, 3 > rotation_matrix_transpose() const
3x3 rotation matrix.
vnl_quaternion()=default
Constructor for null quaternion.
static T dot_product(T const *, T const *, unsigned)
vnl_bignum sqr(vnl_bignum const &x)
Definition: vnl_bignum.h:434
Unit quaternion represents rotation in 3D.
vnl_vector< T > vnl_cross_3d(const vnl_vector< T > &v1, const vnl_vector< T > &v2)
Compute the 3-D cross product.
Definition: vnl_cross.h:65