ORB-SLAM2源码学习:ORBmatcher.cc⑥: int ORBmatcher::Fuse将地图点投影到关键帧中进行匹配和融合

前言

该函数定义在特征匹配的源文件(ORBmatcher.cc)下,用于将地图点投影到关键帧中进行匹配和融合。

当地图点规模比较大时, 其中会存在很多邻近的地图点, 有的来自跟踪线程, 有的来自局部建图线程, 有的来自闭环线程,这些邻近的地图点很可能是同一个路标点在不同阶段形成的“ 影子"。因此,需要将这些地图点融合为一个地图点,这样不仅可以避免冗余,缩小地图点的规模, 还能通过融合过程提高地图点的精度。

在ORB-SLAM2 中, 融合地图点主要出现在两个地方,在局部建图线程中, 将当前关键帧的一级和二级相连关键帧对应的所有地图点投影到当前关键帧中。如果投影的地图点能匹配当前关键帧的特征点, 并且该特征点有对应的地图点,那么选择这两个地图点中被观测数目最多的那个来替换两个地图点, 这称为地图点替换;如果投影的地图点能匹配当前关键帧的特征点,而该特征点没
有对应的地图点,那么新增该地图点和关键帧之间的观测关系,称为地图点新增。

ORB-SLAM2源码学习:LocalMapping.cc: void LocalMapping::SearchInNeighbors 检查并融合当前关键帧和相邻帧(两级相邻)的重复地图点-CSDN博客

该函数在局部建图线程中的检查并融合相邻帧的地图点时被调用进行正反两个方向的融合。 

在闭环线程中,我们将当前关键帧闭环匹配上的关键帧及其共视关键帧组成的所有地图点投影到当前关键帧中,然后执行上述地图点融合(替换或新增)操作。

1.函数声明

int ORBmatcher::Fuse(KeyFrame *pKF, const vector<MapPoint *> &vpMapPoints, const float th)

融合方式:

1.如果地图点能匹配关键帧的特征点,并且该点有对应的地图点,那么选择观测数目多的替换那个地图点 

2.如果地图点能匹配关键帧的特征点,并且该点没有对应的地图点,那么为该点添加该投影地图点

2.函数定义

具体流程如下:

1.取出当前帧位姿、内参、光心在世界坐标系下的坐标。

2.开始遍历所有地图点,判断投影地图点是否有效。如果地图点不存在、无效、已经在当前帧中(无须融合),则跳过该地图点,遍历下一个地图点。

3.对于有效的地图点,将其投影到当前关键帧中的二维图像坐标。

4.投影坐标需要在有效的图像范围内,地图点到关键帧相机光心的距离需满足在有效范围内,地图点到光心的连线与该地图点的平均观测方向向量之间的夹角要小于60° 。同时满足这些条件后继续下一步,否则跳过该地图点,遍历下一个地图点。

5.根据地图点到相机光心的距离预测匹配点所在的金字塔尺度,确定搜索范围,确定候选匹配点。

6.遍历候选匹配点,最佳候选匹配点要同时满足投影点和候选匹配点的距离在合理范围内(因此此时的位姿认为是准确的,投影位置也接近真实匹配点),以及投影点的描述子距离最小。

7.根据匹配点对应的地图点情况执行地图点替换或新增。

