ORB-SLAM2 ---- Initializer::ReconstructF函数

news2024/11/23 11:19:20

目录

1.函数作用

2.函数解析 

2.1 调用函数解析

2.2 Initializer::ReconstructF函数总体思路

2.2.1 代码

2.2.2 总体思路解析 

2.2.3 根据基础矩阵和相机的内参数矩阵计算本质矩阵 

2.2.4 从本质矩阵求解两个R解和两个t解,共四组解

2.2.5 分别验证求解的4种R和t的组合,选出最佳组合


1.函数作用

        从基础矩阵F中求解位姿R,t及三维点。

2.函数解析 

2.1 调用函数解析

return ReconstructF(vbMatchesInliersH,	//输入,匹配成功的特征点对Inliers标记
							H,					//输入,前面RANSAC计算后的单应矩阵
							mK,					//输入,相机的内参数矩阵
							R21,t21,			//输出,计算出来的相机从参考帧1到当前帧2所发生的旋转和位移变换
							vP3D,				//特征点对经过三角测量之后的空间坐标,也就是地图点
							vbTriangulated,		//特征点对是否成功三角化的标记
							1.0,				//这个对应的形参为minParallax,即认为某对特征点的三角化测量中,认为其测量有效时
												//需要满足的最小视差角(如果视差角过小则会引起非常大的观测误差),单位是角度
							50);				//为了进行运动恢复,所需要的最少的三角化测量成功的点个数

        该函数的调用函数为Initializer::Initialize,该函数的目的是初始化SLAM系统,即用单目初始化器的第一帧作为SLAM系统的基点并计算出第一帧和第二帧的变换矩阵并初始化地图点。此函数是在计算出F矩阵的前提下,我们想通过H矩阵来恢复单目初始化器两帧间的位姿。

        输入参数为匹配成功的特征点对Inliers标记、RANSAC计算出的单应矩阵H、相机的内参、认为某对特征点的三角化测量中有效时需要满足的最小视差角、为了进行运动恢复,所需要的最少的三角化测量成功的点个数(如果恢复的3D点小于这个则认为初始化失败)

        输出参数为计算出来的相机从参考帧1到当前帧2所发生的旋转和位移变换、特征点对经过三角测量之后的空间坐标,也就是地图点。

2.2 Initializer::ReconstructF函数总体思路

2.2.1 代码

/**
 * @brief 从基础矩阵F中求解位姿R,t及三维点
 * F分解出E,E有四组解,选择计算的有效三维点(在摄像头前方、投影误差小于阈值、视差角大于阈值)最多的作为最优的解
 * @param[in] vbMatchesInliers          匹配好的特征点对的Inliers标记
 * @param[in] F21                       从参考帧到当前帧的基础矩阵
 * @param[in] K                         相机的内参数矩阵
 * @param[in & out] R21                 计算好的相机从参考帧到当前帧的旋转
 * @param[in & out] t21                 计算好的相机从参考帧到当前帧的平移
 * @param[in & out] vP3D                三角化测量之后的特征点的空间坐标
 * @param[in & out] vbTriangulated      特征点三角化成功的标志
 * @param[in] minParallax               认为三角化有效的最小视差角
 * @param[in] minTriangulated           最小三角化点数量
 * @return true                         成功初始化
 * @return false                        初始化失败
 */
