ORB-SLAM2 ---- ORBmatcher::SearchForInitialization函数

news2024/11/20 7:12:36

目录

1.函数作用

2.执行流程 

3.函数参数解析 

4.code 

5.函数解析 

5.1 旋转直方图的构建与作用

5.2 遍历帧1中的特征点在帧2中找出候选匹配特征点 

5.3 第一层筛选  -- 阈值、最优/次优比例、重复匹配

5.4  第二层筛选  -- 旋转直方图

5.5 final 将最后通过筛选的匹配好的特征点保存到vbPrevMatched 


1.函数作用

        单目初始化中用于参考帧和当前帧的特征点匹配。

2.执行流程 

Step 1 构建旋转直方图
Step 2 在半径窗口内搜索当前帧F2中所有的候选匹配特征点 
Step 3 遍历搜索搜索窗口中的所有潜在的匹配候选点,找到最优的和次优的
Step 4 对最优次优结果进行检查,满足阈值、最优/次优比例,删除重复匹配
Step 5 计算匹配点旋转角度差所在的直方图
Step 6 筛除旋转直方图中“非主流”部分
Step 7 将最后通过筛选的匹配好的特征点保存

3.函数参数解析 

@param[in] F1                        初始化参考帧                  
@param[in] F2                        当前帧
@param[in & out] vbPrevMatched       本来存储的是参考帧的所有特征点坐标,该函数更新为匹配好的当前帧的特征点坐标
@param[in & out] vnMatches12         保存参考帧F1中特征点是否匹配上,index保存是F1对应特征点索引,值保存的是匹配好的F2特征点索引
@param[in] windowSize                搜索窗口
@return int                          返回成功匹配的特征点数目

4.code 

