【Eigen教程04】Eigen高级操作:求解、分解、几何变换与STL交互
原创作者:郑同学的笔记原文链接:https://zhengjunxue.blog.csdn.net/article/details/148910017。
【Eigen教程04】【Eigen教程05】Eigen高级操作:求解、分解、几何变换与STL交互
原创作者:郑同学的笔记
原文链接:https://zhengjunxue.blog.csdn.net/article/details/148910017
一、Eigen高级运算与分解
1. 线性求解
Eigen提供多种线性系统求解方法,包括直接求解法和迭代求解法:
#include <iostream>
#include <Eigen/Dense>
void linearSolvers() {
std::cout << "\n=== 线性求解示例 ===\n";
// 创建矩阵和向量
Eigen::Matrix3d A;
A << 4, -1, 2,
-1, 6, 0,
2, 0, 5;
Eigen::Vector3d b(1, 2, 3);
// 使用PartialPivLU分解求解
Eigen::Vector3d x_lu = A.partialPivLu().solve(b);
std::cout << "LU 分解解: " << x_lu.transpose() << "\n";
// 使用QR分解求解
Eigen::Vector3d x_qr = A.colPivHouseholderQr().solve(b);
std::cout << "QR 分解解: " << x_qr.transpose() << "\n";
// 使用LLT分解求解(正定矩阵)
Eigen::Vector3d x_llt = A.llt().solve(b);
std::cout << "LLT 分解解: " << x_llt.transpose() << "\n";
}
int main()
{
linearSolvers();
return 0;
}
输出
=== 线性求解示例 ===
LU 分解解: 0.043956 0.340659 0.582418
QR 分解解: 0.043956 0.340659 0.582418
LLT 分解解: 0.043956 0.340659 0.582418
求解器选择指南:
- 对于小型稠密矩阵:PartialPivLU(平衡速度与稳定性)
- 最小二乘问题:ColPivHouseholderQR
- 对称正定矩阵:LLT或LDLT
- 大型稀疏系统:迭代法(ConjugateGradient, BiCGSTAB)
2. 矩阵分解
Eigen支持多种矩阵分解方法,适用于不同场景:
#include <Eigen/Eigenvalues>
void matrixDecompositions() {
std::cout << "\n=== 矩阵分解示例 ===\n";
Eigen::Matrix3d B;
B << 3, 2, 1,
2, 3, 2,
1, 2, 3;
// 特征分解
Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eigenSolver(B);
if (eigenSolver.info() == Eigen::Success) {
std::cout << "特征值:\n" << eigenSolver.eigenvalues() << "\n";
std::cout << "特征向量:\n" << eigenSolver.eigenvectors() << "\n";
}
// 奇异值分解(SVD)
Eigen::JacobiSVD<Eigen::Matrix3d> svd(B, Eigen::ComputeFullU | Eigen::ComputeFullV);
std::cout << "奇异值:\n" << svd.singularValues() << "\n";
std::cout << "左奇异向量:\n" << svd.matrixU() << "\n";
std::cout << "右奇异向量:\n" << svd.matrixV() << "\n";
// Cholesky分解
Eigen::LLT<Eigen::Matrix3d> llt = B.llt();
//std::cout << "L 矩阵:\n" << llt.matrixL() << "\n";
std::cout << "L 矩阵:\n"
<< Eigen::MatrixXd(llt.matrixL()) // 关键修改:显式转换为稠密矩阵
<< std::endl;
}
int main()
{
matrixDecompositions();
return 0;
}
输出
=== 矩阵分解示例 ===
特征值:
0.627719
2
6.37228
特征向量:
-0.454401 -0.707107 0.541774
0.766185 -3.90313e-17 0.642621
-0.454401 0.707107 0.541774
奇异值:
6.37228
2
0.627719
左奇异向量:
0.541774 -0.707107 -0.454401
0.642621 -1.47005e-16 0.766185
0.541774 0.707107 -0.454401
右奇异向量:
0.541774 -0.707107 -0.454401
0.642621 -1.597e-16 0.766185
0.541774 0.707107 -0.454401
L 矩阵:
1.73205 0 0
1.1547 1.29099 0
0.57735 1.0328 1.26491
分解方法应用场景:
| 分解类型 | 适用矩阵 | 典型应用 |
|---|---|---|
| LU | 方阵 | 线性求解、行列式计算 |
| QR | 任意矩阵 | 最小二乘、正交化 |
| Cholesky (LLT) | 对称正定 | 高效求解、协方差计算 |
| Eigen | 方阵 | 主成分分析、振动分析 |
| SVD | 任意矩阵 | 伪逆、降维、矩阵近似 |
3. 几何变换
Eigen提供强大的几何变换功能,支持2D和3D空间变换:
#include <Eigen/Geometry>
void geometricTransformations() {
std::cout << "\n=== 几何变换示例 ===\n";
// 创建3D点
Eigen::Vector3d point(1, 0.5, 2);
// 平移变换
Eigen::Translation3d translation(2, 1, 0.5);
Eigen::Vector3d translated_point = translation * point;
std::cout << "平移后: " << translated_point.transpose() << "\n";
// 旋转变换(绕Z轴旋转90度)(绕Z轴旋转45度就是M_PI/4)
Eigen::AngleAxisd rotation(M_PI/2, Eigen::Vector3d::UnitZ());
Eigen::Vector3d rotated_point = rotation * point;
std::cout << "旋转后: " << rotated_point.transpose() << "\n";
// 缩放变换 - 修改为对角矩阵形式
//Eigen::Scaling scaling(2.0, 1.5, 0.8);
Eigen::DiagonalMatrix<double, 3> scaling(2.0, 1.5, 0.8);
Eigen::Vector3d scaled_point = scaling * point;
std::cout << "缩放后: " << scaled_point.transpose() << "\n";
// 组合变换:先缩放 (scaling)再旋转 (rotation)最后平移 (translation)
Eigen::Affine3d transform = scaling * translation * rotation;
Eigen::Vector3d transformed_point = transform * point;
std::cout << "组合变换后: " << transformed_point.transpose() << "\n";
// 四元数旋转
Eigen::Quaterniond quat = Eigen::Quaterniond(rotation);
Eigen::Vector3d quat_rotated = quat * point;
std::cout << "四元数旋转后: " << quat_rotated.transpose() << "\n";
}
int main()
{
geometricTransformations();
return 0;
}
输出
=== 几何变换示例 ===
平移后: 3 1.5 2.5
旋转后: -0.5 1 2
缩放后: 2 0.75 1.6
组合变换后: 3 3 2
四元数旋转后: -0.5 1 2
Eigen::Scaling 类型与其他变换类型直接相乘时存在操作符重载限制
使用对角矩阵 (DiagonalMatrix) 表示缩放可以无缝与其他Eigen变换类型组合
几何变换类型:
- Eigen::Translation:平移变换
- Eigen::AngleAxis:轴角旋转表示
- Eigen::Quaternion:旋转的四元数表示
- Eigen::Scaling:缩放变换(推荐使用DiagonalMatrix)
- Eigen::Affine3d:仿射变换容器
二、与 STL/Eigen库交互
1、Eigen → std::vector
要将 Eigen 数据结构转换为 std::vector,你可以使用 Eigen::Map 或者直接访问 Eigen 数据结构的 data() 方法。以下是一个完整的示例,展示如何从 Eigen::VectorXd 转换到 std::vector。
#include <iostream>
#include <vector>
#include <Eigen/Dense>
int main() {
// 创建一个 Eigen::VectorXd
Eigen::VectorXd eigenVec(5);
eigenVec << 1.0, 2.0, 3.0, 4.0, 5.0;
// Eigen → std::vector
std::vector<float> v_data(eigenVec.data(), eigenVec.data() + eigenVec.size());
// 输出 std::vector 内容
std::cout << "std::vector: ";
for (const auto& val : v_data) {
std::cout << val << " ";
}
std::cout << std::endl;
return 0;
}
其他转换方法(等效):
// 方法1:直接构造(推荐)
std::vector<float> v_data(vec.data(), vec.data() + vec.size());
// 方法2:使用assign
std::vector<float> v_data;
v_data.assign(vec.data(), vec.data() + vec.size());
// 方法3:拷贝构造(C++11起)
std::vector<float> v_data{vec.data(), vec.data() + vec.size()};
2、std::vector→ Eigen
将 std::vector 转换为 Eigen 数据结构的过程相对简单,可以通过 Eigen::Map 来实现。以下是一个完整的示例,演示如何从 std::vector 转换到 Eigen::VectorXd:
Eigen::Map 是 Eigen 库中的一个类,用于将外部内存(例如一个 std::vector、C-style 数组等)映射到 Eigen 数据结构,而无需复制数据。通过 Eigen::Map,你可以在不复制数据的情况下,直接使用外部内存的数据,像操作 Eigen 对象一样进行操作。这种方法非常高效,尤其在处理大型数据时,可以节省内存和时间开销。
使用 Eigen::Map 的好处:
- 避免拷贝:Eigen::Map 使你可以直接使用现有数据,而不需要将数据复制到 Eigen 对象中。
- 高效操作:通过映射外部内存,Eigen 可以像操作普通的 Eigen::VectorXd 或 Eigen::MatrixXd 一样对外部数据进行操作。
- 灵活性:你可以将 Eigen::Map 与不同的数据源(如 std::vector、C 数组等)结合使用,而无需改变原始数据的格式。
语法:
Eigen::Map<Eigen::VectorXd> map(vector_data, size);
Eigen::MapEigen::VectorXd:指定映射的数据类型(在这个例子中是 Eigen::VectorXd,也可以是其他类型如 Eigen::MatrixXd、Eigen::VectorXf 等)。
vector_data:外部数据(例如 std::vector 或 float* 数组)指针。
size:数据的大小,即你要映射的元素数量。
示例:
示例 1:将 std::vector 映射到 Eigen::VectorXd:
cpp
#include <iostream>
#include <vector>
#include <Eigen/Dense>
int main() {
// 创建一个 std::vector
std::vector<float> v_data = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f};
// 使用 Eigen::Map 将 std::vector 映射到 Eigen::VectorXd
Eigen::VectorXd eigenVec = Eigen::Map<Eigen::VectorXd>(v_data.data(), v_data.size());
// 输出 Eigen 向量
std::cout << "Eigen VectorXd: " << eigenVec.transpose() << std::endl;
return 0;
}
示例 2:将 C-style 数组映射到 Eigen::MatrixXd:
cpp
#include <iostream>
#include <Eigen/Dense>
int main() {
// 创建一个 C-style 数组
double arr[6] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0};
// 使用 Eigen::Map 将 C-style 数组映射到 Eigen::MatrixXd
Eigen::Map<Eigen::MatrixXd> matrixMap(arr, 2, 3); // 2 行 3 列
// 输出 Eigen 矩阵
std::cout << "Eigen MatrixXd:\n" << matrixMap << std::endl;
return 0;
}
注意事项:
-
内存对齐:Eigen::Map 仅能用于具有合适内存布局的数据。如果你的数据没有适当的对齐(如非 16-byte 边界对齐),可能会导致性能问题或运行时错误。通常,std::vector 和动态分配的数组(如 new)都有适当的对齐,但静态数组或某些特定的数据源可能需要特别处理。
-
只读映射:Eigen::Map 会映射外部内存,操作时会修改原始数据。如果不希望修改原始数据,可以使用 const 关键字来创建不可修改的映射,例如:Eigen::Map。
-
不拥有数据:Eigen::Map 并不会复制数据,它只是创建了对外部数据的引用。因此,映射的数据在映射对象的生命周期内必须有效。
总结:
Eigen::Map 是一个非常强大且高效的工具,允许你将外部数据(如 std::vector 或 C-style 数组)映射到 Eigen 数据结构,而无需复制数据。它让你能够在处理大数据时,节省内存和提高效率。
3、多维数据展平处理
要将多维数据(如矩阵)按行优先顺序转换为一行(即一个列向量),可以使用Eigen的reshaped()函数并指定行优先顺序
#define _USE_MATH_DEFINES
#include <iostream>
#include <vector>
#include <Eigen/Dense>
int main()
{
Eigen::VectorXi c;
// 预分配结果矩阵 (rows x 5)
Eigen::MatrixXi result(2, 3);
result << 1.0, 2.0, 3.0,
4.0, 5.0,6.0;
c = result.reshaped<Eigen::RowMajor>(result.size(), 1);
std::cout << c;
return 0;
}
三、Eigen和OpenCV互操作
#include <opencv2/opencv.hpp>
// OpenCV转Eigen
cv::Mat cvMat = (cv::Mat_<float>(2,2) << 1,2,3,4;
Eigen::Map<Eigen::Matrix<float,2,2>> eigenMat(cvMat.ptr<float>());
// Eigen转OpenCV(深拷贝)
Eigen::Matrix3f eigenData;
cv::Mat cvData;
cv::eigen2cv(eigenData, cvData);
// 共享内存(避免拷贝)
cv::Mat sharedMat(3,3,CV_32F, eigenData.data());
更多推荐


所有评论(0)