logo back up home forward   further reading more topics »

Maths - Conversion Matrix to Axis Angle

Prerequisites

Definition of terms:

Equations

angle = acos ( ( m00 + m11 + m22 - 1)/2)
x = (m21 - m12)/sqrt((m21 - m12)2+(m02 - m20)2+(m10 - m01)2)
y = (m02 - m20)/sqrt((m21 - m12)2+(m02 - m20)2+(m10 - m01)2)
z = (m10 - m01)/sqrt((m21 - m12)2+(m02 - m20)2+(m10 - m01)2)

There are two singularities at angle = 0° and angle = 180°, in these cases the above formula may not work (as pointed out by David) so we have to test for these cases separately. At 0° the axis is arbitrary (any axis will produce the same result), at 180° the axis is still relevant so we have to calculate it.

Derivation of Equations

I can think of two methods to derive the equations:

I have used both of these and so that I don't interrupt the flow I have put them on this page.

Singularities

So how can we find this case? When we look at the formula for AxisAngle to Matrix the asymmetrical component about the leading diagonal is due to the sin(angle) component.

So when sin(0)=sin(180) = 0
Then the matrix [m]t = [m]

So if we test for symmetry:
Abs(m01-m10)<0.001 and Abs(m02-m20)<0.001 and Abs(m12-m21)<0.001
Then
Angle = 0 or 180°

The angle = 0 case should be easy to find because the matrix will be:
1 0 0
0 1 0
0 0 1

So we can test for this case with:

Abs(m01+m10)<0.001 and Abs(m02+m20)<0.001 and Abs(m12+m21)<0.001

At 180° we need to calculate the axis, to do this we go back to the formula for AxisAngle to Matrix
c = cos(180) = -1
s = sin (180) = 0
t = 1-c = 2

so the matrix is:

2*x*x-1 2*x*y 2*x*z
2*x*y 2*y*y -1 2*y*z
2*x*z 2*y*z 2*z*z*-1

so using the leading diagonal terms,
x = +or- sqrt((m00+1)/2)
y = +or- sqrt((m11+1)/2)
z = +or- sqrt((m22+1)/2)

So we need to use the non-leading-diagonal terms to work out whether they are positive or negative, the permutations of positive, negative and zero values of x,y and z are:

x y z 2*x*y 2*x*z 2*y*z Notes
+ - + - + - + + + Use positive
     

+

+

- Not possible
     

+

-

+ Not possible
     

-

+

+ Not possible
+ - + - - + + - - Use -z
+ - - + + - - + - Use -y
- + + - + - - - + Use -x
      - - - Not possible
    0 +- 1 0 0  
  0   0 +- 1 0  
0     0 0 +- 1  
      0 0 0 Rotation around an axis. Use positive 1
0 + - + - 0 0 + Use positive
+ - 0 + - 0 + 0 Use positive
+ - + - 0 + 0 0 Use positive
0 + - - + 0 0 - Use -z (can be -y or -z)
+ - 0 - + 0 - 0 Use -z (can be -x or -z)
- + + - 0 - 0 0 Use -x (can be -x or -y)

key:

where two sets of values give the same result they are shown on the same line for instance:

+ - + - - +

means x and y have same sign and z has opposite sign.

Note we can only distinguish 4 non-zero combinations from x*y , x*z and y*z, for instance +x,+y,+z gives the same matrix as -x,-y,-z this is because rotation by +180 degrees produces exactly the same rotation as -180° and we don't know which way it went round unless we happen to know any intermediate values.

Where we have to be careful is when x, y or z are close to zero and so a small error might flip between + and -. If x*y , x*z and y*z are all zero then we are rotating purely about one axis, the sign in this case is not important. If one of them is zero then we may have to invert one of the others as shown in the table above.

So the algorithm emerging is as follows:

 if (matrix symmetrical) {
   if (identity matrix) return zero angle
   // this is 180 deg rotation
   calculate axis with positive values
   if (x is zero and y,z are not zero) invert y or z
   else if (y is zero and z is not zero) invert x or z
   else if (z is zero) invert x or y
   else if (x*y , x*z and y*z are all positive) return positive values
   else if (y*z is positive) invert x
   else if (x*z is positive) invert y
   else if (x*y is positive) invert z
}

Java Code

