0
votes

Situation: I'm trying to estimate head pose. I have calibrated my camera and got the camera matrix. I have a 3D model that correspond to the image points. I find the pose and get the rotation and translation vectors:

_, rvec, tvec = cv2.solvePnP(model_points, image_points, camera_matrix, dist_coeffs, flags=...)

I then obtain the rotation matrix from the rotation vector:

rotation_matrix, _ = cv2.Rodrigues(rvec)

Now, I decided to look at Euler angles. I create projection matrix as follows:

projection_matrix = camera_matrix.dot(np.hstack((rotation_matrix, tvec)))

So my projection matrix is now 3x4. I then get the Euler angles:

angles = cv2.decomposeProjectionMatrix(projection_matrix)[-1]

Problem: I found an alternative way to get the Euler angles in this post (Python Opencv SolvePnP yields wrong translation vector), which according to the author should give the same result as my angles (as it did for them):

 y_rot = math.asin(rotation_matrix[2][0])
 x_rot = math.acos(rotation_matrix[2][2]/math.cos(y_rot))
 z_rot = math.acos(rotation_matrix[0][0]/math.cos(y_rot))
 y_rot_angle = y_rot *(180/3.1415)
 x_rot_angle = x_rot *(180/3.1415)
 z_rot_angle = z_rot *(180/3.1415)

What my angles from decompose projection matrix gives:

[164.17979619 19.45087415 1.95279565]

What this other solution gives:

[164.18463841290048, -19.451447820154847, 1.9528532437807946]

The scalar values are the same, but the y angle rotation has a different direction. I was wondering - maybe I have misunderstood something?

1
I don't know anything on the matter, but the documentation says: "Note, there is always more than one sequence of rotations about the three principle axes that results in the same orientation of an object". Could that justify it? Does y consistently have a flipped sign?Jeppe
@Jeppe that's definitely the case and yeah, y constantly has a flipped sign. Btw this is my guess too, but I'm not sure if it's actually a right one. I haven't worked with Euler angles too much and I'm not sure if different sequences could impact the result in this way.karolyzz

1 Answers

3
votes

I don't know exactly which permutation is your implementation to get eulerangles from matrix, but it differs from the one decomposeProjectionMatrix uses. this is the reason for the different angles.

As said before, euler angles depend always on the order. There are 12 different permutations. See Euler Angles for more information.

decomposeProjectionMatrix from opencv uses "XYZ" order for its Euler Angle representation.

there are different rotation libs on pypi. I use for this example transforms3d. I'm setting the input parameters by hand and the rotation matrix by euler2mat function of the transforms3d package. I create a Rotation matrix with the XYZ-convention e.g. roll angle (X): 90°, pitch (Y): 45° , yaw(X): 180°

from transforms3d import euler
from math import pi,radians
rotation_matrix = euler.euler2mat(radians(90), radians(45), 
    radians(180), 'sxyz')
tvec = np.zeros(3).reshape(1,3).T
camera_matrix = np.array([[1500,0,320],[0,1500,256],[0,0,1]])
projection_matrix = camera_matrix.dot(np.hstack((rotation_matrix, tvec)))
rot = cv2.decomposeProjectionMatrix(projection_matrix)

rotation_matrix (input) and rot[1] (output) are the same (but only with the correct euler-order "sxyz"):

array([[-7.07106781e-01, -7.07106781e-01,  7.91668771e-17],
   [ 8.65956056e-17,  2.53632657e-17,  1.00000000e+00],
   [-7.07106781e-01,  7.07106781e-01,  4.32978028e-17]])

rot[-1] is:

[[90.         ]
 [ 45.        ]
 [ 180.        ]]

there are some more pitfalls with 3d rotations. E.g.