bool Initializer::ReconstructF(vector<bool> &vbMatchesInliers, cv::Mat &F21, cv::Mat &K,
                            cv::Mat &R21, cv::Mat &t21, vector<cv::Point3f> &vP3D, vector<bool> &vbTriangulated, float minParallax, int minTriangulated)
{
    // Step 1 统计有效匹配点个数,并用 N 表示
    // vbMatchesInliers 中存储匹配点对是否是有效
    int N=0;
    for(size_t i=0, iend = vbMatchesInliers.size() ; i<iend; i++)
        if(vbMatchesInliers[i]) N++;

    // Step 2 根据基础矩阵和相机的内参数矩阵计算本质矩阵
    cv::Mat E21 = K.t()*F21*K;

    // 定义本质矩阵分解结果,形成四组解,分别是:
    // (R1, t) (R1, -t) (R2, t) (R2, -t)
    cv::Mat R1, R2, t;

    // Step 3 从本质矩阵求解两个R解和两个t解,共四组解
    // 不过由于两个t解互为相反数,因此这里先只获取一个
    // 虽然这个函数对t有归一化,但并没有决定单目整个SLAM过程的尺度. 
    // 因为 CreateInitialMapMonocular 函数对3D点深度会缩放,然后反过来对 t 有改变.
    //注意下文中的符号“'”表示矩阵的转置
    //                          |0 -1  0|
    // E = U Sigma V'   let W = |1  0  0|
    //                          |0  0  1|
    // 得到4个解 E = [R|t]
    // R1 = UWV' R2 = UW'V' t1 = U3 t2 = -U3
    DecomposeE(E21,R1,R2,t);  
    cv::Mat t1=t;
    cv::Mat t2=-t;

    // Reconstruct with the 4 hyphoteses and check
    // Step 4 分别验证求解的4种R和t的组合,选出最佳组合
    // 原理:若某一组合使恢复得到的3D点位于相机正前方的数量最多,那么该组合就是最佳组合
    // 实现:根据计算的解组合成为四种情况,并依次调用 Initializer::CheckRT() 进行检查,得到可以进行三角化测量的点的数目
	// 定义四组解分别在对同一匹配点集进行三角化测量之后的特征点空间坐标
    vector<cv::Point3f> vP3D1, vP3D2, vP3D3, vP3D4;

	// 定义四组解分别对同一匹配点集的有效三角化结果,True or False
    vector<bool> ,vbTriangulated2,vbTriangulated3, vbTriangulated4;

	// 定义四种解对应的比较大的特征点对视差角
    float parallax1,parallax2, parallax3, parallax4;

	// Step 4.1 使用同样的匹配点分别检查四组解,记录当前计算的3D点在摄像头前方且投影误差小于阈值的个数,记为有效3D点个数
    int nGood1 = CheckRT(R1,t1,							//当前组解
						 mvKeys1,mvKeys2,				//参考帧和当前帧中的特征点
						 mvMatches12, vbMatchesInliers,	//特征点的匹配关系和Inliers标记
						 K, 							//相机的内参数矩阵
						 vP3D1,							//存储三角化以后特征点的空间坐标
						 4.0*mSigma2,					//三角化测量过程中允许的最大重投影误差
						 vbTriangulated1,				//参考帧中被成功进行三角化测量的特征点的标记
						 parallax1);					//认为某对特征点三角化测量有效的比较大的视差角
    int nGood2 = CheckRT(R2,t1,mvKeys1,mvKeys2,mvMatches12,vbMatchesInliers,K, vP3D2, 4.0*mSigma2, vbTriangulated2, parallax2);
    int nGood3 = CheckRT(R1,t2,mvKeys1,mvKeys2,mvMatches12,vbMatchesInliers,K, vP3D3, 4.0*mSigma2, vbTriangulated3, parallax3);
    int nGood4 = CheckRT(R2,t2,mvKeys1,mvKeys2,mvMatches12,vbMatchesInliers,K, vP3D4, 4.0*mSigma2, vbTriangulated4, parallax4);

    // Step 4.2 选取最大可三角化测量的点的数目
    int maxGood = max(nGood1,max(nGood2,max(nGood3,nGood4)));

	// 重置变量,并在后面赋值为最佳R和T
    R21 = cv::Mat();
    t21 = cv::Mat();

    // Step 4.3 确定最小的可以三角化的点数 
    // 在0.9倍的内点数 和 指定值minTriangulated =50 中取最大的,也就是说至少50个
    int nMinGood = max(static_cast<int>(0.9*N), minTriangulated);

	// 统计四组解中重建的有效3D点个数 > 0.7 * maxGood 的解的数目
    // 如果有多个解同时满足该条件,认为结果太接近,nsimilar++,nsimilar>1就认为有问题了,后面返回false
    int nsimilar = 0;
    if(nGood1>0.7*maxGood)
        nsimilar++;
    if(nGood2>0.7*maxGood)
        nsimilar++;
    if(nGood3>0.7*maxGood)
        nsimilar++;
    if(nGood4>0.7*maxGood)
        nsimilar++;

    // Step 4.4 四个结果中如果没有明显的最优结果,或者没有足够数量的三角化点,则返回失败
    // 条件1: 如果四组解能够重建的最多3D点个数小于所要求的最少3D点个数(mMinGood),失败
    // 条件2: 如果存在两组及以上的解能三角化出 >0.7*maxGood的点,说明没有明显最优结果,失败
    if(maxGood<nMinGood || nsimilar>1)	
    {
        return false;
    }


    //  Step 4.5 选择最佳解记录结果
    // 条件1: 有效重建最多的3D点,即maxGood == nGoodx,也即是位于相机前方的3D点个数最多
    // 条件2: 三角化视差角 parallax 必须大于最小视差角 minParallax,角度越大3D点越稳定

    //看看最好的good点是在哪种解的条件下发生的
    if(maxGood==nGood1)
    {
		//如果该种解下的parallax大于函数参数中给定的最小值
        if(parallax1>minParallax)
        {
            // 存储3D坐标
            vP3D = vP3D1;

			// 获取特征点向量的三角化测量标记
            vbTriangulated = vbTriangulated1;

			// 存储相机姿态
            R1.copyTo(R21);
            t1.copyTo(t21);
			
            // 结束
            return true;
        }
    }else if(maxGood==nGood2)
    {
        if(parallax2>minParallax)
        {
            vP3D = vP3D2;
            vbTriangulated = vbTriangulated2;

            R2.copyTo(R21);
            t1.copyTo(t21);
            return true;
        }
    }else if(maxGood==nGood3)
    {
        if(parallax3>minParallax)
        {
            vP3D = vP3D3;
            vbTriangulated = vbTriangulated3;

            R1.copyTo(R21);
            t2.copyTo(t21);
            return true;
        }
    }else if(maxGood==nGood4)
    {
        if(parallax4>minParallax)
        {
            vP3D = vP3D4;
            vbTriangulated = vbTriangulated4;

            R2.copyTo(R21);
            t2.copyTo(t21);
            return true;
        }
    }

    // 如果有最优解但是不满足对应的parallax>minParallax,那么返回false表示求解失败
    return false;
}

