Eigen矩阵存储顺序以及转换
一、Eigen矩阵存储顺序
在矩阵运算和线性代数中,"行优先"(Row-major)和"列优先"(Column-major)是两种不同的存储方式,它们决定了多维数组(如矩阵)在内存中的布局顺序。
1. 行优先(Row-major)
-
定义:矩阵按行顺序存储在内存中,即第一行的所有元素连续存储,接着是第二行,依此类推。
-
内存布局示例:
-
对于一个 2x3 矩阵:
-
行优先存储顺序为:
a, b, c, d, e, f
。
-
2. 列优先(Column-major)
-
定义:矩阵按列顺序存储在内存中,即第一列的所有元素连续存储,接着是第二列,依此类推。
-
内存布局示例:
-
同样的 2x3 矩阵:
-
列优先存储顺序为:
a, d, b, e, c, f
。
-
Eigen 中的存储顺序
-
Eigen 默认使用列优先存储(与 MATLAB 一致),但可以通过模板参数显式指定行优先。
-
示例代码:
cpp
#include <Eigen/Dense> using namespace Eigen;// 默认列优先(Column-major) Matrix<float, 3, 3> mat_col_major;// 显式指定行优先(Row-major) Matrix<float, 3, 3, RowMajor> mat_row_major;
Eigen 中切换存储顺序的方法
-
使用
Eigen::RowMajor
或Eigen::ColMajor
模板参数:cpp
typedef Matrix<double, Dynamic, Dynamic, RowMajor> RowMatrixXd; typedef Matrix<double, Dynamic, Dynamic, ColMajor> ColMatrixXd; // 默认
-
运行时转换:
cpp
MatrixXd mat(2, 3); Matrix<double, Dynamic, Dynamic, RowMajor> mat_row = mat;
二、相互转换
在 Eigen 中,行优先(Row-major) 和 列优先(Column-major) 存储方式的矩阵可以相互转换,但需要注意 内存布局的变化 以及 数据访问效率。
1. 使用 Eigen::Map
直接转换(无数据拷贝)
如果只是临时改变访问方式(不修改数据),可以使用 Eigen::Map
将数据重新解释为另一种存储顺序:
cpp
#include <Eigen/Dense>
using namespace Eigen;Matrix<float, 2, 3, RowMajor> mat_row; // 2x3 行优先矩阵
mat_row << 1, 2, 3,4, 5, 6;// 使用 Map 将其视为列优先矩阵(无数据拷贝)
Map<Matrix<float, 2, 3, ColMajor>> mat_col(mat_row.data());std::cout << "Row-major:\n" << mat_row << "\n";
std::cout << "Col-major (reinterpreted):\n" << mat_col << "\n";
输出:
Row-major:
1 2 3
4 5 6Col-major (reinterpreted):
1 4 2
5 3 6
注意:
-
这种方式 不实际改变数据存储顺序,只是改变访问方式。
-
如果数据原本是行优先,但用
Map
解释为列优先,访问时可能 性能下降(因为内存访问不连续)。
2. 显式转换(数据拷贝,存储顺序改变)
如果需要 真正改变存储顺序(例如,让数据在内存中重新排列),可以使用 Matrix::eval()
或直接赋值:
cpp
Matrix<float, 2, 3, RowMajor> mat_row;
mat_row << 1, 2, 3,4, 5, 6;// 转换为列优先(数据会重新排列)
Matrix<float, 2, 3, ColMajor> mat_col = mat_row;std::cout << "Original (Row-major):\n" << mat_row << "\n";
std::cout << "Converted (Col-major):\n" << mat_col << "\n";
输出:
Original (Row-major):
1 2 3
4 5 6Converted (Col-major):
1 4 2
5 3 6
注意:
-
这种方式 会触发数据拷贝,内存布局会真正改变。
-
适用于需要长期使用另一种存储顺序的情况。
3. 运行时动态矩阵的存储顺序转换
对于动态大小的矩阵(Eigen::MatrixXd
),可以直接用 matrix.transpose()
或 matrix.eval()
进行转换:
cpp
MatrixXd mat_row(2, 3); // 默认列优先
mat_row << 1, 2, 3,4, 5, 6;// 转换为行优先(数据拷贝)
Matrix<double, Dynamic, Dynamic, RowMajor> mat_row_major = mat_row;// 或者使用 eval() 强制重新计算
MatrixXd mat_col_major = mat_row_major.eval();std::cout << "Original (Col-major):\n" << mat_row << "\n";
std::cout << "Converted (Row-major):\n" << mat_row_major << "\n";
std::cout << "Back to Col-major:\n" << mat_col_major << "\n";
输出:
Original (Col-major):
1 2 3
4 5 6Converted (Row-major):
1 2 3
4 5 6Back to Col-major:
1 2 3
4 5 6
解释:
-
Original (Col-major)
:原始矩阵按列优先存储(Eigen 默认方式),但输出时仍按数学上的行排列显示。 -
Converted (Row-major)
:转换为行优先存储,但输出时看起来相同,因为operator<<
会按行打印。 -
Back to Col-major
:用eval()
重新计算,强制恢复列优先存储,但输出格式仍然一致。
注意:
-
eval()
会返回一个新的矩阵,存储顺序可能改变(取决于运算优化)。 -
如果只是临时改变访问方式,仍然推荐
Map
以避免拷贝。
4. 性能优化建议
-
优先使用库默认的列优先(ColMajor),因为 Eigen 的许多优化(如矩阵乘法)默认针对列优先布局。
-
如果需要与其他库(如 OpenCV)交互,OpenCV 使用行优先(Row-major),此时可以用
Map
避免数据拷贝:cpp
cv::Mat cv_mat(2, 3, CV_32F); // OpenCV 矩阵(行优先) Eigen::Map<Matrix<float, 2, 3, RowMajor>> eigen_mat(cv_mat.ptr<float>());
-
避免频繁转换存储顺序,因为数据拷贝可能影响性能。