ORB-SLAM(五)KeyFrame类
KeyFrame类利用Frame类来构造。对于什么样的Frame可以认为是关键帧以及何时需要加入关键帧,是实现在tracking模块中的。
由于KeyFrame中一部分数据会被多个线程访问修改,因此需要在这些成员中加线程锁,保证同一时间只有一个线程有访问权。涉及线程安全的有:
关键帧位姿的设置(lock(mMutexPose));
关键帧间连接关系的设置(lock(mMutexConnections));
关键帧对应地图点的操作(lock(mMutexFeatures)),包括通过地图点计算相连关键帧之间的权重。
1. 设置相机位姿参数:设置KeyFrame中成员变量mTcw,mTwc,Ow(左目相机中心坐标),和Cw(双目相机baseline中点坐标),相机坐标Z朝北,X朝东,Y朝地。
并给出了get函数获取姿态参数。
注意
- 这里的Ow等价于twc,表示当前相机光心在世界坐标系下的三维坐标
- Tcw直接求逆计算量比较大,一般矩阵求逆在实现时都会用等价的矩阵表达式去表示,这里Ow就对应Tcw-1中的平移向量-RTt.
- void KeyFrame::SetPose(const cv::Mat &Tcw_)
- {
- unique_lock<mutex> lock(mMutexPose);
- Tcw_.copyTo(Tcw);
- cv::Mat Rcw = Tcw.rowRange(,).colRange(,);
- cv::Mat tcw = Tcw.rowRange(,).col();
- cv::Mat Rwc = Rcw.t();
- Ow = -Rwc*tcw;
- Twc = cv::Mat::eye(,,Tcw.type());
- Rwc.copyTo(Twc.rowRange(,).colRange(,));
- Ow.copyTo(Twc.rowRange(,).col());
- cv::Mat center = (cv::Mat_<float>(,) << mHalfBaseline, , , );
- Cw = Twc*center;
- }
2. 为关键帧之间添加连接,通过关键帧之间的weight连接,weight指的是两个关键帧之间共同观测到的地图点:(注意这里都是接口函数,真实的建立连接关系使用的是void KeyFrame::UpdateConnections()函数)
使用的数据结构是:
- std::map<KeyFrame*,int> mConnectedKeyFrameWeights
每一个关键帧都会维护一个自己的map,其中记录了与其他关键帧之间的weight。每次为当前关键帧添加新的连接关键帧后,都需要根据weight对map结构重新排序,
- UpdateBestCovisibles();
并更新这两个向量:
- mvpOrderedConnectedKeyFrames
- mvOrderedWeights
由于map结构没有sort函数,需要将元素取出放入一个pair组成的vector中,排序后放入上面这两个向量中
- vector<pair<int,KeyFrame*> > vPairs;
- vPairs.reserve(mConnectedKeyFrameWeights.size());
- for(map<KeyFrame*,int>::iterator mit=mConnectedKeyFrameWeights.begin(), mend=mConnectedKeyFrameWeights.end(); mit!=mend; mit++)
- vPairs.push_back(make_pair(mit->second,mit->first));
- sort(vPairs.begin(),vPairs.end());
- list<KeyFrame*> lKFs; // keyframe
- list<int> lWs; // weight
- for(size_t i=, iend=vPairs.size(); i<iend;i++)
- {
- lKFs.push_front(vPairs[i].second);
- lWs.push_front(vPairs[i].first);
- }
- mvpOrderedConnectedKeyFrames = vector<KeyFrame*>(lKFs.begin(),lKFs.end());
- mvOrderedWeights = vector<int>(lWs.begin(), lWs.end());
还有几个相关的API:
- set<KeyFrame*> KeyFrame::GetConnectedKeyFrames();
- vector<KeyFrame*> KeyFrame::GetVectorCovisibleKeyFrames();
- vector<KeyFrame*> KeyFrame::GetBestCovisibilityKeyFrames(const int &N);
vector<KeyFrame*> KeyFrame::GetCovisiblesByWeight(const int &w);- int KeyFrame::GetWeight(KeyFrame *pKF);
都是返回连接的关键帧;
前两个返回所有连接的关键帧,区别在于一个未排序(set),一个排序(vector)。// set是关联容器 vector是顺序容器
中间两个返回满足一定阈值(前N个,权重大于等于w)的关键帧,最后一个返回指定帧与当前帧间的权重。
3. 当前帧对应的地图点的指针均存放在mvpMapPoints(mvp代表:member、vector、pointer)向量中,通过对mvpMapPoints操作封装,可以得到以下API:
- void KeyFrame::AddMapPoint(MapPoint *pMP, const size_t &idx);
- void KeyFrame::EraseMapPointMatch(const size_t &idx);
- void KeyFrame::EraseMapPointMatch(MapPoint* pMP);
- void KeyFrame::ReplaceMapPointMatch(const size_t &idx, MapPoint* pMP);
- // 注意区别下面两个
- set<MapPoint*> KeyFrame::GetMapPoints();
vector<MapPoint*> KeyFrame::GetMapPointMatches();
MapPoint* KeyFrame::GetMapPoint(const size_t &idx);- // 返回高质量MapPoints(被至少minObs个关键帧观察到)的数量,其中会判断MapPoint的Observations()属性,对比给出的阈值
int KeyFrame::TrackedMapPoints(const int &minObs);
mvpMapPoints初始化在Frame.cpp中:
- mvpMapPoints = vector<MapPoint*>(N,static_cast<MapPoint*>(NULL));
其中有N个空指针,因此有的位置上的MapPoint并没有指向实际的地图点(虽然对应有特征点,有索引idx,但是是外点),获取时需要注意。
4. UpdateConnections()函数:建立关键帧之间的连接关系
ORB-SLAM(五)KeyFrame类的更多相关文章
- (转)Qt Model/View 学习笔记 (五)——View 类
Qt Model/View 学习笔记 (五) View 类 概念 在model/view架构中,view从model中获得数据项然后显示给用户.数据显示的方式不必与model提供的表示方式相同,可以与 ...
- Typescript 学习笔记五:类
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- ORB-SLAM(五)KeyFrame类-最小生成树
KeyFrame中维护了一个map,保存了与当前帧共视的KeyFrame*与权重(共视MapPonits数量).对关键帧之间关系是用加权有向图来完成的,那么理解其spanning tree生成树的原理 ...
- c++ 吕凤翥 第五章 类对象一
一 类的声明和实现 1. class tdate //声明部分 { public: void setdate(int y,int m,int d); int isleapyear(); voi ...
- Java基础复习笔记系列 五 常用类
Java基础复习笔记系列之 常用类 1.String类介绍. 首先看类所属的包:java.lang.String类. 再看它的构造方法: 2. String s1 = “hello”: String ...
- Java编程思想学习(五) 复用类
1.继承与组合 复用类的方法有两种:继承与组合.继承就不多说了,组合就是直接在类中new一个对象. 数组也是对象,使用数组也是组合的一种. 2.初始化基类 当创建一个导出类的对象时,该对象包含一个基类 ...
- C#基础(五)——类中私有构造函数作用
如果类成员有private修饰符,就不允许在类范围以外访问这个类成员.对类构造函数应用private修饰符时,则禁止外部类创建该类的实例.尽管看上去有些不好理解(既然不能实例化,那么这个类还有什么用处 ...
- Java解惑五:类之谜
本文是依据JAVA解惑这本书,做的笔记.电子书见:http://download.csdn.net/detail/u010378705/7527721 谜题46 函数重载的问题. JAVA重载解析过程 ...
- PHP学习笔记二十五【类的继承】
<?php //定义父类 class Stu{ public $name; protected $age; protected $grade; private $address;//私有变量不会 ...
随机推荐
- ABAP正则表达式 vs SPLIT INTO
需求: 把如下通过"/"连接起来的三个字符串分别解析出来. 传统的做法见下图第98行的function module SKWF_UTIL_IO_FIND_BY_KEY: 这个fun ...
- 最短路算法——SPFA
用途: 单源最短路径,不可以处理含负权边的图但可以用来判断是否存在负权回路: 复杂度O(kE) [k <= 2, E 为边数]: 算法核心: Bellman-Ford 算法的优化,实质与前算法一 ...
- 七、IntelliJ IDEA 常见文件类型的图标介绍
咱们已经了解了很多关于 IntelliJ IDEA 的内容啦,例如,在 Windows 系统下安装 IntelliJ IDEA.运行 IntelliJ IDEA .创建 Java 项目以及修改 Int ...
- SpringMVC学习记录四——功能开发及参数绑定
9 商品修改功能开发 9.1 需求 操作流程: 1.进入商品查询列表页面 2.点击修改,进入商品修改页面,页面中显示了要修改的商品(从数据库查询) 要修改的商品从数据库查询,根据 ...
- 【luogu P2071 座位安排】 题解
题目链接:https://www.luogu.org/problemnew/show/P2071#sub 邻接表 + 匈牙利 把之前的邻接矩阵匈牙利变成邻接表 要不然存不下... code: #inc ...
- 【luogu P1865 A % B Problem】 题解
题目链接:https://www.luogu.org/problemnew/show/P1865 其实就是埃拉托色尼筛素数模板... 好像每个数暴力枚举到sqrt()也可以...就算当我无聊练手罢 # ...
- Android学习笔记_22_服务Service应用之—与Activity进行相互通信的本地服务
一.启动服务的两种方法方法: 第一种: startService()和stopService()启动关闭服务.适用于服务和Activity之间没有调用交互的情况.如果相互之间需要方法调用或者传递参数 ...
- Zookeeper watch参照表
Watcher 设置是开发中最常见的,需要搞清楚watcher的一些基本特征,对于exists.getdata.getchild对于节点的不同操 作会收到不同的 watcher信息.对父节点的变更以及 ...
- 轻量ORM-SqlRepoEx 重大升级
.Net平台下兼容.NET Standard 2.0,一个实现以Lambda表达式转转换标准SQL语句,支持MySQL.SQL Server数据库方言,使用强类型操作数据的轻量级ORM工具,在减少魔法 ...
- UDP端口启动后一段时间无法接收到数据
接口需求:开发一个UDP协议的接口作为服务端接收来自客户端的认证数据,数据量每分钟7w+条; 数据格式:标准的redius协议,redius协议的相关知识在网上查资料,提供线索:http://blog ...