2018-10-09

/**轉(zhuǎn)載自https://blog.csdn.net/xiaoxiaowenqiang/article/details/79281122

* This file is part of ORB-SLAM2.

*

* LocalMapping作用是將Tracking中送來的關(guān)鍵幀放在mlNewKeyFrame列表中;

* 處理新關(guān)鍵幀,地圖點(diǎn)檢查剔除湖蜕,生成新地圖點(diǎn)雨涛,Local BA市埋,關(guān)鍵幀剔除尝哆。

* 主要工作在于維護(hù)局部地圖,也就是SLAM中的Mapping针肥。

*

*

* Tracking線程 只是判斷當(dāng)前幀是否需要加入關(guān)鍵幀衰抑,并沒有真的加入地圖象迎,

* 因?yàn)門racking線程的主要功能是局部定位,

*

* 而處理地圖中的關(guān)鍵幀,地圖點(diǎn)砾淌,包括如何加入啦撮,

* 如何刪除的工作是在LocalMapping線程完成的

*

* 建圖

* 處理新的關(guān)鍵幀 KeyFrame 完成局部地圖構(gòu)建

* 插入關(guān)鍵幀 ------>? 處理地圖點(diǎn)(篩選生成的地圖點(diǎn) 生成地圖點(diǎn))? -------->? 局部 BA最小化重投影誤差? -調(diào)整-------->? 篩選 新插入的 關(guān)鍵幀

*

* mlNewKeyFrames? ? list 列表隊(duì)列存儲(chǔ)關(guān)鍵幀

* 1】檢查隊(duì)列

*? ? ? CheckNewKeyFrames();

*

* 2】處理新關(guān)鍵幀 Proces New Key Frames

* ProcessNewKeyFrame(); 更新地圖點(diǎn)MapPoints 和 關(guān)鍵幀 KepFrame 的關(guān)聯(lián)關(guān)系? UpdateConnections() 更新關(guān)聯(lián)關(guān)系

*

* 3】剔除 地圖點(diǎn) MapPoints

*? ? ? 刪除地圖中新添加的但 質(zhì)量不好的 地圖點(diǎn)

*? ? ? a)? IncreaseFound 共視點(diǎn)? / IncreaseVisible? 投影在圖像上 < 25%

*? ? ? b) 觀測(cè)到該 點(diǎn)的關(guān)鍵幀太少

*

* 4】生成 地圖點(diǎn) MapPoints

* 運(yùn)動(dòng)過程中和共視程度比較高的 關(guān)鍵幀 通過三角化 恢復(fù)出的一些地圖點(diǎn)

*

* 5】地圖點(diǎn)融合 MapPoints

*? ? ? 檢測(cè)當(dāng)前關(guān)鍵幀和相鄰 關(guān)鍵幀(兩級(jí)相鄰) 中 重復(fù)的 地圖點(diǎn) 留下觀測(cè)幀高的 地圖點(diǎn)

*

* 6】局部 BA 最小化重投影誤差

*? ? ? 和當(dāng)前關(guān)鍵幀相鄰的關(guān)鍵幀 中相匹配的 地圖點(diǎn)對(duì) 局部 BA最小化重投影誤差優(yōu)化點(diǎn)坐標(biāo) 和 位姿

*

* 7】關(guān)鍵幀剔除

*? ? ? 其90%以上的 地圖點(diǎn) 能夠被其他 共視 關(guān)鍵幀(至少3個(gè)) 觀測(cè)到,認(rèn)為該關(guān)鍵幀多余汪厨,可以刪除

*

*/

#include "LocalMapping.h"

#include "LoopClosing.h"

#include "ORBmatcher.h"

#include "Optimizer.h"

#include<mutex>

namespace ORB_SLAM2

