手撕 视觉slam14讲 ch7 / pose_estimation_3d2d.cpp (1)
首先理清我们需要实现什么功能,怎么实现,提供一份整体逻辑:包括主函数和功能函数
主函数逻辑:
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,matchesstd::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)
我们知道:相机的投影模型为:, 即
所以 ,
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:
相关文章:
手撕 视觉slam14讲 ch7 / pose_estimation_3d2d.cpp (1)
首先理清我们需要实现什么功能,怎么实现,提供一份整体逻辑:包括主函数和功能函数 主函数逻辑: 1. 读图,两张rgb(cv::imread) 2. 找到两张rgb图中的特征点匹配对 2.1定义所需要的参数:keypoints…...
Mac安装Dart时,Homebrew报错 Error: Failure while executing
前言: 最近准备开发Flutter项目时,在安装环境时,安装Homebew时遇到了以下报错信息,在这里分享一下。 报错信息: ~ % brew tap dart-lang/dart > Tapping dart-lang/dart Cloning into /opt/homebrew/Library/Tap…...
SSM整合~
构建并配置项目: 第一步:创建maven项目 第二步:配置pom.xml文件 设置打包方式: 为了方便部署,我们通常情况下,将项目打包为WAR,因为WAR文件是一种可执行的压缩文件,它可以将项目…...
Self-supervised 3D Human Pose Estimation from a Single Image
基于单幅图像的自监督三维人体姿态估计 主页: https://josesosajs.github.io/ imagepose/ 源码:未开源 摘要 我们提出了一种新的自我监督的方法预测三维人体姿势从一个单一的图像。预测网络是从描绘处于典型姿势的人的未标记图像的数据集和一组未配对…...
ubuntu下cups部分场景
第一章:部分操作指令 在计算机领域中,cups 是“通用UNIX打印系统”(Common UNIX Printing System)的缩写,它是一种用于在UNIX-like操作系统上管理打印任务的开源打印系统。cups 提供了一个框架,允许用户和…...
通过geoserver imageMosic发布多张tif数据
通过geoserver imageMosic发布多张tif数据 reference: https://zhuanlan.zhihu.com/p/132388558 https://zhuanlan.zhihu.com/p/103674876 https://docs.geoserver.org/latest/en/user/tutorials/imagemosaic_timeseries/imagemosaic_timeseries.html 步骤 下载数据 http…...
输出图元(四)8-2 OpenGL画点函数、OpenGL画线函数
4.3 OpenGL画点函数 要描述一个点的几何要素,我们只需在世界坐标系中指定一个位置。然后该坐标位置和场景中已有的其他几何描述一起被传递给观察子程序。除非指定其他属性值,OpenGL 图元按默认的大小和颜色来显示。默认的图元颜色是白色&#x…...
java八股文
6. 如何保证消息的可靠性? 在RabbitMq的整个消息投递过程中,有三种情况下,会存在消息丢失的问题: 6. RabbitMq如何保证消息的可靠性? 所以从这三个维度保证消息的可靠性去可靠性传递就可以了,从生产者发送…...
算法通关村——解析堆的应用
在数组中找第K大的元素 LeetCode21 Medium 我们要找第 K 大的元素,如果我们找使用大堆的话那么就会造成这个堆到底需要多大的,而且哪一个是第 K 的的元素我们不知道是哪一个索引,我们更想要的结果就是根节点就是我们要找的值,所以…...
爬虫源码---爬取小猫猫交易网站
前言: 本片文章主要对爬虫爬取网页数据来进行一个简单的解答,对与其中的数据来进行一个爬取。 一:环境配置 Python版本:3.7.3 IDE:PyCharm 所需库:requests ,parsel 二:网站页面 我们需要…...
Python的由来和基础语法(一)
目录 一、Python 背景知识 1.1Python 是咋来的? 1.2Python 都能干啥? 1.3Python 的优缺点 二、基础语法 2.1常量和表达式 2.2变量和类型 变量的语法 (1) 定义变量 (2) 使用变量 变量的类型 (1) 整数 (2) 浮点数(小数) (3) 字符串 (4) 布尔 (5) 其他 动态类型…...
使用maven创建springboot项目
创建maven快速启动项目 命令行或者idea、eclipse快捷创建也可以 pom.xml下project项目下导入springboot 父工程 <!--导入springboot 父工程--> <parent><artifactId>spring-boot-starter-parent</artifactId><groupId>org.springframework.bo…...
MySQL 基本操作1
目录 Create insert 插入跟新 1 插入跟新 2 Retrive select where 子句查询 1.查找数学成绩小于 80 的同学。 2.查询数学成绩等于90分的同学。 3.查询总分大于240 的学生 4.查询空值或者非空值 5.查询语文成绩在70~80之间的同学 6.查询英语成绩是99 和 93 和 19 和…...
linux内网yum源服务器搭建
1.nginx: location / {root /usr/local/Kylin-Server-V10-SP3-General-Release-2303-X86_64;autoindex on;autoindex_localtime on;autoindex_exact_size off; } 注:指定到镜像的包名 2.修改yum源地址 cd /etc/yum.repos.d/vim kylin_x86_64.repo 注: --enabled设置为1 3.重…...
机器学习与数据分析
【数据清洗】 异常检测 孤立森林(Isolation Forest)从原理到实践 效果评估:F-score 【1】 保护隐私的时间序列异常检测架构 概率后缀树 PST – (异常检测) 【1】 UEBA架构设计之路5: 概率后缀树模型 【…...
项目总结知识点记录-文件上传下载(三)
(1)文件上传 代码: RequestMapping(value "doUpload", method RequestMethod.POST)public String doUpload(ModelAttribute BookHelper bookHelper, Model model, HttpSession session) throws IllegalStateException, IOExcepti…...
基于LinuxC语言实现的TCP多线程/进程服务器
多进程并发服务器 设计流程 框架一(使用信号回收僵尸进程) void handler(int sig) {while(waitpid(-1, NULL, WNOHANG) > 0); }int main() {//回收僵尸进程siganl(17, handler);//创建服务器监听套接字 serverserver socket();//给服务器地址信息…...
浅谈JVM垃圾回收机制
一、HotSpot VM中的GC分为两大类 1.部分收集(Partial GC): 新生代收集(Minor GC/Young GC):只对新生代进行垃圾收集老年代收集(Major GC/Old GC):只队老年代进行垃圾收集混合收集(Mixed GC):对整个新生代和老年代进行垃圾收集 2.整堆收集(Full GC) 收集整个Java堆和方法区 …...
【80天学习完《深入理解计算机系统》】第十二天3.6数组和结构体
专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客,如有问题交流,欢迎评论区留言,一定尽快回复!(大家可以去看我的专栏,是所有文章的目录) 文章字体风格: 红色文字表示&#…...
基于Python+OpenCV智能答题卡识别系统——深度学习和图像识别算法应用(含Python全部工程源码)+训练与测试数据集
目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境PyCharm安装OpenCV环境 模块实现1. 信息识别2. Excel导出模块3. 图形用户界面模块4. 手写识别模块 系统测试1. 系统识别准确率2. 系统识别应用 工程源代码下载其它资料下载 前言 本项目基于Python和OpenCV图像处…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
