OpenCV cv::Mat到 Eigen 的正确转换——cv2eigen

在进行计算机视觉项目时,我们经常需要处理相机位姿的变换。最近,我在项目中遇到了一个看似简单但实际上颇具挑战性的问题:从 OpenCV 的 cv::Mat 格式转换到 Eigen 库的格式。这个过程中遇到了一些问题,但最终找到了一个稳健的解决方案。

问题描述: 我们有两个表示相机位姿的 4x4 变换矩阵,格式为 cv::Mat。目标是计算这两个位姿之间的变换,并提取出平移向量。

初始尝试: 最初,我们尝试直接从 cv::Mat 中提取平移向量:

cpp 复制代码
Eigen::Vector3d transLast(
    mLastFrameTcw.at<double>(0, 3),
    mLastFrameTcw.at<double>(1, 3),
    mLastFrameTcw.at<double>(2, 3)
);
Eigen::Vector3d transCurrent(
    mCurrentFrameTcw.at<double>(0, 3),
    mCurrentFrameTcw.at<double>(1, 3),
    mCurrentFrameTcw.at<double>(2, 3)
);
TRANS_PRED = transCurrent - transLast;

遇到的问题: **针对简单的工程,代码运行完全没有问题,但是放到复杂工程里面,代码就会输出莫名其妙的结果!!!!!**这种方法可能会导致错误,原因如下:

  1. 直接访问 cv::Mat 的元素可能不安全,特别是当矩阵的存储格式不确定时。
  2. 这种方法忽略了旋转部分的影响,可能导致计算结果不准确。
  3. 在某些情况下,可能会出现索引错误或类型不匹配的问题。

改进的解决方案: 经过多次尝试和改进,我们最终采用了以下方法:

cpp 复制代码
// 直接相减 cv::Mat
cv::Mat diff = mCurrentFrameTcw - mLastFrameTcw;

// 将 cv::Mat 转换为 Eigen 矩阵
Eigen::MatrixXd eigenDiff;
cv::cv2eigen(diff, eigenDiff);

// 从 eigenDiff 的最后一列提取前三个元素赋值给 TRANS_PRED
TRANS_PRED = eigenDiff.block<3,1>(0, eigenDiff.cols()-1);

这个解决方案的优点:

  1. 使用 cv::Mat 的矩阵减法,保持了原始数据的完整性。
  2. 利用 OpenCV 提供的 cv2eigen 函数,安全地将 cv::Mat 转换为 Eigen 矩阵。
  3. 使用 Eigen 的 block 操作,精确地提取所需的平移向量。

结论: 在处理计算机视觉中的坐标变换问题时,正确地在不同库(如 OpenCV 和 Eigen)之间转换数据格式是至关重要的。通过采用矩阵减法和适当的类型转换,我们可以准确地计算相机位姿之间的变换。这个经验教训提醒我们,在处理不同库之间的数据转换时,要特别注意数据类型的一致性和操作的正确性。在future类似的问题中,我们可以借鉴这种方法,确保在不同数学库之间进行安全和准确的数据转换。

下面给出完整的代码(注意:下面的两个版本都可以用,只是区分两个中哪个更鲁棒!!!)

cpp 复制代码
#include <iostream>
#include <iomanip>
#include <opencv2/core.hpp>
#include <Eigen/Dense>
#include <opencv2/core/eigen.hpp>

void CalculateTransPred_Method1(const cv::Mat& mLastFrameTcw, const cv::Mat& mCurrentFrameTcw, Eigen::Vector3d& TRANS_PRED) {
    std::cout << "Method 1: Direct extraction from cv::Mat" << std::endl;
    
    Eigen::Vector3d transLast(
            mLastFrameTcw.at<double>(0, 3),
            mLastFrameTcw.at<double>(1, 3),
            mLastFrameTcw.at<double>(2, 3)
    );
    
    Eigen::Vector3d transCurrent(
            mCurrentFrameTcw.at<double>(0, 3),
            mCurrentFrameTcw.at<double>(1, 3),
            mCurrentFrameTcw.at<double>(2, 3)
    );
    
    std::cout << "transLast: " << transLast.transpose() << std::endl;
    std::cout << "transCurrent: " << transCurrent.transpose() << std::endl;
    
    TRANS_PRED = transCurrent - transLast;
    
    std::cout << "TRANS_PRED: " << TRANS_PRED.transpose() << std::endl;
}

