# Quaternions

## What's that?

• quaternions have 4 parameters
• they form an algebra with 3 operations
• while the addition and the scalar multiplication is as in R^4
• the multiplication of two quaternions is non-commutative
• as the 3D rotations in space!
• unit quaternions can be used to represent 3D rotations

## Pros/Cons for quaternions?

• no gimbal lock
• no singularities
• successive rotations can be expressed by multiplication of quaternions
• rotate a vector x given a quaternion q: q * x * q^(-1)
• interpolating rotations can also be done easily with quaternions (SLERP)

• non-minimal representation: we have 4 numbers to encode a 3DOF rotation

## Was is a good intiution for quaternions?

There is a strong connection between the (i) axis-angle and the (ii) quaternions representation for 3D rotations.

### Axis-angle to quaternions

Assume you have an axis-angle representation of a 3D rotation given by some unit vector v=(x,y,z) and an angle alpha.

Then

q = cos(alpha/2) + i (x*sin(alpha/2)) + j (y*sin(alpha/2)) + k (z*sin(alpha/2))

is the corresponding unit quaternion that represents the same 3D rotation.

So you can think of a quaternion to be similar to an axis-angle representation except that the real part is equal to cos(alpha/2) and the imaginery part is made up of the axis vector times sin(angle/2).

### Quaternion to axis-angle

Given q = [a,b,c,d], we can compute the axis-angle representation by

alpha = 2*acos(a)

axis = (b,c,d)^T / ||(b,c,d)^T||

Quaternions are a cool mathematical construct that lets you represent an arbitrary rotation as a 4D vector. The 3D XYZ parts of the vector give the rotation axis for the direction. The magnitude of the 3D part is the sine of half the rotation angle; the cosine of half the rotation angle is stored in the final W component of the vector.

It's a strange definition, but the neat part is that 'multiplying' quaternions (using a cross product looking formula) gives you the composition of the underlying rotations.

## Conversion code: rotation matrix to quaternion

```inline float SIGN(float x)
{return (x >= 0.0f) ? +1.0f : -1.0f;}
inline float NORM(float a, float b, float c, float d)
{return sqrt(a * a + b * b + c * c + d * d);}

static quaternion get_quaternion_from_rot_mat(cv::Mat& rotMat)
{
float r11 = rotMat.at<float>(0,0);
float r21 = rotMat.at<float>(1,0);
float r31 = rotMat.at<float>(2,0);

float r12 = rotMat.at<float>(0,1);
float r22 = rotMat.at<float>(1,1);
float r32 = rotMat.at<float>(2,1);

float r13 = rotMat.at<float>(0,2);
float r23 = rotMat.at<float>(1,2);
float r33 = rotMat.at<float>(2,2);

float q0 = ( r11 + r22 + r33 + 1.0f) / 4.0f;
float q1 = ( r11 - r22 - r33 + 1.0f) / 4.0f;
float q2 = (-r11 + r22 - r33 + 1.0f) / 4.0f;
float q3 = (-r11 - r22 + r33 + 1.0f) / 4.0f;
if(q0 < 0.0f) q0 = 0.0f;
if(q1 < 0.0f) q1 = 0.0f;
if(q2 < 0.0f) q2 = 0.0f;
if(q3 < 0.0f) q3 = 0.0f;
q0 = sqrt(q0);
q1 = sqrt(q1);
q2 = sqrt(q2);
q3 = sqrt(q3);
if(q0 >= q1 && q0 >= q2 && q0 >= q3) {
q0 *= +1.0f;
q1 *= SIGN(r32 - r23);
q2 *= SIGN(r13 - r31);
q3 *= SIGN(r21 - r12);
} else if(q1 >= q0 && q1 >= q2 && q1 >= q3) {
q0 *= SIGN(r32 - r23);
q1 *= +1.0f;
q2 *= SIGN(r21 + r12);
q3 *= SIGN(r13 + r31);
} else if(q2 >= q0 && q2 >= q1 && q2 >= q3) {
q0 *= SIGN(r13 - r31);
q1 *= SIGN(r21 + r12);
q2 *= +1.0f;
q3 *= SIGN(r32 + r23);
} else if(q3 >= q0 && q3 >= q1 && q3 >= q2) {
q0 *= SIGN(r21 - r12);
q1 *= SIGN(r31 + r13);
q2 *= SIGN(r32 + r23);
q3 *= +1.0f;
} else {
printf("coding error\n");
}
float r = NORM(q0, q1, q2, q3);
q0 /= r;
q1 /= r;
q2 /= r;
q3 /= r;

quaternion q;
q.x = q0;
q.y = q1;
q.z = q2;
q.w = q3;

return q;

} // get_quaternion_from_rot_mat```

## Conversion code: quaternion to rotation matrix

```static cv::Mat get_rot_mat_from_quaternion_values
(float q0, float q1, float q2, float q3)
{
cv::Mat rotMat(3,3, CV_32FC1);

float q0q0 = q0*q0;
float q1q1 = q1*q1;
float q2q2 = q2*q2;
float q3q3 = q3*q3;

rotMat.at<float>(0,0) = q0q0 + q1q1 - q2q2 -q3q3;
rotMat.at<float>(1,0)	= 2*(q1*q2 + q0*q3);
rotMat.at<float>(2,0)	= 2*(q1*q3 - q0*q2);

rotMat.at<float>(0,1)	= 2*(q1*q2 - q0*q3);
rotMat.at<float>(1,1)	= q0q0 - q1q1 + q2q2 - q3q3;
rotMat.at<float>(2,1)	= 2*(q2*q3 + q0*q1);

rotMat.at<float>(0,2)	= 2*(q1*q3 + q0*q2);
rotMat.at<float>(1,2)	= 2*(q2*q3 - q0*q1);
rotMat.at<float>(2,2)	= q0q0 - q1q1 -q2q2 + q3q3;

return rotMat;

} // get_rot_mat_from_quaternion```

## Videos

### Gives some intuition

… and underlines the similarities of quaternions to the axis-angle representation.