2D rotations in Eigen (C++).

Eigen, the matrix library in C++, is so cool. Having written some of these functions during the 00s in my own code, agonizing about whether I got the subscripts right, and now to have this – it feels like magic. Here’s an example.

So I have a homography, which is a 3x3 matrix that happens to be in OpenCV’s Mat format. So that this gets written, I’ll use what I am working on now. I want to extract the rotation matrix from this homography, and grab the rotation angle. For this I can use Eigen’s Rotation2D class.

You’ll want to include these includes, possibly using namespace Eigen if you like doing so. I am putting the namespace in front of everything in the code example so you know where everything comes from, though it reads a little clunky.

#include <Eigen/Dense>
#include <Eigen/Eigenvalues>
#include <Eigen/Geometry>


So I have

cv::Mat H = findHomography(... , ...);


and know that we you can decompose a homography into a chain of transformations (see Richard Hartley and Andrew Zisserman’s book Multiple View Geometry in Computer Vision, chapter two and specifically 2.4.6). I have some prior information about this case, and a general idea that the rotations will fall into 4 general cases. I just want that angle!

Ok, to get from a homography to a rotation matrix we use our friend SVD. First, copy the OpenCV Mat over to an Eigen MatrixXd object and then run Eigen’s SVD function on it. SVD needs a matrix with a dynamic size, so MatrixXd, versus Matrix2d. How do I know this? Runtime error.

Eigen::MatrixXd mat2d(2,2);

// copy
for (int r = 0; r < 2; r++){
for (int c = 0; c < 2; c++){
mat2d(r, c) = H.at<double>(r, c);
}
}

// svd
Eigen::JacobiSVD<MatrixXd> svd(mat2d, ComputeThinU | ComputeThinV);

// ortho = U*V^T
Eigen::Matrix2d ortho2d = svd.matrixU()*svd.matrixV().transpose();


ortho2d is an orthonormal 2x2 matrix, in other words, the rotation matrix we’ve been looking for. Here’s the Eigen::Rotation2D class part.

Eigen::Rotation2D<double> rot2d(ortho2d);


Eigen::Rotation2D is a templated class, so when you declare a variable it has to be with a type. I was initially confused because in Eigen d is double and say for matrices, when you declare a variable, you select types Matrix3f (3x3, float), Matrix2d (2x2, double), etc. In this case, 2D does not change, which represents ‘two dimensional’ and you change the type to float, double, whatever.

This isn’t super well-documented in the front matter of the class, but this is the standard that the angle of the rotation will be in radians and counter clock-wise. I used the matrix constructor by sticking in the Matrix2d ortho2d.

Then, I can quickly use the angle:

out << "angle: " << rot2d.smallestPositiveAngle() <<  endl;


smallestPositiveAngle() is very handy because it returns the angle within [0, 2pi]. For anyone who has ever converted angles to try to reason about them … this is great. There are other functions too.

I haven’t tried to break it yet and see what happens if I try to construct an Eigen:Rotation2d object with a non orthonormal matrix. The constructor documentation says the argument matrix needs to be a rotation matrix.

© Amy Tabb 2018 - 2022. All rights reserved. The contents of this site reflect my personal perspectives and not those of any other entity.