前言
这个函数除了利用当前帧构造新的关键帧和将这个关键帧设置为当前帧的参考关键帧外,对于双目和rgbd摄像头还要为当前帧生成新的地图点。
1.函数声明
void Tracking::CreateNewKeyFrame()
2.函数定义
1.确保局部地图线程处于开启状态,保证能接受并处理新产生的关键帧。
// 如果局部建图线程关闭了,就无法插入关键帧
if(!mpLocalMapper->SetNotStop(true))
return;
2.将当前帧构造成关键帧
// Step 1:将当前帧构造成关键帧
KeyFrame* pKF = new KeyFrame(mCurrentFrame,mpMap,mpKeyFrameDB);
3.将当前关键帧设置为当前帧的参考关键帧
// Step 2:将当前关键帧设置为当前帧的参考关键帧
// 在UpdateLocalKeyFrames函数中会将与当前关键帧共视程度最高的关键帧设定为当前帧的参考关键帧
mpReferenceKF = pKF;
mCurrentFrame.mpReferenceKF = pKF;
4.对于rgbd和双目相机还要为关键帧生成新的地图点
这一部分与Tracking::UpdateLastFrame 中的那一部分代码功能相同只不过对象是不同的。
ORB-SLAM2源码学习: Frame.cc: cv::Mat Frame::UnprojectStereo将某个特征点反投影到三维世界坐标系中-优快云博客
// Step 3:对于双目或rgbd摄像头,为当前帧生成新的地图点;单目无操作
if(mSensor!=System::MONOCULAR)
{
// 根据Tcw计算mRcw、mtcw和mRwc、mOw
mCurrentFrame.UpdatePoseMatrices();
// We sort points by the measured depth by the stereo/RGBD sensor.
// We create all those MapPoints whose depth < mThDepth.
// If there are less than 100 close points we create the 100 closest.
// Step 3.1:得到当前帧有深度值的特征点(不一定是地图点)
vector<pair<float,int> > vDepthIdx;
vDepthIdx.reserve(mCurrentFrame.N);
for(int i=0; i<mCurrentFrame.N; i++)
{
float z = mCurrentFrame.mvDepth[i];
if(z>0)
{
// 第一个元素是深度,第二个元素是对应的特征点的id
vDepthIdx.push_back(make_pair(z,i));
}
}
if(!vDepthIdx.empty())
{
// Step 3.2:按照深度从小到大排序
sort(vDepthIdx.begin(),vDepthIdx.end());
// Step 3.3:从中找出不是地图点的包装为地图点
// 处理的近点的个数
int nPoints = 0;
for(size_t j=0; j<vDepthIdx.size();j++)
{
int i = vDepthIdx[j].second;
bool bCreateNew = false;
// 如果这个点对应在上一帧中的地图点没有,或者创建后就没有被观测到,那么就包装为地图点
MapPoint* pMP = mCurrentFrame.mvpMapPoints[i];
if(!pMP)
bCreateNew = true;
else if(pMP->Observations()<1)
{
bCreateNew = true;
mCurrentFrame.mvpMapPoints[i] = static_cast<MapPoint*>(NULL);
}
// 如果需要就新建地图点,这里的地图点不是临时的,是全局地图中新建地图点,用于跟踪
if(bCreateNew)
{
cv::Mat x3D = mCurrentFrame.UnprojectStereo(i);
MapPoint* pNewMP = new MapPoint(x3D,pKF,mpMap);
// 这些添加属性的操作是每次创建MapPoint后都要做的
pNewMP->AddObservation(pKF,i);
pKF->AddMapPoint(pNewMP,i);
pNewMP->ComputeDistinctiveDescriptors();
pNewMP->UpdateNormalAndDepth();
mpMap->AddMapPoint(pNewMP);
mCurrentFrame.mvpMapPoints[i]=pNewMP;
nPoints++;
}
else
{
// 因为从近到远排序,记录其中不需要创建地图点的个数
nPoints++;
}
// Step 3.4:停止新建地图点必须同时满足以下条件:
// 1、当前的点的深度已经超过了设定的深度阈值.
// 2、nPoints已经超过100个点,说明距离比较远了,可能不准确,停掉退出
if(vDepthIdx[j].first>mThDepth && nPoints>100)
break;
}
}
}
5.插入新构建的关键帧
// 关键帧插入到列表 mlNewKeyFrames中,等待local mapping线程临幸
mpLocalMapper->InsertKeyFrame(pKF);
调用InsertKeyFrame函数将关键帧插入到局部地图的关键帧的队列中等待进行处理。
void LocalMapping::InsertKeyFrame(KeyFrame *pKF)
{
unique_lock<mutex> lock(mMutexNewKFs);
// 将关键帧插入到列表中
mlNewKeyFrames.push_back(pKF);
mbAbortBA=true;//在插入关键帧时BA是停止优化的,同理在进行BA时也要等待队列处于空的状态并且线程处于运行状态
}
6.如果已经插入后就可以允许局部建图停止并更新最新的关键帧和Id
// 插入好了,允许局部建图停止
mpLocalMapper->SetNotStop(false);
// 当前帧成为新的关键帧,更新
mnLastKeyFrameId = mCurrentFrame.mnId;
mpLastKeyFrame = pKF;
3.完整的代码
/*
创建新的关键帧
对于非单目的情况,同时创建新的MapPoints
1:将当前帧构造成关键帧
2:将当前关键帧设置为当前帧的参考关键帧
3:对于双目或rgbd摄像头,为当前帧生成新的MapPoints
*/
void Tracking::CreateNewKeyFrame()
{
// 如果局部建图线程关闭了,就无法插入关键帧
if(!mpLocalMapper->SetNotStop(true))
return;
// Step 1:将当前帧构造成关键帧
KeyFrame* pKF = new KeyFrame(mCurrentFrame,mpMap,mpKeyFrameDB);
// Step 2:将当前关键帧设置为当前帧的参考关键帧
// 在UpdateLocalKeyFrames函数中会将与当前关键帧共视程度最高的关键帧设定为当前帧的参考关键帧
mpReferenceKF = pKF;
mCurrentFrame.mpReferenceKF = pKF;
// 这段代码和 Tracking::UpdateLastFrame 中的那一部分代码功能相同
// Step 3:对于双目或rgbd摄像头,为当前帧生成新的地图点;单目无操作
if(mSensor!=System::MONOCULAR)
{
// 根据Tcw计算mRcw、mtcw和mRwc、mOw
mCurrentFrame.UpdatePoseMatrices();
// We sort points by the measured depth by the stereo/RGBD sensor.
// We create all those MapPoints whose depth < mThDepth.
// If there are less than 100 close points we create the 100 closest.
// Step 3.1:得到当前帧有深度值的特征点(不一定是地图点)
vector<pair<float,int> > vDepthIdx;
vDepthIdx.reserve(mCurrentFrame.N);
for(int i=0; i<mCurrentFrame.N; i++)
{
float z = mCurrentFrame.mvDepth[i];
if(z>0)
{
// 第一个元素是深度,第二个元素是对应的特征点的id
vDepthIdx.push_back(make_pair(z,i));
}
}
if(!vDepthIdx.empty())
{
// Step 3.2:按照深度从小到大排序
sort(vDepthIdx.begin(),vDepthIdx.end());
// Step 3.3:从中找出不是地图点的包装为地图点
// 处理的近点的个数
int nPoints = 0;
for(size_t j=0; j<vDepthIdx.size();j++)
{
int i = vDepthIdx[j].second;
bool bCreateNew = false;
// 如果这个点对应在上一帧中的地图点没有,或者创建后就没有被观测到,那么就包装为地图点
MapPoint* pMP = mCurrentFrame.mvpMapPoints[i];
if(!pMP)
bCreateNew = true;
else if(pMP->Observations()<1)
{
bCreateNew = true;
mCurrentFrame.mvpMapPoints[i] = static_cast<MapPoint*>(NULL);
}
// 如果需要就新建地图点,这里的地图点不是临时的,是全局地图中新建地图点,用于跟踪
if(bCreateNew)
{
cv::Mat x3D = mCurrentFrame.UnprojectStereo(i);
MapPoint* pNewMP = new MapPoint(x3D,pKF,mpMap);
// 这些添加属性的操作是每次创建MapPoint后都要做的
pNewMP->AddObservation(pKF,i);
pKF->AddMapPoint(pNewMP,i);
pNewMP->ComputeDistinctiveDescriptors();
pNewMP->UpdateNormalAndDepth();
mpMap->AddMapPoint(pNewMP);
mCurrentFrame.mvpMapPoints[i]=pNewMP;
nPoints++;
}
else
{
// 因为从近到远排序,记录其中不需要创建地图点的个数
nPoints++;
}
// Step 3.4:停止新建地图点必须同时满足以下条件:
// 1、当前的点的深度已经超过了设定的深度阈值(35倍基线)
// 2、nPoints已经超过100个点,说明距离比较远了,可能不准确,停掉退出
if(vDepthIdx[j].first>mThDepth && nPoints>100)
break;
}
}
}
// Step 4:插入关键帧
// 关键帧插入到列表 mlNewKeyFrames中,等待local mapping线程临幸
mpLocalMapper->InsertKeyFrame(pKF);
// 插入好了,允许局部建图停止
mpLocalMapper->SetNotStop(false);
// 当前帧成为新的关键帧,更新
mnLastKeyFrameId = mCurrentFrame.mnId;
mpLastKeyFrame = pKF;
}
结束语
以上就是我学习到的内容,如果对您有帮助请多多支持我,如果哪里有问题欢迎大家在评论区积极讨论,我看到会及时回复。