logo back up home forward   further reading more topics »

Maths - Conversion Matrix to Quaternion

Prerequisites

Definition of terms:

Equations

if:

then: 

qw= sqrt (1 + m00 + m11 + m22) /2
qx = (m21 - m12)/( 4 *qw)
qy = (m02 - m20)/( 4 *qw)
qz = (m10 - m01)/( 4 *qw)

Java Code

Simple Code

public final void set(Matrix4f m1) {
	w = Math.sqrt(1.0 + m1.m00 + m1.m11 + m1.m22) / 2.0;
	double w4 = (4.0 * w);
	x = (m1.m21 - m1.m12) / w4 ;
	y = (m1.m02 - m1.m20) / w4 ;
	z = (m1.m10 - m1.m01) / w4 ;
}

The above should work assuming that the matrix is orthogonal (it represents a pure rotation), but if the matrix only represents a pure rotation then it has a lot of redundant information (9 values which we are converting down to 4) . If it is not a pure rotation then the following algorithm may be more resilient.

Even with orthogonal matrix the value of qw could be very small (or just 0) and produce big numerical errors when dividing. So it is better to use the following code:

Better code

1) Calculate the trace(the sum of the diagonal elements) of the matrix T from the equation:


T = 4 - 4*qx2 - 4*qy2 - 4*qz2
= 4( 1 -qx2 - qy2 - qz2 )
= m00 + m11 + m22 + 1

If the trace of the matrix is greater than zero, then the result is:

      S = 0.5 / sqrt(T)
      W = 0.25 / S
      X = ( m21 - m12 ) * S
      Y = ( m02 - m20 ) * S
      Z = ( m10 - m01 ) * S

If the trace of the matrix is less than or equal to zero then identify which major diagonal element has the greatest value.

if (m00 > m11)&(m00 > m22)) { 
   S = sqrt( 1.0 + m00 - m11 - m22 ) * 2; // S=4*qx 
   qw = (m12 - m21) / S;
   qx = 0.25 * S;
   qy = (m01 + m10) / S; 
   qz = (m02 + m20) / S; 
} else if (m11 > m22)) { 
   S = sqrt( 1.0 + m11 - m00 - m22 ) * 2; // S=4*qy
   qw = (m02 - m20) / S;
   qx = (m01 + m10) / S; 
   qy = 0.25 * S;
   qz = (m12 + m21) / S; 
} else { 
   S = sqrt( 1.0 + m22 - m00 - m11 ) * 2; // S=4*qz
   qw = (m01 - m10) / S;
   qx = (m02 + m20) / S; 
   qy = (m12 + m21) / S; 
   qz = 0.25 * S;
} 

C++ Code (kindly sent to me by Angel)

There is also code from minorlogic at the end of this page. This includes code for both normalised and unnormalised quaternions.

inline void CalculateRotation( Quaternion& q ) const {
  float trace = a[0][0] + a[1][1] + a[2][2] + 1.0f;
  if( trace > M_EPSILON ) {
    float s = 0.5f / sqrtf(trace);
    q.w = 0.25f / s;
    q.x = ( a[2][1] - a[1][2] ) * s;
    q.y = ( a[0][2] - a[2][0] ) * s;
    q.z = ( a[1][0] - a[0][1] ) * s;
  } else {
    if ( a[0][0] > a[1][1] && a[0][0] > a[2][2] ) {
      float s = 2.0f * sqrtf( 1.0f + a[0][0] - a[1][1] - a[2][2]);
      q.w = (a[1][2] - a[2][1] ) / s;
      q.x = 0.25f * s;
      q.y = (a[0][1] + a[1][0] ) / s;
      q.z = (a[0][2] + a[2][0] ) / s;
    } else if (a[1][1] > a[2][2]) {
      float s = 2.0f * sqrtf( 1.0f + a[1][1] - a[0][0] - a[2][2]);
      q.w = (a[0][2] - a[2][0] ) / s;
      q.x = (a[0][1] + a[1][0] ) / s;
      q.y = 0.25f * s;
      q.z = (a[1][2] + a[2][1] ) / s;
    } else {
      float s = 2.0f * sqrtf( 1.0f + a[2][2] - a[0][0] - a[1][1] );
      q.w = (a[0][1] - a[1][0] ) / s;
      q.x = (a[0][2] + a[2][0] ) / s;
      q.y = (a[1][2] + a[2][1] ) / s;
      q.z = 0.25f * s;
    }
  }
}

