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

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

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

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

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. 在某些情况下,可能会出现索引错误或类型不匹配的问题。

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

// 直接相减 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类似的问题中,我们可以借鉴这种方法,确保在不同数学库之间进行安全和准确的数据转换。

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

#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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/746637.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

高考成绩加分,西藏学生推荐使用的《藏文翻译词典》APP,藏文作文高考大纲,初中高中学习内容与考试同步更新!

2024年高考成绩出炉啦&#xff01;在这个特别的时刻&#xff0c;我想向大家表达最真挚的祝贺。高考不仅是一场考试&#xff0c;更是你多年学习旅程的一次总结。当你的成绩揭晓&#xff0c;无论结果如何&#xff0c;你都应该为自己感到骄傲。 在高原&#xff0c;藏语如同雪山上…

从官方源码精简出第1个FreeRTOS程序

一、下载官方源码 1、打开百度搜索freerots&#xff0c;找到官网:FreeRTOS官网 2、将源码解压到没有中文目录的路径下 二、删减目录 1、删除FreeRTOS-Plus和tools 2、删除FreeRTOS/Demo下除CORTEX_STM32F103_Keil外的所有文件 3、删除FreeRTOS\Source\portable下除RVDS和MemM…

字符串匹配 --- BF算法 KMP算法

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 算法Journey 本篇博客我们将介绍关于字符串匹配的BF算法以及KMP算法&#xff0c;请放心食用~ &#x1f3e0; 字符串匹配 假设有一个字符串为主串str&#x…

算法07 深度优先搜索及相关问题详解

深搜与广搜是搜索算法中最常用的两种算法&#xff0c;通过深度优先搜索解决问题还会用到回溯和剪枝&#xff0c;让我们一起进入本章&#xff0c;了解深搜的基本概念和模板&#xff0c;并学会解决一些常见问题。 目录 问题导入 走迷宫问题 如何走&#xff1f; 问题建模 如何…

(2024,频域 LoRA,DFT,DCT,自适应门控,基于适配器组合的图像编辑)FouRA:傅里叶 LoRA

FouRA: Fourier Low Rank Adaptation 公和众与号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 2. 相关工作 3. 提出的方法 3.1 低秩适应的公式 3.2 频域中的低秩适应 3.3 频率变换 …

【个人博客搭建】(26)发布后端webapi项目

1、选择启动的webapi&#xff0c;右击发布 2、选择左下角的“显示所有设置” 在上一页按钮那边是发布文件夹的目录 地址&#xff0c; 现在界面的就是配置的信息&#xff0c; 配置&#xff1a;Debug、Release 目标框架&#xff1a;我们用的net8.0&#xff0c;就是他&#xff…

2.移植freertos到stm32f103c8t6

目录 1.步骤 2.freertos配置时常见的选项卡意思 1.步骤 2.freertos配置时常见的选项卡意思

Michael.W基于Foundry精读Openzeppelin第60期——Clones.sol

