1

I am using opencv::solvePnP to return a camera pose. I run PnP, and it returns the rvec and tvec values.(rotation vector and position).

I then run this function to convert the values to the camera pose:

void GetCameraPoseEigen(cv::Vec3d tvecV, cv::Vec3d rvecV, Eigen::Vector3d &Translate, Eigen::Quaterniond &quats)
{
    Mat R;
    Mat tvec, rvec;

    tvec = DoubleMatFromVec3b(tvecV);
    rvec = DoubleMatFromVec3b(rvecV);

    cv::Rodrigues(rvec, R); // R is 3x3
    R = R.t();                 // rotation of inverse
    tvec = -R*tvec;           // translation of inverse

    Eigen::Matrix3d mat;
    cv2eigen(R, mat);

    Eigen::Quaterniond EigenQuat(mat);

    quats = EigenQuat;


    double x_t = tvec.at<double>(0, 0);
    double y_t = tvec.at<double>(1, 0);
    double z_t = tvec.at<double>(2, 0);

    Translate.x() = x_t * 10;
    Translate.y() = y_t * 10;
    Translate.z() = z_t * 10;   

}

This works, yet at some rotation angles, the converted rotation values flip randomly between positive and negative values. Yet, the source rvecV value does not. I assume this means I am going wrong with my conversion. How can i get a stable Quaternion from the PnP returned cv::Vec3d?

EDIT: This seems to be Quaternion flipping, as mentioned here:

Quaternion is flipping sign for very similar rotations?

Based on that, i have tried adding:

if(quat.w() < 0)
 {
 quat = quat.Inverse();
 } 

But I see the same flipping.

anti
  • 3,011
  • 7
  • 36
  • 86
  • You should actually have read the other answer to the question you found: https://stackoverflow.com/a/42516812 – chtz Mar 17 '18 at 18:38
  • So basically, if w is negative, negate x, y and z? Is that right? – anti Mar 17 '18 at 18:40

2 Answers2

2

Both quat and -quat represent the same rotation. You can check that by taking a unit quaternion, converting it to a rotation matrix, then doing

quat.coeffs() = -quat.coeffs();

and converting that to a rotation matrix as well. If for some reason you always want a positive w value, negate all coefficients if w is negative.

chtz
  • 17,329
  • 4
  • 26
  • 56
  • Thank you. I am assigning the data to a 3d model, and the flips are apparent as huge rotation jumps. – anti Mar 17 '18 at 18:39
  • How are you "assigning the data to a 3d model"? I assume that is where your actual error is. – chtz Mar 17 '18 at 18:41
  • Inside unreal engine, I am confident that side is ok. I have also tried in opengl. I see the same flip in both. – anti Mar 17 '18 at 18:46
  • Well, I am confident that the sign of a quaternion does not matter when used to represent a 3d rotation ... – chtz Mar 17 '18 at 20:09
  • I will double check my application, but it is the same approach to apply rotation I have used many many times before. Perhaps I am hitting a different issue than the flipping. Does my conversion above look correct? – anti Mar 17 '18 at 20:13
  • I don't see anything wrong with that (except for some unecessary intermediate variables -- which won't break anything, though) – chtz Mar 17 '18 at 20:17
1

The sign should not matter...

... rotation-wise, as long as all four fields of the 4D quaternion are getting flipped. There's more to it explained here: Quaternion to EulerXYZ, how to differentiate the negative and positive quaternion

Think of it this way: Angle/axis both flipped mean the same thing and mind the clockwise to counterclockwise transition much like in a mirror image.

There may be convention to keep the quat.w() or quat[0] component positive and change other components to opposite accordingly. Assume w = cos(angle/2) then setting w > 0 just means: I want angle to be within the (-pi, pi) range. So that the -270 degrees rotation becomes +90 degrees rotation.

Doing the quat.Inverse() is probably not what you want, because this creates a rotation in the opposite direction. That is -quat != quat.Inverse().

Also: check that both systems have the same handedness (chirality)! Test if your rotation matrix determinant is +1 or -1.

(sry for the image link, I don't have enough reputation to embed them).

  • Hi, I am still having problems with this. I am doing the following: `if (EigenQuat.w() < 0) { EigenQuat.x() = EigenQuat.x() * -1; EigenQuat.y() = EigenQuat.y() * -1; EigenQuat.z() = EigenQuat.z() * -1; EigenQuat.w() = EigenQuat.w() * -1; }` and it has helped, but I still see the flip in my 3d model at a certain angle. Is this the right approach? – anti Mar 20 '18 at 18:37
  • ..I have also tried just flipping xyz when w is negative, with the same effect. – anti Mar 20 '18 at 18:38
  • Can you provide more info on the actual data / screenshots / output values? See also: https://github.com/opencv/opencv/issues/8813 – Jakub Krzesłowski Mar 20 '18 at 20:36
  • I am logging the data, and see something strange. With the code as in the original question above, the flip looks like this: `rvec: [-1.9554, 0.0461893, 0.762008] quat: 0.807827 -0.019082 -0.314805 -0.497945 rvec: [-1.94081, 0.0626131, 0.774592] quat: -0.803091 0.0259088 0.32052 0.501638` Where you can clearly see the quat flip, while the rvec values stay the same. When I (negate xyz if w <0), I get this: `rvec: [2.87162, 0.466238, -1.13857] quat: 0.919152 0.149234 -0.364435 0.00875124 rvec: [-2.84968, -0.447527, 1.14067] quat: -0.918494 -0.144244 0.367654 -0.0198193` RVEC now flips – anti Mar 20 '18 at 22:00
  • ...In 3d space, on my model, the flip looks like a very fast rotate, 180 degrees, and then back. – anti Mar 20 '18 at 22:00
  • I'm out of ideas. You can perform comparison between rotation matrices calculated from `rvec` and `quat` as described here: https://stackoverflow.com/a/12977143/9133626 and check the `cv::determinant(R)`. If matrices are consistent then it's either the inaccuracy of the rotation vector or you're actually switching between different `solvePnP` solutions. – Jakub Krzesłowski Mar 21 '18 at 02:07