int ORBmatcher::SearchForInitialization(Frame &F1, Frame &F2, vector<cv::Point2f> &vbPrevMatched, vector<int> &vnMatches12, int windowSize)
{
    int nmatches=0;
    // F1中特征点和F2中匹配关系,注意是按照F1特征点数目分配空间
    vnMatches12 = vector<int>(F1.mvKeysUn.size(),-1);

    // Step 1 构建旋转直方图,HISTO_LENGTH = 30
    vector<int> rotHist[HISTO_LENGTH];
    for(int i=0;i<HISTO_LENGTH;i++)
    // 每个bin里预分配500个,因为使用的是vector不够的话可以自动扩展容量
        rotHist[i].reserve(500);   

    //! 原作者代码是 const float factor = 1.0f/HISTO_LENGTH; 是错误的,更改为下面代码   
    const float factor = HISTO_LENGTH/360.0f;

    // 匹配点对距离,注意是按照F2特征点数目分配空间
    vector<int> vMatchedDistance(F2.mvKeysUn.size(),INT_MAX);
    // 从帧2到帧1的反向匹配,注意是按照F2特征点数目分配空间
    vector<int> vnMatches21(F2.mvKeysUn.size(),-1);

    // 遍历帧1中的所有特征点
    for(size_t i1=0, iend1=F1.mvKeysUn.size(); i1<iend1; i1++)
    {
        cv::KeyPoint kp1 = F1.mvKeysUn[i1];
        int level1 = kp1.octave;
        // 只使用原始图像上提取的特征点
        if(level1>0)
            continue;

        // Step 2 在半径窗口内搜索当前帧F2中所有的候选匹配特征点 
        // vbPrevMatched 输入的是参考帧 F1的特征点
        // windowSize = 100,输入最大最小金字塔层级 均为0
        vector<size_t> vIndices2 = F2.GetFeaturesInArea(vbPrevMatched[i1].x,vbPrevMatched[i1].y, windowSize,level1,level1);

        // 没有候选特征点,跳过
        if(vIndices2.empty())
            continue;

        // 取出参考帧F1中当前遍历特征点对应的描述子
        cv::Mat d1 = F1.mDescriptors.row(i1);

        int bestDist = INT_MAX;     //最佳描述子匹配距离,越小越好
        int bestDist2 = INT_MAX;    //次佳描述子匹配距离
        int bestIdx2 = -1;          //最佳候选特征点在F2中的index

        // Step 3 遍历搜索搜索窗口中的所有潜在的匹配候选点,找到最优的和次优的
        for(vector<size_t>::iterator vit=vIndices2.begin(); vit!=vIndices2.end(); vit++)
        {
            size_t i2 = *vit;
            // 取出候选特征点对应的描述子
            cv::Mat d2 = F2.mDescriptors.row(i2);
            // 计算两个特征点描述子距离
            int dist = DescriptorDistance(d1,d2);

            if(vMatchedDistance[i2]<=dist)
                continue;
            // 如果当前匹配距离更小,更新最佳次佳距离
            if(dist<bestDist)
            {
                bestDist2=bestDist;
                bestDist=dist;
                bestIdx2=i2;
            }
            else if(dist<bestDist2)
            {
                bestDist2=dist;
            }
        }

        // Step 4 对最优次优结果进行检查,满足阈值、最优/次优比例,删除重复匹配
        // 即使算出了最佳描述子匹配距离,也不一定保证配对成功。要小于设定阈值
        if(bestDist<=TH_LOW)
        {
            // 最佳距离比次佳距离要小于设定的比例,这样特征点辨识度更高
            if(bestDist<(float)bestDist2*mfNNratio)
            {
                // 如果找到的候选特征点对应F1中特征点已经匹配过了,说明发生了重复匹配,将原来的匹配也删掉
                if(vnMatches21[bestIdx2]>=0)
                {
                    vnMatches12[vnMatches21[bestIdx2]]=-1;
                    nmatches--;
                }
                // 次优的匹配关系,双向建立
                // vnMatches12保存参考帧F1和F2匹配关系,index保存是F1对应特征点索引,值保存的是匹配好的F2特征点索引
                vnMatches12[i1]=bestIdx2;
                vnMatches21[bestIdx2]=i1;
                vMatchedDistance[bestIdx2]=bestDist;
                nmatches++;

                // Step 5 计算匹配点旋转角度差所在的直方图
                if(mbCheckOrientation)
                {
                    // 计算匹配特征点的角度差,这里单位是角度°,不是弧度
                    float rot = F1.mvKeysUn[i1].angle-F2.mvKeysUn[bestIdx2].angle;
                    if(rot<0.0)
                        rot+=360.0f;
                    // 前面factor = HISTO_LENGTH/360.0f 
                    // bin = rot / 360.of * HISTO_LENGTH 表示当前rot被分配在第几个直方图bin  
                    int bin = round(rot*factor);
                    // 如果bin 满了又是一个轮回
                    if(bin==HISTO_LENGTH)
                        bin=0;
                    assert(bin>=0 && bin<HISTO_LENGTH);
                    rotHist[bin].push_back(i1);
                }
            }
        }

    }

    // Step 6 筛除旋转直方图中“非主流”部分
    if(mbCheckOrientation)
    {
        int ind1=-1;
        int ind2=-1;
        int ind3=-1;
        // 筛选出在旋转角度差落在在直方图区间内数量最多的前三个bin的索引
        ComputeThreeMaxima(rotHist,HISTO_LENGTH,ind1,ind2,ind3);

        for(int i=0; i<HISTO_LENGTH; i++)
        {
            if(i==ind1 || i==ind2 || i==ind3)
                continue;
            // 剔除掉不在前三的匹配对,因为他们不符合“主流旋转方向”    
            for(size_t j=0, jend=rotHist[i].size(); j<jend; j++)
            {
                int idx1 = rotHist[i][j];
                if(vnMatches12[idx1]>=0)
                {
                    vnMatches12[idx1]=-1;
                    nmatches--;
                }
            }
        }

    }

    //Update prev matched
    // Step 7 将最后通过筛选的匹配好的特征点保存到vbPrevMatched
    for(size_t i1=0, iend1=vnMatches12.size(); i1<iend1; i1++)
        if(vnMatches12[i1]>=0)
            vbPrevMatched[i1]=F2.mvKeysUn[vnMatches12[i1]].pt;

    return nmatches;
}

5.函数解析 

