手敲视觉slam14讲 ch7 / pose_estimation_3d2d.cpp (1)

news2025/5/14 6:51:12

首先理清我们需要实现什么功能,怎么实现,提供一份整体逻辑:包括主函数和功能函数

主函数逻辑:

 1. 读图,两张rgb(cv::imread)

 2. 找到两张rgb图中的特征点匹配对

       2.1定义所需要的参数:keypoints1, keypoints2,matches

       2.2 提取每张图像的检测 Oriented FAST 角点位置并匹配筛选(调用功能函数1)

 3. 建立3d点(像素坐标到相机坐标)

        3.1读出深度图(cv::imread)

        3.2取得每个匹配点对的深度

                3.2.1 得到第y行,第x个像素的深度值

                   (ushort d = d1.ptr<unsigned short> (row)[column])

                3.2.2 去除没有深度的点

                3.2.3 转到相机坐标系(调用功能函数2)

4. 调用epnp求解(input:3d点,2d点对,内参,是否去畸变,求解方式)

        4.1求解(cv::solvePnP)

         4.2 求解结果为向量,需要转成矩阵(cv::Rodrigues)

int main( int agrc, char** agrv) {
//  1. 读图(两张rgb)
    Mat image1 = imread(agrv[1] , CV_LOAD_IMAGE_COLOR );
    Mat image2 = imread(agrv[2] , CV_LOAD_IMAGE_COLOR );
    assert(image1.data && image2.data && "Can not load images!");

//  2. 找到两张rgb图中的特征点匹配对
    // 2.1定义keypoints1, keypoints2,matches
    std::vector<KeyPoint>keypoints1,keypoints2;
    std::vector<DMatch>matches;

    // 2.2 提取每张图像的检测 Oriented FAST 角点位置并匹配筛选
    Featurematcher(image1,image2, keypoints1,keypoints2,matches);

//  3. 建立3d点(像素坐标到相机坐标)
    Mat K  = (Mat_<double>(3, 3) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1);//内参
    vector<Point3f> pts_3d;
    vector<Point2f> pts_2d;

    //3.1读出深度图
    Mat d1 =imread(agrv[3],CV_LOAD_IMAGE_UNCHANGED);
    //3.2取得每个匹配点对的深度(ushort d = d1.ptr<unsigned short> (row)[column];就是指向d1的第row行的第column个数据。数据类型为无符号的短整型 )
    for (DMatch m: matches)
    {
        //3.2.1 得到第y行,第x个位置的像素的深度值
        ushort d = d1.ptr<unsigned short>(int (keypoints1[m.queryIdx].pt.y)) [int(keypoints1[m.queryIdx].pt.x)];
        // 3.2.2 去除没有深度的点
        if(d==0){
            continue;
        }
       float dd=d/5000.0 ;
       //3.2.3 转到相机坐标系
       Point2d p1 = pixtocam(keypoints1[m.queryIdx].pt , K);
        pts_3d.push_back(Point3f(p1.x*dd,p1.y*dd,dd));
        pts_2d.push_back(keypoints2[m.trainIdx].pt);
    }
    cout << "3d-2d pairs: " << pts_3d.size() << endl;

//  4. 调用epnp求解(input:3d点,2d点对,内参,false,求解方式)
            // solvePnP( InputArray objectPoints, InputArray imagePoints, InputArray cameraMatrix, InputArray distCoeffs, OutputArray rvec, OutputArray tvec, bool useExtrinsicGuess = false, int flags = SOLVEPNP_ITERATIVE );
        Mat r,t;
        // 4.1求解
        solvePnP(pts_3d,pts_2d,K,Mat(), r,t,false,SOLVEPNP_EPNP);
        // 4.2 求解结果为向量,需要转成矩阵
        Mat R;
        cv::Rodrigues(r,R);
        cout<<"R="<<R<<endl;
        cout<<"T="<<t<<endl;

// 5.可视化匹配
        Mat img_goodmatch;
        drawMatches(image1, keypoints1, image2, keypoints2, matches, img_goodmatch);
        imshow("good matches", img_goodmatch);
        waitKey(0);
        return 0;
}

功能函数1:  Featurematcher

实现过程在前几篇中已经详细说明:视觉slam14讲 逐行解析代码 ch7 / orb_cv.cpp

2.2.1初始化存储特征点数据的变量

2.2.2 提取每张图像的检测 Oriented FAST 角点位置

2.2.3 计算图像角点的BRIEF描述子

2.2.4 根据刚刚计算好的BRIEF描述子,对两张图的角点进行匹配

2.2.5 匹配点对筛选计算最小距离和最大距离

2.2.6 当描述子之间的距离大于两倍的最小距离时,即认为匹配有误.但有时候最小距离会非常小,设置一个经验值30作为下限.