Alternative Method

Christian has suggested an alternative algorithm and he makes a convincing argument that this is more efficient here.
 
quaternion.w = sqrt( max( 0, 1 + m00 + m11 + m22 ) ) / 2; 
quaternion.x = sqrt( max( 0, 1 + m00 - m11 - m22 ) ) / 2; 
quaternion.y = sqrt( max( 0, 1 - m00 + m11 - m22 ) ) / 2; 
quaternion.z = sqrt( max( 0, 1 - m00 - m11 + m22 ) ) / 2; 
Q.x = _copysign( Q.x, m21 - m12 ) 
Q.y = _copysign( Q.y, m02 - m20 ) 
Q.z = _copysign( Q.z, m10 - m01 ) 

Scaling

Since it is possible for both matrices and quaternions to represent scaling in addition to rotation, then it would be possible to include this in the convertion. In most cases this is not necessary, as we only want to represent rotations, but if you do need to include a scale factor then you might want to try this method suggested by Christian.

Of course, this will mean that the matrix is no longer orthogonal and that the quaternion is no longer normalised.

We first calculate absQ2 which is the cube root of the volume spanned by the matrix axes. 
 
absQ2 = det( matrix )^(1/3) 
 
quaternion.w = sqrt( max( 0, absQ2 + m00 + m11 + m22 ) ) / 2;  
 
...etc 

Rounding Errors

When comparing different methods of doing this conversion I think we should consider sensitivity to rounding errors. In other words, the matrix contains redundant information so, althrough a matrix may initially be orthogonal, if we then do some rotation operations rounding errors may de-orthogonalise the matrix, if this happens which method is most likely to cancel out the errors? 
 
I don't know how best to tackle this? say, for instance, rounding errors introduce an error 'delta e' to one element of the matrix, but we don't know which element has the error. Which method is most likely to cancel out this error? Which method is most likely to produce a normalised quaternion?

Derivation of Equations

Quaternion multiplication and orthogonal matrix multiplication can both be used to represent rotation. If a quaternion is represented by qw + i qx + j qy + k qz , then the equivalent matrix, to represent the same rotation, is:

1 - 2*qy2 - 2*qz2 2*qx*qy - 2*qz*qw 2*qx*qz + 2*qy*qw
2*qx*qy + 2*qz*qw 1 - 2*qx2 - 2*qz2 2*qy*qz - 2*qx*qw
2*qx*qz - 2*qy*qw 2*qy*qz + 2*qx*qw 1 - 2*qx2 - 2*qy2

This assumes that the quaternion is normalised (qw2 + qx2 + qy2 + qz2 =1) and that the matrix is orthogonal.

This page discusses the equivalence of quaternion multiplication and orthogonal matrix multiplication.

So this gives the following equations:

This contains more information than we need, so we need to choose a method which is less sensitive to matrix not being orthogonal.

If the quaternion is normalised: qx2 + qy2 + qz2+ qw2 = 1

so,

qw2 = 1 - qx2 - qy2 - qz2

multiply both sides by 4 gives,

4 * qw2 = 4 - 4*qx2 - 4*qy2 - 4*qz2

4 * qw2 = 1 + 1 - 2*qy2 - 2*qz2 + 1 - 2*qx2 - 2*qz2 + 1 - 2*qx2 - 2*qy2

4 * qw2 = 1 + m00 + m11 + m22

so qw= sqrt (1 + m00 + m11 + m22) /2

the sum of the diagonal elements is known as the trace (Tr), this gives,

qw= sqrt (1 + Tr) /2

this method can only be used if we are taking the square root of a positive number ie,

1 + Tr <= 0

Is this always the case for orthogonal matrices ???

So we have an equation for qw, to get the other values we can use:

m21 - m12 = 2*qy*qz - 2*qx*qw - 2*qy*qz - 2*qx*qw

m21 - m12 = 4 *qx*qw

therefore:

qx = (m21 - m12)/( 4 *qw)

now calculate qy

m02 - m20 = 2*qx*qz + 2*qy*qw - 2*qx*qz + 2*qy*qw

m02 - m20 = 4*qy*qw

therefore:

qy = (m02 - m20)/( 4 *qw)