5.1 旋转直方图的构建与作用

        一般的直方图如下图所示,频数和区间是描述某一范围的样本数量的图形化表示,比如我们将0-300分为10个区间,那么第一个小块代表的就是0-30分的样本数量。

        在ORBSLAM2中,我们用旋转直方图表达“旋转角度差”。这个是描述下面一个事情:

        我们在提取特征点时,计算了一个特征点的灰度质心角,当我们匹配两帧特征点时,即使两帧间发生了一定的旋转,但是他们的灰度质心角的差应该是相似的,如下图:

        我们构建旋转直方图,创建了HISTO_LENGTH=30个区间,给个区间对应360/30=20°,每个空间预留空间为500。

5.2 遍历帧1中的特征点在帧2中找出候选匹配特征点 

        我们遍历每一个帧一中的每一个特征点:

        同level1变量记录帧一中正在遍历特征点的在图像金字塔中的层数,在单目初始化时我们只使用原始图像上提取的特征点,因此如果level1>0则不对这个特征点进行匹配。

        我们用GetFeaturesInArea函数来求在帧二中与当前特征点待匹配特征点的候选匹配特征点的集合。

ORB-SLAM2 ---- Frame::GetFeaturesInArea函数解析icon-default.png?t=M85Bhttps://blog.csdn.net/qq_41694024/article/details/128227154        到这里,我们对于帧一的某特征点找到了帧二中的候选特征点集合vIndices2

5.3 第一层筛选  -- 阈值、最优/次优比例、重复匹配

        我们在上一步得到了第二帧中与第一帧的某一特征点候选的匹配特征点的集合vIndices2。

        现在我们计算帧一中的特征点与vIndices2中的每一个待匹配特征点的描述子距离。我们知道,描述子的汉明距离越小匹配度越好,我们遍历搜索搜索窗口中的所有潜在的匹配候选点,找到最优的和次优的特征点匹配结果,它们在vIndices2的索引是bestDistbestDist2

        ①阈值判定:我们即使计算出了最优距离和次优距离,但是这个距离越大的话也就意味着这个匹配越不精准,因此我们设置阈值TH_LOW,只有最佳描述子匹配距离小于设定阈值才保证匹配的精准性,若不满足则舍弃这对匹配点。

        ②最优/次优比例:最佳距离比次佳距离要小于设定的比例,这样特征点辨识度更高。

        ③重复匹配:由于我们匹配的特征点有可能存在重复匹配问题,因此这步也是必须的:

        vnMatches21向量存储了帧2到帧1的匹配关系,如果帧二的vnMatches21[bestIdx2]不为空,即第二帧的我们刚刚计算出的次好待匹配特征点的索引不为空,则帧2的bestIdx2号索引的特征点与第一帧中索引号为vnMatches21[bestIdx2]的特征点存在匹配关系,则存在重复匹配的问题,我们将vnMatches12[vnMatches21[bestIdx2]],即第一帧的索引为vnMatches21[bestIdx2]的帧的匹配关系置为空(-1)(清除匹配关系),成功匹配数量nmatches--。

        ④若①②③通过检查后,我们建立双向索引

vnMatches12[i1]=bestIdx2;
vnMatches21[bestIdx2]=i1;

        存储匹配点最优距离

vMatchedDistance[bestIdx2]=bestDist;

        成功匹配数量nmatches++。     

5.4  第二层筛选  -- 旋转直方图

        计算匹配点旋转角度差所在的直方图,筛选出在旋转角度差落在在直方图区间内数量最多的前三个bin的索引,剔除掉不在前三的匹配对,因为他们不符合“主流旋转方向” 。

5.5 final 将最后通过筛选的匹配好的特征点保存到vbPrevMatched 

        在匹配完帧一与帧二的特征点后,我们看看我们的参数向量还有局部向量里面都是什么

        ①vbPrevMatched:现在存储的是参考帧(第一帧)的所有特征点坐标

        ②vnMatches12:vnMatches12[i]=j,表示第一帧中索引为i的特征点与第二帧中索引为j的特征点相匹配。若vnMatches12[i] = -1,则表示表示第一帧中索引为i的特征点在第二帧中没有待匹配的特征点。

        ③vMatchedDistance,它的大小为第二帧特征点的数量,里面存放的int型向量是与第一帧中某一特征点匹配的最佳描述子距离。

        ④vnMatches21:从帧2到帧1的反向匹配,前面已经解释过。

        ⑤nmatches:成功匹配的特征点数量

        我们在最后要更新vbPrevMatched向量:

