EigenのMatrixクラスに配列を入力したい場合があると思います。今回はEigen::Mapクラスを使用してEigenのMatrixクラスに配列の値を入力する方法と、それを応用してOpenCVのMatクラスとEigenのMatrixクラスの相互変換を行うよう法についてまとめました。
1. Eigen::Mapクラス
Mapクラスは以下のように定義されています。
Map<Matrix<typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime>>
上記のようにテンプレートパラメータを指定する必要があり、例えば以下のように変数を宣言します。
コンストラクタの引数はポインタ、Rowサイズ、Colサイズです。
テンプレートパラメータが固定サイズの場合はコンストラクタにサイズを指定する必要はありません。
int data[9] = {0};
Map<Matrix3i> map(data);
int data[9] = {0};
Map<MatrixXi> map(data, 3, 3);
MapクラスはMatrixクラスと同じように使用することができ、代入のみで変換できるようです。
int data[9] = {0};
for (int i = 0; i < 9; i++) {
data[i] = i;
}
Eigen::Map<Eigen::Matrix3i> map(data);
Eigen::Matrix3i mat = map;
std::cout << mat << std::endl;
0~9までの値を入れた配列をMapクラスに入れてMatrixクラスに代入しました。

2. 配列とMapクラスの相互変換
まずは配列とMapクラスの相互変換をしてみます。
ここでは0~8の値が入った1次元配列を3×3のMatrixに変換し、また1次元配列に戻します。
int data[9];
int result[9];
std::cout << "data:";
for (int i = 0; i < 9; i++) {
data[i] = i;
std::cout << data[i] << " ";
}
std::cout << std::endl;
Eigen::Map<Eigen::Matrix3i> map(data);
std::cout << "map:" << std::endl;
std::cout << map << std::endl;
memcpy(result, &map(0), sizeof(int) * 9);
std::cout << "result:";
for (int i = 0; i < 9; i++) {
std::cout << result[i] << " ";
}
std::cout << std::endl;
実行結果はこちら。

なお、typedefで定義されているMatrixは列優先であるため、行優先で変換したい場合は以下のようにする。
int data[9];
int result[9];
std::cout << "data:";
for (int i = 0; i < 9; i++) {
data[i] = i;
std::cout << data[i] << " ";
}
std::cout << std::endl;
typedef Eigen::Matrix<int, 3, 3, Eigen::RowMajor> MatrixType;
Eigen::Map<MatrixType> map(data);
std::cout << "map:" << std::endl;
std::cout << map << std::endl;
memcpy(result, &map(0), sizeof(int) * 9);
std::cout << "result:";
for (int i = 0; i < 9; i++) {
std::cout << result[i] << " ";
}
std::cout << std::endl;
実行結果はこちら。

3. OpenCVのMatクラスとEigenのMapクラスの相互変換
次にOpenCVのMatクラスをEigenのMapクラスへ変換してみます。
まずは5×5のMatクラスに0~24までの値を代入し、それをMapクラスに変化してまた戻してみます。
int width = 5;
int height = 5;
cv::Mat mat_img(height, width, CV_8UC1);
int count = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
mat_img.data[y * width + x] = count++;
}
}
std::cout << "mat_img:" << std::endl;
std::cout << mat_img << std::endl;
typedef Eigen::Matrix<uchar, 5, 5, Eigen::RowMajor> MatrixType;
Eigen::Map<MatrixType> map(mat_img.ptr());
std::cout << "map:" << std::endl;
std::cout << map << std::endl;
cv::Mat mat_result = cv::Mat::zeros(height, width, CV_8UC1);
memcpy(mat_result.ptr(), &map(0), sizeof(uchar) * width * height);
std::cout << "mat_result:" << std::endl;
std::cout << mat_result << std::endl;
実行結果はこちら。

今度はこちらの画像を読み込んでMapクラスに変換し、値を半分にしてからMatクラスへ戻してみます。

cv::Mat mat_img = cv::imread("shimons_labo.png");
int width = mat_img.cols;
int height = mat_img.rows;
std::vector<cv::Mat> vMat_img;
cv::split(mat_img, vMat_img);
// cv::Mat -> Eigen::Matrix
typedef Eigen::Matrix<uchar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> MatrixType;
std::vector<Eigen::Map<MatrixType>> vmap;
for (int i = 0; i < 3; i++) {
Eigen::Map<MatrixType> map(vMat_img[i].ptr(), height, width);
vmap.push_back(map);
}
for (int i = 0; i < 3; i++) {
vmap[i].array() = vmap[i].array() / 2;
}
// Eigen::Matrix -> cv::Mat
std::vector<cv::Mat> vMat_result(3);
for (int i = 0; i < 3; i++) {
vMat_result[i] = cv::Mat::zeros(height, width, CV_8UC1);
memcpy(vMat_result[i].ptr(), &vmap[i](0), sizeof(uchar) * width * height);
}
cv::Mat mat_result;
cv::merge(vMat_result, mat_result);
cv::imshow("mat_result", mat_result);
cv::waitKey();
実行結果はこちら。

以上のように、Mapクラスを使えば配列やOpenCVのMatクラスもEigenのMatrixクラスへ変換することが可能です。
今回は以上です。
4. 参考サイト
Eigenリファレンス
https://eigen.tuxfamily.org/dox/group__TutorialMapClass.html#TutorialMapPlacementNew





コメント