02 特征点提取器 ORBextractor

文章目录

    • 02 特征点提取器 ORBextractor
      • 2.0 基础知识
        • 2.0.1 图像金字塔
        • 2.0.2 ORB 特征点的关键点和描述子
      • 2.1 构造函数:ORBextractor()
      • 2.2 构建图像金字塔 ComputePyramid()
      • 2.3 提取特征点并筛选 ComputeKeyPointsOctTree()
      • 2.4 筛选特征点 DistributeOctTree()
      • 2.5 计算特征点方向 computeOrientation()
      • 2.6 计算特征点描述子 computeOrbDescriptor()
      • 2.7 ORBextractor 类总结
        • 2.7.1 主要流程
        • 2.7.2 与其他类的关系

02 特征点提取器 ORBextractor

2.0 基础知识

2.0.1 图像金字塔

(1)尺度不变性:一幅图片中的某个物体的大小永远是那么大(设置一个标准尺度来度量),而不是因为它所在图片的放大缩小而改变。这就是尺度不变性。

(2)图像金字塔
在这里插入图片描述

将原始图像按照 1:1.2 的比例依次缩小,得到 n 幅图像,形如金字塔。观察这 n 幅图像,我们似乎离场景越来越远,这样就用一幅图像制造出了深度变化的效果。

在 SLAM 中,我们对这 n 张图像分别提取特征点,就相当于得到了不同距离的特征,涵盖了更多的尺度。试想一下,假设第一次我们在 10m 处获取一幅图像,经过图像金字塔,相当于得到了 10 1.2 \frac{10}{1.2} 1.210 10 1. 2 2 \frac{10}{1.2^2} 1.2210 10 1. 2 3 \frac{10}{1.2^3} 1.2310… 等处共 n 个不同距离的图像;当我们下次再处于不同距离拍摄该处场景时,就有更多的图像与特征点进行匹配。这样就解决了尺度导致的特征点匹配问题。

参考:https://blog.csdn.net/u011341856/article/details/103707313

2.0.2 ORB 特征点的关键点和描述子

ORB 的关键点是在 FAST 关键点基础上进行了改进,主要是增加了特征点的主方向,称之为 Oriented FAST。

描述子是在 BRIEF 描述子基础上加入了上述方向信息,称之为 Rotated BRIEF。

2.1 构造函数:ORBextractor()

构造函数原型

ORBextractor::ORBextractor(int _nfeatures, float _scaleFactor, int _nlevels, int _iniThFAST, int _minThFAST)

(1)从 yaml 文件中读取参数(以 KITTI00-02.yaml 为例)

成员变量意义在yaml中变量名
int _nfeatures期望提取的特征点个数ORBextractor.nFeatures2000
float _scaleFactor金字塔相邻层级缩放因子ORBextractor.scaleFactor1.2
int _nlevels金字塔层数ORBextractor.nLevels8
int _iniThFAST提取 FAST 特征点的默认阈值ORBextractor.iniThFAST20
int _minThFAST如果使用默认阈值提取不到特征点则使用最小阈值再次提取ORBextractor.minThFAST7

根据上述变量的值计算出下述成员变量:

