ORB-SLAM2 ---- Frame中在主函数中被调用的函数

文章目录

  • 一、Frame::isInFrustum
    • 1. 函数讲解
    • 2. 源码
  • 二、Frame::GetFeaturesInArea
    • 1. 函数讲解
    • 2. 函数源码
  • 三、Frame::ComputeBoW
    • 1. 函数讲解
    • 2. 函数源码
  • 四、Frame::UnprojectStereo
    • 1. 函数讲解
    • 2. 函数源码
  • 五、总结

一、Frame::isInFrustum

1. 函数讲解

此函数判断地图点是否在视野中,只有同时满足Step 2、Step 3、Step 4、Step 5

  • Step 1 获得这个地图点的世界坐标,经过以下层层关卡的判断,通过的地图点才认为是在视野中
  • Step 2 关卡一:将这个地图点变换到当前帧的相机坐标系下,如果深度值为正才能继续下一步。
  • Step 3 关卡二:将地图点投影的到当前帧像素坐标,如果在图像有效范围内才能继续下一步。
  • Step 4 关卡三:计算地图点到相机中心的距离,如果在有效距离范围内才能继续下一步。
  • Step 5 关卡四:计算当前相机指向地图点向量和地图点的平均观测方向夹角,小于60°才能进入下一步。
  • Step 6 根据地图点到光心的距离来预测一个尺度(仿照特征点金字塔层级)
  • Step 7 记录计算得到的一些参数

2. 源码

bool Frame::isInFrustum(MapPoint *pMP, float viewingCosLimit)
{// mbTrackInView是决定一个地图点是否进行重投影的标志// 这个标志的确定要经过多个函数的确定,isInFrustum()只是其中的一个验证关卡。这里默认设置为否pMP->mbTrackInView = false;// 3D in absolute coordinates// 获得当前地图点的世界坐标cv::Mat P = pMP->GetWorldPos(); //  在MapPoint中定义// 3D in camera coordinates// 根据当前帧(粗糙)位姿转化到当前相机坐标系下的三维点Pcconst cv::Mat Pc = mRcw*P+mtcw;// 读取x,y,z坐标const float &PcX = Pc.at<float>(0);const float &PcY= Pc.at<float>(1);const float &PcZ = Pc.at<float>(2);// Check positive depth// 如果深度小于0,则返回false(不投影)if(PcZ<0.0f)return false;// Project in image and check it is not outside// 将地图点投影到当前帧的像素坐标,如果在图像有效范围内才能继续下一步const float invz = 1.0f/PcZ;const float u=fx*PcX*invz+cx;const float v=fy*PcY*invz+cy;// 判读特征点是否在边界类,不在就返回false(不投影)if(u<mnMinX || u>mnMaxX)return false;if(v<mnMinY || v>mnMaxY)return false;// Check distance is in the scale invariance region of the MapPoint// 计算地图点到相机中心的距离,如果在有效距离范围内才能继续下一步// 获取认可的地图点到相机中心的距离const float maxDistance = pMP->GetMaxDistanceInvariance();const float minDistance = pMP->GetMinDistanceInvariance();// 得到当前地图点距离当前帧相机光心的距离const cv::Mat PO = P-mOw;// 取模就得到了距离const float dist = cv::norm(PO);// 如果不在范围内,就返回falseif(dist<minDistance || dist>maxDistance)return false;// Check viewing angle// 计算当前相机指向地图点向量和地图点的平均观测方向夹角,小于60°才能进入下一步。cv::Mat Pn = pMP->GetNormal(); // 在MapPoint中定义// 计算当前相机指向地图点向量和地图点的平均观测方向夹角的余弦值,注意平均观测方向为单位向量const float viewCos = PO.dot(Pn)/dist;// 如果夹角超出范围,就返回falseif(viewCos<viewingCosLimit)return false;// Predict scale in the image// 根据地图点到光心的距离来预测一个尺度(仿照特征点金字塔层级)const int nPredictedLevel = pMP->PredictScale(dist,this);// Data used by the tracking// 通过置位标记 MapPoint::mbTrackInView 来表示这个地图点要被投影 pMP->mbTrackInView = true;                  pMP->mTrackProjX = u;                       // 该地图点投影在当前图像(一般是左图)的像素横坐标pMP->mTrackProjXR = u - mbf*invz;           // bf/z其实是视差,相减得到右图(如有)中对应点的横坐标pMP->mTrackProjY = v;                       // 该地图点投影在当前图像(一般是左图)的像素纵坐标pMP->mnTrackScaleLevel= nPredictedLevel;    // 根据地图点到光心距离,预测的该地图点的尺度层级pMP->mTrackViewCos = viewCos;               // 保存当前相机指向地图点向量和地图点的平均观测方向夹角的余弦值// 前面的条件都符合,就返回truereturn true;
}

