# MapPoint.cpp
### MapPoint::MapPoint()
用三維點構造地圖點,構建地圖點
在[Tracking::CreateInitialMapMonocular()](#Tracking::CreateInitialMapMonocular()),[Tracking::CreateNewKeyFrame()](#Tracking::CreateNewKeyFrame()),
[Tracking::UpdateLastFrame()](#Tracking::UpdateLastFrame())中引用
#### 定義
```
MapPoint::MapPoint(const cv::Mat &Pos, //3維點
KeyFrame *pRefKF, //關鍵幀
Map* pMap): //地圖
```
#### 函式流程
1. 構建地圖點
#### 函式詳解
1. 構建地圖點
```
Pos.copyTo(mWorldPos);
//平均觀測方向初始化為0
mNormalVector = cv::Mat::zeros(3,1,CV_32F);
// MapPoints can be created from Tracking and Local Mapping. This mutex avoid conflicts with id.
unique_lock<mutex> lock(mpMap->mMutexPointCreation);
mnId=nNextId++;
```
### MapPoint::AddObservation()
在地圖點上添加與關鍵幀的觀測關係
在[Tracking::CreateInitialMapMonocular()](#Tracking::CreateInitialMapMonocular())中引用
#### 定義
```
void MapPoint::AddObservation(KeyFrame* pKF, //關鍵幀
size_t idx) //關鍵幀的地圖點ID
```
#### 函式流程
1. 確認是否有觀測關係
2. 將關鍵幀相對應索引添加到地圖點上
#### 函式詳解
1. 確認是否有觀測關係
```
unique_lock<mutex> lock(mMutexFeatures);
//如果已經有觀測關係,跳過
if(mObservations.count(pKF))
return;
```
2. 將關鍵幀相對應索引添加到地圖點上
```
// 記錄下能觀測到該MapPoint的KF和該MapPoint在KF中的索引
mObservations[pKF]=idx;
if(pKF->mvuRight[idx]>=0)
nObs+=2; // 雙目或者grbd
else
nObs++; // 單目
```
### MapPoint::ComputeDistinctiveDescriptors()
從眾多觀測到該地圖點的特徵點中挑選最具有代表性的描述子
先獲得當前點的所有描述子,然後計算描述子之間的兩兩距離,最好的描述子與其他描述子應該具有最小的距離中值。
在[Tracking::CreateInitialMapMonocular()](#Tracking::CreateInitialMapMonocular()),[Tracking::CreateNewKeyFrame()](#Tracking::CreateNewKeyFrame()),[LocalMapping::SearchInNeighbors()](#LocalMapping::SearchInNeighbors())中引用
#### 函式流程
1. 獲取所有的觀測,跳過壞點
2. 遍歷觀測到3d點的所有關鍵幀,獲得orb描述子
3. 獲得這些描述子兩兩之間的距離
4. 選擇最具有代表性的描述子
#### 函式詳解
1. 獲取所有的觀測,跳過壞點
```
vector<cv::Mat> vDescriptors;
map<KeyFrame*,size_t> observations;
{
unique_lock<mutex> lock1(mMutexFeatures);
if(mbBad)
return;
observations=mObservations;
}
if(observations.empty())
return;
```
2. 遍歷觀測到3d點的所有關鍵幀,獲得orb描述子
```
vDescriptors.reserve(observations.size());
// 遍歷觀測到3d點的所有關鍵幀,獲得orb描述子,並插入到vDescriptors中
for(map<KeyFrame*,size_t>::iterator mit=observations.begin(), mend=observations.end(); mit!=mend; mit++)
{
KeyFrame* pKF = mit->first;
if(!pKF->isBad())
vDescriptors.push_back(pKF->mDescriptors.row(mit->second)); //取得對應的描述子
}
if(vDescriptors.empty())
return;
```
3. 獲得這些描述子兩兩之間的距離
```
// Compute distances between them
const size_t N = vDescriptors.size();
//float Distances[N][N];
//距離
std::vector<std::vector<float> > Distances;
Distances.resize(N, vector<float>(N, 0));
//紀錄距離對稱矩陣
for (size_t i = 0; i<N; i++)
{
//與自己的距離為0
Distances[i][i]=0;
for(size_t j=i+1;j<N;j++)
{
int distij = ORBmatcher::DescriptorDistance(vDescriptors[i],vDescriptors[j]);
Distances[i][j]=distij;
Distances[j][i]=distij;
}
}
```
4. 選擇最具有代表性的描述子
```
// Take the descriptor with least median distance to the rest
//最小的距離中值
int BestMedian = INT_MAX;
int BestIdx = 0;
for(size_t i=0;i<N;i++)
{
// 第i個描述子到其它所有所有描述子之間的距離
//vector<int> vDists(Distances[i],Distances[i]+N);
vector<int> vDists(Distances[i].begin(), Distances[i].end());
sort(vDists.begin(), vDists.end());
// 獲得中值
int median = vDists[0.5*(N-1)];
// 尋找最小的中值
if(median<BestMedian)
{
BestMedian = median;
BestIdx = i;
}
}
{
unique_lock<mutex> lock(mMutexFeatures);
// 最好的描述子,該描述子相對於其他描述子有最小的距離中值
// 簡化來講,中值代表了這個描述子到其它描述子的平均距離
// 最好的描述子就是和其它描述子的平均距離最小
mDescriptor = vDescriptors[BestIdx].clone();
}
```
### MapPoint::UpdateNormalAndDepth()
更新平均觀測方向
在[Tracking::CreateNewKeyFrame()](#Tracking::CreateNewKeyFrame()),[Tracking::CreateInitialMapMonocular()](#Tracking::CreateInitialMapMonocular()),[LocalMapping::SearchInNeighbors()](#LocalMapping::SearchInNeighbors()) 中引用
#### 函式流程
1. 取得觀測數值
2. 計算法線方向
#### 函式詳解
1. 獲得觀測到該地圖點的所有關鍵幀、坐標等信息
```
map<KeyFrame*,size_t> observations;
KeyFrame* pRefKF;
cv::Mat Pos;
{
unique_lock<mutex> lock1(mMutexFeatures);
unique_lock<mutex> lock2(mMutexPos);
if(mbBad)
return;
observations=mObservations; // 獲得觀測到該地圖點的所有關鍵幀
pRefKF=mpRefKF; // 觀測到該點的參考關鍵幀(第一次創建時的關鍵幀)
Pos = mWorldPos.clone(); // 地圖點在世界坐標系中的位置
}
if(observations.empty())
return;
```
2. 計算該地圖點的平均觀測方向(法線方向)
```
// 能觀測到該地圖點的所有關鍵幀,對該點的觀測方向歸一化為單位向量,然後進行求和得到該地圖點的朝向
// 初始值為0向量,累加為歸一化向量,最後除以總數n
cv::Mat normal = cv::Mat::zeros(3,1,CV_32F);
int n=0;
for(map<KeyFrame*,size_t>::iterator mit=observations.begin(), mend=observations.end(); mit!=mend; mit++)
{
KeyFrame* pKF = mit->first;
//相機光心座標
cv::Mat Owi = pKF->GetCameraCenter();
// 獲得地圖點和觀測到它關鍵幀的向量並歸一化
cv::Mat normali = mWorldPos - Owi;
normal = normal + normali/cv::norm(normali);
n++;
}
cv::Mat PC = Pos - pRefKF->GetCameraCenter(); // 參考關鍵幀相機指向地圖點的向量(在世界坐標系下的表示)
const float dist = cv::norm(PC); // 該點到參考關鍵幀相機的距離
const int level = pRefKF->mvKeysUn[observations[pRefKF]].octave; // 觀測到該地圖點的當前幀的特徵點在金字塔的第幾層
const float levelScaleFactor = pRefKF->mvScaleFactors[level]; // 當前金字塔層對應的尺度因子,scale^n,scale=1.2,n為層數
const int nLevels = pRefKF->mnScaleLevels; // 金字塔總層數,默認為8
{
unique_lock<mutex> lock3(mMutexPos);
// 使用方法見PredictScale函數前的註釋
mfMaxDistance = dist*levelScaleFactor; // 觀測到該點的距離上限
mfMinDistance = mfMaxDistance/pRefKF->mvScaleFactors[nLevels-1]; // 觀測到該點的距離下限
mNormalVector = normal/n; // 獲得地圖點平均的觀測方向
}
```
### MapPoint::PredictScale()
預測地圖點對應特徵點的金字塔尺度
在[ORBmatcher::Fuse()](ORBmatcher::Fuse())中引用
#### 定義
```
int MapPoint::PredictScale(const float ¤tDist, //相機光心離地圖點的距離
KeyFrame* pKF) //關鍵幀
```
#### 函式流程
1. 預測地圖點對應特徵點的金字塔尺度
#### 函式詳解
1. 預測地圖點對應特徵點的金字塔尺度
```
float ratio;
{
unique_lock<mutex> lock(mMutexPos);
// mfMaxDistance = ref_dist*levelScaleFactor為參考幀考慮上尺度後的距離
// ratio = mfMaxDistance/currentDist = ref_dist/cur_dist
ratio = mfMaxDistance/currentDist;
}
// 同時取log線性化
int nScale = ceil(log(ratio)/pKF->mfLogScaleFactor);
if(nScale<0)
nScale = 0;
else if(nScale>=pKF->mnScaleLevels)
nScale = pKF->mnScaleLevels-1;
return nScale;
```
### MapPoint::GetMaxDistanceInvariance()
最大的尺度變化範圍
在[Frame::isInFrustum()](#Frame::isInFrustum)中引用
#### 函式流程
1. 回傳1.2倍的最大範圍
#### 函式詳解
1. 回傳1.2倍的最大範圍
```
unique_lock<mutex> lock(mMutexPos);
return 1.2f*mfMaxDistance;
```
### MapPoint::GetMinDistanceInvariance()
最小的尺度變化範圍
在[Frame::isInFrustum()](#Frame::isInFrustum)中引用
#### 函式流程
1. 回傳0.8倍的最小範圍
#### 函式詳解
1. 回傳0.8倍的最小範圍
```
unique_lock<mutex> lock(mMutexPos);
return 0.8f*mfMinDistance;
```
### MapPoint::GetNormal()
平均觀測方向
mNormalVector值在[MapPoint::UpdateNormalAndDepth()](#MapPoint::UpdateNormalAndDepth())中更新
在[Frame::isInFrustum()](#Frame::isInFrustum)中引用
#### 函式流程
1. 回傳平均觀測方向
#### 函式詳解
1. 回傳平均觀測方向
```
unique_lock<mutex> lock(mMutexPos);
return mNormalVector.clone();
```
### MapPoint::GetReplaced()
地圖點是否需要替換
mpReplaced 在[MapPoint::Replace](#MapPoint::Replace)中更新
在[Tracking::CheckReplacedInLastFrame()](#Tracking::CheckReplacedInLastFrame())中引用
#### 函式流程
1. 地圖點是否需要替換
#### 函式詳解
1. 地圖點是否需要替換
```
unique_lock<mutex> lock1(mMutexFeatures);
unique_lock<mutex> lock2(mMutexPos);
return mpReplaced;
```
### MapPoint::IsInKeyFrame()
地圖點是否在關鍵幀中,
在[LocalMapping::ProcessNewKeyFrame()](#LocalMapping::ProcessNewKeyFrame())中引用
#### 定義
```
bool MapPoint::IsInKeyFrame(KeyFrame *pKF) //輸入關鍵幀
```
#### 函式流程
1. 地圖點是否在關鍵幀中
#### 函式詳解
1. 地圖點是否在關鍵幀中
```
unique_lock<mutex> lock(mMutexFeatures);
return (mObservations.count(pKF));
```
### MapPoint::GetFoundRatio()
計算被找到的比例
mnFound :地圖點被多少幀(包括普通幀)看到,次數越多越好
mnVisible:地圖點應該被看到的次數
(mnFound/mnVisible):對於大FOV鏡頭這個比例會高,對於窄FOV鏡頭這個比例會低
在[LocalMapping::MapPointCulling()](#LocalMapping::MapPointCulling())中引用
#### 函式流程
1. 計算被找到的比例
#### 函式詳解
1. 計算被找到的比例
```
unique_lock<mutex> lock(mMutexFeatures);
return static_cast<float>(mnFound)/mnVisible;
```
### MapPoint::SetBadFlag()
告知可以觀測到該MapPoint的Frame,該MapPoint已被刪除
在[LocalMapping::MapPointCulling()](#LocalMapping::MapPointCulling())中引用
#### 函式流程
1. 清除該地圖點的觀測關鍵幀
2. 將觀測到該地圖點的關鍵幀的匹配關係刪除
#### 函式詳解
1. 清除該地圖點的觀測關鍵幀
```
map<KeyFrame*,size_t> obs;
{
unique_lock<mutex> lock1(mMutexFeatures);
unique_lock<mutex> lock2(mMutexPos);
mbBad=true;
// 把mObservations轉存到obs,obs和mObservations裡存的是指針,賦值過程為淺拷貝
obs = mObservations;
// 把mObservations指向的內存釋放,obs作為局部變量之後自動刪除
mObservations.clear();
}
```
2. 將觀測到該地圖點的關鍵幀的匹配關係刪除
```
//遍歷觀測到該地圖點的關鍵幀
for(map<KeyFrame*,size_t>::iterator mit=obs.begin(), mend=obs.end(); mit!=mend; mit++)
{
KeyFrame* pKF = mit->first;
// 告訴可以觀測到該MapPoint的KeyFrame,該MapPoint被刪了
pKF->EraseMapPointMatch(mit->second);
}
// 擦除該MapPoint申請的內存
mpMap->EraseMapPoint(this);
}
```
pKF->EraseMapPointMatch(): 跳到[KeyFrame::EraseMapPointMatch()ID](#KeyFrame::EraseMapPointMatchID)