int ORBmatcher::Fuse(KeyFrame *pKF, const vector<MapPoint *> &vpMapPoints, const float th)
{// 取出当前帧位姿、内参、光心在世界坐标系下坐标cv::Mat Rcw = pKF->GetRotation();cv::Mat tcw = pKF->GetTranslation();const float &fx = pKF->fx;const float &fy = pKF->fy;const float &cx = pKF->cx;const float &cy = pKF->cy;const float &bf = pKF->mbf;cv::Mat Ow = pKF->GetCameraCenter();int nFused=0;const int nMPs = vpMapPoints.size();// 遍历所有的待投影地图点for(int i=0; i<nMPs; i++){MapPoint* pMP = vpMapPoints[i];// Step 1 判断地图点的有效性 if(!pMP)continue;// 地图点无效 或 已经是该帧的地图点(无需融合),跳过if(pMP->isBad() || pMP->IsInKeyFrame(pKF))continue;// 将地图点变换到关键帧的相机坐标系下cv::Mat p3Dw = pMP->GetWorldPos();cv::Mat p3Dc = Rcw*p3Dw + tcw;// Depth must be positive// 深度值为负,跳过if(p3Dc.at<float>(2)<0.0f)continue;// Step 2 得到地图点投影到关键帧的图像坐标const float invz = 1/p3Dc.at<float>(2);const float x = p3Dc.at<float>(0)*invz;const float y = p3Dc.at<float>(1)*invz;const float u = fx*x+cx;const float v = fy*y+cy;// Point must be inside the image// 投影点需要在有效范围内if(!pKF->IsInImage(u,v))continue;const float ur = u-bf*invz;const float maxDistance = pMP->GetMaxDistanceInvariance();const float minDistance = pMP->GetMinDistanceInvariance();cv::Mat PO = p3Dw-Ow;const float dist3D = cv::norm(PO);// Depth must be inside the scale pyramid of the image// Step 3 地图点到关键帧相机光心距离需满足在有效范围内if(dist3D<minDistance || dist3D>maxDistance )continue;// Viewing angle must be less than 60 deg// Step 4 地图点到光心的连线与该地图点的平均观测向量之间夹角要小于60°cv::Mat Pn = pMP->GetNormal();if(PO.dot(Pn)<0.5*dist3D)continue;// 根据地图点到相机光心距离预测匹配点所在的金字塔尺度int nPredictedLevel = pMP->PredictScale(dist3D,pKF);// Search in a radius// 确定搜索范围const float radius = th*pKF->mvScaleFactors[nPredictedLevel];// Step 5 在投影点附近搜索窗口内找到候选匹配点的索引const vector<size_t> vIndices = pKF->GetFeaturesInArea(u,v,radius);if(vIndices.empty())continue;// Match to the most similar keypoint in the radius// Step 6 遍历寻找最佳匹配点const cv::Mat dMP = pMP->GetDescriptor();int bestDist = 256;int bestIdx = -1;for(vector<size_t>::const_iterator vit=vIndices.begin(), vend=vIndices.end(); vit!=vend; vit++)// 步骤3:遍历搜索范围内的features{const size_t idx = *vit;const cv::KeyPoint &kp = pKF->mvKeysUn[idx];const int &kpLevel= kp.octave;// 金字塔层级要接近(同一层或小一层),否则跳过if(kpLevel<nPredictedLevel-1 || kpLevel>nPredictedLevel)continue;// 计算投影点与候选匹配特征点的距离,如果偏差很大,直接跳过if(pKF->mvuRight[idx]>=0){// Check reprojection error in stereo// 双目情况const float &kpx = kp.pt.x;const float &kpy = kp.pt.y;const float &kpr = pKF->mvuRight[idx];const float ex = u-kpx;const float ey = v-kpy;// 右目数据的偏差也要考虑进去const float er = ur-kpr;        const float e2 = ex*ex+ey*ey+er*er;//自由度为3, 误差小于1个像素,这种事情95%发生的概率对应卡方检验阈值为7.82if(e2*pKF->mvInvLevelSigma2[kpLevel]>7.8)   continue;}else{// 计算投影点与候选匹配特征点的距离,如果偏差很大,直接跳过// 单目情况const float &kpx = kp.pt.x;const float &kpy = kp.pt.y;const float ex = u-kpx;const float ey = v-kpy;const float e2 = ex*ex+ey*ey;// 自由度为2的,卡方检验阈值5.99(假设测量有一个像素的偏差)if(e2*pKF->mvInvLevelSigma2[kpLevel]>5.99)continue;}const cv::Mat &dKF = pKF->mDescriptors.row(idx);const int dist = DescriptorDistance(dMP,dKF);// 和投影点的描述子距离最小if(dist<bestDist){bestDist = dist;bestIdx = idx;}}// If there is already a MapPoint replace otherwise add new measurement// Step 7 找到投影点对应的最佳匹配特征点,根据是否存在地图点来融合或新增// 最佳匹配距离要小于阈值if(bestDist<=TH_LOW){MapPoint* pMPinKF = pKF->GetMapPoint(bestIdx);if(pMPinKF){// 如果最佳匹配点有对应有效地图点,选择被观测次数最多的那个替换if(!pMPinKF->isBad()){if(pMPinKF->Observations()>pMP->Observations())pMP->Replace(pMPinKF);elsepMPinKF->Replace(pMP);}}else{// 如果最佳匹配点没有对应地图点,添加观测信息pMP->AddObservation(pKF,bestIdx);pKF->AddMapPoint(pMP,bestIdx);}nFused++;}}return nFused;
}