二、Frame::GetFeaturesInArea

1. 函数讲解

本函数的作用是,找出以(x,y)为中心,以r为半径的圆内的所有特征点,方法为,先找到包含这个圆的最小网格边界(四条红色线形成的方形网格),因为前面我们将特征存入一个个网格中了,这样更方便查找。然后找出这个最小边界网格中的每一个小网格内的特征点,并记录其坐标(中间有涉及到坐标变换,是为了方便计算距离),最后计算这个坐标到(x,y)的距离,如果大于r,则抛弃(在圆外)

2. 函数源码

// 找到符合条件的特征点(以x,y为中心,半径为r的圆形内且金字塔层级在[minLevel, maxLevel])
vector<size_t> Frame::GetFeaturesInArea(const float &x, const float  &y, const float  &r, const int minLevel, const int maxLevel) const
{// 开辟大小为N的特征点容器vector<size_t> vIndices;vIndices.reserve(N);// 计算半径为r圆左右上下边界所在的网格列和行的idconst int nMinCellX = max(0,(int)floor((x-mnMinX-r)*mfGridElementWidthInv));if(nMinCellX>=FRAME_GRID_COLS)return vIndices;// 计算圆所在的右边界网格列索引const int nMaxCellX = min((int)FRAME_GRID_COLS-1,(int)ceil((x-mnMinX+r)*mfGridElementWidthInv));if(nMaxCellX<0)return vIndices;// 计算圆所在的上边界网格列索引const int nMinCellY = max(0,(int)floor((y-mnMinY-r)*mfGridElementHeightInv));if(nMinCellY>=FRAME_GRID_ROWS)return vIndices;// 计算圆所在的下边界网格列索引const int nMaxCellY = min((int)FRAME_GRID_ROWS-1,(int)ceil((y-mnMinY+r)*mfGridElementHeightInv));if(nMaxCellY<0)return vIndices;// 这个判断条件有问题,改为const bool bCheckLevels = (minLevel>=0) || (maxLevel>=0);const bool bCheckLevels = (minLevel>0) || (maxLevel>=0);// 遍历这个区域,找出这个区域内的所有特征点for(int ix = nMinCellX; ix<=nMaxCellX; ix++){for(int iy = nMinCellY; iy<=nMaxCellY; iy++){const vector<size_t> vCell = mGrid[ix][iy];if(vCell.empty())continue;// 读取每一个特征点,判断其特征点层级是否在给定范围(决定于传进来的参数minLevel,maxLeve)for(size_t j=0, jend=vCell.size(); j<jend; j++){const cv::KeyPoint &kpUn = mvKeysUn[vCell[j]];if(bCheckLevels){if(kpUn.octave<minLevel)continue;if(maxLevel>=0)if(kpUn.octave>maxLevel)continue;}// 判断特征点是否在圆内,如是在圆内就记录const float distx = kpUn.pt.x-x;const float disty = kpUn.pt.y-y;if(fabs(distx)<r && fabs(disty)<r)vIndices.push_back(vCell[j]);}}}return vIndices;
}// 确定特征点在网格中的位置,返回值为bool类型
bool Frame::PosInGrid(const cv::KeyPoint &kp, int &posX, int &posY)
{// 计算特征点在哪个网格内posX = round((kp.pt.x-mnMinX)*mfGridElementWidthInv);posY = round((kp.pt.y-mnMinY)*mfGridElementHeightInv);//Keypoint's coordinates are undistorted, which could cause to go out of the image// 超出边界返回falseif(posX<0 || posX>=FRAME_GRID_COLS || posY<0 || posY>=FRAME_GRID_ROWS)return false;// 可以正常分配返回truereturn true;
}

三、Frame::ComputeBoW

1. 函数讲解

本函数的作用是将描述子转化成词袋BoW的模式,这样的好处是,在后续的特征点匹配中,先找出对应的词袋,只需要在这个词袋范围内来逐个匹配,这样大大的减少了计算量和计算时间

2. 函数源码

void Frame::ComputeBoW()
{   // 判断是否以前已经计算过了,计算过了就跳过if(mBowVec.empty()){// 将描述子mDescriptors转换为DBOW要求的输入格式vector<cv::Mat> vCurrentDesc = Converter::toDescriptorVector(mDescriptors);// 将特征点的描述子转换成词袋向量mBowVec以及特征向量mFeatVecmpORBvocabulary->transform(vCurrentDesc,mBowVec,mFeatVec,4);}
}

