Eigen C++ / Matlab quaternion and rotation matrix mismatch - c++

I noticed that there's a difference in Eigen C++ and Matlab when calculating with quaternions.
In Eigen C++, the code
Eigen::Quaterniond q;
q.x() = 0.270598;
q.y() = 0.653281;
q.z() = -0.270598;
q.w() = 0.653281;
Eigen::Matrix3d R = q.normalized().toRotationMatrix();
std::cout << "R=" << std::endl << R << std::endl;
gives the rotation matrix:
R=
-2.22045e-16 0.707107 0.707107
0 0.707107 -0.707107
-1 0 -2.22045e-16
In Matlab (which uses wxyz), however, I get the following result:
q =
0.6533 0.2706 0.6533 -0.2706
>> quat2dcm(q)
ans =
-0.0000 0 -1.0000
0.7071 0.7072 0
0.7072 -0.7071 -0.0000
which is the transpose! Can somebody explain me what is going on? I made sure that the positions of wxyz are correct.
Thank you

With Matlab, you are calculating the direction cosine matrix. It is indeed a rotation matrix like the one you are calculating with Eigen C++, and as such is also unitary (all rows and all columns have a norm of 1 and either form a perpendicular set of vectors).
Now, it so happens that the inverse of a unitary matrix is equal to its conjugate transpose (*), i.e.:
U*U = UU* = I
In other words, what must be happening is that the convention of Matlab is the opposite of that of Eigen C++.
From Wikipedia:
The coordinates of a point P may change due to either a rotation of the coordinate system CS (alias), or a rotation of the point P (alibi).
In most cases the effect of the ambiguity is equivalent to the effect of a rotation matrix inversion (for these orthogonal matrices equivalently matrix transpose).

Related

I am having trouble using Eigen translate() and rotate() and doesn't behave as expected

I am trying to find transformation matrices between different coordinate frames. In order to rotate, we basically multiply the rotation matrices and append the translation vector to obtain the final homogeneous matrix.
Here I have attached a snippet of my code where tf_matrix and output are Eigen::Transform variables.
tf_matrix.setIdentity();
tf_matrix.rotate( output.rotation() );
tf_matrix.translate( output.translation() );
When I look at their outputs, it seems like it is generating the rotation and translation matrix into 4x4 matrices and multiplying it instead of appending the translation vector
Output:
//This is rotation matrix
output.rotation()
1 0 0
0 0.0707372 -0.997495
0 0.997495 0.0707372
//translation vector
output.translation()
0.3
0.3
0.3
//After applying rotate() and translate() tf_matrix.transform.matrix() looks like the below
1 0 0 0.3
0 0.0707372 -0.997495 -0.278027
0 0.997495 0.0707372 0.32047
0 0 0 1
//Printing just the tf_matrix.transform.rotation()
1 0 0
0 0.0707372 -0.997495
0 0.997495 0.0707372
//Printing just the tf_matrix.transform.translation()
0.3
-0.278027
0.32047
//Ideally it should look like the below
1 0 0 0.3
0 0.0707372 -0.997495 0.3
0 0.997495 0.0707372 0.3
0 0 0 1
What did I try
I tried to generate a simple 4x4 identity Eigen::Trnasform and append it to the output matrix after the rotation, but the value 1 of the identity matrix gets added
I also tried, multiply tf_matrix.col(3) += output_matrix.col(3) , but it faces similar issues as above.
I am not sure how to go about rotation because my understanding is that I need to just multiply the 3x3 rotation matrix and append/add the 3x3 translation vector to the final column of this matrix. It seems like Eigen should be able to handle this without me writing extra code. But, this rotate, translate clearly doesn't give the right answers.
Could you please point out what am I missing if any or if there's a better way to go about it.
The order of operations is reversed from what you seem to expect: see here. Suppose you have a coordinate in R3 that you want to translate (matrix Mt) and then rotate (matrix Mr), you might expect to write Vec3 = Vec3 * Mt * Mr. Many game-engines and math libraries (eg Ogre, XNA, CRYENGINE, Unity, I believe) use this order of operations. However, Eigen requires Vec3 = Mr * Mt * Vec3; in Eigen, the coordinate being passed through is a column vector, in game engines, it is a row vector. Correspondingly, the matrices in the two different forms are transposes of one another.
To solve you problem:
tf_matrix.setIdentity();
tf_matrix = output.rotation() * tf_matrix;
tf_matrix = translate * tf_matrix;
or
tf_matrix = translate * output.rotation();
The pretranslate() and premultiply() methods can also be used to do this.