now calculate qz,

m10 - m01 = 2*qx*qy + 2*qz*qw - 2*qx*qy + 2*qz*qw

m10 - m01 = 4*qz*qw

therefore:

qz = (m10 - m01)/( 4 *qw)

so summarising the results:

Some other results:

1 + m00 - m11 - m22
= 1 + 1 - 2*qy2 - 2*qz2 - 1 + 2*qx2 + 2*qz2 - 1 + 2*qx2 + 2*qy2
= 4*qx2

therefore:

qx =sqrt (1 + m00 - m11 - m22) /2

1 + m11 - m00 - m22
= 1 + 1 - 2*qx2 - 2*qz2 - 1 + 2*qy2 + 2*qz2 - 1 + 2*qx2 + 2*qy2
= 4*qy2

therefore:

qy =sqrt (1 + m11 - m00 - m22) /2

1 + m22 - m00 - m11
= 1 +1 - 2*qx2 - 2*qy2 - 1 + 2*qy2 + 2*qz2 - 1 + 2*qx2 + 2*qz2
= 4*qz2

therefore:

qz =sqrt (1 + m22 - m00 - m11) /2

Issues

This page assumes:

If these conditions are satisfied then the resulting quaternion should be normalised.

Rotation about a point other than origin

Quaternions and 3x3 matrices alone can only represent rotations about the origin. But if we include a 3D vector with the quaternion we can use this to represent the point about which we are rotating. Also if we use a 4x4 matrix then this can hold a translation (as explained here) and therefore can specify a rotation about a point.

The following code generates a 3D vector (representing the centre of rotation) from the 4x4 matrix. The theory is given here.

Cx
Cy
Cz
= 1/det[m] *
(m11 - 1)*m22 - m12*m21 m02*m21 - m01*(m22 - 1) m01*m12 - m02*(m11 - 1)
m12*m20 - m10*(m22 - 1) m00*(m22 - 1) - m02*m20 m02*m10 - (m00 - 1)*m12
m10*m21 - (m11 - 1)*m20 m01*m20 - (m00 - 1)*m21 m00*(m11 - 1) - m01*m10
m03
m13
m23

Example

we take the 90 degree rotation from this: to this:

As shown here the matrix for this rotation is:

[R] =
1 0 0
0 0 -1
0 1 0

So using the above result:

qw= sqrt (1 + m00 + m11 + m22) /2 = sqrt (2)/2 = 1.4142/2 = 0.7071
qx = (m21 - m12)/( 4 *qw) = 2/(4 * 0.7071) = 1/ 1.4142 = 0.7071
qy = (m02 - m20)/( 4 *qw) = 0
qz = (m10 - m01)/( 4 *qw) = 0

This gives the quaternion 0.7071 + i 0.7071

As you can see here, this gives the result that we are looking for.

Angle Calculator and Further examples

I have put a java applet here which allows the values to be entered and the converted values shown along with a graphical representation of the orientation.

Also further examples in 90 degree steps here


metadata block
see also:

 

Correspondence about this page

Book Shop - Further reading.

Where I can, I have put links to Amazon for books that are relevant to the subject, click on the appropriate country flag to get more details of the book or to buy it from them.

cover Mathematics for 3D Game Programming. - Second edition provides new information on illumination, collision detection, polygonal techniques. New chapters cover rendering pipeline, shadows, numerical methods, and curves and surfaces.

Commercial Software Shop

Where I can, I have put links to Amazon for commercial software, not directly related to the software project, but related to the subject being discussed, click on the appropriate country flag to get more details of the software or to buy it from them.

 

cover Dark Basic Professional Edition - It is better to get this professional edition

cover This is a version of basic designed for building games, for example to rotate a cube you might do the following:
make object cube 1,100
for x=1 to 360
rotate object 1,x,x,0
next x

cover Game Programming with Darkbasic - book for above software

Can you help?

Please send me any improvements to here. I would appreciate ideas to make the pages more useful including error correction, ideas for new pages, improvements to wording. It helps if you quote the full URL of the page.

 

progam

I am working on a project which uses these principles, if you would like to help me with this you are welcome to join in, here:

http://sourceforge.net/projects/mjbworld/

This site may have errors. Don't use for critical systems.

Copyright (c) 1998-2008 Martin John Baker - All rights reserved - privacy policy.