四、Frame::UnprojectStereo

1. 函数讲解

本函数是将符合条件的特征点反投影到三维世界坐标中,在地图点创建中,这个步骤尤为重要,这个段代码的后两行是冗余的,在主线程中调用此函数前,都筛选过深度

2. 函数源码

// 将符合条件的特征点反投影到三维世界坐标中
cv::Mat Frame::UnprojectStereo(const int &i)
{// 记录特征点深度,const float z = mvDepth[i];// 深度大于0则(明显是多人编程,在计算深度时已经判断过了,这里是冗余的),计算其三维坐标,并转换到世界坐标if(z>0){// 获取去畸变后的特征点坐标const float u = mvKeysUn[i].pt.x;const float v = mvKeysUn[i].pt.y;// 计算当前相机坐标系下的坐标const float x = (u-cx)*z*invfx;const float y = (v-cy)*z*invfy;// 将其转换到三维坐标系下cv::Mat x3Dc = (cv::Mat_<float>(3,1) << x, y, z);// 将其转换为世界坐标return mRwc*x3Dc+mOw;}// 如果深度不合格,就返回空矩阵(冗余代码)elsereturn cv::Mat();
}

五、总结

这四个函数不在Frame.cc中调用,但是作用很重要,帧在三个线程中被普遍应用,如果帧的内容不熟悉,学三个主线程时会有很多的疑问。这两个函数因为比较简单,并且本人对代码做了很详细的标注,所以没有过多的函数讲解,若大家有疑问欢迎讨论。

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

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

相关文章

【Linux】进程池

目录 进程池 进程池的概念&#xff1a; 手搓进程池&#xff1a; 1、创建信道和子进程 2、通过channel控制子进程 3、回收管道和子进程 进程池 进程池的概念&#xff1a; 定义一个池子&#xff0c;在里面放上固定数量的进程&#xff0c;有需求来了&#xff0c;就拿一个池中…

YoloV10——专栏目录

摘要 &#x1f525;&#x1f680;本专栏教你如何嗨翻YoloV10&#xff01;&#x1f680;&#x1f525; &#x1f4a1;升级大招&#xff1a;汲取最新论文精华&#xff0c;给你一整套YoloV10升级秘籍&#xff01;包括但不限于&#xff1a;注意力加持、卷积大换血、Block革新、Ba…

微软运用欺骗性策略大规模打击网络钓鱼活动

微软正在利用欺骗性策略来打击网络钓鱼行为者&#xff0c;方法是通过访问 Azure 生成外形逼真的蜜罐租户&#xff0c;引诱网络犯罪分子进入以收集有关他们的情报。 利用收集到的数据&#xff0c;微软可以绘制恶意基础设施地图&#xff0c;深入了解复杂的网络钓鱼操作&#xff…

使用JMeter进行Spring Boot接口的压力测试

使用 Apache JMeter 对接口进行压力测试是一个相对简单的过程。以下是详细的步骤&#xff0c;包括安装、配置和执行测试计划。 1. 下载和安装 JMeter 下载 JMeter 从 JMeter 官方网站https://jmeter.apache.org/download_jmeter.cgi 下载最新版本的 JMeter。 解压缩 将下载的 …

MATLAB支持的字体