for(size_t i1=0, iend1=vnMatches12.size(); i1<iend1; i1++)
        if(vnMatches12[i1]>=0)
            vbPrevMatched[i1]=F2.mvKeysUn[vnMatches12[i1]].pt;

        对于每一个匹配的特征点对,我们将vbPrevMatched向量中的内容改变为与第一帧中索引i1相匹配的第二帧中的某一特征点的坐标。

        最后我们返回给调用函数成功匹配的特征点对数目nmatches,函数结束。

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

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

相关文章

web前端课程设计——动漫网页2个网页HTML+CSS web前端开发技术 web课程设计 网页规划与设计

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 ⚽精彩专栏推荐&#x1…

【图】深度优先遍历 广度优先遍历

文章目录一、广度优先遍历二、深度优先遍历深度优先遍历和广度优先遍历是遍历图的两种常见方式&#xff0c;接下来就通过这两种方式来实现一下图具体遍历的过程 当我位于游乐园的景区 A 时&#xff0c;为了玩遍所有的景区我们有两种玩的方式&#xff1a; 方式一&#xff1a; …

HDLbits——移位寄存器

移位寄存器 1 4 位移位寄存器 module top_module(input clk,input areset, // async active-high reset to zeroinput load,input ena,input [3:0] data,output reg [3:0] q); always (posedge clk or posedge areset) beginif (areset)q<4h0;else if(load)q<data;els…

linux|shell脚本|有趣的知识---格式化输出日志和脚本调试方法以及kubernetes集群核心服务重启和集群证书备份脚本

前言&#xff1a; shell脚本的功能十分强大&#xff0c;这一点毋庸置疑的。那么&#xff0c;平常的工作中总是免不了和脚本打交道&#xff0c;也免不了要自己编写一些脚本。 每个人都希望自己编写的脚本强壮&#xff0c;简单&#xff0c;易用&#xff0c;功能多&#xff0c;并…

CDH中某一结点任务异常,节点服务重启失败报错:No space left on device

文章目录Error Message - 报错信息Analysis Process - 分析思路Solution - 解决方案Error Message - 报错信息 今天发现cdh集群的某一个节点任务爆红了&#xff0c;因为是测试的服务器&#xff0c;一般我都会尝试直接重启&#xff0c;但是该节点服务关闭后&#xff0c;竟然都无…

Spring Web

目录 概述 SpringMVC的组件 DispatcherServlet HandlerMapping HandlerAdapter SpringWeb的运行流程 Controller类的编写 RestController注解 RequestMapping注解 SpringWeb搭建 获取请求参数 解决POST请求中文乱码问题 Ajax返回JSON数据 跨域问题的解决 拦截器 …

LabVIEW与SQL Server 2919 Express通讯

LabVIEW与SQL Server 2919 Express通讯 ​LabVIEW与数据库通讯&#xff0c;可以使用数据库连接工具包。一般小型数据库用Access就可以了。但是对于长时间&#xff0c;需要存储空间较大的场合&#xff0c;Access一般不超过2G。这样就需要更换其他数据了。 SQL Server不同版本存…

MDC Service 基于 ESP32 推出树莓派 4 形态的 EsPiFF

当您的应用在树莓派 4 上运行不够稳定或耗电量过大时&#xff0c;您可以考虑使用 EsPiFF。这是一款由 MDC-Service 基于乐鑫 ESP32 构建的开发板。EsPiFF 配备有线和无线以太网、SD 卡插槽和 RP2040 协处理器。如果您尚未找到适合您项目的树莓派&#xff0c;不妨尝试一下这款低…

使用 Docker 来快速上手中文 Stable Diffusion 模型:太乙

本篇文章&#xff0c;我们聊聊如何使用 Docker 快速运行中文 Stable Diffusion 模型&#xff1a;太乙。 写在前面 上个月的时候&#xff0c;有朋友和我推荐了一个 “Stable Diffusion” 模型&#xff0c;来自深圳大湾区数字经济研究院(IDEA)的封神榜大模型中的 “太乙” 。 最…

web期末大作业 使用HTML+CSS制作蓝色版爱宠之家带留言板(5页)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

Metal每日分享,4x4颜色矩阵滤镜效果

本案例的目的是理解如何用Metal实现图像4x4颜色矩阵效果滤镜,通过4x4矩阵对RGBA像素处理; Demo HarbethDemo地址实操代码 // 绿色通道加倍 let filter = C7ColorMatrix4x4(matrix: Matrix4x4.Color.greenDouble)// 方案1: ImageView.image = try? BoxxIO(element: originIm…

【Python开发】Flask中的单点登录解决方案

Flask中的单点登录解决方案1.SSO 和 CAS 单点登录&#xff08;Single Sign On&#xff0c;SSO&#xff09;就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后&#xff0c;即可获得访问单点登录系统中其他关联系统和应用软件的权限&#xff0c;同时这种实现…

E. DS哈希查找--Trie树

目录 题目描述 思路分析 AC代码 题目描述 Trie树又称单词查找树&#xff0c;是一种树形结构&#xff0c;如下图所示。 它是一种哈希树的变种。典型应用是用于统计&#xff0c;排序和保存大量的字符串&#xff08;但不仅限于字符串&#xff09;&#xff0c;所以经常被搜索引擎…

【经验版】Linux相关教程(二)

一、参考资料 二、常用指令 1. 安装run软件包 # 可执行权限 chmod x 软件包名.run# 校验软件包安装文件的一致性和完整性 ./软件包名.run --check# 指定安装路径 ./软件包名.run --install如果用户未指定安装路径&#xff0c;则软件会安装到默认路径下&#xff0c;默认安装路…

kafka一致性保证

1、概念 水位标记&#xff1a; 水位或水印&#xff08;watermark&#xff09;一词&#xff0c;表示位置信息&#xff0c;即位移&#xff08;offset&#xff09;。Kafka源码中使用的名字是高水位&#xff0c;HW&#xff08;high watermark&#xff09;。 副本角色&#xff1a;…

华硕编程竞赛11月JAVA专场 E题太空漫步 题解

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;Java全栈软件工程师一枚&#xff0c;来自浙江宁波&#xff0c;负责开发管理公司OA项目&#xff0c;专注软件前后端开发&#xff08;Vue、SpringBoot和微信小程序&#xff09;、系统定制、远程技术指导。CSDN学院、蓝桥云…

cleanmymac4.12最新版下载安装教程

cleanmymac2023的“智能扫描”功能略不同于两外两款软件。除垃圾扫描以外&#xff0c;它还连带有搜索mac潜在威胁以及寻找提升系统性能方案的功能。在垃圾文件分类方面&#xff0c;它将垃圾首先分为系统垃圾、iTunes垃圾、照片垃圾3大类&#xff0c;每一类再做具体细分。但这样…

AUTOSAR OTA升级

一、OTA技术概念 随着高级辅助驾驶的发展和自动驾驶的引入&#xff0c;汽车变得越来越智能&#xff0c;这些智能汽车被软件控制&#xff0c;装有巨量的软件程序&#xff0c;当出现一个软件程序问题或者更新时&#xff0c;如果 按照传统的解决方式 &#xff0c;那都将是一项很繁…

美腾科技科创板上市:预计年营收4.7亿到5.7亿 市值44亿

雷递网 雷建平 12月9日天津美腾科技股份有限公司&#xff08;简称&#xff1a;“美腾科技”&#xff0c;股票代码为&#xff1a;“688420”&#xff09;今日在科创板上市。美腾科技此次发行2211万股&#xff0c;发行价为48.96元&#xff0c;募资总额为10.83亿元。美腾科技开盘价…

Leetcode 1691. 堆叠长方体的最大高度 [Java/C++] 排序+动态规划(附详细证明过程)

给你 n 个长方体 cuboids &#xff0c;其中第 i 个长方体的长宽高表示为 cuboids[i] [widthi, lengthi, heighti]&#xff08;下标从 0 开始&#xff09;。请你从 cuboids 选出一个 子集 &#xff0c;并将它们堆叠起来。如果 widthi < widthj 且 lengthi < lengthj 且 h…