变量意义
std::vector<float> mvScaleFactor各层级缩放系数{1, 1.2, 1.44, 1.728, 2.074, 2.488, 2.986, 3.583
std::vector<float> mvInvScaleFactor各层级缩放系数的倒数{1, 0.833, 0.694, 0.579, 0.482, 0.402, 0.335, 0.2791}
std::vector<float> mvLevelSigma2各层级缩放系数的平方{1, 1.44, 2.074, 2.986, 4.300, 6.190, 8.916, 12.838}
std::vector<float> mvInvLevelSigma2各层级缩放系数的平方的倒数{1, 0.694, 0.482, 0.335, 0.233, 0.162, 0.112, 0.078}
std::vector<int> mnFeaturesPerLevel每一层期望提取的特征点个数(正比于图层边长,总和为 nfeatures{122, 146, 174, 210, 252, 302, 362, 432}

(2)初始化用于计算描述子的 pattern 变量,也就是用于计算描述子的 256 对坐标

static int bit_pattern_31_[256*4] =
{
    8,-3, 9,5/*mean (0), correlation (0)*/,
    4,2, 7,-12/*mean (1.12461e-05), correlation (0.0437584)*/,
    -11,9, -8,2/*mean (3.37382e-05), correlation (0.0617409)*/,
    ...
}

共 256 行,每一行表示一对坐标点,如第一行为 (8, -3)(9, 5)

(3) 在提取 Oriented FAST 关键点后,还需要计算每个点的描述子。即以关键点为圆心,在半径为 16 的圆的范围内,计算特征点主方向和描述子。

在这里插入图片描述

(注:图片中半径为 8,仅作示意)

成员变量 std::vector<int> umax 中存储的是逼近圆的第一象限内 1 4 \frac{1}{4} 41 圆周上每个 v 坐标对应的 u 坐标。为保证严格对称性,先计算下 45° 圆周上点的坐标,再根据对称性补全上 45° 圆周上点的坐标。

int vmax = cvFloor(HALF_PATCH_SIZE * sqrt(2.f) / 2 + 1); 	// 45°射线与圆周交点的纵坐标
int vmin = cvCeil(HALF_PATCH_SIZE * sqrt(2.f) / 2);			// 45°射线与圆周交点的纵坐标

// 先计算下半45度的umax
for (int v = 0; v <= vmax; ++v) {
	umax[v] = cvRound(sqrt(15 * 15 - v * v));	
}

// 根据对称性补出上半45度的umax
for (int v = HALF_PATCH_SIZE, v0 = 0; v >= vmin; --v) {
    while (umax[v0] == umax[v0 + 1])
        ++v0;
    umax[v] = v0;
    ++v0;
}

2.2 构建图像金字塔 ComputePyramid()

变量访问控制意义
std::vector<cv::Mat> mvImagePyramidpublic存储图像金字塔每层的图像
const int EDGE_THRESHOLD全局变量为计算描述子和提取特征点补的 padding 厚度

函数原型

void ORBextractor::ComputePyramid(cv::Mat image)
{
	//开始遍历所有的图层,levels是yaml文件里面的
    for (int level = 0; level < nlevels; ++level)
    {
		//获取本层图像的缩放系数,mvInvScaleFactor[level]是从orbextrator得到的
        float scale = mvInvScaleFactor[level];
		//计算本层图像的像素尺寸大小
        Size sz(cvRound((float)image.cols*scale), cvRound((float)image.rows*scale));
		//全尺寸图像。包括无效图像区域的大小。将图像进行“补边”,EDGE_THRESHOLD区域外的图像不进行FAST角点检测
        Size wholeSize(sz.width + EDGE_THRESHOLD*2, sz.height + EDGE_THRESHOLD*2);
		// temp是扩展了边界的图像,是一个构造函数,拷贝了wholeSize的图像
        Mat temp(wholeSize, image.type()), masktemp;
        // mvImagePyramid 刚开始时是个...空的vector<Mat>
		// 将扩充后的图像拷贝给mvImagePyramid容器
        mvImagePyramid[level] = temp(Rect(EDGE_THRESHOLD, EDGE_THRESHOLD, sz.width, sz.height));

        // Compute the resized image
		//计算第0层以上resize后的图像
        if( level != 0 )
        {
			//将上一层金字塔图像根据前文设定sz缩放到当前层级
            resize(mvImagePyramid[level-1],	//输入图像
				   mvImagePyramid[level], 	//输出图像
				   sz, 						//输出图像的尺寸
				   0, 						//水平方向上的缩放系数,留0表示自动计算
				   0,  						//垂直方向上的缩放系数,留0表示自动计算
				   cv::INTER_LINEAR);		//图像缩放的差值算法类型,这里的是线性插值算法


			//把源图像拷贝到目的图像的中央,四面填充指定的像素。图片如果已经拷贝到中间,只填充边界
			//这样做是为了能够正确提取边界的FAST角点
			//EDGE_THRESHOLD指的这个边界的宽度,由于这个边界之外的像素不是原图像素而是算法生成出来的,所以不能够在EDGE_THRESHOLD之外提取特征点			
            copyMakeBorder(mvImagePyramid[level], 					//源图像
						   temp, 									//目标图像(此时其实就已经有大了一圈的尺寸了)
						   EDGE_THRESHOLD, EDGE_THRESHOLD, 			//top & bottom 需要扩展的border大小
						   EDGE_THRESHOLD, EDGE_THRESHOLD,			//left & right 需要扩展的border大小
                           BORDER_REFLECT_101+BORDER_ISOLATED);     //扩充方式,opencv给出的解释:						
        }
        else
        {
			//对于第0层未缩放图像,直接将图像深拷贝到temp的中间,并且对其周围进行边界扩展。此时temp就是对原图扩展后的图像
            copyMakeBorder(image,			//这里是原图像
						   temp, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD,
                           BORDER_REFLECT_101);            
        }
    }
}

包括两步:

  • 将图像缩放到 mvInvScaleFactor 对应尺寸;

  • 在图像四周补一圈厚度为 EDGE_THRESHOLD 的 padding(提取 FAST 特征点需要特征点周围半径为 3 的圆域,计算描述子需要特征点周围半径为 16 的圆域),copyMakeBorder() 函数实现。

在这里插入图片描述

  • 深灰色 为缩放后的原始图像;

  • 包含绿色边界在内的矩形 用于提取 FAST 特征点;

  • 包含浅灰色边界在内的整个矩形 用于计算 ORB 描述子。

为什么要扩充图像边界呢?

利用 FAST 算法在提取特征点时,图像边缘的特征点半径为3的圆无法取到(边界外无像素点),为了解决此问题,我们对图像边界进行填充。

参考:https://www.pudn.com/news/62f50f18f97302478e3581fc.html

2.3 提取特征点并筛选 ComputeKeyPointsOctTree()

在这里插入图片描述

我们希望 特征点均匀地分布在图像的所有部分。所以在提取时会将图片分成 30*30(单位像素)的一个一个小格子(cell)来提取特征点。并且在提取 FAST 角点时,我们设计了两个阈值 _iniThFAST_minThFAST,这样在每个 cell 中,就可以根据实际情况进行调整,尽可能保证每个 cell 中都能提取到特征点。
在这里插入图片描述
代码实现主要有两步:

  • 划分 cell,先用默认阈值提取特征点,如果找不到,就降低阈值,用 _minThFAST 搜索特征点;

  • 对得到的所有特征点进行八叉树筛选,若某区域内特征点数目过于密集,则只取其中响应值最大的那个。
    在这里插入图片描述

2.4 筛选特征点 DistributeOctTree()

筛选完特征点后,还是可能出现某些 cell 中特征点密集,某些 cell 中稀疏甚至没有特征点。因此采用类似八叉树的方法重新分发角点,使一个区域只有一个特征点(注意,这里的区域是重新划分的,不是之前的 cell)。
在这里插入图片描述

(没有特征点或只有一个特征点的区域不再分裂。)

2.5 计算特征点方向 computeOrientation()

使用特征点周围半径为 19 的圆的重心方向作为特征点方向。

M 00 = ∑ X = − R R ∑ Y = − R R I ( x , y ) M 10 = ∑ X = − R R ∑ X = − R R x I ( x , y ) M 01 = ∑ X = − R R ∑ X = − R R y I ( x , y ) Q X = = M 10 M 00 , Q Y = M 01 M 00 C = ( m 10 m 00 , m 00 m 00 ) θ = atan ⁡ 2 ( m 01 , m 10 ) \begin{aligned} & M_{00}=\sum_{X=-R}^R \sum_{Y=-R}^R I(x, y) \\ & M_{10}=\sum_{X=-R}^R \sum_{X=-R}^R x I(x, y) \\ & M_{01}=\sum_{X=-R}^R \sum_{X=-R}^R y I(x, y) \\ & Q_{X=}=\frac{M_{10}}{M_{00}}, Q_Y=\frac{M_{01}}{M_{00}} \\ & C=\left(\frac{m_{10}}{m_{00}}, \frac{m_{00}}{m_{00}}\right) \\ & \theta=\operatorname{atan} 2\left(m_{01}, m_{10}\right) \end{aligned} M00=X=RRY=RRI(x,y)M10=X=RRX=RRxI(x,y)M01=X=RRX=RRyI(x,y)QX==M00M10,QY=M00M01C=(m00m10,m00m00)θ=atan2(m01,m10)
c x = ∑ x = − R ∑ y = − R R x I ( x , y ) ⏞ m 10 ∑ x = − R R ∑ y = − R R I ( x , y ) ⏟ m 00 , c y = ∑ x = − R R ∑ y = − R R y I ( x , y ) ⏞ m 01 ∑ x = − R R ∑ y = − R R I ( x , y ) ⏟ m 00 θ = arctan ⁡ 2 ( c y , c x ) = arctan ⁡ 2 ( m 01 , m 10 ) \begin{aligned} & c_x=\frac{\overbrace{\sum_{x=-R} \sum_{y=-R}^R x I_{(x, y)}}^{m_{10}}}{\underbrace{\sum_{x=-R}^R \sum_{y=-R}^R I_{(x, y)}}_{m_{00}}}, c_y=\frac{\overbrace{\sum_{x=-R}^R \sum_{y=-R}^R y I_{(x, y)}}^{m_{01}}}{\underbrace{\sum_{x=-R}^R \sum_{y=-R}^R I_{(x, y)}}_{m_{00}}} \\ & \theta=\arctan 2\left(c_y, c_x\right)=\arctan 2\left(m_{01}, m_{10}\right) \\ & \end{aligned} cx=m00 x=RRy=RRI(x,y)x=Ry=RRxI(x,y) m10,cy=m00 x=RRy=RRI(x,y)x=RRy=RRyI(x,y) m01θ=arctan2(cy,cx)=arctan2(m01,m10)

2.6 计算特征点描述子 computeOrbDescriptor()

在特征点周围半径为 16 的圆域内选取 256 对点,比较,得到 256 位描述子。
在这里插入图片描述

computeOrientation() 中,我们求出了每个特征点的主方向,因此在计算描述子之前,要先将特征点周围像素旋转到主方向上来。
在这里插入图片描述

2.7 ORBextractor 类总结

2.7.1 主要流程

ORBextractor 类用于 tracking 线程中第一步预处理。

主要流程为
在这里插入图片描述

2.7.2 与其他类的关系

Frame类 中与 ORBextractor 有关的成员变量和函数

成员变量/函数访问控制意义
ORBextractor* mpORBextractorLeftpublic左目特征点提取器
ORBextractor* mpORBextractorRightpublic右目特征点提取器(单目/RGBD时为空指针)
ExtractORB()public提取特征点,直接调用 mpORBextractorLeftmpORBextractorRight
FramepublicFrame类的构造函数,调用 ExtractORB() 提取特征点

每次提取完 ORB 特征点之后,图像金字塔信息就会作废,下一帧图像到来时调用 ComputePyramid() 函数会覆盖掉上一帧的图像金字塔信息;但已经提取到的特征点信息会被保留在 Frame 对象中。所以 ORB-SLAM2 是稀疏重建,每帧图像只会保留最多 nfeatures 个特征点。

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

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

相关文章

springboot+vue前后端分离的社区养老服务管理管理系统(有文档)

springbootvue前后端分离的社区养老服务管理管理系统。系统功能齐全&#xff0c;配置完成可运行&#xff0c;有文档&#xff0c;演示视频&#xff0c;配置说明&#xff0c;数据库文件&#xff0c;虚拟产品下单不退不换&#xff01; 技术&#xff1a;springbootmybatisplusmysql…

NPOI 导出Excel提示内容有问题的解决方法

NPOI导出Excel 使用Microsoft Excel 打开提示内容有问题&#xff0c;如下&#xff1a; 原因是&#xff1a;在使用NPOI导出excel时&#xff0c;获得 workbook.Write(ms)生成的 MemoryStream后&#xff0c;使用了 ms.GetBuffer()返回文件内容&#xff0c;导致生成的 Excel文件结尾…

【读论文】PSFusion

【读论文】Rethinking the necessity of image fusion in high-level vision tasks: A practical infrared and visible image fusion network based on progressive semantic injection and scene fidelity 介绍解决的问题网络架构整体架构稀疏语义感知分支&#xff08; spars…

Jmeter 性能测试 —— 评估一个系统TPS与并发数!

问题&#xff1a;性能压测&#xff0c;如何评估一个系统的TPS和并发数&#xff1f; 1、对于新系统 由业务部门或开发人员预估交易量和TPS指标 可以参考公式&#xff1a;并发用户 在线用户数 * 10%。 当一个系统还没有上线时&#xff0c;我们可以预判的是这个系统准备要给多…

PCL配置记录

PCL配置记录 1. Windows10vs2019pcl win10vs2019pcl 1.11.1 1.下载与安装 https://github.com/PointCloudLibrary/pcl/releases ) 双击exe安装 注意&#xff1a; ( ) 解压 “pcl-1.11.0-pdb-msvc2019-win64.zip”&#xff0c;将解压得到的文件夹中的内容添加“…\PCL…

云手机解决Tik Tok运营难题

对于海外营销的企业来说&#xff0c;Tik Tok是不可错过的宣传平台。本文将介绍Tik Tok运营过程中会遇到的几个问题&#xff0c;以及如何通过海外云手机解决。 Tik Tok运营的主要问题 1、高昂的硬件成本和封号风险 很多企业会选择矩阵式营销&#xff0c;但为了不封号每个账号都…

ansible的脚本:playbook剧本

&#xff08;一&#xff09;playbook的组成部分 tasks 任务&#xff0c;包含要在主机上执行的操作&#xff0c;使用模块定义这些操作&#xff0c;每一个任务都是一个模块的调用 variables 变量&#xff0c;存储和传递数据&#xff08;和shell脚本中的变量是一个意思&#xf…

理解Java中锁的应用

一、死锁 1.1 定义&#xff1a; 当线程 A 持有独占锁a&#xff0c;并尝试去获取独占锁 b 的同时&#xff0c;线程 B 持有独占锁 b&#xff0c;并尝试获取独占锁 a 的情况下&#xff0c;就会发生 AB 两个线程由于互相持有对方需要的锁&#xff0c;而发生的阻塞现象&#xff0c…

雄雄的小课堂微信机器人流程图

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。 最近改造了下微信机器人&#xff0c;新版本还未上线&#xff0c;预计下周一&#xff08;12.25&#xff09;左右能上线第一版。 下面是改造之后的流程图 大家可以看看&#xff0c;有疑问可…

【计算机视觉中的多视图几何系列】深入浅出理解针孔相机模型

温故而知新&#xff0c;可以为师矣&#xff01; 一、参考资料 《计算机视觉中的多视图几何-第五章》-Richard Hartley, Andrew Zisserman. 二、针孔模型相关介绍 1. 重要概念 1.1 投影中心/摄像机中心/光心 投影中心称为摄像机中心&#xff0c;也称为光心。投影中心位于一…

Ubuntu及Docker 安装rabbitmq

安装ubuntu 前 先暴露端口&#xff1a; 5672 用于与mq服务器通信用 15672 管理界面使用的端口 docker命令&#xff1a;docker run -itd --name ubuntu -p 5672:5672 -p 15672:15672 ubuntu 进入docker : docker exec -it ubuntu /bin/bash 步骤&#xff1a; 1. 更新安装源…

算法学习系列(九):离散化

目录 引言一、离散化概念二、离散化模板三、例题四、测试 引言 这个离散化我的理解就是你如果要用到数组的下标进行存数&#xff0c;会有多个询问针对下标进行操作&#xff0c;然后这个下标特别的大&#xff0c;而且存的数也是特别的分散&#xff0c;举个例子就是有三个数&…

VScode 工作区配置 和 用户配置

一、工作区配置 通常不同的项目都有不同的配置&#xff0c;我一般都是使用eslint和prettier一起用&#xff0c;所以经常会有这几个文件&#xff1a; 这里简单介绍一下这几个文件的作用吧。 1.vscode文件夹下 一般有两个文件&#xff0c;extensions.json和settings.json。 e…

07 Vue3中的三元表达式

概述 三元表达式时JavaScript中比较常用的一种原生语法&#xff0c;能够在一行代码中实现if-else的分支逻辑。 在Vue的双大括号中&#xff0c;我们也可以使用三元表达式去实现一些简单的条件渲染。 基本用法 我们创建src/components/Demo07.vue&#xff0c;先尝试一下三元表…

【泛型中K T V E? Object等分别代表什么含 义】

✅ 泛型中K T V E? Object等分别代表什么含义 ✅ 典型解析✅代码示例 ✅ 典型解析 E - Element (在集合中使用&#xff0c;因为集合中存放的是元素) T-Type (Java 类) K- Key (键) V - Value (值) N - Number (数值类型) ? - 表示不确定的iava类型 (无限制通配符类型) …

linux 中 C++的环境搭建以及测试工具的简单介绍

文章目录 makefleCMakegdb调试 与 coredumpValgrind 内存检测gtest 单元测试 makefile 介绍 安装 : sudo apt install make makefile 的规则: 举例说明 包括&#xff1a;目标文件 、 依赖文件 、 生成规则 使用 &#xff1a; make make clean CMake : CMake是一个…

OpenHarmony 4.0 Release发布,同步升级API 10

不久之前&#xff0c;OpenHarmony 正式发布了4.0 版本&#xff0c;开发套件也同步升级到 API 10。相比 3.2 Release 版本&#xff0c;4.0 版本新增 4000 多个 ArkTS API&#xff0c;应用开发能力更加丰富&#xff1b;HDF 新增 200 多个 HDI 接口&#xff0c;硬件适配更加便捷&a…

浅谈在线监测系统与配电能效平台在供水水厂的应用

贾丽丽 安科瑞电气股份有限公司 上海嘉定 201800 【摘要】针对自来水厂工艺老化资金有限的问题&#xff0c;设计水厂在线监测系统&#xff0c;采用安科瑞&#xff0c;对原水滤后水、出厂水进行采样分析&#xff0c;并通过基于组态的上位机系统实现水质数据的实时监测。该系统…

听GPT 讲Rust源代码--src/tools(23)

File: rust/src/tools/clippy/rustc_tools_util/src/lib.rs 在Rust源代码中&#xff0c;rust/src/tools/clippy/rustc_tools_util/src/lib.rs文件的作用是为Clippy提供了一些实用工具和辅助函数。 该文件中定义了VersionInfo结构体&#xff0c;它有三个字段&#xff0c;分别为m…

opencv入门到精通——图像上的算术运算

目录 目标 图像加法 图像融合 按位运算 目标 学习图像的几种算术运算&#xff0c;例如加法&#xff0c;减法&#xff0c;按位运算等。 您将学习以下功能&#xff1a;cv.add&#xff0c;cv.addWeighted等。 图像加法 您可以通过OpenCV函数cv.add()或仅通过numpy操作res …