Tracking::CreateInitialMapMonocular()成员函数
void Tracking::CreateInitialMapMonocular()
{
// Create KeyFrames 认为单目初始化时候的参考帧和当前帧都是关键帧
KeyFrame* pKFini = new KeyFrame(mInitialFrame,mpMap,mpKeyFrameDB); // 第一帧
KeyFrame* pKFcur = new KeyFrame(mCurrentFrame,mpMap,mpKeyFrameDB); // 第二帧
// Step 1 将初始关键帧,当前关键帧的描述子转为BoW
pKFini->ComputeBoW();
pKFcur->ComputeBoW();
// Insert KFs in the map
// Step 2 将关键帧插入到地图
mpMap->AddKeyFrame(pKFini);
mpMap->AddKeyFrame(pKFcur);
// Create MapPoints and asscoiate to keyframes
// step 4:将3D点包装成MapPoints
for(size_t i=0; i<mvIniMatches.size();i++)
{
// 没有匹配,跳过
if(mvIniMatches[i]<0)
continue;
//Create MapPoint.
//空间点的世界坐标
cv::Mat worldPos(mvIniP3D[i]);
// step 4.1:用3D点构造MapPoint
MapPoint* pMP = new MapPoint(
worldPos,
pKFcur, // NOTE 这里之所以让新建的地图点的参考关键帧是第二帧,原因是在Tracking过程中有个阶段需要根据上一帧的地图点来进行
// 重投影得到在图像区域上的匹配;如果设置了参考关键帧是第一帧的话,那么第三帧就会很尴尬。。。找不到第二帧的地图点
// ? 可是下面的 4.3 中,pKFcur也会添加观测信息?
// 我怀疑这样做的目的是给这些初始地图点删除的机会。第一帧不会被优化也不会被删除,如果参考关键帧设置成为了第一帧的话,
// 就会导致这些初始地图点没有被删除??
// 额。。。好像这样子也说不通
mpMap);
// step 4.2:为该MapPoint添加属性:
// a.观测到该MapPoint的关键帧
// b.该MapPoint的描述子
// c.该MapPoint的平均观测方向和深度范围
// step 4.3:表示该KeyFrame的哪个特征点可以观测到哪个3D点
pKFini->AddMapPoint(pMP,i);
pKFcur->AddMapPoint(pMP,mvIniMatches[i]);
// a.表示该MapPoint可以被哪个KeyFrame的哪个特征点观测到
pMP->AddObservation(pKFini,i);
pMP->AddObservation(pKFcur,mvIniMatches[i]);
// b.从众多观测到该MapPoint的特征点中挑选区分度最高的描述子
pMP->ComputeDistinctiveDescriptors();
// c.更新该MapPoint平均观测方向以及观测距离的范围
pMP->UpdateNormalAndDepth();
//Fill Current Frame structure
//mvIniMatches下标i表示在初始化参考帧中的特征点的序号
//mvIniMatches[i]是初始化当前帧中的特征点的序号
mCurrentFrame.mvpMapPoints[mvIniMatches[i]] = pMP;
mCurrentFrame.mvbOutlier[mvIniMatches[i]] = false;
//Add to Map
// step 4.4:在地图中添加该MapPoint
mpMap->AddMapPoint(pMP);
}//将3D点包装成MapPoints
// Update Connections
// step 4.5:更新关键帧间的连接关系
// 在3D点和关键帧之间建立边,每个边有一个权重,边的权重是该关键帧与当前帧公共3D点的个数
pKFini->UpdateConnections();
pKFcur->UpdateConnections();
// Bundle Adjustment
cout << "New Map created with " << mpMap->MapPointsInMap() << " points" << endl;
// step 5:BA优化
Optimizer::GlobalBundleAdjustemnt(mpMap,20);
// Set median depth to 1
// step 6:!!!将MapPoints的中值深度归一化到1,并归一化两帧之间变换 NOTICE
// 评估关键帧场景深度,q=2表示中值
//? 为什么是 pKFini 而不是 pKCur ?
float medianDepth = pKFini->ComputeSceneMedianDepth(2);
float invMedianDepth = 1.0f/medianDepth;
//两个条件,一个是平均深度要大于0,另外一个是在当前帧中被观测到的地图点的数目应该大于100
if(medianDepth<0 || pKFcur->TrackedMapPoints(1)<100)
{
cout << "Wrong initialization, reseting..." << endl;
Reset();
return;
}
//将两帧之间的变换归一化到平均深度1的尺度下
// Scale initial baseline
cv::Mat Tc2w = pKFcur->GetPose();
// x/z y/z 将z归一化到1
Tc2w.col(3).rowRange(0,3) = Tc2w.col(3).rowRange(0,3)*invMedianDepth;
pKFcur->SetPose(Tc2w);
// Scale points
// 把3D点的尺度也归一化到1
//? 为什么是pKFini? 是不是就算是使用 pKFcur 得到的结果也是相同的?
vector<MapPoint*> vpAllMapPoints = pKFini->GetMapPointMatches();
for(size_t iMP=0; iMP<vpAllMapPoints.size(); iMP++)
{
if(vpAllMapPoints[iMP])
{
MapPoint* pMP = vpAllMapPoints[iMP];
pMP->SetWorldPos(pMP->GetWorldPos()*invMedianDepth);
}
}
// 这部分和SteroInitialization()相似
mpLocalMapper->InsertKeyFrame(pKFini);
mpLocalMapper->InsertKeyFrame(pKFcur);
mCurrentFrame.SetPose(pKFcur->GetPose());
mnLastKeyFrameId=mCurrentFrame.mnId;
mpLastKeyFrame = pKFcur;
mvpLocalKeyFrames.push_back(pKFcur);
mvpLocalKeyFrames.push_back(pKFini);
// 单目初始化之后,得到的初始地图中的所有点都是局部地图点
mvpLocalMapPoints=mpMap->GetAllMapPoints();
mpReferenceKF = pKFcur;
//也只能这样子设置了,毕竟是最近的关键帧
mCurrentFrame.mpReferenceKF = pKFcur;
mLastFrame = Frame(mCurrentFrame);
mpMap->SetReferenceMapPoints(mvpLocalMapPoints);
mpMapDrawer->SetCurrentCameraPose(pKFcur->GetPose());
mpMap->mvpKeyFrameOrigins.push_back(pKFini);
mState=OK;// 初始化成功,至此,初始化过程完成
}
KeyFrame构造函数
//关键帧的构造函数
KeyFrame::KeyFrame(Frame &F, Map *pMap, KeyFrameDatabase *pKFDB):
//初始化的参数列表先不看
mnFrameId(F.mnId), mTimeStamp(F.mTimeStamp), mnGridCols(FRAME_GRID_COLS), mnGridRows(FRAME_GRID_ROWS),
mfGridElementWidthInv(F.mfGridElementWidthInv), mfGridElementHeightInv(F.mfGridElementHeightInv),
mnTrackReferenceForFrame(0), mnFuseTargetForKF(0), mnBALocalForKF(0), mnBAFixedForKF(0),
mnLoopQuery(0), mnLoopWords(0), mnRelocQuery(0), mnRelocWords(0), mnBAGlobalForKF(0),
fx(F.fx), fy(F.fy), cx(F.cx), cy(F.cy), invfx(F.invfx), invfy(F.invfy),
mbf(F.mbf), mb(F.mb), mThDepth(F.mThDepth), N(F.N), mvKeys(F.mvKeys), mvKeysUn(F.mvKeysUn),
mvuRight(F.mvuRight), mvDepth(F.mvDepth), mDescriptors(F.mDescriptors.clone()),
mBowVec(F.mBowVec), mFeatVec(F.mFeatVec), mnScaleLevels(F.mnScaleLevels), mfScaleFactor(F.mfScaleFactor),
mfLogScaleFactor(F.mfLogScaleFactor), mvScaleFactors(F.mvScaleFactors), mvLevelSigma2(F.mvLevelSigma2),
mvInvLevelSigma2(F.mvInvLevelSigma2), mnMinX(F.mnMinX), mnMinY(F.mnMinY), mnMaxX(F.mnMaxX),
mnMaxY(F.mnMaxY), mK(F.mK), mvpMapPoints(F.mvpMapPoints), mpKeyFrameDB(pKFDB),
mpORBvocabulary(F.mpORBvocabulary), mbFirstConnection(true), mpParent(NULL), mbNotErase(false),
mbToBeErased(false), mbBad(false),
mHalfBaseline(F.mb/2), // 计算双目相机长度的一半
mpMap(pMap)
{
// 获取id
mnId=nNextId++;
// 根据指定的普通帧, 初始化用于加速匹配的网格对象信息; 其实就把每个网格中有的特征点的索引如数复制过来
mGrid.resize(mnGridCols);
for(int i=0; i<mnGridCols;i++)
{
mGrid[i].resize(mnGridRows);
for(int j=0; j<mnGridRows; j++)
mGrid[i][j] = F.mGrid[i][j];
}
// 设置当前关键帧的位姿
SetPose(F.mTcw);
}
作用