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类的更多相关文章

  1. (转)Qt Model/View 学习笔记 (五)——View 类

    Qt Model/View 学习笔记 (五) View 类 概念 在model/view架构中,view从model中获得数据项然后显示给用户.数据显示的方式不必与model提供的表示方式相同,可以与 ...

  2. Typescript 学习笔记五:类

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  3. ORB-SLAM(五)KeyFrame类-最小生成树

    KeyFrame中维护了一个map,保存了与当前帧共视的KeyFrame*与权重(共视MapPonits数量).对关键帧之间关系是用加权有向图来完成的,那么理解其spanning tree生成树的原理 ...

  4. c++ 吕凤翥 第五章 类对象一

    一   类的声明和实现 1. class tdate   //声明部分 { public: void setdate(int y,int m,int d); int isleapyear(); voi ...

  5. Java基础复习笔记系列 五 常用类

    Java基础复习笔记系列之 常用类 1.String类介绍. 首先看类所属的包:java.lang.String类. 再看它的构造方法: 2. String s1 = “hello”: String ...

  6. Java编程思想学习(五) 复用类

    1.继承与组合 复用类的方法有两种:继承与组合.继承就不多说了,组合就是直接在类中new一个对象. 数组也是对象,使用数组也是组合的一种. 2.初始化基类 当创建一个导出类的对象时,该对象包含一个基类 ...

  7. C#基础(五)——类中私有构造函数作用

    如果类成员有private修饰符,就不允许在类范围以外访问这个类成员.对类构造函数应用private修饰符时,则禁止外部类创建该类的实例.尽管看上去有些不好理解(既然不能实例化,那么这个类还有什么用处 ...

  8. Java解惑五:类之谜

    本文是依据JAVA解惑这本书,做的笔记.电子书见:http://download.csdn.net/detail/u010378705/7527721 谜题46 函数重载的问题. JAVA重载解析过程 ...

  9. PHP学习笔记二十五【类的继承】

    <?php //定义父类 class Stu{ public $name; protected $age; protected $grade; private $address;//私有变量不会 ...

随机推荐

  1. 动态规划(DP),0-1背包问题

    题目链接:http://poj.org/problem?id=3624 1.p[i][j]表示,背包容量为j,从i,i+1,i+2,...,n的最优解. 2.递推公式 p[i][j]=max(p[i+ ...

  2. POJ 3461 Oulipo 【KMP统计子串数】

    传送门:http://poj.org/problem?id=3461 Oulipo Time Limit: 1000MS   Memory Limit: 65536K Total Submission ...

  3. App版本号定义与说明基础知识

    版本控制比较普遍的三种命名格式 GNU 风格的版本号命名格式 主版本号 . 次版本号 [. 修正版本号 [. 编译版本号 ]] 示例 : 1.2.1, 2.0, 5.0.0 build-13124 W ...

  4. vim 操作手册

    三种模式 编辑模式(i当前位置插入光标:a后一位置插入光标).控制模式(esc).可视模式(v). 编辑模式时可以正常输入. 控制模式是vim的核心,通过按键可以快速实现操作. 可视模式是进行选块操作 ...

  5. 取火柴游戏||Nim博弈

    好久之前看的sg函数了 好像就记住一个nim博弈qwq 第一次啊看的时候很迷,现在感觉可以了qwq 首先我们来看一个其他的游戏.(以下游戏只有两个人参与,且足够聪明) 两个人在一张圆形的桌子上放等大的 ...

  6. AngularJS 四 服务

    AngularJS服务: API:  https://docs.angularjs.org/api/ng/service        下面只会介绍几种,需要的话可以去官网查看 AngularJS服务 ...

  7. linux mariadb

    https://www.linuxidc.com/Linux/2016-03/128880.htm -- sql 导入 接着输入你所导入到Centos下的数据库文Student.sql文件的位置例如: ...

  8. Flask—05-理解掌握flask数据模型(01)

    数据模型 数据库回顾 分类: 关系型数据库:MySQL.sqlite.… 非关系型数据库:Redis.MongoDB.… 操作: 执行原生SQL语句,每次都需要拼接SQL语句,非常繁琐而且特别容易出错 ...

  9. 【oracle使用笔记2】使用Oracle数据库遇到的若干问题总结

    一. 关于Oracle 11g数据库在查询表中数据显示中文乱码问题 [描述]本人一开始使用的Oracle是11g版本的,用PLSQL一次查询表中的数据时出现了中文显示乱码,为此搜了许多解决办法,最终通 ...

  10. [NOI2015]程序自动分析(并查集)

    题目描述 在实现程序自动分析的过程中,常常需要判定一些约束条件是否能被同时满足. 考虑一个约束满足问题的简化版本:假设x1,x2,x3...代表程序中出现的变量,给定n个形如xi=xj或xi≠xj的变 ...