void Featurematcher( const Mat &image1, const Mat &image2, std::vector<KeyPoint>&keypoints1, std::vector<KeyPoint> &keypoints2,  std::vector<DMatch> &matches){
    // 2.2.1初始化存储特征点数据的变量
        Mat descr1, descr2;
        Ptr<FeatureDetector> detector = ORB::create();
        Ptr<DescriptorExtractor> descriptor = ORB::create();
        Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");

        // 2.2.2 提取每张图像的检测 Oriented FAST 角点位置
        detector->detect(image1, keypoints1);
        detector->detect(image2, keypoints2);

        // 2.2.3 计算图像角点的BRIEF描述子
        descriptor->compute(image1, keypoints1, descr1);
        descriptor->compute(image2, keypoints2, descr2);

        // 2.2.4 根据刚刚计算好的BRIEF描述子,对两张图的角点进行匹配
        std::vector<DMatch> match;
        matcher->match(descr1, descr2, match);

        Mat img_match;
        drawMatches(image1, keypoints1, image2, keypoints2, match, img_match);
        imshow("all matches", img_match);
        waitKey(0);

        // 2.2.5 匹配点对筛选计算最小距离和最大距离
        double min_dis = 10000, max_dis = 0;
        // 2.2.5.1找出所有匹配之间的最小距离和最大距离, 即是最相似的和最不相似的两组点之间的距离
        for (int i = 0; i < descr1.rows; i++)
        {
        double dist = match[i].distance;
        if (dist < min_dis)
            min_dis = dist;
        if (dist > max_dis)
            max_dis = dist;
        }
    cout<<"max_dis="<<max_dis<<endl;
    cout<<"min_dis="<<min_dis<<endl;

    //2.2.6 当描述子之间的距离大于两倍的最小距离时,即认为匹配有误.但有时候最小距离会非常小,设置一个经验值30作为下限.
    for (int i = 0; i < descr1.rows; i++)
    {
        if (match[i].distance<= max(2*min_dis,30.0))
        {
            matches.push_back(match[i]);
        }       
    }
    cout<<"matches.size="<<matches.size()<<endl;
}

功能函数2:

将输入的像素坐标(x ,y)转化到归一化相机坐标系下得到(X,Y)

我们知道:相机的投影模型为:u=KP, 即

\begin{bmatrix} x \\ y \\ 1 \end{bmatrix}=\begin{bmatrix} f_{x} &0&c_x\\ 0&f_y&c_y\\ 0&0&1 \end{bmatrix} \begin{bmatrix} X \\ Y \\ 1 \end{bmatrix}

所以 X=(x-c_x)/f_x     ,    Y=(y-c_y)/f_y

Point2d pixtocam(const  Point2d &p ,  const Mat  &K){
    return Point2d(
        // X=(u-cx)/fx
        (p.x - K.at<double>(0,2)) / K.at<double>(0,0) ,
        // Y=(v-cy)/fy
        (p.y-K.at<double>(1,2)) / K.at<double>(1,1)
    );
}

最后匹配效果及位姿结果:

allmatch:

goodmatch:

位姿输出:R,T:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BOM对MES管理系统的影响与作用

在建设MES管理系统中&#xff0c;BOM&#xff08;物料清单&#xff09;具有至关重要的作用。它提供了产品的组成部分和结构信息&#xff0c;支持生产过程的监控、协调和管理。本文将详细探讨BOM在MES管理系统中的影响和作用。 一、生产过程指导 BOM为MES系统提供了产品的组成部…

MP中的字段还可以利用函数来查询拼接sql

//根据value查询GetMapping("getTest")public List<HashMap> getTest() {QueryWrapper<TTest> queryWrapper new QueryWrapper<>();queryWrapper.eq("substr(name,1,2)","99999");List<TTest> list1 testService.list…

电脑使用快捷键的各种方法

电脑使用快捷键可以帮助我们提高日常操作效率&#xff0c;例如&#xff1a; CTRLC&#xff1a;复制选中内容。 CTRLV&#xff1a;粘贴复制的内容。 CTRLX&#xff1a;剪切选中内容。 CTRLA&#xff1a;全选当前页面内容。 SHIFTDELETE&#xff1a;永久删除选中内容。 CTRL…

银河麒麟V10安装libmcrypt

本次安装是在华为云上执行。cpu是鲲鹏&#xff0c;操作系统是银河麒麟V10. 先下载安装包&#xff1a; wget http://downloads.sourceforge.net/mcrypt/libmcrypt-2.5.8.tar.gz 解包&#xff0c;进入目录中。 执行如下命令&#xff1a; ./configure make make install 执…

GarageSale for Mac:Mac上最好的eBay在线拍卖客户端

GarageSale for Mac是一款适用于Mac操作系统的应用&#xff0c;它可以帮助用户在苹果电脑上创建、管理和组织自己的个人销售活动。如果你希望在Mac上进行有效的推广&#xff0c;以下是一些可能有帮助的建议&#xff1a; 确定目标受众&#xff1a;在推广之前&#xff0c;了解你…

Leetcode415 字符串相加

思路&#xff1a; 从末尾开始相加&#xff0c;进位可以最后统一处理&#xff0c;因为再怎么进也是最多只进一位 class Solution:def addStrings(self, num1: str, num2: str) -> str:# 确保1里是更长的字符串if len(num1) < len(num2):num1_list list(num2)num2_list …