2.2.2 总体思路解析 

        我们先根据基础矩阵和相机的内参数矩阵计算本质矩阵,利用多视图几何的知识分解本质矩阵E得到两个R解和两个t解,共四组解。再分别验证求解的4种R和t的组合,选出最佳组合作为初始化两帧的R,t位姿变换,并用三角化得到的地图点作为地图初始化的地图点。

2.2.3 根据基础矩阵和相机的内参数矩阵计算本质矩阵 

        对极约束如下:具体推导见博客对极约束推导

p_{2}^{T}K^{-T}EK^{-1}p_{1}=0

        我们定义本质矩阵EE= t^{\wedge }R,我们定义基础矩阵FF=K^{-T}EK^{-1}

        由于我们之前计算出了举出矩阵E,于是通过下列矩阵变换我们可以求出本质矩阵:

    cv::Mat E21 = K.t()*F21*K;

2.2.4 从本质矩阵求解两个R解和两个t解,共四组解

         通过Initializer::DecomposeE函数可以通过本质矩阵计算出两组R ,t共四组解,具体推导见多视图几何书,这个我们不需要掌握,只需知道通过这个函数可以解出四组(R,t)解。

2.2.5 分别验证求解的4种R和t的组合,选出最佳组合

        我们对于每组解调用Initializer::CheckRT函数,求得从每组(R,t)解中通过三角化恢复的3D地图点坐标数量,再由一系列的判定手段判断该三角化的地图点是否满足一系列要求。