Eigen library, Jacobi SVD

I'm trying to estimate a 3D rotation matrix between two sets of points, and I want to do that by computing the SVD of the covariance matrix, say C, as follows:
U,S,V = svd(C)
R = V * U^T
C in my case is 3x3 . I am using the Eigen's JacobiSVD module for this and I only recently found out that it stores matrices in column-major format. So that has had me confused.
So, when using Eigen, should I do:
V*U.transpose() or V.transpose()*U ?
Additionally, the rotation is accurate upto changing the sign of the column of U corresponding to the smallest singular value,such that determinant of R is positive. Let's say the index of the smallest singular value is minIndex .
So when the determinant is negative, because of the column major confusion, should I do:
U.col(minIndex) *= -1 or U.row(minIndex) *= -1
Thanks!
This has nothing to do with matrices being stored row-major or column major. svd(C) gives you:
U * S.asDiagonal() * V.transpose() == C
so the closest rotation R to C is:
R = U * V.transpose();
If you want to apply R to a point p (stored as column-vector), then you do:
q = R * p;
Now whether you are interested R or its inverse R.transpose()==V.transpose()*U is up to you.
The singular values scale the columns of U, so you should invert the columns to get det(U)=1. Again, nothing to do with storage layout.

DirectX 3D rotate eyePt around lookAt