listfonts 列出可用的系统字体 {Adobe Devanagari } {Agency FB } {Algerian } {AlienCaret } {AMS } {Arial } {Arial Black …

炒股VS炒游戏装备,哪个更好做

这个项目&#xff0c;赚个10%都是要被嫌弃的 虽然天天都在抒发自己对股市的看法&#xff0c;但自己自始至终也没有买进任何一支股票。之所以对这个话题感兴趣&#xff0c;着实是因为手上的游戏搬砖项目也是国际性买卖&#xff0c;跟国际形势&#xff0c;国际汇率挂钩&#xff0…

【C++ 11】移动构造函数

文章目录 【 1. 问题背景&#xff1a;深拷贝引起的内存开销问题 】【 2. 移动构造函数 】【 3. 左值的移动构造函数: move 实现 】 【 1. 问题背景&#xff1a;深拷贝引起的内存开销问题 】 拷贝构造函数 在 C 11 标准之前&#xff08;C 98/03 标准中&#xff09;&#xff0c;…

不再手动处理繁琐任务!Python自动化方案梳理

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 文章内容 📒📝 文件和文件夹操作📝 Web自动化📝 自动化办公任务📝 网络请求和API调用📝 任务调度📝 桌面自动化📝 邮件自动化⚓️ 相关链接 ⚓️📖 介绍 📖 想象一下,只需一个Python程序,就能让你的电脑自…

深度学习--CNN实现猫狗识别二分类(附带下载链接, 长期有效)

1. 代码实现(包含流程解释) 样本量: 8005 # # 1.导入数据集(加载图片)数据预处理# 进行图像增强, 通过对图像的旋转 ,缩放,剪切变换, 翻转, 平移等一系列操作来生成新样本, 进而增加样本容量, # 同时对图片数值进行归一化[0:1] from tensorflow.keras.preprocessing.image …

Mysql 和MongoDB用户访问权限问题

Mysql 刚给二线运维排查了一个问题&#xff0c;Mysql安装完可用&#xff0c;且可用navicat连接&#xff0c;项目中通过127.0.0.1去连数据库报错了。错误是access denied for user ‘root’localhost,排查思路 1. 密码是否正确 &#xff08;不需要重置。到Mysql的安装目录下找…

软件I2C的代码

I2C的函数 GPIO的配置——scl和sda都配置为开漏输出 void MyI2C_Init(void) {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStruture;GPIO_InitStruture.GPIO_Mode GPIO_Mode_Out_OD;GPIO_InitStruture.GPIO_PinGPIO_Pin_10 | GPIO_Pin_…

Linux服务器前后端项目部署vue+springboot—搭建服务器上的运行环境(JDK、Redis、MySQL、Nginx)

Linux服务器前后端项目部署—①搭建服务器上的运行环境 一、系统参数信息和使用工具 1、服务器信息 华为云 CenteOS7.8 64 配置信息&#xff1a;2核4G 2、使用工具 Xshell6 二、环境安装和配置 &#xff08;一&#xff09;JDK的下载和安装 1、创建一个新目录或者进入目…

Spring event实战

什么是spring event&#xff1f; Spring Event 是 Spring 框架提供的一种事件驱动编程模型。它允许应用程序中的组件通过发布和监听事件来进行松耦合的交互。这种机制基于观察者设计模式&#xff0c;其中组件可以扮演事件发布者的角色&#xff0c;而其他组件则作为事件监听器来…

UEFI BIOSAPP编程开发查询手册.pdf

UEFI BIOS&APP编程开发查询手册.pdf 独家整理推荐。 享受&#xff0c; 半年免费更新服务&#xff0c; 一年免费咨询服务。

django5入门【03】新建一个hello界面

注意 ⭐前提&#xff1a;将上节的项目导入到pycharm中操作步骤总结&#xff1a; 1、HelloDjango/HelloDjango目录下&#xff0c;新建一个views.py 2、HelloDjango/HelloDjango/urls.py 文件中&#xff0c;配置url路由信息 3、新建终端&#xff0c;执行运行命令python manag…

RuoYi-Vue若依 环境搭建 速成

一、若依简介 RuoYi-Vue 是一个开源的后台管理系统&#xff0c;适用于快速开发企业级应用。该平台由两部分组成&#xff1a;前端和后端。 &#xff08;1&#xff09;技术框架 前端技术&#xff1a; Vue.js: 前端框架使用 Vue.js&#xff0c;这是一种流行的JavaScript框架&a…

[实时计算flink]基于Paimon的数据库实时入湖快速入门

Apache Paimon是一种流批统一的湖存储格式&#xff0c;支持高吞吐的写入和低延迟的查询。本文通过Paimon Catalog和MySQL连接器&#xff0c;将云数据库RDS中的订单数据和表结构变更导入Paimon表中&#xff0c;并使用Flink对Paimon表进行简单分析。 背景信息 Apache Paimon是一…

(46)MATLAB仿真从正弦波转换为方波

文章目录 前言一、MATLAB代码二、仿真结果画图三、吉布斯效应 前言 本文使用MATLAB仿真的方法&#xff0c;给出从正弦波转换为方波的过程&#xff0c;说明方波的傅里叶级数展开式是如何由奇次谐波的和构成的。另外&#xff0c;说明了在此过程中的吉布斯效应。 一、MATLAB代码 …

pm2 部署vue

1、为什么要使用pm2运行vue项目 为什么&#xff01;&#xff01;&#xff01;我们一般是将打出来的DIST目录上传到服务器发布即可&#xff0c;为啥我会使用PM2来运行部署呢&#xff1f; 前提&#xff1a;vue2mysqlexpress不使用中间服务器&#xff0c;即不要后端人员开发接口服…

Bands Page 乐队页面

“带区”页面提供了用于添加和删除带区、自定义带区设置以及更改带区和列布局的设计时工具。此页面如下图所示。 该页面说明了一个预览部分、一个用于访问所选频段设置的属性网格以及一组按钮&#xff0c;这些按钮提供了下面列表中描述的功能。 添加新乐队…- 创建新带。创建新…