在连续性检测中会用到几个变量如下,可以先理解一下这几个变量之间的关系如图:
总结:回环部分对候选帧的检测其实可以理解为静态检测和动态检测;静态检测也就是对于图片相似度的检测,通过候选帧及其共视帧的commonwords 和score进行选择,保存下来的帧都具有静态一致性;空间上的连续性检测主要是动态检测,用于筛选那些落单的相似帧,通俗来讲就是这一候选、帧与当前帧相似,但是走两步之后就不一定相似了(传说中的没病走两步),怎么实现走两步的效果检测呢?那就是看簇与簇之间有没有共同帧了;
PS:簇 = 候选帧 + 候选帧的共视帧;
在来看一下回环中连续性检测部分的代码:
其实可以将代码简化来看:
1. 首先第一步,就是从database中去检测有足够相似度的候选帧,其实这里的检测就已经不仅仅是单帧的检测,对当前帧和其共视帧一起计算得分,满足条件的才能称之为candidate;
// Query the database imposing the minimum score
vector<KeyFrame*> vpCandidateKFs = mpKeyFrameDB->
DetectLoopCandidates(mpCurrentKF, minScore);
2. 接下来第二步的判断其实还是很重要,即如果没有检测到candidate的话就需要将mvConsistentGroups清除,这点说明,其实mvConsistentGroups从一开始就是空的;
// If there are no loop candidates, just add new keyframe and return false
if(vpCandidateKFs.empty())
{
mpKeyFrameDB->add(mpCurrentKF);
mvConsistentGroups.clear();
mpCurrentKF->SetErase();
return false;
}
3. 那既然从一开始是空的,那我们不妨就先从它为空时来理解代码,接下来是对符合条件的candidate进行筛选了,如果mvConsistentGroups为空,那么代码将会变成下面这个样子:
//初始化阶段其实这些所有的变量size都是0;
mvpEnoughConsistentCandidates.clear();
vector<ConsistentGroup> vCurrentConsistentGroups;
vector<bool> vbConsistentGroup(mvConsistentGroups.size(),false);
for(size_t i=0, iend=vpCandidateKFs.size(); i<iend; i++)
{
KeyFrame* pCandidateKF = vpCandidateKFs[i];
//获取每个候选帧的关联簇,其实就是共视帧和其本身
set<KeyFrame*> spCandidateGroup = pCandidateKF->GetConnectedKeyFrames();
spCandidateGroup.insert(pCandidateKF);
bool bEnoughConsistent = false;
bool bConsistentForSomeGroup = false;
for(size_t iG=0, iendG=mvConsistentGroups.size(); iG<iendG; iG++)
{
//因为mvConsistentGroups的size为0,所以这里面根本不会执行,也就说明当前簇其实与别人没有关联
}
// If the group is not consistent with any previous group insert with consistency counter set to zero
if(!bConsistentForSomeGroup)
{//在这里将这个候选帧对应的一簇帧 的 连续度 设置为 0,并且保存为ConsistentGroup 的形式;
ConsistentGroup cg = make_pair(spCandidateGroup,0);
vCurrentConsistentGroups.push_back(cg); //这时,vCurrentConsistentGroups,我们称他为簇村儿,因为它里面保存的都是一簇一簇的“东西”,簇村儿终于有内容了
}
}
4. 既然vCurrentConsistentGroups中已经有了刚刚添加进去的第一个关键帧簇,那么接下来我们看一下循环里的内容:
//首先获取到刚刚放入的那一簇,我们叫他老一簇
set<KeyFrame*> sPreviousGroup = mvConsistentGroups[iG].first;
//接下来for循环中的spCandidateGroup 代表新的一簇
bool bConsistent = false;
//这个for循环主要做的就是检查老一簇中与新一簇有没有共同话题,也就是共同帧
for(set<KeyFrame*>::iterator sit=spCandidateGroup.begin(), send=spCandidateGroup.end(); sit!=send;sit++)
{
if(sPreviousGroup.count(*sit))
{
bConsistent=true;
bConsistentForSomeGroup=true; //有的话,说明新一簇的表现是很好的,比较连贯
break;
}
}
5. 至此,对新一簇的连贯性检测实际已经完成了,其实就是看老一簇与新一簇有没有共同的帧,有共同帧才能说明这两个关键帧距离大概比较近,否则就是孤立的一个候选人;但是仅仅验证完连贯性还不行,还需要将新的簇也保存进入 簇村儿 中:
if(bConsistent)
{
int nPreviousConsistency = mvConsistentGroups[iG].second;
int nCurrentConsistency = nPreviousConsistency + 1;
if(!vbConsistentGroup[iG])
//这个参数可以理解为连续性作证的一次性,就是同样一簇帧,只能在连续性作证时给一簇作证,不能重复使用,
//比如第一次放进去的老一簇,如果与新一簇有联系,那么就算是给新一簇作过证了,接下来有了新新一簇,即使与老一簇有共同帧,其实也不算数了,新新一簇只能与
新一簇进行比较
{
ConsistentGroup cg = make_pair(spCandidateGroup,nCurrentConsistency);
vCurrentConsistentGroups.push_back(cg);
vbConsistentGroup[iG]=true; //this avoid to include the same group more than once
}
//当连续性大于3的时候就可以把它放入最终的候选帧mvpEnoughConsistentCandidates
if(nCurrentConsistency>=mnCovisibilityConsistencyTh && !bEnoughConsistent)
{
mvpEnoughConsistentCandidates.push_back(pCandidateKF);
bEnoughConsistent=true; //this avoid to insert the same candidate more than once
}
}
最后一个初步的关系图如下:
(这里稍微注意一下,Group的Consistency会进行累加,表示截止到当前Group,已经有n个Group是连在一起的)