(1)mConnectedKeyFrameWeights:map
(2)mvpOrderedConnectedKeyFrames:vector
(3)mpParent:关键帧的父关键帧,即共视程度最高的关键帧。一个帧的父关键帧只能有一个,而子关键帧可以有很多个。
在局部建图线程中,取出关键帧之后会计算该关键帧的词袋信息。
void KeyFrame::ComputeBoW()
{
// 只有当词袋向量或者节点和特征序号的特征向量为空的时候执行
if(mBowVec.empty() || mFeatVec.empty())
{
// 那么就从当前帧的描述子中转换得到词袋信息
vector<cv::Mat> vCurrentDesc = Converter::toDescriptorVector(mDescriptors);
// Feature vector associate features with nodes in the 4th level (from leaves up)
// We assume the vocabulary tree has 6 levels, change the 4 otherwise //?
mpORBvocabulary->transform(vCurrentDesc,mBowVec,mFeatVec,4);
}
}
对共视关键帧根据权值的大小进行排序。这里使用list的push_front实现了sort之后的从大到小排序。
void KeyFrame::UpdateBestCovisibles()
vector<KeyFrame*> KeyFrame::GetCovisiblesByWeight(const int &w)
int KeyFrame::TrackedMapPoints(const int &minObs)
在局部建图线程中,取出一个关键帧处理完之后会被调用。
void LocalMapping::ProcessNewKeyFrame()
(1)获得该关键帧的所有路标点,并寻找共视关键帧。
(2)建立当前关键帧和共视关键帧的联系,其中权值为共视的特征点数。
(3)权重必须大于一个阈值,如果没有超过该阈值的权重,那么就只建立该关键帧和与其共视程度最大的关键帧间的联系。
(4)对这些连接按照权重从大到小进行排序,以方便将来的处理。
void KeyFrame::UpdateConnections()
map
for(vector<MapPoint*>::iterator vit=vpMP.begin(), vend=vpMP.end(); vit!=vend; vit++)
{
MapPoint* pMP = *vit;
if(!pMP)
continue;
if(pMP->isBad())
continue;
// 对于每一个MapPoint点,observations记录了可以观测到该MapPoint的所有关键帧
map<KeyFrame*,size_t> observations = pMP->GetObservations();
for(map<KeyFrame*,size_t>::iterator mit=observations.begin(), mend=observations.end(); mit!=mend; mit++)
{
// 除去自身,自己与自己不算共视
if(mit->first->mnId==mnId)
continue;
// 保存结果
KFcounter[mit->first]++;
}
}
保存在变量vector
vector<pair<int,KeyFrame*> > vPairs;
vPairs.reserve(KFcounter.size());
// Step 2 找到对应权重最大的关键帧(共视程度最高的关键帧)
for(map<KeyFrame*,int>::iterator mit=KFcounter.begin(), mend=KFcounter.end(); mit!=mend; mit++)
{
if(mit->second>nmax)
{
nmax=mit->second;
pKFmax=mit->first;
}
if(mit->second>=th)
{
// 对应权重需要大于阈值,对这些关键帧建立连接
vPairs.push_back(make_pair(mit->second,mit->first));
// 对方关键帧也要添加这个信息
// 更新KFcounter中该关键帧的mConnectedKeyFrameWeights
// 更新其它KeyFrame的mConnectedKeyFrameWeights,更新其它关键帧与当前帧的连接权重
(mit->first)->AddConnection(this,mit->second);
}
}
// Step 3 如果没有连接到关键(超过阈值的权重),则对权重最大的关键帧建立连接
if(vPairs.empty())
{
// 如果每个关键帧与它共视的关键帧的个数都少于th,
// 那就只更新与其它关键帧共视程度最高的关键帧的mConnectedKeyFrameWeights
// 这是对之前th这个阈值可能过高的一个补丁
vPairs.push_back(make_pair(nmax,pKFmax));
pKFmax->AddConnection(this,nmax);
}
// Step 4 对共视程度比较高的关键帧对更新连接关系及权重(从大到小)
// vPairs里存的都是相互共视程度比较高的关键帧和共视权重,接下来由大到小进行排序
sort(vPairs.begin(),vPairs.end()); // sort函数默认升序排列
// 将排序后的结果分别组织成为两种数据类型
list<KeyFrame*> lKFs;
list<int> lWs;
for(size_t i=0; i<vPairs.size();i++)
{
// push_front 后变成了从大到小顺序
lKFs.push_front(vPairs[i].second);
lWs.push_front(vPairs[i].first);
}
在局部建图线程中,删除冗余关键帧的时候被调用。
地图点也有一个同名函数,也是在局部建图线程中去除错误路标点函数MapPointCulling中被调用
void KeyFrame::SetBadFlag()
需要删除的是该关键帧和其他所有帧、地图点之间的连接关系
mbNotErase作用:表示要删除该关键帧及其连接关系但是这个关键帧有可能正在回环检测或者计算sim3操作,这时候虽然这个关键帧冗余,但是却不能删除,仅设置mbNotErase为true。这时候调用setbadflag函数时,不会将这个关键帧删除,只会把mbTobeErase变成true,代表这个关键帧可以删除但不到时候,先记下来以后处理。在闭环线程里调用 SetErase()会根据mbToBeErased 来删除之前可以删除还没删除的帧。
// Step 2 遍历所有和当前关键帧共视的关键帧,删除他们与当前关键帧的联系
for(map<KeyFrame*,int>::iterator mit = mConnectedKeyFrameWeights.begin(), mend=mConnectedKeyFrameWeights.end(); mit!=mend; mit++)
mit->first->EraseConnection(this); // 让其它的KeyFrame删除与自己的联系
减少该地图点的被观测次数。
// Step 3 遍历每一个当前关键帧的地图点,删除每一个地图点和当前关键帧的联系
for(size_t i=0; i<mvpMapPoints.size(); i++)
if(mvpMapPoints[i])
mvpMapPoints[i]->EraseObservation(this); // 让与自己有联系的MapPoint删除与自己的联系
sParentCandidates:子关键帧候选父关键帧
pKF:子关键帧
vpConnected:子关键帧的共视关键帧
mpMap->EraseKeyFrame(this);
mpKeyFrameDB->erase(this);
将图像分成了很多小块,每一块里面都存放着特征点。遍历特征点周围的方格子,然后遍历方格子里面的特征点,判断该特征点和圆心的距离是否小于r,小于r的话就是一个候选特征点。
vector<size_t> KeyFrame::GetFeaturesInArea(const float &x, const float &y, const float &r) const
以该关键帧的路标点的中值深度为单位1,用于单目初始化时的尺度归一化。归一化的对象包括初始化时计算的t以及路标点坐标。
// 其中q为2
float KeyFrame::ComputeSceneMedianDepth(const int q)