{

LocalMapping::LocalMapping(Map *pMap, const float bMonocular):

? ? mbMonocular(bMonocular), mbResetRequested(false), mbFinishRequested(false), mbFinished(true), mpMap(pMap),

? ? mbAbortBA(false), mbStopped(false), mbStopRequested(false), mbNotStop(false), mbAcceptKeyFrames(true)

{

}

void LocalMapping::SetLoopCloser(LoopClosing* pLoopCloser)

{

? ? mpLoopCloser = pLoopCloser;

}

void LocalMapping::SetTracker(Tracking *pTracker)

{

? ? mpTracker=pTracker;

}

void LocalMapping::Run()

{

? ? mbFinished = false;

? ? while(1)

? ? {

// Tracking will see that Local Mapping is busy

// 步驟1:設(shè)置進(jìn)程間的訪問標(biāo)志 告訴Tracking線程赃春,LocalMapping線程正在處理新的關(guān)鍵幀,處于繁忙狀態(tài)

? ? ? ? ? ? ? // LocalMapping線程處理的關(guān)鍵幀都是Tracking線程發(fā)過的

? ? ? ? ? ? ? // 在LocalMapping線程還沒有處理完關(guān)鍵幀之前Tracking線程最好不要發(fā)送太快

SetAcceptKeyFrames(false);

// Check if there are keyframes in the queue

// 等待處理的關(guān)鍵幀列表不為空

if(CheckNewKeyFrames())

{

? ? // BoW conversion and insertion in Map

// 步驟2:計(jì)算關(guān)鍵幀特征點(diǎn)的詞典單詞向量BoW映射劫乱,將關(guān)鍵幀插入地圖

? ? ProcessNewKeyFrame();

? ? // Check recent MapPoints

? // 剔除ProcessNewKeyFrame函數(shù)中引入的不合格MapPoints

// 步驟3:對(duì)新添加的地圖點(diǎn)融合 對(duì)于 ProcessNewKeyFrame 和 CreateNewMapPoints 中 最近添加的MapPoints進(jìn)行檢查剔除 ? ?

? //? MapPointCulling();

? ? // Triangulate new MapPoints


// 步驟4: 創(chuàng)建新的地圖點(diǎn) 相機(jī)運(yùn)動(dòng)過程中與相鄰關(guān)鍵幀通過三角化恢復(fù)出一些新的地圖點(diǎn)MapPoints ? ?

? ? CreateNewMapPoints();


? ? MapPointCulling();// 從上面 移到下面

? ? ? // 已經(jīng)處理完隊(duì)列中的最后的一個(gè)關(guān)鍵幀

? ? if(!CheckNewKeyFrames())

? ? {

// Find more matches in neighbor keyframes and fuse point duplications

// 步驟5:相鄰幀地圖點(diǎn)融合 檢查并融合當(dāng)前關(guān)鍵幀與相鄰幀(兩級(jí)相鄰)重復(fù)的MapPoints

SearchInNeighbors();

? ? }

? ? mbAbortBA = false;

// 已經(jīng)處理完隊(duì)列中的最后的一個(gè)關(guān)鍵幀织中,并且閉環(huán)檢測(cè)沒有請(qǐng)求停止LocalMapping

? ? if(!CheckNewKeyFrames() && !stopRequested())

? ? {

// 步驟6:局部地圖優(yōu)化 Local BA

if(mpMap->KeyFramesInMap() > 2)

? ? Optimizer::LocalBundleAdjustment(mpCurrentKeyFrame,&mbAbortBA, mpMap);

// 步驟7: 關(guān)鍵幀融合 檢測(cè)并剔除當(dāng)前幀相鄰的關(guān)鍵幀中冗余的關(guān)鍵幀 Check redundant local Keyframes

? ? ? ? ? ? ? ? // 剔除的標(biāo)準(zhǔn)是:該關(guān)鍵幀的90%的MapPoints可以被其它關(guān)鍵幀觀測(cè)到

// Tracking中先把關(guān)鍵幀交給LocalMapping線程

// 并且在Tracking中InsertKeyFrame函數(shù)的條件比較松,交給LocalMapping線程的關(guān)鍵幀會(huì)比較密

? ? ? ? ? ? ? ? ? ? ? ? // 在這里再刪除冗余的關(guān)鍵幀

KeyFrameCulling();

? ? }

// 步驟8:將當(dāng)前幀加入到閉環(huán)檢測(cè)隊(duì)列中

? ? mpLoopCloser->InsertKeyFrame(mpCurrentKeyFrame);

}

// 步驟9:等待線程空閑 完成一幀關(guān)鍵幀的插入融合工作

else if(Stop())

{

? ? // Safe area to stop

? ? while(isStopped() && !CheckFinish())

? ? {

usleep(3000);

? ? }

? ? if(CheckFinish())//檢查 是否完成

break;

}

? ? ? ? ? ? ? // 檢查重置

ResetIfRequested();

// Tracking will see that Local Mapping is not busy

// 步驟10:告訴 Tracking 線程? Local Mapping 線程 空閑 可一處理接收 下一個(gè) 關(guān)鍵幀

SetAcceptKeyFrames(true);

if(CheckFinish())

? ? break;

usleep(3000);

? ? }

? ? SetFinish();

}

/**

* @brief 插入關(guān)鍵幀

*

* 將關(guān)鍵幀插入到地圖中衷戈,以便將來進(jìn)行局部地圖優(yōu)化

* 這里僅僅是將關(guān)鍵幀插入到列表中進(jìn)行等待

* @param pKF KeyFrame

*/

void LocalMapping::InsertKeyFrame(KeyFrame *pKF)

{

? ? unique_lock<mutex> lock(mMutexNewKFs);

? ? // 將關(guān)鍵幀插入到 等待處理的關(guān)鍵幀列表中

? ? mlNewKeyFrames.push_back(pKF);

? ? mbAbortBA=true;// BA優(yōu)化停止

}

/**

* @brief? ? 查看列表中是否有等待 被處理的關(guān)鍵幀

* @return 如果存在狭吼,返回true

*/

bool LocalMapping::CheckNewKeyFrames()

{

? ? unique_lock<mutex> lock(mMutexNewKFs);

? ? return(!mlNewKeyFrames.empty());// 等待處理的關(guān)鍵幀列表是否為空

}

? /*

? a. 根據(jù)詞典 計(jì)算當(dāng)前關(guān)鍵幀Bow,便于后面三角化恢復(fù)新地圖點(diǎn)殖妇;

? b. 將TrackLocalMap中跟蹤局部地圖匹配上的地圖點(diǎn)綁定到當(dāng)前關(guān)鍵幀

? ? ? (在Tracking線程中只是通過匹配進(jìn)行局部地圖跟蹤刁笙,優(yōu)化當(dāng)前關(guān)鍵幀姿態(tài)),

? ? ? 也就是在graph 中加入當(dāng)前關(guān)鍵幀作為node谦趣,并更新edge采盒。


? ? ? 而CreateNewMapPoint()中則通過當(dāng)前關(guān)鍵幀,在局部地圖中添加與新的地圖點(diǎn)蔚润;

? c. 更新加入當(dāng)前關(guān)鍵幀之后關(guān)鍵幀之間的連接關(guān)系,包括更新Covisibility圖和Essential圖

? (最小生成樹spanning tree尺栖,共視關(guān)系好的邊subset of edges from covisibility graph

? ? with high covisibility (θ=100)嫡纠, 閉環(huán)邊)。

? ? */


? /**

* @brief 處理列表中的關(guān)鍵幀

*

* - 計(jì)算Bow延赌,加速三角化新的MapPoints

* - 關(guān)聯(lián)當(dāng)前關(guān)鍵幀至MapPoints除盏,并更新MapPoints的平均觀測(cè)方向和觀測(cè)距離范圍

* - 插入關(guān)鍵幀,更新Covisibility圖和Essential圖

* @see VI-A keyframe insertion

*/

void LocalMapping::ProcessNewKeyFrame()

{ ?

// 步驟1:從緩沖隊(duì)列中取出一幀待處理的關(guān)鍵幀

? ? ? ? ? ? // Tracking線程向LocalMapping中插入關(guān)鍵幀存在該隊(duì)列中

? ? {

unique_lock<mutex> lock(mMutexNewKFs);

// 從列表中獲得一個(gè)等待被插入的關(guān)鍵幀

mpCurrentKeyFrame = mlNewKeyFrames.front();

mlNewKeyFrames.pop_front();// 出隊(duì)

? ? }

? ? // Compute Bags of Words structures

// 步驟2:計(jì)算該關(guān)鍵幀特征點(diǎn)的Bow映射關(guān)系? ?

? ? //? 根據(jù)詞典 計(jì)算當(dāng)前關(guān)鍵幀Bow挫以,便于后面三角化恢復(fù)新地圖點(diǎn)? ?

? ? mpCurrentKeyFrame->ComputeBoW();// 幀描述子 用字典單詞線性表示的 向量

? ? // Associate MapPoints to the new keyframe and update normal and descriptor

? ? // 當(dāng)前關(guān)鍵幀? TrackLocalMap中跟蹤局部地圖 匹配上的 地圖點(diǎn)

// 步驟3:跟蹤局部地圖過程中新匹配上的MapPoints和當(dāng)前關(guān)鍵幀綁定

? ? ? // 在TrackLocalMap函數(shù)中將局部地圖中的MapPoints與當(dāng)前幀進(jìn)行了匹配者蠕,

? ? ? // 但沒有對(duì)這些匹配上的MapPoints與當(dāng)前幀進(jìn)行關(guān)聯(lián)? ?

? ? const vector<MapPoint*> vpMapPointMatches = mpCurrentKeyFrame->GetMapPointMatches();

? ? for(size_t i=0; i<vpMapPointMatches.size(); i++)

? ? {

MapPoint* pMP = vpMapPointMatches[i];// 每一個(gè)與當(dāng)前關(guān)鍵幀匹配好的地圖點(diǎn)

if(pMP)//地圖點(diǎn)存在

{

? ? if(!pMP->isBad())

? ? {

? ? ? // 為當(dāng)前幀在tracking過重跟蹤到的MapPoints更新屬性

if(!pMP->IsInKeyFrame(mpCurrentKeyFrame))//下視野內(nèi)

{

? ? pMP->AddObservation(mpCurrentKeyFrame, i);// 地圖點(diǎn)添加關(guān)鍵幀

? ? pMP->UpdateNormalAndDepth();// 地圖點(diǎn)更新 平均觀測(cè)方向 和 觀測(cè)距離深度

? ? pMP->ComputeDistinctiveDescriptors();// 加入關(guān)鍵幀后,更新地圖點(diǎn)的最佳描述子

}

else // 雙目追蹤時(shí)插入的點(diǎn) 可能不在 幀上this can only happen for new stereo points inserted by the Tracking

{

? // 將雙目或RGBD跟蹤過程中新插入的MapPoints放入mlpRecentAddedMapPoints掐松,等待檢查

? ? ? ? ? ? ? ? ? ? ? ? ? // CreateNewMapPoints函數(shù)中通過三角化也會(huì)生成MapPoints

? ? ? ? ? ? ? ? ? ? ? ? ? // 這些MapPoints都會(huì)經(jīng)過MapPointCulling函數(shù)的檢驗(yàn)

? ? mlpRecentAddedMapPoints.push_back(pMP);

? ? // 候選待檢查地圖點(diǎn)存放在mlpRecentAddedMapPoints

}

? ? }

}

? ? }? ?

? ? // Update links in the Covisibility Graph

// 步驟4:更新關(guān)鍵幀間的連接關(guān)系踱侣,Covisibility圖和Essential圖(tree)

? ? mpCurrentKeyFrame->UpdateConnections();

? ? // Insert Keyframe in Map

// 步驟5:將該關(guān)鍵幀插入到地圖中

? ? mpMap->AddKeyFrame(mpCurrentKeyFrame);

}

/**

* @brief 剔除ProcessNewKeyFrame(不在幀上的地圖點(diǎn) 進(jìn)入待查list)和

*? ? ? ? ? ? CreateNewMapPoints(兩幀三角變換產(chǎn)生的新地圖點(diǎn)進(jìn)入 待查list)

*? ? ? ? ? ? 函數(shù)中引入的質(zhì)量不好的MapPoints

* @see VI-B recent map points culling

*/

// 對(duì)于ProcessNewKeyFrame和CreateNewMapPoints中最近添加的MapPoints進(jìn)行檢查剔除

void LocalMapping::MapPointCulling()

{

? ? // Check Recent Added MapPoints

? ? list<MapPoint*>::iterator lit = mlpRecentAddedMapPoints.begin();//待檢測(cè)的地圖點(diǎn) 迭代器

? ? const unsigned long int nCurrentKFid = mpCurrentKeyFrame->mnId;//當(dāng)前關(guān)鍵幀id

? ? //? 從添加該地圖點(diǎn)的關(guān)鍵幀算起的 初始三個(gè)關(guān)鍵幀,

? ? // 第一幀不算大磺,后面兩幀看到該地圖點(diǎn)的幀數(shù)抡句,對(duì)于單目<=2,對(duì)于雙目和RGBD<=3杠愧;

? ? int nThObs;

? ? if(mbMonocular)

nThObs = 2;

? ? else

nThObs = 3;

? ? const int cnThObs = nThObs;

// 遍歷等待檢查的地圖點(diǎn)MapPoints

? ? while(lit !=mlpRecentAddedMapPoints.end())

? ? {

MapPoint* pMP = *lit;//? 新添加的地圖點(diǎn)

if(pMP->isBad())

{

// 步驟1:已經(jīng)是壞點(diǎn)的MapPoints直接從檢查鏈表中刪除 ?

? ? lit = mlpRecentAddedMapPoints.erase(lit);

}

//? 跟蹤(匹配上)到該地圖點(diǎn)的普通幀幀數(shù)(IncreaseFound)< 應(yīng)該觀測(cè)到該地圖點(diǎn)的普通幀數(shù)量(25%*IncreaseVisible):

// 該地圖點(diǎn)雖在視野范圍內(nèi)待榔,但很少被普通幀檢測(cè)到。 剔除

else if(pMP->GetFoundRatio()<0.25f )

{

// 步驟2:將不滿足VI-B條件的MapPoint剔除

// VI-B 條件1:

// 跟蹤到該MapPoint的Frame數(shù)相比預(yù)計(jì)可觀測(cè)到該MapPoint的Frame數(shù)的比例需大于25%

// IncreaseFound() / IncreaseVisible(該地圖點(diǎn)在視野范圍內(nèi)) < 25%流济,注意不一定是關(guān)鍵幀锐锣。 ?

? ? pMP->SetBadFlag();

? ? lit = mlpRecentAddedMapPoints.erase(lit);//從待查? list中刪除

}

//

// 初始三個(gè)關(guān)鍵幀 地圖點(diǎn)觀測(cè)次數(shù) 不能太少

// 而且單目的要求更嚴(yán)格腌闯,需要三幀都看到

else if(( (int)nCurrentKFid - (int)pMP->mnFirstKFid) >=2 && pMP->Observations() <= cnThObs)

{

? // 步驟3:將不滿足VI-B條件的MapPoint剔除

? ? ? ? ? ? // VI-B 條件2:從該點(diǎn)建立開始,到現(xiàn)在已經(jīng)過了不小于2幀雕憔,

? ? ? ? ? ? // 但是觀測(cè)到該點(diǎn)的關(guān)鍵幀數(shù)卻不超過cnThObs幀姿骏,那么該點(diǎn)檢驗(yàn)不合格

? ? pMP->SetBadFlag();

? ? lit = mlpRecentAddedMapPoints.erase(lit);//從待查? list中刪除

}

else if(((int)nCurrentKFid - (int)pMP->mnFirstKFid ) >= 3)

// 步驟4:從建立該點(diǎn)開始,已經(jīng)過了3幀(前三幀地圖點(diǎn)比較寶貴需要特別檢查)橘茉,放棄對(duì)該MapPoint的檢測(cè) ?

? ? lit = mlpRecentAddedMapPoints.erase(lit);//從待查? list中刪除

else

? ? lit++;

? ? }

}

/**

* @brief 相機(jī)運(yùn)動(dòng)過程中和共視程度比較高的關(guān)鍵幀通過三角化恢復(fù)出一些MapPoints

*? 根據(jù)當(dāng)前關(guān)鍵幀恢復(fù)出一些新的地圖點(diǎn)工腋,不包括和當(dāng)前關(guān)鍵幀匹配的局部地圖點(diǎn)(已經(jīng)在ProcessNewKeyFrame中處理)

*? 先處理新關(guān)鍵幀與局部地圖點(diǎn)之間的關(guān)系,然后對(duì)局部地圖點(diǎn)進(jìn)行檢查畅卓,

*? 最后再通過新關(guān)鍵幀恢復(fù) 新的局部地圖點(diǎn):CreateNewMapPoints()

*

* 步驟1:在當(dāng)前關(guān)鍵幀的 共視關(guān)鍵幀 中找到 共視程度 最高的前nn幀 相鄰幀vpNeighKFs

* 步驟2:遍歷和當(dāng)前關(guān)鍵幀 相鄰的 每一個(gè)關(guān)鍵幀vpNeighKFs

* 步驟3:判斷相機(jī)運(yùn)動(dòng)的基線在(兩針間的相機(jī)相對(duì)坐標(biāo))是不是足夠長(zhǎng)

* 步驟4:根據(jù)兩個(gè)關(guān)鍵幀的位姿計(jì)算它們之間的基本矩陣 F =? inv(K1 轉(zhuǎn)置) * t12 叉乘 R12 * inv(K2)

* 步驟5:通過幀間詞典向量加速匹配擅腰,極線約束限制匹配時(shí)的搜索范圍身害,進(jìn)行特征點(diǎn)匹配

* 步驟6:對(duì)每對(duì)匹配點(diǎn) 2d-2d 通過三角化生成3D點(diǎn),和 Triangulate函數(shù)差不多

*? 步驟6.1:取出匹配特征點(diǎn)

*? 步驟6.2:利用匹配點(diǎn)反投影得到視差角? 用來決定使用三角化恢復(fù)(視差角較大) 還是 直接2-d點(diǎn)反投影(視差角較小)

*? 步驟6.3:對(duì)于雙目巍糯,利用雙目基線 深度 得到視差角

*? 步驟6.4:視差角較大時(shí) 使用 三角化恢復(fù)3D點(diǎn)

*? 步驟6.4:對(duì)于雙目 視差角較小時(shí) 二維點(diǎn) 利用深度值 反投影 成 三維點(diǎn)? ? 單目的話直接跳過

*? 步驟6.5:檢測(cè)生成的3D點(diǎn)是否在相機(jī)前方

*? 步驟6.6:計(jì)算3D點(diǎn)在當(dāng)前關(guān)鍵幀下的重投影誤差? 誤差較大跳過

*? 步驟6.7:計(jì)算3D點(diǎn)在 鄰接關(guān)鍵幀 下的重投影誤差 誤差較大跳過

*? 步驟6.9:三角化生成3D點(diǎn)成功抓艳,構(gòu)造成地圖點(diǎn) MapPoint

*? 步驟6.9:為該MapPoint添加屬性

*? 步驟6.10:將新產(chǎn)生的點(diǎn)放入檢測(cè)隊(duì)列 mlpRecentAddedMapPoints? 交給 MapPointCulling() 檢查生成的點(diǎn)是否合適

* @see?

*/

void LocalMapping::CreateNewMapPoints()

{

? ? // Retrieve neighbor keyframes in covisibility graph

? ? int nn = 10;// 雙目/深度 共視幀 數(shù)量

? ? if(mbMonocular)

nn=20;//單目

// 步驟1:在當(dāng)前關(guān)鍵幀的 共視關(guān)鍵幀 中找到 共視程度 最高的nn幀 相鄰幀vpNeighKFs

? ? const vector<KeyFrame*> vpNeighKFs = mpCurrentKeyFrame->GetBestCovisibilityKeyFrames(nn);

? ? ORBmatcher matcher(0.6,false);// 描述子匹配器

? ? ? ? ? ? // 當(dāng)前關(guān)鍵幀 旋轉(zhuǎn)平移矩陣向量

? ? cv::Mat Rcw1 = mpCurrentKeyFrame->GetRotation();// 世界---> 當(dāng)前關(guān)鍵幀

? ? cv::Mat Rwc1 = Rcw1.t();// 當(dāng)前關(guān)鍵幀---> 世界

? ? cv::Mat tcw1 = mpCurrentKeyFrame->GetTranslation();

? ? cv::Mat Tcw1(3,4,CV_32F);// 世界---> 當(dāng)前關(guān)鍵幀 變換矩陣

? ? Rcw1.copyTo(Tcw1.colRange(0,3));

? ? tcw1.copyTo(Tcw1.col(3));

? ? // 得到當(dāng)前當(dāng)前關(guān)鍵幀在世界坐標(biāo)系中的坐標(biāo)

? ? cv::Mat Ow1 = mpCurrentKeyFrame->GetCameraCenter();

? ? ? ? ? // 相機(jī)內(nèi)參數(shù)

? ? const float &fx1 = mpCurrentKeyFrame->fx;

? ? const float &fy1 = mpCurrentKeyFrame->fy;

? ? const float &cx1 = mpCurrentKeyFrame->cx;

? ? const float &cy1 = mpCurrentKeyFrame->cy;

? ? const float &invfx1 = mpCurrentKeyFrame->invfx;

? ? const float &invfy1 = mpCurrentKeyFrame->invfy;

? ? const float ratioFactor = 1.5f*mpCurrentKeyFrame->mfScaleFactor;//匹配點(diǎn) 查找范圍

? ? int nnew=0;

? ? // Search matches with epipolar restriction and triangulate

// 步驟2:遍歷和當(dāng)前關(guān)鍵幀 相鄰的 每一個(gè)關(guān)鍵幀vpNeighKFs ? ?

? ? for(size_t i=0; i<vpNeighKFs.size(); i++)

? ? {

if(i>0 && CheckNewKeyFrames())

? ? return;

KeyFrame* pKF2 = vpNeighKFs[i];//關(guān)鍵幀

// Check first that baseline is not too short

? // 鄰接的關(guān)鍵幀在世界坐標(biāo)系中的坐標(biāo)

cv::Mat Ow2 = pKF2->GetCameraCenter();

? // 基線向量战惊,兩個(gè)關(guān)鍵幀間的相機(jī)相對(duì)坐標(biāo)

cv::Mat vBaseline = Ow2-Ow1;

? // 基線長(zhǎng)度

const float baseline = cv::norm(vBaseline);

// 步驟3:判斷相機(jī)運(yùn)動(dòng)的基線是不是足夠長(zhǎng)

if(!mbMonocular)

{

? ? if(baseline < pKF2->mb)

? ? continue;// 如果是立體相機(jī)淹父,關(guān)鍵幀間距太小時(shí)不生成3D點(diǎn)

}

else// 單目相機(jī)

{

? // 鄰接關(guān)鍵幀的場(chǎng)景深度中值

? ? const float medianDepthKF2 = pKF2->ComputeSceneMedianDepth(2);//中值深度

? ? // baseline與景深的比例

? ? const float ratioBaselineDepth = baseline/medianDepthKF2;

? ? ? ? ? ? ? ? ? // 如果特別遠(yuǎn)(比例特別小)喂走,那么不考慮當(dāng)前鄰接的關(guān)鍵幀蜕该,不生成3D點(diǎn)

? ? if(ratioBaselineDepth < 0.01)

continue;

}

// Compute Fundamental Matrix

// 步驟4:根據(jù)兩個(gè)關(guān)鍵幀的位姿計(jì)算它們之間的基本矩陣

? ? ? // 根據(jù)兩關(guān)鍵幀的姿態(tài)計(jì)算兩個(gè)關(guān)鍵幀之間的基本矩陣

? ? ? // F =? inv(K1 轉(zhuǎn)置)*E*inv(K2) = inv(K1 轉(zhuǎn)置) * t12 叉乘 R12 * inv(K2)

cv::Mat F12 = ComputeF12(mpCurrentKeyFrame,pKF2);

// Search matches that fullfil epipolar constraint

// 步驟5:通過幀間詞典向量加速匹配蔗彤,極線約束限制匹配時(shí)的搜索范圍俩莽,進(jìn)行特征點(diǎn)匹配

vector<pair<size_t,size_t> > vMatchedIndices;// 特征匹配候選點(diǎn)

matcher.SearchForTriangulation(mpCurrentKeyFrame,pKF2,F12,vMatchedIndices,false);

? ? ? ? // 相鄰關(guān)鍵幀 旋轉(zhuǎn)平移矩陣向量

cv::Mat Rcw2 = pKF2->GetRotation();

cv::Mat Rwc2 = Rcw2.t();

cv::Mat tcw2 = pKF2->GetTranslation();

cv::Mat Tcw2(3,4,CV_32F);// 轉(zhuǎn)換矩陣

Rcw2.copyTo(Tcw2.colRange(0,3));

tcw2.copyTo(Tcw2.col(3));

// 相機(jī)內(nèi)參

const float &fx2 = pKF2->fx;

const float &fy2 = pKF2->fy;

const float &cx2 = pKF2->cx;

const float &cy2 = pKF2->cy;

const float &invfx2 = pKF2->invfx;

const float &invfy2 = pKF2->invfy;

// Triangulate each match

// 三角化每一個(gè)匹配點(diǎn)對(duì)

// 步驟6:對(duì)每對(duì)匹配點(diǎn) 2d-2d 通過三角化生成3D點(diǎn),和 Triangulate函數(shù)差不多

const int nmatches = vMatchedIndices.size();

for(int ikp=0; ikp<nmatches; ikp++)

{

// 步驟6.1:取出匹配特征點(diǎn) ?

? ? const int &idx1 = vMatchedIndices[ikp].first; // 當(dāng)前匹配對(duì)在當(dāng)前關(guān)鍵幀中的索引

? ? const int &idx2 = vMatchedIndices[ikp].second;// 當(dāng)前匹配對(duì)在鄰接關(guān)鍵幀中的索引

? ? //當(dāng)前關(guān)鍵幀 特征點(diǎn) 和 右圖像匹配點(diǎn)橫坐標(biāo)

? ? const cv::KeyPoint &kp1 = mpCurrentKeyFrame->mvKeysUn[idx1];

? ? const float kp1_ur=mpCurrentKeyFrame->mvuRight[idx1];

? ? bool bStereo1 = kp1_ur >= 0;//右圖像匹配點(diǎn)橫坐標(biāo)>=0是雙目/深度相機(jī)

? ? // 鄰接關(guān)鍵幀 特征點(diǎn) 和 右圖像匹配點(diǎn)橫坐標(biāo)

? ? const cv::KeyPoint &kp2 = pKF2->mvKeysUn[idx2];

? ? const float kp2_ur = pKF2->mvuRight[idx2];

? ? bool bStereo2 = kp2_ur >= 0;

// 步驟6.2:利用匹配點(diǎn)反投影得到視差角? ?

? ? // Check parallax between rays

? ? // 相機(jī)歸一化平面上的點(diǎn)坐標(biāo)

? ? cv::Mat xn1 = (cv::Mat_<float>(3,1) << (kp1.pt.x - cx1)*invfx1, (kp1.pt.y - cy1)*invfy1, 1.0);

? ? cv::Mat xn2 = (cv::Mat_<float>(3,1) << (kp2.pt.x - cx2)*invfx2, (kp2.pt.y - cy2)*invfy2, 1.0);


? ? // 由相機(jī)坐標(biāo)系轉(zhuǎn)到世界坐標(biāo)系旺坠,得到視差角余弦值

? ? cv::Mat ray1 = Rwc1*xn1;// 相機(jī)坐標(biāo)系 ------> 世界坐標(biāo)系

? ? cv::Mat ray2 = Rwc2*xn2;

? ? // 向量a × 向量b / (向量a模 × 向量吧模) = 夾角余弦值

? ? const float cosParallaxRays = ray1.dot(ray2) / (cv::norm(ray1)*cv::norm(ray2));


? ? // 加1是為了讓cosParallaxStereo隨便初始化為一個(gè)很大的值

? ? float cosParallaxStereo = cosParallaxRays+1;

? ? float cosParallaxStereo1 = cosParallaxStereo;

? ? float cosParallaxStereo2 = cosParallaxStereo;

// 步驟6.3:對(duì)于雙目,利用雙目基線 深度 得到視差角

? ? if(bStereo1)

cosParallaxStereo1 = cos(2*atan2(mpCurrentKeyFrame->mb/2,mpCurrentKeyFrame->mvDepth[idx1]));

? ? else if(bStereo2)

cosParallaxStereo2 = cos(2*atan2(pKF2->mb/2,pKF2->mvDepth[idx2]));

? ? // 得到雙目觀測(cè)的視差角

? ? cosParallaxStereo = min(cosParallaxStereo1,cosParallaxStereo2);


// 步驟6.4:三角化恢復(fù)3D點(diǎn)

? ? cv::Mat x3D;

? // cosParallaxRays>0 && (bStereo1 || bStereo2 || cosParallaxRays<0.9998)表明視差角正常

? ? ? ? ? ? ? ? ? // cosParallaxRays < cosParallaxStereo表明視差角很小

? ? ? ? ? ? ? ? ? // 視差角度小時(shí)用三角法恢復(fù)3D點(diǎn)扮超,視差角大時(shí)用雙目恢復(fù)3D點(diǎn)(雙目以及深度有效)

? ? if(cosParallaxRays < cosParallaxStereo && cosParallaxRays>0 && (bStereo1 || bStereo2 || cosParallaxRays<0.9998))

? ? {

// Linear Triangulation Method

? ? ? // p1 = k × [R1 t1] × D? ? ? k逆 × p1 =? [R1 t1] × D? ? x1 = T1 × D? ? x1叉乘x1 = x1叉乘T1 × D = 0

? ? ? // p2 = k × [ R2 t2]? × D? ? k逆 × p2 =? [R2 t2] × D? ? x2 = T2 × D? ? x2叉乘x2 = x2叉乘T2 × D = 0

? ? ? //

? ? ? //p = ( x,y,1)

? ? ? //其叉乘矩陣為

? ? ? //? 叉乘矩陣 = [0? -1? y;? ? ? ? ? ? ? ? T0

? ? ? //? ? ? ? ? ? ? ? ? ? ? 1? 0? -x;? ? ? ? ? *? T1? *D? ===>( y * T2 - T1 ) *D = 0

? ? ? //? ? ? ? ? ? ? ? ? ? ? -y? x? 0 ] ? ? T2? ? ? ? ? ? ? ? ? ( x * T2 - T0 ) *D = 0

? ? ? //一個(gè)點(diǎn)兩個(gè)方程? 兩個(gè)點(diǎn) 四個(gè)方程? A × D =0? 求三維點(diǎn) D? 對(duì) A奇異值分解

cv::Mat A(4,4,CV_32F);

A.row(0) = xn1.at<float>(0)*Tcw1.row(2) - Tcw1.row(0);

A.row(1) = xn1.at<float>(1)*Tcw1.row(2) - Tcw1.row(1);

A.row(2) = xn2.at<float>(0)*Tcw2.row(2) - Tcw2.row(0);

A.row(3) = xn2.at<float>(1)*Tcw2.row(2) - Tcw2.row(1);

cv::Mat w,u,vt;

cv::SVD::compute(A,w,u,vt,cv::SVD::MODIFY_A| cv::SVD::FULL_UV);

x3D = vt.row(3).t();

if(x3D.at<float>(3)==0)

? ? continue;

// Euclidean coordinates

x3D = x3D.rowRange(0,3) / x3D.at<float>(3);//其次點(diǎn)坐標(biāo) 除去尺度

? ? }

? //? 步驟6.4:對(duì)于雙目 視差角較小時(shí) 二維點(diǎn) 利用深度值 反投影 成 三維點(diǎn)

? ? else if(bStereo1 && cosParallaxStereo1<cosParallaxStereo2)// 雙目 視差角 小

? ? {

x3D = mpCurrentKeyFrame->UnprojectStereo(idx1);// 二維點(diǎn) 反投影 成 三維點(diǎn)? ? ? ? ? ? ?

? ? }

? ? else if(bStereo2 && cosParallaxStereo2 < cosParallaxStereo1)

? ? {

x3D = pKF2->UnprojectStereo(idx2);

? ? }

? ? ? // 單目 視差角 較小時(shí) 生成不了三維點(diǎn)

? ? else

continue; //沒有雙目/深度 且兩針視角差太小? 三角測(cè)量也不合適 得不到三維點(diǎn) No stereo and very low parallax

? ? cv::Mat x3Dt = x3D.t();


// 步驟6.5:檢測(cè)生成的3D點(diǎn)是否在相機(jī)前方

? ? //Check triangulation in front of cameras

? ? float z1 = Rcw1.row(2).dot(x3Dt) + tcw1.at<float>(2);// 只算z坐標(biāo)值

? ? if(z1<= 0)

continue;

? ? float z2 = Rcw2.row(2).dot(x3Dt)+tcw2.at<float>(2);

? ? if(z2 <= 0)

continue;


? ? ? ? // 步驟6.6:計(jì)算3D點(diǎn)在當(dāng)前關(guān)鍵幀下的重投影誤差

? ? //Check reprojection error in first keyframe

? ? const float &sigmaSquare1 = mpCurrentKeyFrame->mvLevelSigma2[kp1.octave];//誤差 分布參數(shù)

? ? const float x1 = Rcw1.row(0).dot(x3Dt) + tcw1.at<float>(0);//相機(jī)歸一化坐標(biāo)

? ? const float y1 = Rcw1.row(1).dot(x3Dt) + tcw1.at<float>(1);

? ? const float invz1 = 1.0/z1;

? ? if(!bStereo1)

? ? {// 單目

float u1 = fx1*x1*invz1 + cx1;//像素坐標(biāo)

float v1 = fy1*y1*invz1 + cy1;

float errX1 = u1 - kp1.pt.x;

float errY1 = v1 - kp1.pt.y;

if((errX1*errX1+errY1*errY1) > 5.991*sigmaSquare1)

? ? continue;//投影誤差過大 跳過

? ? }

? ? else

? ? {// 雙目 / 深度 相機(jī)? 有右圖像匹配點(diǎn)橫坐標(biāo)差值

float u1 = fx1*x1*invz1+cx1;

float u1_r = u1 - mpCurrentKeyFrame->mbf * invz1;//左圖像坐標(biāo)值 - 視差 =? 右圖像匹配點(diǎn)橫坐標(biāo)

float v1 = fy1*y1*invz1+cy1;

float errX1 = u1 - kp1.pt.x;

float errY1 = v1 - kp1.pt.y;

float errX1_r = u1_r - kp1_ur;

// 基于卡方檢驗(yàn)計(jì)算出的閾值(假設(shè)測(cè)量有一個(gè)一個(gè)像素的偏差)

if((errX1*errX1 + errY1*errY1 + errX1_r*errX1_r) > 7.8*sigmaSquare1)

? ? continue;//投影誤差過大 跳過

? ? }

? ? ? ? // 步驟6.7:計(jì)算3D點(diǎn)在 鄰接關(guān)鍵幀 下的重投影誤差

? ? //Check reprojection error in second keyframe

? ? const float sigmaSquare2 = pKF2->mvLevelSigma2[kp2.octave];

? ? const float x2 = Rcw2.row(0).dot(x3Dt)+tcw2.at<float>(0);

? ? const float y2 = Rcw2.row(1).dot(x3Dt)+tcw2.at<float>(1);

? ? const float invz2 = 1.0/z2;

? ? if(!bStereo2)

? ? {// 單目

float u2 = fx2*x2*invz2+cx2;

float v2 = fy2*y2*invz2+cy2;

float errX2 = u2 - kp2.pt.x;

float errY2 = v2 - kp2.pt.y;

if((errX2*errX2+errY2*errY2)>5.991*sigmaSquare2)

? ? continue;//投影誤差過大 跳過

? ? }

? ? else

? ? {// 雙目 / 深度 相機(jī)? 有右圖像匹配點(diǎn)橫坐標(biāo)差值

float u2 = fx2*x2*invz2+cx2;

float u2_r = u2 - mpCurrentKeyFrame->mbf*invz2;//左圖像坐標(biāo)值 - 視差 =? 右圖像匹配點(diǎn)橫坐標(biāo)

float v2 = fy2*y2*invz2+cy2;

float errX2 = u2 - kp2.pt.x;

float errY2 = v2 - kp2.pt.y;

float errX2_r = u2_r - kp2_ur;

if((errX2*errX2+errY2*errY2+errX2_r*errX2_r)>7.8*sigmaSquare2)

? ? continue;//投影誤差過大 跳過

? ? }


? ? ? ? ? // 步驟6.8:檢查尺度連續(xù)性

? ? //Check scale consistency

? ? cv::Mat normal1 = x3D-Ow1;//? 世界坐標(biāo)系下取刃,3D點(diǎn)與相機(jī)間的向量,方向由相機(jī)指向3D點(diǎn)

? ? float dist1 = cv::norm(normal1);// 模長(zhǎng)

? ? cv::Mat normal2 = x3D-Ow2;

? ? float dist2 = cv::norm(normal2);

? ? if(dist1==0 || dist2==0)

continue;// 模長(zhǎng)為0 跳過

? ? ? ? ? ? ? ? ? // ratioDist是不考慮金字塔尺度下的距離比例

? ? const float ratioDist = dist2/dist1;

? // 金字塔尺度因子的比例

? ? const float ratioOctave = mpCurrentKeyFrame->mvScaleFactors[kp1.octave] / pKF2->mvScaleFactors[kp2.octave];

? ? /*if(fabs(ratioDist-ratioOctave)>ratioFactor)

continue;*/

? ? // 深度比值和 兩幅圖像下的金字塔層級(jí)比值應(yīng)該相差不大

? ? // |ratioDist/ratioOctave |<ratioFactor

? ? if(ratioDist * ratioFactor<ratioOctave || ratioDist > ratioOctave*ratioFactor)

continue;


? // 步驟6.9:三角化生成3D點(diǎn)成功出刷,構(gòu)造成地圖點(diǎn) MapPoint

? ? // Triangulation is succesfull

? ? MapPoint* pMP = new MapPoint(x3D,mpCurrentKeyFrame,mpMap);


? ? ? ? ? ? // 步驟6.9:為該MapPoint添加屬性:

? ? ? // a.觀測(cè)到該MapPoint的關(guān)鍵幀

? ? pMP->AddObservation(mpCurrentKeyFrame,idx1); // 地圖點(diǎn) 添加觀測(cè)幀?

? ? pMP->AddObservation(pKF2,idx2);//

? ? mpCurrentKeyFrame->AddMapPoint(pMP,idx1);// 關(guān)鍵幀 添加地圖點(diǎn)

? ? pKF2->AddMapPoint(pMP,idx2);

? ? ? ? ? ? ? // b.該MapPoint的描述子

? ? pMP->ComputeDistinctiveDescriptors();

? ? ? ? ? ? ? // c.該MapPoint的平均觀測(cè)方向和深度范圍

? ? pMP->UpdateNormalAndDepth();

? ? ? ? ? ? ? // d.地圖添加地圖點(diǎn)

? ? mpMap->AddMapPoint(pMP);

? ? ? ? ? ? // 步驟6.10:將新產(chǎn)生的點(diǎn)放入檢測(cè)隊(duì)列 mlpRecentAddedMapPoints

? ? ? ? ? ? ? ? ? // 這些MapPoints都會(huì)經(jīng)過MapPointCulling函數(shù)的檢驗(yàn)

? ? mlpRecentAddedMapPoints.push_back(pMP);

? ? nnew++;

}

? ? }

}

/**

* @brief? ? 檢查并融合 當(dāng)前關(guān)鍵幀 與 相鄰幀(一級(jí)二級(jí)相鄰幀)重復(fù)的地圖點(diǎn) MapPoints

* 步驟1:獲得當(dāng)前關(guān)鍵幀在covisibility幀連接圖中權(quán)重排名前nn的一級(jí)鄰接關(guān)鍵幀(按觀測(cè)到當(dāng)前幀地圖點(diǎn)次數(shù)選取)

* 步驟2:獲得當(dāng)前關(guān)鍵幀在 其一級(jí)相鄰幀 在 covisibility圖中權(quán)重排名前5 的二級(jí)鄰接關(guān)鍵幀

* 步驟3:將當(dāng)前幀的 地圖點(diǎn)MapPoints 分別與 其一級(jí)二級(jí)相鄰幀的 地圖點(diǎn) MapPoints 進(jìn)行融合(保留觀測(cè)次數(shù)最高的)

* 步驟4:找到一級(jí)二級(jí)相鄰幀所有的地圖點(diǎn)MapPoints 與當(dāng)前幀 的? 地圖點(diǎn)MapPoints 進(jìn)行融合

* 步驟5:更新當(dāng)前幀 地圖點(diǎn) MapPoints 的描述子璧疗,深度,觀測(cè)主方向等屬性

* 步驟5:更新當(dāng)前 與其它幀的連接關(guān)系 (? 觀測(cè)到互相的地圖點(diǎn)的次數(shù)等信息 )

* @return? 無

*/

void LocalMapping::SearchInNeighbors()

{

? ? // Retrieve neighbor keyframes

// 步驟1:獲得當(dāng)前關(guān)鍵幀在covisibility圖中權(quán)重排名前nn的一級(jí)鄰接關(guān)鍵幀

? ? ? ? ? // 找到當(dāng)前幀一級(jí)相鄰與二級(jí)相鄰關(guān)鍵幀

? ? int nn = 10;

? ? if(mbMonocular)

nn=20;//單目 多找一些

? // 一級(jí)相鄰

? ? const vector<KeyFrame*> vpNeighKFs = mpCurrentKeyFrame->GetBestCovisibilityKeyFrames(nn);

? ? vector<KeyFrame*> vpTargetKFs;// 最后合格的一級(jí)二級(jí)相鄰關(guān)鍵幀

? ? // 遍歷每一個(gè) 一級(jí)相鄰幀

? ? for(vector<KeyFrame*>::const_iterator vit=vpNeighKFs.begin(), vend=vpNeighKFs.end(); vit!=vend; vit++)

? ? {

KeyFrame* pKFi = *vit;// 一級(jí)相鄰關(guān)鍵幀

if(pKFi->isBad() || pKFi->mnFuseTargetForKF == mpCurrentKeyFrame->mnId)//壞幀? 或者 已經(jīng)加入過

? ? continue;// 跳過

vpTargetKFs.push_back(pKFi);// 加入 最后合格的相鄰關(guān)鍵幀

pKFi->mnFuseTargetForKF = mpCurrentKeyFrame->mnId;// 已經(jīng)做過相鄰匹配? 標(biāo)記已經(jīng)加入

// 步驟2:獲得當(dāng)前關(guān)鍵幀在 其一級(jí)相鄰幀的? covisibility圖中權(quán)重排名前5的二級(jí)鄰接關(guān)鍵幀

? ? ? ? // 二級(jí)相鄰

// Extend to some second neighbors

const vector<KeyFrame*> vpSecondNeighKFs = pKFi->GetBestCovisibilityKeyFrames(5);

// 遍歷每一個(gè) 二級(jí)相鄰幀

for(vector<KeyFrame*>::const_iterator vit2=vpSecondNeighKFs.begin(), vend2=vpSecondNeighKFs.end(); vit2!=vend2; vit2++)

{

? ? KeyFrame* pKFi2 = *vit2;// 二級(jí)相鄰關(guān)鍵幀

? ? if(pKFi2->isBad() || pKFi2->mnFuseTargetForKF==mpCurrentKeyFrame->mnId || pKFi2->mnId==mpCurrentKeyFrame->mnId)

continue;// 二級(jí)相鄰關(guān)鍵幀是壞幀 在一級(jí)時(shí)已經(jīng)加入 或者 又找回來了找到當(dāng)前幀了 跳過

? ? vpTargetKFs.push_back(pKFi2);

}

? ? }

// 步驟3:將當(dāng)前幀的 地圖點(diǎn)MapPoints 分別與 其一級(jí)二級(jí)相鄰幀的 地圖點(diǎn) MapPoints 進(jìn)行融合

? ? // Search matches by projection from current KF in target KFs

? ? ORBmatcher matcher;

? ? vector<MapPoint*> vpMapPointMatches = mpCurrentKeyFrame->GetMapPointMatches();//與當(dāng)前幀 匹配的地圖點(diǎn)

? ? // vector<KeyFrame*>::iterator

? ? for(auto? vit=vpTargetKFs.begin(), vend=vpTargetKFs.end(); vit!=vend; vit++)

? ? {

KeyFrame* pKFi = *vit;//其一級(jí)二級(jí)相鄰幀

// 投影當(dāng)前幀的MapPoints到相鄰關(guān)鍵幀pKFi中馁龟,在附加區(qū)域搜索匹配關(guān)鍵點(diǎn)崩侠,并判斷是否有重復(fù)的MapPoints

// 1.如果MapPoint能匹配關(guān)鍵幀的特征點(diǎn),并且該點(diǎn)有對(duì)應(yīng)的MapPoint坷檩,那么將兩個(gè)MapPoint合并(選擇觀測(cè)數(shù)多的)

// 2.如果MapPoint能匹配關(guān)鍵幀的特征點(diǎn)却音,并且該點(diǎn)沒有對(duì)應(yīng)的MapPoint,那么為該點(diǎn)添加MapPoint

matcher.Fuse(pKFi,vpMapPointMatches);

? ? }



// 步驟4:將一級(jí)二級(jí)相鄰幀所有的地圖點(diǎn)MapPoints 與當(dāng)前幀(的MapPoints)進(jìn)行融合

? ? ? ? ? ? // 遍歷每一個(gè)一級(jí)鄰接和二級(jí)鄰接關(guān)鍵幀 找到所有的地圖點(diǎn)

? ? // Search matches by projection from target KFs in current KF

? ? // 用于存儲(chǔ)一級(jí)鄰接和二級(jí)鄰接關(guān)鍵幀所有MapPoints的集合

? ? vector<MapPoint*> vpFuseCandidates;// 一級(jí)二級(jí)相鄰幀所有地圖點(diǎn)

? ? vpFuseCandidates.reserve(vpTargetKFs.size() * vpMapPointMatches.size());// 幀數(shù)量 × 每一幀地圖點(diǎn)數(shù)量

? ? ? ? ? ? // vector<KeyFrame*>::iterator

? ? for(auto vitKF=vpTargetKFs.begin(), vendKF=vpTargetKFs.end(); vitKF!=vendKF; vitKF++)

? ? {

KeyFrame* pKFi = *vitKF;//其一級(jí)二級(jí)相鄰幀

vector<MapPoint*> vpMapPointsKFi = pKFi->GetMapPointMatches();//地圖點(diǎn)

// vector<MapPoint*>::iterator

for(auto vitMP=vpMapPointsKFi.begin(), vendMP=vpMapPointsKFi.end(); vitMP!=vendMP; vitMP++)

{

? ? MapPoint* pMP = *vitMP;//? 一級(jí)二級(jí)相鄰幀 的每一個(gè)地圖點(diǎn)

? ? if(!pMP)

continue;

? ? if(pMP->isBad() || pMP->mnFuseCandidateForKF == mpCurrentKeyFrame->mnId)

continue;

? ? // 加入集合淌喻,并標(biāo)記 已經(jīng)加入

? ? pMP->mnFuseCandidateForKF = mpCurrentKeyFrame->mnId; //標(biāo)記 已經(jīng)加

? ? vpFuseCandidates.push_back(pMP); // 加入 一級(jí)二級(jí)相鄰幀 地圖點(diǎn) 集合

}

? ? }


? ? ? ? ? ? //一級(jí)二級(jí)相鄰幀 所有的 地圖點(diǎn) 與當(dāng)前幀 融合

? ? ? ? ? ? // 投影 地圖點(diǎn)MapPoints到當(dāng)前幀上僧家,在附加區(qū)域搜索匹配關(guān)鍵點(diǎn),并判斷是否有重復(fù)的地圖點(diǎn)

? ? ? ? // 1.如果MapPoint能匹配當(dāng)前幀的特征點(diǎn)裸删,并且該點(diǎn)有對(duì)應(yīng)的MapPoint八拱,那么將兩個(gè)MapPoint合并(選擇觀測(cè)數(shù)多的)

? ? ? ? // 2.如果MapPoint能匹配當(dāng)前幀的特征點(diǎn),并且該點(diǎn)沒有對(duì)應(yīng)的MapPoint,那么為該點(diǎn)添加MapPoint

? ? matcher.Fuse(mpCurrentKeyFrame,vpFuseCandidates);

// 步驟5:更新當(dāng)前幀MapPoints的描述子肌稻,深度清蚀,觀測(cè)主方向等屬性

? ? // Update points

? ? vpMapPointMatches = mpCurrentKeyFrame->GetMapPointMatches();//當(dāng)前幀 所有的 匹配地圖點(diǎn)

? ? for(size_t i=0, iend=vpMapPointMatches.size(); i<iend; i++)

? ? {

MapPoint* pMP=vpMapPointMatches[i];//當(dāng)前幀 每個(gè)關(guān)鍵點(diǎn)匹配的地圖點(diǎn)

if(pMP)//存在

{

? ? if(!pMP->isBad())//非 壞點(diǎn)

? ? {

pMP->ComputeDistinctiveDescriptors();// 更新 地圖點(diǎn)的描述子(在所有觀測(cè)在的描述子中選出最好的描述子)

pMP->UpdateNormalAndDepth();? ? ? ? ? // 更新平均觀測(cè)方向和觀測(cè)距離

? ? }

}

? ? }

// 步驟5:更新當(dāng)前幀的MapPoints后 更新與其它幀的連接關(guān)系 觀測(cè)到互相的地圖點(diǎn)的次數(shù)等信息

? ? ? ? ? ? // 更新covisibility圖

? ? // Update connections in covisibility graph

? ? mpCurrentKeyFrame->UpdateConnections();

}

/**

* @brief? ? 關(guān)鍵幀剔除

*? 在Covisibility Graph 關(guān)鍵幀連接圖 中的關(guān)鍵幀,

*? 其90%以上的地圖點(diǎn)MapPoints能被其他關(guān)鍵幀(至少3個(gè))觀測(cè)到爹谭,

*? 則認(rèn)為該關(guān)鍵幀為冗余關(guān)鍵幀枷邪。

* @param? pKF1 關(guān)鍵幀1

* @param? pKF2 關(guān)鍵幀2

* @return 兩個(gè)關(guān)鍵幀之間的基本矩陣 F

*/

void LocalMapping::KeyFrameCulling()

{

? ? // Check redundant keyframes (only local keyframes)

? ? // A keyframe is considered redundant if the 90% of the MapPoints it sees, are seen

? ? // in at least other 3 keyframes (in the same or finer scale)

? ? // We only consider close stereo points


// 步驟1:根據(jù)Covisibility Graph 關(guān)鍵幀連接 圖提取當(dāng)前幀的 所有共視關(guān)鍵幀(關(guān)聯(lián)幀) ?

? ? vector<KeyFrame*> vpLocalKeyFrames = mpCurrentKeyFrame->GetVectorCovisibleKeyFrames();


? ? ? ? ? ? // vector<KeyFrame*>::iterator

? ? ? ? ? // 對(duì)所有的局部關(guān)鍵幀進(jìn)行遍歷 ? ?

? ? for(auto? vit=vpLocalKeyFrames.begin(), vend=vpLocalKeyFrames.end(); vit!=vend; vit++)

? ? {

KeyFrame* pKF = *vit;// 當(dāng)前幀的每一個(gè) 局部關(guān)聯(lián)幀

if(pKF->mnId == 0)//第一幀關(guān)鍵幀為 初始化世界關(guān)鍵幀 跳過

? ? continue;

// 步驟2:提取每個(gè)共視關(guān)鍵幀的 地圖點(diǎn) MapPoints

const vector<MapPoint*> vpMapPoints = pKF->GetMapPointMatches();// 局部關(guān)聯(lián)幀 匹配的 地圖點(diǎn)

int nObs = 3;

const int thObs=nObs; //3

int nRedundantObservations=0;

int nMPs=0;

// 步驟3:遍歷該局部關(guān)鍵幀的MapPoints,判斷是否90%以上的MapPoints能被其它關(guān)鍵幀(至少3個(gè))觀測(cè)到

for(size_t i=0, iend=vpMapPoints.size(); i<iend; i++)

{

? ? MapPoint* pMP = vpMapPoints[i];// 該局部關(guān)鍵幀的 地圖點(diǎn) MapPoints

? ? if(pMP)

? ? {

if(!pMP->isBad())

{

? ? if(!mbMonocular)// 雙目/深度

? ? {? // 對(duì)于雙目诺凡,僅考慮近處的MapPoints东揣,不超過mbf * 35 / fx

if(pKF->mvDepth[i] > pKF->mThDepth || pKF->mvDepth[i] < 0)

? ? continue;

? ? }

? ? nMPs++;

? ? // 地圖點(diǎn) MapPoints 至少被三個(gè)關(guān)鍵幀觀測(cè)到

? ? if(pMP->Observations() > thObs)// 觀測(cè)幀個(gè)數(shù) > 3

? ? {

const int &scaleLevel = pKF->mvKeysUn[i].octave;// 金字塔層數(shù)

const map<KeyFrame*, size_t> observations = pMP->GetObservations();// 局部 觀測(cè)關(guān)鍵幀地圖

int nObs=0;

for(map<KeyFrame*, size_t>::const_iterator mit=observations.begin(), mend=observations.end(); mit!=mend; mit++)

{

? ? KeyFrame* pKFi = mit->first;

? ? if(pKFi==pKF)// 跳過 原地圖點(diǎn)的幀

continue;

? ? const int &scaleLeveli = pKFi->mvKeysUn[mit->second].octave;// 金字塔層數(shù)


? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 尺度約束,要求MapPoint在該局部關(guān)鍵幀的特征尺度大于(或近似于)其它關(guān)鍵幀的特征尺度

? ? if(scaleLeveli <= scaleLevel+1)

? ? {

nObs++;

if(nObs >= thObs)

? ? break;

? ? }

}

if(nObs >= thObs)

{// 該MapPoint至少被三個(gè)關(guān)鍵幀觀測(cè)到

? ? nRedundantObservations++;

}

? ? }

}

? ? }

}?

// 步驟4:該局部關(guān)鍵幀90%以上的MapPoints能被其它關(guān)鍵幀(至少3個(gè))觀測(cè)到腹泌,則認(rèn)為是冗余關(guān)鍵幀

if(nRedundantObservations > 0.9*nMPs)

? ? pKF->SetBadFlag();

? ? }

}

/**

* @brief? ? 根據(jù)兩關(guān)鍵幀的姿態(tài)計(jì)算兩個(gè)關(guān)鍵幀之間的基本矩陣

*? ? ? ? ? ? ? ? F =? inv(K1 轉(zhuǎn)置)*E*inv(K2) = inv(K1 轉(zhuǎn)置)*t叉乘R*inv(K2)

* @param? pKF1 關(guān)鍵幀1

* @param? pKF2 關(guān)鍵幀2

* @return 兩個(gè)關(guān)鍵幀之間的基本矩陣 F

*/

cv::Mat LocalMapping::ComputeF12(KeyFrame *&pKF1, KeyFrame *&pKF2)

{

? ? // Essential Matrix: t12叉乘R12

? ? // Fundamental Matrix: inv(K1轉(zhuǎn)置)*E*inv(K2) ?

? ? cv::Mat R1w = pKF1->GetRotation();? // Rc1w

? ? cv::Mat t1w = pKF1->GetTranslation();

? ? cv::Mat R2w = pKF2->GetRotation();? // Rc2w

? ? cv::Mat t2w = pKF2->GetTranslation();// t c2 w

? ? cv::Mat R12 = R1w*R2w.t();// R12 =Rc1w *? Rwc2 // c2 -->w --->c1

? ? cv::Mat t12 = -R12*t2w + t1w; // tw2? + t1w? ? // c2 -->w --->c1

? ? // t12 的叉乘矩陣

? ? cv::Mat t12x = SkewSymmetricMatrix(t12);

? ? const cv::Mat &K1 = pKF1->mK;

? ? const cv::Mat &K2 = pKF2->mK;

? ? return K1.t().inv()*t12x*R12*K2.inv();

}

/**

* @brief? ? 請(qǐng)求停止局部建圖線程? 設(shè)置停止標(biāo)志

* @return 無

*/

void LocalMapping::RequestStop()

{

? ? unique_lock<mutex> lock(mMutexStop);

? ? mbStopRequested = true;//局部建圖 請(qǐng)求停止

? ? unique_lock<mutex> lock2(mMutexNewKFs);

? ? mbAbortBA = true;//停止BA 優(yōu)化

}

/**

* @brief? ? 停止局部建圖線程? 設(shè)置停止標(biāo)志

* @return 無

*/

bool LocalMapping::Stop()

{

? ? unique_lock<mutex> lock(mMutexStop);

? ? if(mbStopRequested && !mbNotStop)

? ? {

mbStopped = true;

cout << "局部建圖停止 Local Mapping STOP" << endl;

return true;

? ? }

? ? return false;

}

/**

* @brief? ? 檢查局部建圖線程 是否停止

* @return 是否停止標(biāo)志

*/

bool LocalMapping::isStopped()

{

? ? unique_lock<mutex> lock(mMutexStop);

? ? return mbStopped;

}

/**

* @brief? 返回 請(qǐng)求停止局部建圖線程? 標(biāo)志

* @return 返回 請(qǐng)求停止局部建圖線程? 標(biāo)志

*/

bool LocalMapping::stopRequested()

{

? ? unique_lock<mutex> lock(mMutexStop);

? ? return mbStopRequested;

}

/**

* @brief? ? 釋放局部建圖線程?

* @return? 無

*/

void LocalMapping::Release()

{

? ? unique_lock<mutex> lock(mMutexStop);

? ? unique_lock<mutex> lock2(mMutexFinish);

? ? if(mbFinished)

return;

? ? mbStopped = false;

? ? mbStopRequested = false;

? ? // list<KeyFrame*>::iterator

? ? for(auto lit = mlNewKeyFrames.begin(), lend=mlNewKeyFrames.end(); lit!=lend; lit++)

delete *lit;//刪除關(guān)鍵幀

? ? mlNewKeyFrames.clear();

? ? cout << "局部建圖釋放 Local Mapping RELEASE" << endl;

}

/**

* @brief? ? 返回 可以接收新的一個(gè)關(guān)鍵幀標(biāo)志

* @return 是否 可以接收新的一個(gè)關(guān)鍵幀

*/

bool LocalMapping::AcceptKeyFrames()

{

? ? unique_lock<mutex> lock(mMutexAccept);

? ? return mbAcceptKeyFrames;

}

/**

* @brief? ? 設(shè)置 可以接收新的一個(gè)關(guān)鍵幀標(biāo)志

* @return 無

*/

void LocalMapping::SetAcceptKeyFrames(bool flag)

{

? ? unique_lock<mutex> lock(mMutexAccept);

? ? mbAcceptKeyFrames=flag;

}

/**

* @brief? ? 設(shè)置不要停止標(biāo)志

* @return? 成功與否

*/

bool LocalMapping::SetNotStop(bool flag)

{

? ? unique_lock<mutex> lock(mMutexStop);

? ? if(flag && mbStopped)//? 在已經(jīng)停止的情況下 設(shè)置不要停止? 錯(cuò)誤

return false;

? ? mbNotStop = flag;

? ? return true;

}

/**

* @brief? ? 停止 全局優(yōu)化 BA

* @return 是否 可以接收新的一個(gè)關(guān)鍵幀

*/

void LocalMapping::InterruptBA()

{

? ? mbAbortBA = true;

}

/**

* @brief? 計(jì)算向量的 叉乘矩陣? ? 變叉乘為 矩陣乘

* @return 該向量的叉乘矩陣

*/

cv::Mat LocalMapping::SkewSymmetricMatrix(const cv::Mat &v)

{

// 向量 t=(a1 a2 a3) t叉乘A

// 等于 向量t的叉乘矩陣 * A

//? t的叉乘矩陣

//|0? ? -a3? a2 |

//|a3? ? 0? -a1|

//|-a2? a1? ? 0 |

? ? return (cv::Mat_<float>(3,3) <<? ? ? ? ? ? 0, -v.at<float>(2), v.at<float>(1),

? ? v.at<float>(2),? ? ? ? ? ? ? 0,-v.at<float>(0),

? ? -v.at<float>(1),? v.at<float>(0),? ? ? ? ? ? ? 0);

}

/**

* @brief? ? 請(qǐng)求重置

* @return? 無

*/

void LocalMapping::RequestReset()

{

? ? {

unique_lock<mutex> lock(mMutexReset);

mbResetRequested = true;

? ? }

? ? while(1)

? ? {

{

? ? unique_lock<mutex> lock2(mMutexReset);

? ? if(!mbResetRequested)

break;

}

usleep(3000);

? ? }

}

/**

* @brief? ? 重置線程

* @return? 無

*/

void LocalMapping::ResetIfRequested()

{

? ? unique_lock<mutex> lock(mMutexReset);

? ? if(mbResetRequested)

? ? {

mlNewKeyFrames.clear();

mlpRecentAddedMapPoints.clear();

mbResetRequested=false;

? ? }

}

void LocalMapping::RequestFinish()

{

? ? unique_lock<mutex> lock(mMutexFinish);

? ? mbFinishRequested = true;

}

bool LocalMapping::CheckFinish()

{

? ? unique_lock<mutex> lock(mMutexFinish);

? ? return mbFinishRequested;

}

void LocalMapping::SetFinish()

{

? ? unique_lock<mutex> lock(mMutexFinish);

? ? mbFinished = true;? ?

? ? unique_lock<mutex> lock2(mMutexStop);

? ? mbStopped = true;

}

bool LocalMapping::isFinished()

{

? ? unique_lock<mutex> lock(mMutexFinish);

? ? return mbFinished;

}

} //namespace ORB_SLAM

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末嘶卧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子凉袱,更是在濱河造成了極大的恐慌芥吟,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件专甩,死亡現(xiàn)場(chǎng)離奇詭異钟鸵,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)涤躲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門棺耍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人种樱,你說我怎么就攤上這事烈掠。” “怎么了缸托?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)瘾蛋。 經(jīng)常有香客問我俐镐,道長(zhǎng),這世上最難降的妖魔是什么哺哼? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任佩抹,我火速辦了婚禮,結(jié)果婚禮上取董,老公的妹妹穿的比我還像新娘棍苹。我一直安慰自己,他們只是感情好茵汰,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布枢里。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪栏豺。 梳的紋絲不亂的頭發(fā)上彬碱,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音奥洼,去河邊找鬼巷疼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛灵奖,可吹牛的內(nèi)容都是我干的嚼沿。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼瓷患,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼骡尽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起尉尾,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤爆阶,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后沙咏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辨图,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年肢藐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了故河。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吆豹,死狀恐怖鱼的,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情痘煤,我是刑警寧澤凑阶,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站衷快,受9級(jí)特大地震影響宙橱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蘸拔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一师郑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧调窍,春花似錦宝冕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽菊卷。三九已至,卻和暖如春湿刽,著一層夾襖步出監(jiān)牢的瞬間的烁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工诈闺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留渴庆,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓雅镊,卻偏偏與公主長(zhǎng)得像襟雷,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子仁烹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 2018-10-09 Day1 認(rèn)識(shí)Excel卓缰,突破理論 第四期的訓(xùn)練營(yíng)如期而至计呈,回顧點(diǎn)點(diǎn)滴滴,感慨頗多征唬,感激那個(gè)...
    晨E戰(zhàn)到底閱讀 243評(píng)論 0 0
  • 文章速讀的三步驟的捌显。 1.快速瀏覽:用兩三分鐘的時(shí)間快速瀏覽所有文字,大概知道在講什么...
    橋本奈奈末閱讀 150評(píng)論 0 0
  • 今天語文課的設(shè)想总寒,又超出了學(xué)生的實(shí)際水平扶歪。原先的教學(xué)設(shè)計(jì)是整體把握《做客喀什》的主要內(nèi)容,從中體會(huì)出作者的...
    曉小利麗閱讀 539評(píng)論 0 0
  • 來源:WebSocket通信過程與實(shí)現(xiàn) 什么是 WebSocket 摄闸? WebSocket 是一種標(biāo)準(zhǔn)協(xié)議善镰,用于在...
    四火流年閱讀 364評(píng)論 0 1
  • 使用Delphi編個(gè)木馬玩 板塊禁止發(fā)布 “電子書資料” ,此類主題請(qǐng)發(fā)布至 剛學(xué)電腦時(shí)很喜歡網(wǎng)絡(luò)安全年枕,看著高手們...
    netppp閱讀 185評(píng)論 0 0