地圖點可以通過關鍵幀來構造,也可以通過普通幀構造,但是最終,必須是和關鍵幀對應的,通過普通幀構造的地圖點只是臨時被Tracking用來追蹤用的。
構造函數(地圖點3D坐標及其參考幀):
// 參考幀是關鍵幀,該地圖點將於許多幀關鍵幀對應,建立關鍵幀之間的共視關系 MapPoint::MapPoint(const cv::Mat &Pos, KeyFrame *pRefKF, Map* pMap) // 參考幀是普通幀,該地圖點只與當前普通幀的特征點對應 MapPoint::MapPoint(const cv::Mat &Pos, Map* pMap, Frame* pFrame, const int &idxF)
地圖點和關鍵幀之間的觀測關系是最重要的,參考關鍵幀是哪一幀,該地圖點被哪些關鍵幀觀測到,對應的哪個(idx)特征點?通過兩個成員維護:
std::map<KeyFrame*,size_t> mObservations; // 觀測到該地圖點的相機數 int nObs;
添加地圖點觀測:能夠觀測到同一個地圖點的關鍵幀之間存在共視關系
void MapPoint::AddObservation(KeyFrame* pKF, size_t idx);
刪除地圖點觀測:從當前地圖點的mObservation和nObs成員中刪掉對應關鍵幀觀測關系,若該關鍵幀恰好是參考幀,則需要重新指定。當觀測相機數小於等於2時,該地圖點需要剔除。刪掉觀測關系,和刪掉地圖點(以及替換地圖點),需要區分開!
void MapPoint::EraseObservation(KeyFrame* pKF);
剔除MapPoint不僅需要刪掉地圖點中維護的關鍵幀觀測關系,還需要刪掉對應關鍵中對應的地圖點,以及Map中該地圖點的內存:
void KeyFrame::EraseMapPointMatch(const size_t &idx) { unique_lock<mutex> lock(mMutexFeatures); mvpMapPoints[idx]=static_cast<MapPoint*>(NULL); } mpMap->EraseMapPoint(this);
下面是一個重要的函數:
void MapPoint::Replace(MapPoint* pMP);
將當前地圖點(this),替換成pMp。主要使用在閉環時,調整地圖點和關鍵幀,建立新的關系:
關鍵幀將聯系的this替換成pMap:
pKF->ReplaceMapPointMatch(mit->second, pMP);// KeyFrame中mit->second索引對應的地圖點,用pMP替換掉原來的this pMP->AddObservation(pKF,mit->second);// pMp地圖點添加觀測關鍵幀
// 刪掉Map中該地圖點
mpMap->EraseMapPoint(this);
無論是SetBadFlag()還是Replace(),當前地圖點的mbBad標志都被記為true,表明當前地圖點是個壞點。
visible和found的區別:該地圖點在視野范圍內,該地圖點有對應特征點的幀數。通常來說,found的地圖點一定是visible的,但是visible的地圖點很可能not found
float MapPoint::GetFoundRatio() { unique_lock<mutex> lock(mMutexFeatures); return static_cast<float>(mnFound)/mnVisible; }
GetFoundRatio低表示該地圖點在很多關鍵幀的視野范圍內,但是沒有匹配上很多特征點。
最后是MapPoint中幾個比較重要的函數:
void MapPoint::ComputeDistinctiveDescriptors(); void MapPoint::UpdateNormalAndDepth(); int MapPoint::PredictScale(const float ¤tDist, KeyFrame* pKF);
1. 計算地圖點描述子:
從mObservations中獲取觀察到當前地圖點的關鍵幀及對應描述子,描述子放入vDescriptor描述子向量組成的向量中。在這些描述子中,選擇距離(類似hamming距離)其他描述子最近的(中值距離最小,看代碼去體會一下是什么意思)作為地圖點的描述子mDescriptor。
2. 計算地圖點平均觀測方向和深度
地圖點到所有觀測到的關鍵幀相機中心向量,歸一化后相加。
深度范圍:地圖點到參考幀(只有一幀)相機中心距離,乘上參考幀中描述子獲取時金字塔放大尺度,得到最大距離mfMaxDistance;最大距離除以整個金字塔最高層的放大尺度得到最小距離mfMinDistance。通常來說,距離較近的地圖點,將在金字塔層數較高的地方提取出,距離較遠的地圖點,在金字塔層數較低的地方提取出(金字塔層數越低,分辨率越高,才能識別出遠點)。因此通過地圖點的信息(主要是對應描述子),我們可以獲得該地圖點對應的金字塔層級:
const int level = pRefKF->mvKeysUn[observations[pRefKF]].octave;
從而預測該地圖點在什么距離范圍內能夠被觀測到!
3. int MapPoint::PredictScale(const float ¤tDist, KeyFrame* pKF)
注意金字塔ScaleFactor和距離的關系:當前特征點對應ScaleFactor為1.2的意思是:圖片分辨率下降1.2倍后,可以提取出該特征點(分辨率更高時,肯定也可以提取出,這里取金字塔中能夠提取出該特征點最高層級作為該特征點的層級)
同時,由當前特征點的距離,可以推測所在層級。
Map則比較簡單,主要負責維護其中關鍵幀和地圖點容器,設置參考地圖點用於繪圖:
std::set<MapPoint*> mspMapPoints; std::set<KeyFrame*> mspKeyFrames;