/**
This requires a pure rotation matrix 'm' as input.
*/
public axisAngle toAxisAngle(matrix m) {
  double angle,x,y,z; // variables for result
	double epsilon = 0.01; // margin to allow for rounding errors
	// optional check that input is pure rotation, 'isRotationMatrix' is defined here:
	// http://www.euclideanspace.com/maths/algebra/matrix/orthogonal/rotation/
	assert isRotationMatrix(m) : "not valid rotation matrix" ;// check during debugging only
	if ((Math.abs(m[0][1]-m[1][0])< epsilon)
	  && (Math.abs(m[0][2]-m[2][0])< epsilon)
	  && (Math.abs(m[1][2]-m[2][1])< epsilon)) {
		// singularity found
		// first check for identity matrix which must have +1 for all terms in leading diagonal
		// and zero in other terms
		if ((Math.abs(m[0][1]+m[1][0]) < 0.1)
		  && (Math.abs(m[0][2]+m[2][0]) < 0.1)
		  && (Math.abs(m[1][2]+m[2][1]) < 0.1)
		  && (Math.abs(m[0][0]+m[1][1]+m[2][2])-3) < 0.1) {
			// this singularity is identity matrix so angle = 0
			// note epsilon is greater in this case since we only have to distinguish between 0 and 180 degrees
			return new axisAngle(0,1,0,0); // zero angle, arbitrary axis
		}
		// otherwise this singularity is angle = 180
		angle = Math.PI;
		x = (m[0][0]+1)/2;
		if (x > 0) { // can only take square root of positive number, always true for orthogonal matrix
			x = Math.sqrt(x);
		} else {
			x = 0; // in case matrix has become de-orthogonalised
		}
		y = (m[1][1]+1)/2;
		if (y > 0) { // can only take square root of positive number, always true for orthogonal matrix
			y = Math.sqrt(y);
		} else {
			y = 0; // in case matrix has become de-orthogonalised
		}
		z = (m[2][2]+1)/2;
		if (z > 0) { // can only take square root of positive number, always true for orthogonal matrix
			z = Math.sqrt(z);
		} else {
			z = 0; // in case matrix has become de-orthogonalised
		}
		boolean xZero = (Math.abs(x)<epsilon);
		boolean yZero = (Math.abs(y)<epsilon);
		boolean zZero = (Math.abs(z)<epsilon);
		boolean xyPositive = (m[0][1] > 0);
		boolean xzPositive = (m[0][2] > 0);
		boolean yzPositive = (m[1][2] > 0);
		if (xZero && !yZero && !zZero) { // implements  last 6 rows of above table
		  if (!yzPositive) y = -y;
		} else if (yZero && !zZero) {
		  if (!xzPositive) z = -z;
		} else if (zZero) {
		  if (!xyPositive) x = -x;
		}
		return new axisAngle(angle,x,y,z);
	}
	double s = Math.sqrt((m[2][1] - m[1][2])*(m[2][1] - m[1][2])+(m[0][2] - m[2][0])*(m[0][2] - m[2][0])+(m[1][0] - m[0][1])*(m[1][0] - m[0][1])); // used to normalise
	if (Math.abs(s) < 0.001) s=1; // prevent divide by zero, should not happen if matrix is orthogonal and should be
		// caught by singularity test above, but I've left it in just in case
	angle = Math.acos(( m[0][0] + m[1][1] + m[2][2] - 1)/2);
	x = (m[2][1] - m[1][2])/s;
	y = (m[0][2] - m[2][0])/s;
	z = (m[1][0] - m[0][1])/s;
   return new axisAngle(angle,x,y,z);
}

thanks to Vladimir Smutny for the correction and Andreas Keil here.

Issues

Examples

In order to check if the axis is reversed when we have a minus angle here are 2 examples, example 1 represents + 30° about the z axis and example 2 represents - 30 degrees about the z axis, so these examples should produce either, equal and opposite angles, or equal and opposite axes.

Example 1

In this example heading is + 30 degrees about the z axis.

matrix =
cos(heading) = 0.866 sin(heading) = 0.5 0
-sin(heading) = -0.5 cos(heading) = 0.866 0
0 0 1

angle = acos ( ( m00 + m11 + m22 - 1)/2)

angle = acos ( ( 0.866 + 0.866 + 1 - 1)/2)

angle = acos ( 0.866 )

angle = 30 degrees

x = (m21 - m12) = 0 - 0 =0
y = (m02 - m20) = 0 - 0 =0
z = (m10 - m01) = -0.5 - 0.5 = -1

example 2

now the same example with heading = -30 degrees about the z axis.

matrix =
cos(heading) = 0.866 sin(heading) = -0.5 0
-sin(heading) = 0.5 cos(heading) = 0.866 0
0 0 1

angle = 30 degrees

x = (m21 - m12) = 0 - 0 =0
y = (m02 - m20) = 0 - 0 =0
z = (m10 - m01) = 0.5 + 0.5 = 1

So reversing the angle reverses the axis instead of the angle. However these are equivalent (we can reverse both the angle and axis and still represent the same rotation). So this is equivalent to -30° about z = -1 axis.

This gives me some confidence that whatever quadrant the rotation is in the above formula will convert this correctly to the correct axis angle.

example 3

matrix =
0.96608673169969 -0.25800404198456 -0.01050433974302
0.25673182392846 0.95537412871306 0.14611312318926
-0.02766220194012 -0.14385474794174 0.98921211783846

angle = acos ( ( 0.966 + 0.955 + 0.989 - 1)/2)

angle = acos (0.955336489125605)

angle = 17.3 degrees

x = (m21 - m12) = -0.14385474794174 - 0.14611312318926 = -0.289967871131
y = (m02 - m20) = -0.01050433974302 + 0.02766220194012 = 0.0171578621971
z = (m10 - m01) = 0.25673182392846 + 0.25800404198456 = 0.51473586591302

This axis can then be normalised if required.

Here I am not sure that the matrix represents a pure rotation? The matrix is not skew symmetric?

Specially m02 and m20 as they are both small and have the same sign. This leads me to the question about how sensitive the conversion is to input errors? Perhaps a small error here might put the axis into the wrong quadrant. And this error would be increased when the axis is normalised. If the original matrix may not be orthogonal then, perhaps we should use a different conversion method, depending on the ratio of values in the leading diagonal as we did here: matrix to quaternion page.

Another 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:

angle = acos ( ( m00 + m11 + m22 - 1)/2) = acos (0) = 90 degrees
x = (m21 - m12)/sqrt((m21 - m12)2+(m02 - m20)2+(m10 - m01)2) = 2 / sqrt (4) = 1
y = (m02 - m20)/sqrt((m21 - m12)2+(m02 - m20)2+(m10 - m01)2) = 0
z = (m10 - m01)/sqrt((m21 - m12)2+(m02 - m20)2+(m10 - m01)2) = 0

angle = 90°
axis = 1,0,0

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° 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 Introductory Techniques for 3-D Computer Vision by Emanuele Trucco, Alessandro Verri

 

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.