---
# System prepended metadata

title: MapPoint.cpp

---

# 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 &currentDist,     //相機光心離地圖點的距離
                           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)