持续加码,科士达重仓储能!

储能的热度&#xff0c;如温度计一样真实展现在各种数据榜单上&#xff1a;新注册企业的数量&#xff0c;转型跨界的企业&#xff0c;尤其IPO募资扩产规模&#xff0c;更是成为了储能企业竞赛的新标准。 日前&#xff0c;科士达一则新的定向募资预案&#xff0c;吸引了业内广泛…

腾讯音乐如何基于大模型 + OLAP 构建智能数据服务平台

本文导读&#xff1a; 当前&#xff0c;大语言模型的应用正在全球范围内引发新一轮的技术革命与商业浪潮。腾讯音乐作为中国领先在线音乐娱乐平台&#xff0c;利用庞大用户群与多元场景的优势&#xff0c;持续探索大模型赛道的多元应用。本文将详细介绍腾讯音乐如何基于 Apach…

【算法篇】动态规划(一)

文章目录 拆分字符串三角形最小路径和不同路径最小路径和背包问题 class Solution { public:int fib(int n) {// if(n0)// {// return 0;// }// if(n1||n2)// {// return 1;// }// return fib(n-1)fib(n-2);//上面的方法会发现时间复杂度太大&#xff0c;超出时间限制O…

今天使用python进行开发

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…

EOCR-SE2/EOCRSE2在数控技术行业的应用

EOCR-SE2电动机保护器是施耐德EOCR系列中一款以低成本、高性能著称的产品&#xff0c;其广泛应用于各种机床设备中。 EOCRSE2-05RS品牌&#xff1a;施耐德&#xff0c;产地&#xff1a;韩国益山工厂&#xff0c;型号&#xff1a;EOCR-SE2,电流范围&#xff1a;3-30A&#xff0…

如果应对2023年国赛

国赛倒计时一周&#xff0c;大家多看看优秀论文&#xff0c;赛前多思考&#xff0c;使大脑在活跃状态&#xff0c;更好的应对题目。 需要历年游戏论文的小伙伴可私信我&#xff0c;或关注微信公众号私信我

机器学习基础16-建立预测模型项目模板

机器学习是一项经验技能&#xff0c;经验越多越好。在项目建立的过程中&#xff0c;实 践是掌握机器学习的最佳手段。在实践过程中&#xff0c;通过实际操作加深对分类和回归问题的每一个步骤的理解&#xff0c;达到学习机器学习的目的 预测模型项目模板 不能只通过阅读来掌握…

恒运资本:概念股是什么意思

概念股是指在特定的经济布景、方针环境、职业远景或社会热点等方面具有某种特别的发展远景和投资价值的股票。在投资者心目中&#xff0c;概念股的危险较大&#xff0c;可是或许带来高于商场平均水平的收益率。那么&#xff0c;概念股到底是什么意思&#xff1f;在本文中&#…

vue3 +element-plus中避免一打开表单的下拉选择的change事件自动校验问题

背景&#xff1a;新增和编辑共用一个弹窗时&#xff0c;每次打开新增弹窗&#xff0c;el-select的表单项会触发change事件的自动校验。我这里的下拉选择是支持多选的&#xff0c;也就是属性上有multiple。 以下解决方法&#xff0c;亲测有用&#xff01;&#xff01; 1. 该表…

【运维】hadoop3.0.3集群安装(一)多节点安装

文章目录 一.Purpose二. Prerequisites三. Installation1. 节点规划2. Configuring Hadoop in Non-Secure Mode3. 准备工作4. 配置core-site.xmlhdfs-site.xmlyarn-site.xmlmapred-site.xmlworkers 4. 分发配置、创建文件夹5. 格式化6. 操作进程6.1. hdfs启动停止 6.2. yarn启动…

华为Mate60 麒麟5G归来,B站都燃了

8月29日&#xff0c;B站华为官方号“华为终端”&#xff0c;发布作品《【HUAWEI Mate 60系列】致敬奔腾不息的力量》公布了华为Mate 60系列产品面世。 该作品一经发布后&#xff0c;一小时最高能增长45.1万的播放量&#xff0c;截至目前发布两日播放量已经高达316.9万&#xf…

关于vue3组合式api (父组件和子组件之间的数据传递)笔记

目录 1.父组件转子组件 2.子组件传父组件 1.父组件转子组件 直接上代码 运行结果 2.子组件传父组件 子组件向父组件传递数据较为复杂一些&#xff0c;需要加深理解才可以明白&#xff01;我是这样理解的 父组件先写一个带参的函数&#xff0c;然后再将函数传给子组件&#xf…

Acwing793. 高精度乘法

Acwing793. 高精度乘法 题目描述代码展示 题目描述 代码展示 #include <iostream> #include <vector>using namespace std;vector<int> mul(vector<int> &A, int b) {vector<int> C;int t 0;for (int i 0; i < A.size() || t; i ){if…

一文通关MyBatisPlus

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; MyBatis ✨特色专栏&#xff1a; MySQ…