Michael.W基于Foundry精读Openzeppelin第60期——Clones.sol 0. 版本0.1 Clones.sol 1. 目标合约2. 代码精读2.1 clone(address implementation) internal2.2 cloneDeterministic(address implementation, bytes32 salt)2.3 predictDeterministicAddress(address implementatio…

Upload-Labs-Linux1 使用 一句话木马

解题步骤&#xff1a; 1.新建一个php文件&#xff0c;编写内容&#xff1a; <?php eval($_REQUEST[123]) ?> 2.将编写好的php文件上传&#xff0c;但是发现被阻止&#xff0c;网站只能上传图片文件。 3.解决方法&#xff1a; 将php文件改为图片文件&#xff08;例…

小程序开发平台源码系统——社区团购小程序功能 前后端分离 带完整的安装代码包以及搭建教程

系统概述 在当今数字化时代&#xff0c;社区团购已经成为一种热门的购物模式。为了满足市场需求&#xff0c;拥有一个功能强大的社区团购小程序是至关重要的。本文将深入探讨一款具备前后端分离特性的小程序开发平台源码系统&#xff0c;着重介绍其社区团购小程序功能&#xf…

CleanMyMac2024免费版下载!轻松清理垃圾文件、优化系统性能

亲爱的小伙伴们&#xff5e;&#x1f44b;今天我要给大家分享一款神奇的软件&#xff0c;它就是 CleanMyMac 2024 免费版&#xff01;这款软件不仅能帮你轻松清理垃圾文件、优化系统性能&#xff0c;还有更多惊喜功能等你来探索哦&#xff01;&#x1f38a; CleanMyMac绿色免费…

第三十二篇——大数据2:大数据思维的四个层次

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 我们生活在这个时代&#xff0c;我们是否按照这个时代需要的思维方式去思…

【Linux】使用chrony同步时间

chrony介绍 chrony 是一个开源的网络时间协议 (NTP) 客户端和服务器&#xff0c;旨在保持计算机系统的时间精确同步。它是Linux和其他类Unix系统中广泛使用的工具&#xff0c;特别是在需要高精度时间同步的环境中。chrony 的设计考虑了现代网络的挑战&#xff0c;如不稳定的连…

MyPostMan:按照项目管理接口,基于迭代生成接口文档、执行接口自动化联合测试

MyPostMan 是一款类似 PostMan 的接口请求软件&#xff0c;不同于 PostMan 的是&#xff0c;它按照 项目&#xff08;微服务&#xff09;、目录来管理我们的接口&#xff0c;基于迭代来管理我们的接口文档&#xff0c;可导出或者在局域网内共享&#xff0c;按照迭代编写自动化测…

数据结构与算法笔记:高级篇 - 概率统计:如何利用朴素贝叶斯算法过滤垃圾短信?

概述 上篇文章我们讲到&#xff0c;如何用位图、布隆过滤器&#xff0c;来过滤重复数据。本章&#xff0c;我们再讲一个跟过滤相关的问题&#xff0c;如果过滤垃圾短信&#xff1f; 垃圾短信和骚扰电话&#xff0c;我想每个人都收到过吧&#xff1f;买房、贷款、投资理财、开…

带你学习PID算法2

#PID讲解 前言&#xff1a;本文参考华南小虎队的PID视频&#xff0c;视频连接放在最后 下图工人控制水阀可以满足&#xff1a; 1流量稳定 2随时改变流量 如果预期流量是1L/s&#xff0c;实际流量确实0.8L/s&#xff0c;工人就会调节阀门&#xff0c;使其达到&#xff0…

论文学习_基于导向式模糊测试的二进制程序漏洞验证方法

1. 引言 研究背景及现存问题:基于代码相似性比较的漏洞检测方法属于静态分析方法,不可避免地存在误报率高的问题,对静态检测方法得到的疑似漏洞代码进行人工分析存在工作量大, 效率低的问题。解决该问题的有效的方案之一是使用导向式模糊测试方法,生成能够执行到疑似漏洞…

【C++LeetCode】【热题100】三数之和【中等】-不同效率的题解【6】

题目&#xff1a; 暴力方法&#xff1a; class Solution { public:vector<vector<int>> threeSum(vector<int>& nums) {vector<vector<int>> res;std::unordered_set<std::string> uniqueValues;//保证结果唯一for(int i0;i<n…

【第2章】MyBatis-Plus代码生成器

文章目录 前言一、安装二、生成方式1.DefaultQuery (元数据查询)2.存在问题 三、快速生成1. 生成代码2. 目录结构 四、交互式总结 前言 全新的 MyBatis-Plus 代码生成器&#xff0c;通过 builder 模式可以快速生成你想要的代码&#xff0c;快速且优雅&#xff0c;跟随下面的代…

vue draggable

一、安装&#xff1a; npm i -S vuedraggablenext 二、代码 <draggable :list"projectOptions" item-key"name" class"w-25" ghost-class"ghost"chosen-class"chosen" update"updateSort" animation"3…