结束语

以上就是我学习到的内容,如果对您有帮助请多多支持我,如果哪里有问题欢迎大家在评论区积极讨论,我看到会及时回复。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

正态分布检验(JB检验和威尔克检验)和斯皮尔曼相关系数(继上回)

正态分布的检验 1,JB检验(n>30) (1)偏度和峰度 描述函数正不正&#xff0c;高不高的 Matlab中计算偏度和峰度的函数是&#xff1a;skewness() 和 kurtosis() 我们以normrnd来生成一个100*1的均值为2,标准差为3的正态分布(这里采用的第一个公式) 得到下面的数据,因为这个…

搭建一个基于Spring Boot的书籍学习平台

搭建一个基于Spring Boot的书籍学习平台可以涵盖多个功能模块&#xff0c;例如用户管理、书籍管理、学习进度跟踪、笔记管理、评论和评分等。以下是一个简化的步骤指南&#xff0c;帮助你快速搭建一个基础的书籍学习平台。 — 1. 项目初始化 使用 Spring Initializr 生成一个…

基于Python的心电图报告解析与心电吸引子绘制

一、引言 1.1 研究背景与意义 心脏作为人体的核心器官&#xff0c;其正常电活动对于维持生命活动至关重要。心电图&#xff08;Electrocardiogram&#xff0c;ECG&#xff09;作为记录心脏电活动随时间变化的重要工具&#xff0c;能够直观反映心脏的节律、传导等功能状态&…

【大数据】机器学习------支持向量机(SVM)

支持向量机的基本概念和数学公式&#xff1a; 1. 线性可分的支持向量机 对于线性可分的数据集 &#xff0c;其中(x_i \in R^d) 是特征向量 是类别标签&#xff0c;目标是找到一个超平面 &#xff0c;使得对于所有 的样本 &#xff0c;对于所有(y_i -1) 的样本&#xff0c;…

左神算法基础提升--4

文章目录 树形dp问题Morris遍历 树形dp问题 求解这个问题需要用到我们在基础班上学到的从节点的左子树和右子树上拿信息的方法。 求最大距离主要分为两种情况&#xff1a;1.当前节点参与最大距离的求解&#xff1b;2.当前节点不参与最大距离的求解&#xff1b; 1.当前节点参与最…

53,【3】BUUCTF WEB october 2019 Twice SQLinjection

题目得到信息&#xff0c;2次注入&#xff0c;进入靶场 登录页面&#xff0c;很自然想到SQL 第一次注入应该是这个可以登录&#xff0c;注册&#xff0c;提交简介的页面 第二次注入应该是在info处注入&#xff0c;信息显示在简介处 我真的纯脑子有病&#xff0c;人家二次注入不…

python编程-OpenCV(图像读写-图像处理-图像滤波-角点检测-边缘检测)图像变换

形态变换 图像处理中的形态学操作是处理图像结构的有效方法。以下是一些常见的形态学操作的介绍及其在 OpenCV 中的实现示例。 1. 腐蚀&#xff08;Erosion&#xff09; 腐蚀操作通过消除图像边界来减少图像中的白色区域&#xff08;前景&#xff09;&#xff0c;使物体的边…

Spring Boot + Apache POI 实现 Excel 导出:BOM物料清单生成器(支持中文文件名、样式美化、数据合并)

目录 引言 Apache POI操作Excel的实用技巧 1.合并单元格操作 2.设置单元格样式 1. 创建样式对象 2. 设置边框 3. 设置底色 4. 设置对齐方式 5. 设置字体样式 6.设置自动换行 7. 应用样式到单元格 3. 定位和操作指定单元格 4.实现标签-值的形式 5.列宽设置 1. 设…

python(25) : 含有大模型生成的公式的文本渲染成图片并生成word文档(支持flask接口调用)