CheckRT函数解析https://blog.csdn.net/qq_41694024/article/details/127945657

        我们得到这四组解成功初始化的地图点的数量nGood1、nGood2、nGood3、nGood4。        

        我们记录最大可三角化测量的点的数目maxGood、确定最小的可以三角化的点数(在0.9倍的内点数和 指定值minTriangulated =50 中取最大的,也就是说至少50个) ,再统计四组解中重建的有效3D点个数 > 0.7 * maxGood 的解的数目nsimilar,这是因为如果有多个解同时满足该条件,认为结果太接近,nsimilar++,nsimilar>1就认为有问题了,后面返回false。四个结果中如果没有明显的最优结果,或者没有足够数量的三角化点,则返回失败。最后记录最佳结果(初始化的3D点、最优的R,t,特征点是否三角化成功的bool向量vbTriangulated)并返回给上层函数调用表示地图初始化成功!

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

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

相关文章

准备面试题【面试】

前言 写作于 2022-11-13 19:27:08 发布于 2022-11-20 16:34:44 准备 程序员囧辉 我要进大厂 面试阿里&#xff0c;HashMap 这一篇就够了 Java 基础高频面试题&#xff08;2022年最新版&#xff09; 问遍了身边的面试官朋友&#xff0c;我整理出这份 Java 集合高频面试题…

【mysql】mysql 数据备份与恢复使用详解

一、前言 对一个运行中的线上系统来说&#xff0c;定期对数据库进行备份是非常重要的&#xff0c;备份不仅可以确保数据的局部完整性&#xff0c;一定程度上也为数据安全性提供了保障&#xff0c;设想如果某种极端的场景下&#xff0c;比如磁盘损坏导致某个时间段数据丢失&…

什么是Spring,Spring的核心和设计思想你了解吗?

目录 1.初识Spring 1.1 什么是容器 1.2 什么是IoC 2.什么是IoC容器. 2.1 什么是DI 哈喽呀,你好呀,欢迎呀,快来看一下这篇宝藏博客吧~~~ 1.初识Spring Srping指的是Spring Framework(Spring 框架).我们经常会听见框架二字,其中java中最最主流的框架当属Spring.Spring是一…

SAP S4 FI后台详细配置教程- PART4 (科目及税费相关配置篇)

目录 1、总帐科目 1.1编辑科目表清单 1.2 科目表分配给公司代码 1.3 定义科目组 1.4 定义留存收益科目 2、销售/购置税 2.1 维护销售/购置税务代码税率 2.2 配置销项/销项税会计科目 大家好本篇是&#xff1a;SAP S4 FI后台详细配置教程- PART4 &#xff08;科目及税…

Fiddler的安装和使用

文章目录1、Fiddler的安装2、Fiddler的使用3、抓包工具的原理1、Fiddler的安装 官网链接&#xff1a;https://www.telerik.com/fiddler 进入官网首页 页面跳转后 2、Fiddler的使用 下载好后直接安装&#xff0c;安装后打开&#xff0c;它就会自动抓取HTTP包&#xff0c;在左…

[附源码]SSM计算机毕业设计-东湖社区志愿者管理平台JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

力扣113题引发的关于DFS和回溯的一点思考

最近刚学回溯和DFS&#xff0c;刷力扣遇到一道题&#xff08;113题&#xff09;&#xff0c;如下&#xff1a; 我们不细究回溯和DFS的区别联系。关于这道题的2种写法&#xff0c;我把第一种称为回溯。 class Solution {List<List<Integer>> res new LinkedList&l…

29.Nacos的简介与安装(springcloud)

1.Nacos 简介官网&#xff1a; https://nacos.io/zh-cn/Nacos 致力于发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集&#xff0c;帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。 Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是…

【Android Studio Gradle】发布aar到私有Artifactory仓库

1. 前言 在【Android Studio Gradle】使用Artifactory构建本地仓库中介绍了如何利用工具配置一个maven私有库&#xff0c;那么在开发library的时候为了方便难免会用到需要将该库发布到这个仓库的功能。经过测试和配置&#xff0c;确实在Artifactory仓库中也可以通过gradlew命令…

【MySQL基础】为什么大部分人选择使用MySQL数据库?