I'm really stuggling with the maths i need for this maths has never been my strong point once you get into Calculus and Geometry.
So how do i rotate a vector3 around another vector3
D3DXVECTOR3 lookAt = this->cam->getLookAt();
D3DXVECTOR3 eyePt = this->cam->getEyePt();
I need to rotate lookAt around eyePt how do I do this I know I need a Matrix but what I'm supposed to fill it with and how I do the rotation in it I just don't understand.
So if some one could provide code with explanations of the steps used to do it would really help
Another note is that i only want to translate on X,Z axis as i'm rotation around Y axis so here is an image of what im trying to do
Take the unit vector eyePt, which will be the axis of rotations. (I presume it's a unit vector; if not, I can show you how to turn it into a unit vector.) Let's call it E:
Ex
E = Ey
Ez
(This is the vector (Ex, Ey, Ez), but it's hard to do mathematical notation here.)
Now construct three matrices. The identity matrix I:
1 0 0
I = 0 1 0
0 0 1
the tensor product of E and E, which we'll call T:
0 -Ez Ey
T = Ez 0 -Ex
-Ey Ex 0
and the cross-product matrix of E, which we'll call P:
ExEx ExEy ExEz
P = ExEy EyEy EyEz
ExEz EyEz EzEz
Now choose an angle of rotation, theta (in radians). The rotation matrix will be:
R = cos(theta)I + (1-cos(theta))T + sin(theta)P
Now to rotate the vector v (which in this case is lookAt), just multiply R by it:
vafter = Rvbefore

Calculating scale, rotation and translation from Homography matrix

I am trying to calculate scale, rotation and translation between two consecutive frames of a video. So basically I matched keypoints and then used opencv function findHomography() to calculate the homography matrix.
homography = findHomography(feature1 , feature2 , CV_RANSAC); //feature1 and feature2 are matched keypoints
My question is: How can I use this matrix to calculate scale, rotation and translation?.
Can anyone provide me the code or explanation as to how to do it?
if you can use opencv 3.0, this decomposition method is available
http://docs.opencv.org/3.0-beta/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#decomposehomographymat
The right answer is to use homography as it is defined dst = H ⋅ src and explore what it does to small segments around a particular point.
Translation
Given a single point, for translation do
T = dst - (H ⋅ src)
Rotation
Given two points p1 and p2
p1 = H ⋅ p1
p2 = H ⋅ p2
Now just calculate the angle between vectors p1 p2 and p1' p2'.
Scale
You can use the same trick but now just compare the lengths: |p1 p2| and |p1' p2'|.
To be fair, use another segment orthogonal to the first and average the result. You will see that there is no constant scale factor or translation one. They will depend on the src location.
Given Homography matrix H:
|H_00, H_01, H_02|
H = |H_10, H_11, H_12|
|H_20, H_21, H_22|
Assumptions:
H_20 = H_21 = 0 and normalized to H_22 = 1 to obtain 8 DOF.
The translation along x and y axes are directly calculated from H:
tx = H_02
ty = H_12
The 2x2 sub matrix on the top left corner is decomposed to calculate shear, scaling and rotation. An easy and quick decomposition method is explained here.
Note: this method assumes invertible matrix.
Since i had to struggle for a couple of days to create my homography transformation function I'm going to put it here for the benefit of everyone.
Here you can see the main loop where every input position is multiplied by the homography matrix h. Then the result is used to copy the pixel from the original position to the destination position.
for (tempIn[0] = 0; tempIn[0] < stride; tempIn[0]++)
{
for (tempIn[1] = 0; tempIn[1] < rows; tempIn[1]++)
{
double w = h[6] * tempIn[0] + h[7] * tempIn[1] + 1; // very important!
//H_20 = H_21 = 0 and normalized to H_22 = 1 to obtain 8 DOF. <-- this is wrong
tempOut[0] = ((h[0] * tempIn[0]) + (h[1] * tempIn[1]) + h[2])/w;
tempOut[1] =(( h[3] * tempIn[0]) +(h[4] * tempIn[1]) + h[5])/w;
if (tempOut[1] < destSize && tempOut[0] < destSize && tempOut[0] >= 0 && tempOut[1] >= 0)
dest_[destStride * tempOut[1] + tempOut[0]] = src_[stride * tempIn[1] + tempIn[0]];
}
}
After such process an image with some kind of grid will be produced. Some kind of filter is needed to remove the grid. In my code i have used a simple linear filter.
Note: Only the central part of the original image is really required for producing a correct image. Some rows and columns can be safely discarded.
For estimating a tree-dimensional transform and rotation induced by a homography, there exist multiple approaches. One of them provides closed formulas for decomposing the homography, but they are very complex. Also, the solutions are never unique.
Luckily, OpenCV 3 already implements this decomposition (decomposeHomographyMat). Given an homography and a correctly scaled intrinsics matrix, the function provides a set of four possible rotations and translations.
The question seems to be about 2D parameters. Homography matrix captures perspective distortion. If the application does not create much perspective distortion, one can approximate a real world transformation using affine transformation matrix (that uses only scale, rotation, translation and no shearing/flipping). The following link will give an idea about decomposing an affine transformation into different parameters.
https://math.stackexchange.com/questions/612006/decomposing-an-affine-transformation

What does the resultant matrix of Homography denote?

I have 2 frames of shaky video. I applied homography on all the inliers points. Now the resultant matrix that i get for different frames are like this
0.2711 -0.0036 0.853
-0.0002 0.2719 -0.2247
0.0000 -0.0000 0.2704
0.4787 -0.0061 0.5514
0.0007 0.4798 -0.0799
0.0000 -0.0000 0.4797
What are those similar values in the diagonal and how can I retrieve the translation component from this matrix ?
Start with the following observation: a homography matrix is only defined up to scale. This means that if you divide or multiply all the matrix coefficients by the same number, you obtain a matrix that represent the same geometrical transformation. This is because, in order to apply the homography to a point at coordinates (x, y), you multiply its matrix H on the right by the column vector [x, y, 1]' (here I use the apostrophe symbol to denote transposition), and then divide the result H * x = [u, v, w]' by the third component w. Therefore, if instead of H you use a scaled matrix (s * H), you end up with [s*u, s*v, s*w], which represents the same 2D point.
So, to understand what is going on with your matrices, start by dividing both of them by their bottom-right component:
octave:1> a = [
> 0.2711 -0.0036 0.853
> -0.0002 0.2719 -0.2247
> 0.0000 -0.0000 0.2704
> ];
octave:2> b=[
> 0.4787 -0.0061 0.5514
> 0.0007 0.4798 -0.0799
> 0.0000 -0.0000 0.4797];
octave:3> a/a(3,3)
ans =
1.00259 -0.01331 3.15459
-0.00074 1.00555 -0.83099
0.00000 -0.00000 1.00000
octave:4> b/b(3,3)
ans =
0.99792 -0.01272 1.14947
0.00146 1.00021 -0.16656
0.00000 -0.00000 1.00000
Now suppose, for the moment, that the third column elements in both matrices were [0, 0, 1]'. Then the effect of applying it to any point (x, y) would be to move it by approx 1/100 units (say, pixels). Basically, not changing it by much.
Plugging back the actual values for the third column shows that both matrices are, essentially, translating the whole images by constant amounts.
So, in conclusion, having equal values on the diagonals, and very small values at indices (1,2) and (2,1), means that these homographies are both (essentially) pure translations.
Various transformations involve all elementary operations such as addition, multiplication, division, and addition of a constant. Only the first two can be modeled by regular matrix multiplication. Note that addition of a constant and, in case of a Homography, division is impossible to represent with matrix multiplication in 2D. Adding a third coordinate (that is converting points to homogeneous representation) solves this problem. For example, if you want to add constant 5 to x you can do this like this
1 0 5 x x+5
0 1 0 * y = y
1
Note that matrix is 2x3, not 2x2 and coordinates have three numbers though they represent 2D points. Also, the last transition is converting back from homogeneous to Euclidian representation. Thus two results are achieved: all operations (multiplication, division, addition of variables and additions of constants) can be represented by matrix multiplication; second, we can chain multiple operations (via multiplying their matrices) and still have only a single matrix as the result (of matrix multiplication).
Ok, now let’s explain Homography. Homography is better to consider in the context of the whole family of transformation moving from simple ones to complex ones. In other words, it is easier to understand the meaning of Homography coefficients by comparing them to the meaning of coefficients of simpler Euclidean, Similarity and Affine transforms. The Euclidwan transformation is the simplest and represents a rigid rotation and translation in space (note that matrix is 2x3). For 2D case,
cos(a) -sin(a) Tx
sin(a) cos(a) Ty
Similarity adds scaling to the rotation coefficients. So now the matrix looks like this:
Scl*cos(a) -scl*sin(a) Tx
Scl*sin(a) scl*cos(a) Ty
Affiliate transformation adds shearing so the rotation coefficients become unrestricted:
a11 a12 Tx
a21 a22 Ty
Homography adds another row that divides the output x and y (see how we explained the division during the transition form homogeneous to Euclidean coordinates above) and thus introduces projectivity or non uniform scaling that is a function of point coordinates. This is better understood by looking at the transition to Euclidean coordinates.
a11 a12 Tx x a11*x+a12*y+Tx (a11*x+a12*y+Tx)/(a32*x+a32*y+a33)
a21 a22 Ty * y = a21*x+a22*y+Ty -> (a21*x+a22*y+Ty)/(a32*x+a32*y+a33)
a31 a32 a33 1 a32*x+a32*y+a33
Thus homography has an extra row compared to other transformations such as affine or similarity. This extra row allows to scale objects depending on their coordinates which is how projectivity is formed.
Finally, speaking of your numbers:
0.4787 -0.0061 0.5514
0.0007 0.4798 -0.0799
0.0000 -0.0000 0.4797
This is not homography!. Just look at the last row and you will see that the first two coefficients are 0 thus there is no projectivity. Since a11=a22 this is not even an Affine transformation. This is rather a similarity transform. The translation is
Tx=0.5514/0.4797 and Ty=-0.0799/0.4797