原创作者:郑同学的笔记
原文链接: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());
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