void CalculateTransPred_Method2(const cv::Mat& mLastFrameTcw, const cv::Mat& mCurrentFrameTcw, Eigen::Vector3d& TRANS_PRED) {
    std::cout << "\nMethod 2: Using cv::Mat subtraction and Eigen conversion" << std::endl;
    
    cv::Mat diff = mCurrentFrameTcw - mLastFrameTcw;
    
    Eigen::MatrixXd eigenDiff;
    cv::cv2eigen(diff, eigenDiff);
    
    std::cout << "Difference (diff) in Eigen::MatrixXd format:" << std::endl;
    std::cout << eigenDiff << std::endl;
    
    TRANS_PRED = eigenDiff.block<3,1>(0, eigenDiff.cols()-1);
    
    std::cout << "TRANS_PRED: " << TRANS_PRED.transpose() << std::endl;
}

int main() {
    cv::Mat mLastFrameTcw = (cv::Mat_<double>(4, 4) 
        -0.1642483, 0.094168551, -0.98191381, 0.90703607,
        0.0095526827, 0.99553794, 0.093877248, -0.038507219,
        0.98637277, 0.0060392693, -0.164415, -3.4207926,
        0, 0, 0, 1);

    cv::Mat mCurrentFrameTcw = (cv::Mat_<double>(4, 4) 
        -0.16892175, 0.093616515, -0.98117346, 0.92409742,
        0.009967736, 0.99559039, 0.093275994, -0.039425559,
        0.98557907, 0.0059762667, -0.16911002, -3.4853551,
        0, 0, 0, 1);

    Eigen::Vector3d TRANS_PRED;

    std::cout << std::fixed << std::setprecision(6);

    std::cout << "mLastFrame.mTcw:" << std::endl;
    std::cout << mLastFrameTcw << std::endl;

    std::cout << "\nmCurrentFrame.mTcw:" << std::endl;
    std::cout << mCurrentFrameTcw << std::endl;

    CalculateTransPred_Method1(mLastFrameTcw, mCurrentFrameTcw, TRANS_PRED);
    CalculateTransPred_Method2(mLastFrameTcw, mCurrentFrameTcw, TRANS_PRED);

    return 0;
}
相关推荐
dazzle1 小时前
计算机视觉处理:OpenCV车道线检测实战(二):车道线提取技术详解
人工智能·opencv·计算机视觉
烟锁池塘柳01 小时前
C++程序脱离环境运行:详解OpenCV动态库依赖部署 (Deployment)
c++·opencv·webpack
无垠的广袤2 小时前
【工业树莓派 CM0 NANO 单板计算机】基于舵机和人脸识别的智能门禁系统
linux·python·opencv·yolo·ai·树莓派
sali-tec2 小时前
C# 基于OpenCv的视觉工作流-章10-中值滤波
图像处理·人工智能·opencv·算法·计算机视觉
智驱力人工智能3 小时前
视觉分析赋能路面漏油检测 从产品设计到城市治理的实践 漏油检测 基于YOLO的漏油识别算法 加油站油罐泄漏实时预警技术
人工智能·opencv·算法·yolo·目标检测·计算机视觉·边缘计算
Pyeako4 小时前
Opencv计算机视觉--图像边缘检测
人工智能·python·opencv·计算机视觉·sobel·canny·图像边缘检测
子夜江寒4 小时前
基于 OpenCV 的模板匹配技术实例
opencv·计算机视觉
一招定胜负21 小时前
OpenCV轮廓检测完全指南:从原理到实战
人工智能·opencv·计算机视觉
dazzle1 天前
计算机视觉处理(OpenCV基础教学(二十二):霍夫变换技术详解)
人工智能·opencv·计算机视觉
格林威1 天前
印刷电路板阻焊层缺失识别:防止短路风险的 7 个核心策略,附 OpenCV+Halcon 实战代码!
人工智能·数码相机·opencv·机器学习·计算机视觉·视觉检测·工业相机