公式样例 渲染前 \[ \sqrt{1904.615384} \approx 43.64 \] 渲染后 安装依赖 pip install matplotlib -i https://mirrors.aliyun.com/pypi/simple/ requestspip install sympy -i https://mirrors.aliyun.com/pypi/simple/ requestspip install python-docx -i https…

基于32QAM的载波同步和定时同步性能仿真,包括Costas环的gardner环

目录 1.算法仿真效果 2.算法涉及理论知识概要 3.MATLAB核心程序 4.完整算法代码文件获得 1.算法仿真效果 matlab2022a仿真结果如下&#xff08;完整代码运行后无水印&#xff09;&#xff1a; 仿真操作步骤可参考程序配套的操作视频。 2.算法涉及理论知识概要 载波同步是…

ARP Check

ARP Check所解决的问题 ARP Check主要用于解决ARP欺骗的问题&#xff0c;依赖于DHCP SnoopingIP Source Guard或者是端口安全全局地址绑定来达到防止ARP欺骗的作用 一旦在端口下配置了ARP Check功能&#xff0c;那么如果不是表项中所对应的IPMAC或是IP的话&#xff0c;就会拒…

通信协议之多摩川编码器协议

前言 学习永无止境&#xff01;本篇是通信协议之多摩川编码器协议&#xff0c;主要介绍RS485硬件层以及软件层帧格式。 注&#xff1a;本文章为学习笔记&#xff0c;部分图片与文字来源于网络/应用手册&#xff0c;如侵权请联系&#xff01;谢谢&#xff01; 一、多摩川协议概述…

Web前端第一次作业

主页代码&#xff1a; <!DOCTYPE html> <html lang"zh"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>主页</title> …

力扣动态规划-2【算法学习day.96】

前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;建议灵神的题单和代码随想录&#xff09;和记录自己的学习过程&#xff0c;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关…

LINUX 内核设计于实现 阅读记录(2025.01.14)

文章目录 一、内核历史1、内核简介2、LINUX 内核与 UNIX 内核比较3、LINUX内核版本命名 二、从内核出发1、获取内核源码&#xff08;1&#xff09;查看Linux内核版本 uname -r&#xff08;2&#xff09;下载源码 https://www.kernel.org/&#xff08;3&#xff09;编译内核 2、…

Phi小模型开发教程:用C#开发本地部署AI聊天工具,只需CPU,不需要GPU,3G内存就可以运行,不输GPT-3.5

大家好&#xff0c;我是编程乐趣。 行业诸多大佬一直在说&#xff1a;“‌2025年将是AI应用元年‌”&#xff0c;虽然说大佬的说法不一定对&#xff0c;但AI趋势肯定没错的。 对于我们程序员来说&#xff0c;储备AI应用开发技能&#xff0c;不管对找工作、接项目、创业肯定是…

Android系统开发(一):AOSP 架构全解析:开源拥抱安卓未来

引言 当我们手握智能手机&#xff0c;流畅地滑动屏幕、切换应用、欣赏动画时&#xff0c;背后其实藏着一套庞大且精密的开源系统——Android AOSP&#xff08;Android Open Source Project&#xff09;。这套系统不仅是所有安卓设备的根基&#xff0c;也是系统开发者的终极 pl…

【机器学习实战入门】基于深度学习的乳腺癌分类

什么是深度学习&#xff1f; 作为对机器学习的一种深入方法&#xff0c;深度学习受到了人类大脑和其生物神经网络的启发。它包括深层神经网络、递归神经网络、卷积神经网络和深度信念网络等架构&#xff0c;这些架构由多层组成&#xff0c;数据必须通过这些层才能最终产生输出。…

ASP .NET Core 学习(.NET9)配置接口访问路由

新创建的 ASP .NET Core Web API项目中Controller进行请求时&#xff0c;是在地址:端口/Controller名称进行访问的&#xff0c;这个时候Controller的默认路由配置如下 访问接口时&#xff0c;是通过请求方法&#xff08;GET、Post、Put、Delete&#xff09;进行接口区分的&…

TextButton组件的功能与用法

文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了CircleAvatar Widget,本章回中将介绍Button这种Widget&#xff0c;闲话休提&#xff0c;让我们一起Talk Flutter吧。 1 概念介绍 关于Button相信大家都很熟悉&#xff0c;也就是我们常用的按钮。用户按下按钮后…