目录 一、为什么大部分人选择使用MySQL数据库&#xff1f; 二、MySQL简介 1.MySQL介绍 2.MySQL的特点 3. MySQL的版本 从用户的角度&#xff0c;针对不同的用户 从单纯的版本数字区分 &#x1f49f; 创作不易&#xff0c;不妨点赞&#x1f49a;评论❤️收藏&#x1f49…

我把皮小浪の的 蓝色妖姬系列做进了java窗口

— &#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 收录于专栏 unity实战入门 ⭐效果图如下 ⭐⭐涉及的相关类的包含关系图 ⭐# 视频入口&#xff1a;请点击 文章目录一…

比 O(nlog(n)) 做得更好——2.改变问题以及排序和填充数组

改变问题&#xff0c;以及对键进行排序和填充数组。 目录 【第1篇】比 O(nlog(n)) 做得更好——1.创造合适的条件 长按关注《Python学研大本营》&#xff0c;加入读者群&#xff0c;分享更多精彩 扫码关注《Python学研大本营》&#xff0c;加入读者群&#xff0c;分享更多精彩 …

【Redis-04】Redis两种持久化方式

Redis是基于内存的数据结构服务器&#xff0c;保存了大量的键值对数据&#xff0c;所以持久化到磁盘是非常必要的&#xff0c;Redis提供了两种持久化的方式&#xff0c;分别是RDB和AOF。下面我们看下这两种持久化方式的具体实现原理。 1.RDB持久化 首先&#xff0c;RDB持久化方…

【Spring(二)】java对象属性的配置(Bean的配置)

有关Spring的所有文章都收录于我的专栏&#xff1a;&#x1f449;Spring&#x1f448; 目录 一、前言 二、通过Setter方法配置Bean 三、通过构造器配置Bean   1. 通过属性名配置   2. 通过索引配置   3. 通过属性类型配置 四、通过p命名空间配置bean 五、引用/注入其他bean…

openEuler快速入门-openEuler系统安装openGauss数据库安装

文章目录前言一、安装openEuler系统安装二、运行虚拟机&#xff0c;配置三、安装openGauss数据库总结前言 openEuler&#xff1a;openEuler 是一款开源操作系统。当前 openEuler 内核源于 Linux&#xff0c;支持鲲鹏及其它多种处理器&#xff0c;能够充分释放计算芯片的潜能&a…

[附源码]java毕业设计网络身份认证技术及方法

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

_001_Zotero入门

Zetoro大纲一、安装Zotero二、收集题录2.1 浏览器插件2.2 通过标识符添加2.3 拖拽文献2.4 从剪切板导入2.5 批量导入2.6 手动添加&#xff08;不建议使用&#xff09;2.7 方法总结三、管理题录3.1 移动分类3.2 查重3.3 关联3.4 标签3.5 笔记3.6 RSS订阅3.7 快捷键3.8 总结四、在…

Verilog 时序逻辑 UDP

时序逻辑 UDP 与组合逻辑 UDP 在定义形式和行为功能上均有不同&#xff0c;主要区别如下&#xff1a; 1、时序逻辑 UDP 的输出端必须声明为 reg 型。2、时序逻辑 UDP 可以用 initial 语句初始化。3、状态表格式也稍有不同&#xff1a; ... : <current_state> : &l…

RabbitMQ初步到精通-第七章-RabbitMQ之延迟队列

目录 第七章-RabbitMQ之延迟队列 1. 延迟队列概念 2. 应用场景 3. 架构模式 3.1 队列TTL实现 3.2 消息TTL实现 3.3 插件实现 4. 代码验证 5. 总结 第七章-RabbitMQ之延迟队列 1. 延迟队列概念 延迟-意即 非实时&#xff0c;之前我们讨论大部分的案例都是生产者将消息发…

【毕业设计】61-基于单片机的超声波测距仪设计(原理图、仿真工程、低重复率参考设计文档、PPT、开题报告、任务书)

【毕业设计】61-基于单片机的超声波测距仪设计&#xff08;原理图、仿真工程、低重复率参考设计文档、PPT、开题报告、任务书&#xff09;[toc] 资料下载链接 资料下载链接 资料链接&#xff1a;https://www.cirmall.com/circuit/33762/ 包含此题目毕业设计全套资料&#xff…