Convert Matrix to Quaternion

Started by
12 comments, last by Jan K 15 years, 8 months ago
Hi there I have a problem with my Matrix-to-Quaternion code. I took it from the Matrix&Quaternion FAQ (http://web.archive.org/web/20041029003853/http:/www.j3d.org/matrix_faq/matrfaq_latest.html#Q55), but it seems not to work. All my other Matrix and Quaternion code is tested well, so i know that is not the problem. If i use a Rotation-Matrix to rotate a vector the results are fine. If i now use my conversion-code to create a Quaternion, and then use that Quaternion to rotate a vector, the results are usually incorrect. I have compared my code to the code given on that page a thousand times and i don't see any difference (except for a few optimizations, but i also tried it with IDENTICAL code), the results are incorrect. Could someone please take a look and tell me, what i might be doing wrong?
[SOURCE]


Quaterniong Matrix::CalculateQuaternion ()
{
	//! The elements are stored in row-major order.
	/*! Layout is as follows:
		00 01 02 03
		10 11 12 13
		20 21 22 23
		30 31 32 33
	*/

	Matrix m = *this;
	Quaternion q;
	
	float trace = m.m_fElement[0][0] + m.m_fElement[1][1] + m.m_fElement[2][2] + 1.0f;  
	
	if (trace > 0.00001f) 
	{
		float s = 0.5f / sqrt (trace);
	
		q.w = 0.25f / s;
		q.v.x = ( m.m_fElement[2][1] - m.m_fElement[1][2] ) * s;
		q.v.y = ( m.m_fElement[0][2] - m.m_fElement[2][0] ) * s;
		q.v.z = ( m.m_fElement[1][0] - m.m_fElement[0][1] ) * s;
	} 
	else 
	{
		if ( m.m_fElement[0][0] > m.m_fElement[1][1] && m.m_fElement[0][0] > m.m_fElement[2][2] ) 
		{
			float s = 2.0f * sqrt( 1.0f + m.m_fElement[0][0] - m.m_fElement[1][1] - m.m_fElement[2][2]);
	
			q.v.x = 0.25f * s;
			q.v.y = (m.m_fElement[0][1] + m.m_fElement[1][0] ) / s;
			q.v.z = (m.m_fElement[0][2] + m.m_fElement[2][0] ) / s;
			q.w = (m.m_fElement[1][2] - m.m_fElement[2][1] ) / s;    
		} 
		else 
		if (m.m_fElement[1][1] > m.m_fElement[2][2]) 
		{
			float s = 2.0f * sqrt( 1.0f + m.m_fElement[1][1] - m.m_fElement[0][0] - m.m_fElement[2][2]);
	
			q.v.x = (m.m_fElement[0][1] + m.m_fElement[1][0] ) / s;
			q.v.y = 0.25f * s;
			q.v.z = (m.m_fElement[1][2] + m.m_fElement[2][1] ) / s;
			q.w = (m.m_fElement[0][2] - m.m_fElement[2][0] ) / s;
		}
		else 
		{
			float s = 2.0f * sqrt( 1.0f + m.m_fElement[2][2] - m.m_fElement[0][0] - m.m_fElement[1][1]);
	
			q.v.x = (m.m_fElement[0][2] + m.m_fElement[2][0] ) / s;
			q.v.y = (m.m_fElement[1][2] + m.m_fElement[2][1] ) / s;
			q.v.z = 0.25f * s;
			q.w = (m.m_fElement[0][1] - m.m_fElement[1][0] ) / s;
		}
	}
	
	return (q);
}


[/SOURCE]
Thanks. Jan. [Edited by - Jan K on July 28, 2008 8:04:52 AM]
Advertisement
The source tags on this forum are actually [source][/source] rather than [CODE][/CODE]. You can fix the tags by using the 'edit' button in the upper-right of your post.

When you rotate the vector with the extracted quaternion, how are the results incorrect? I ask because if the axis and amount of rotation are correct but the rotation is in the 'wrong' direction, it may be that the function assumes the use of row vectors and your math library uses column vectors (or vice versa), or that you're applying the quaternion rotation incorrectly.

If you need more help, perhaps you could post the test code (where you extract the quaternion and apply the rotation), and also specify whether your library uses row or column vectors.
Well, my "test-code" is a bit more complicated (a spline-function), but what it comes down to is this:

Matrix m;
... set m to some rotation-matrix...
Quaternion q = m.CalculateQuaternion ();

Vector vm = m * v;
Vector vq = q * v;

After this vm is how it should be, but vq is incorrect. As far as i see it, the rotation is completely wrong, not simply in the wrong direction, or such.

As stated in my code, my Matrix-class is row-major. I also tried calculating the quaternion from the transposed matrix, but the results are still wrong.

I can create Quaternions in several different ways (e.g. from an axis/angle pair), and then everything works fine, so i know, that my quaternion/vector multiplication works as expected.

What puzzles me a bit, is that when i print out "trace" in the code, it is ALWAYS greater than 0, so always the first if is executed (at least i have not found yet a matrix that did otherwise). I don't know whether that is the usual expected behavior.

Does anyone use the same code (from that page)? Or does anyone have different code to convert a matrix into a quaternion? I don't need to figure out, what's wrong, i really only need it to work. So any help is appreciated.

Jan.
Quote:As stated in my code, my Matrix-class is row-major.
I didn't ask about the matrix storage (as you note, that is specified in your code), but rather whether the matrix is intended for use with row or column vectors. Your 'm * v' example uses column vectors, so you'll want to make sure that the example uses column vectors as well (I'm guessing it does, but I don't know for sure).

Another thing you might try is:

1. Create a matrix from an axis-angle pair
2. Create a quaternion (q1) from the same axis-angle pair
3. Extract a quaternion (q2) from the matrix created in step 1
4. Compare q1 and q2 and see if they're equal (to within a tolerance, of course)
Quote:What puzzles me a bit, is that when i print out "trace" in the code, it is ALWAYS greater than 0, so always the first if is executed (at least i have not found yet a matrix that did otherwise). I don't know whether that is the usual expected behavior.
IIRC, the trace will only be near zero when the rotation is near 180 degrees, so it's not surprising that you're not encountering that case often.
Quote:Or does anyone have different code to convert a matrix into a quaternion?
If you're still stuck on this later today, I can post some matrix-to-quaternion code that you can use as a reference.
Sorry, i didn't understand that correct with the vectors. My vectors are column-vectors, therefore i also use the notation to multiply matrix*vector and not the other way round.

I tried your advice to compare quaternions created from matrices and from axis/angle pairs and got very interesting results:

I used random axises at first and, surprise, all the quaternions created directly from those and quaternions created from a matrix, were absolutely identical.

Now that is not the result, that i see in my application, so i dug a bit deeper.

When i create a rotation matrix around one of the main-axes, ie. around the X, Y or Z axis, my code to convert that into a quaternion fails badly. I don't know why, yet, but i assume it's some kind of instability that shows up, when the rotation-axis is close to one of the main-axis'es (damn, what's the plural of axis? and what's the genitive of that? and i thought German would be complicated...)

Well, i'll try to find out more about it.

Jan.
I'm using that matrix to quaternion code and it's working properly. It was broken at first, but I remember finding some little problem that fixed it. I don't remember if that problem was with the original code or my copying of it though. I'm currently at work and don't have it with me, but I'll give it a look when I get home and let you know.
After searching for a while, i have found several sources:

http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q55

http://www.gamasutra.com/features/19980703/quaternions_01.htm

http://cache-www.intel.com/cd/00/00/29/37/293748_293748.pdf

All are pretty close to each other, but still sometimes slightly different.

But the problem is, that no matter what you do, when there are zeros in you matrix, for example in a rotation-matrix around the X-axis, the formula always returns a quaternion with a bunch of zeroes in there, although a quaternion directly created from that axis and an angle, is different.

So, as i see it now, the formula is wrong by design. I just don't know, what you need to do, to get it right. And i don't understand, why it is used everywhere, i seem to overlook something.

And insights?
Jan.
Hmm, the only difference I can find from mine is that the w in 2 of your cases is negative (first and last of the diagonal cases). But if I remember right, almost all rotations fall into the first case, so I'm not sure why you'd be having major problems.

Here's my code (tested and working), for reference. Matrices are row-major like yours (layed out in memory as m00,m01,m02,m10,m11,m12,m20,m21,m22). Quaternions are ordered x,y,z,w.

Quaternion QuaternionFromMatrix33(const Matrix33 *src){	const float trace = 1.0f + src->m00 + src->m11 + src->m22;	if (trace > 0.00001f)	{		const float s = sqrt(trace) * 2;		return Quaternion(			(src->m21 - src->m12) / s,			(src->m02 - src->m20) / s,			(src->m10 - src->m01) / s,			s / 4);	}	else if (src->m00 > src->m11 && src->m00 > src->m22)	{		const float s = sqrt(1.0f + src->m00 - src->m11 - src->m22) * 2;		return Quaternion(			s / 4,			(src->m10 + src->m01) / s,			(src->m02 + src->m20) / s,			(src->m21 - src->m12) / s);	}	else if (src->m11 > src->m22)	{		const float s = sqrt(1.0f + src->m11 - src->m00 - src->m22) * 2;		return Quaternion(			(src->m10 + src->m01) / s,			s / 4,			(src->m21 + src->m12) / s,			(src->m02 - src->m20) / s);	}	else	{		const float s = sqrt(1.0f + src->m22 - src->m00 - src->m11) * 2;		return Quaternion(			(src->m02 + src->m20) / s,			(src->m21 + src->m12) / s,			s / 4,			(src->m10 - src->m01) / s);	}}


EDIT: Another thing try: Convert from matrix to quaternion, and then back to matrix. If it doesn't come back roughly the same, post a few before-and-after matrices. Might help track down exactly what kind of badness is happening to them.

[Edited by - DekuTree64 on July 29, 2008 1:30:01 AM]
Great, thank you!
I have found a mistake, that i made during testing, it is working much better now, though not completely error-free, yet. I'll try your code or at least use it as a comparison. I hope to get everything working soon, then i'll post, what was wrong.

Thanks,
Jan.
I don't get it. When i create a rotation-matrix (around any axis) and a rotation-quaternion, both can be converted into each other, without problems.
That suggests, that everything is working fine.

However, in the code, that uses the conversion, i use a look-at matrix, which does work fine, but i want it as a quaternion. Now, when i convert THAT matrix into a quaternion, the result is wrong. Here is an example:

LookAt Matrix:
0.97 | -0.08 | 0.21 | 0.00
-0.00 | 0.93 | 0.37 | 0.00
0.22 | 0.37 | -0.90 | 0.00
-0.00 | -0.00 | -0.00 | 1.00

Quaternion calculated from that matrix (x,y,z,w):
-0.00 | -0.01 | 0.04 | 1.00

That quaternion converted back into a matrix:
1.00 | -0.08 | -0.02 | 0.00
0.08 | 1.00 | 0.01 | 0.00
0.02 | -0.01 | 1.00 | 0.00
0.00 | 0.00 | 0.00 | 1.00

THAT matrix again converted into a Quaternion:
-0.00 | -0.01 | 0.04 | 1.00


As you can see, the quaternion does not convert back into the same matrix. However, that matrix, that it is converted to, DOES convert back into the SAME quaternion!

I also tested rotating a vector with all 4 variations, only the very first matrix rotates it correctly, the two (equal) quaternions and the matrix derived from the quaternion rotate the vector equally, but wrong. So it looks, as if the very first step, the conversion of the first matrix into a quaternion, is wrong. It yields a wrong quaternion. But it seems this only happens with certain matrices. Only "look-at" matrices yield such results, all other rotation-matrices seem to be convertible forwards and backwards correctly.

I am pretty much out of ideas, why this happens.

Jan.


EDIT: Note, that the quaternions given above are all normalized.

[Edited by - Jan K on July 29, 2008 5:35:00 AM]

This topic is closed to new replies.

Advertisement