ORBmatacher

Posted 奋斗的妮子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ORBmatacher相关的知识,希望对你有一定的参考价值。

该类主要用于ORB特征点匹配。

public中:

1.计算两个ORB描述子之间的汉明距离:

1 static int DescriptorDistance(const cv::Mat &a, const cv::Mat &b);

  (1)该函数是静态成员函数,为类的全部服务而不是某一个类的具体对象服务。具体用来,可以保证当前计算得到的距离是刚刚调用过该函数的最新结果。

  (2)计算汉明距离的参考代码:http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel

2.四个重载的SearchByProjection()函数:

(1)寻找当前帧和投影地图的特征匹配点,返回匹配点数量。用于跟踪局部地图。

1 int SearchByProjection(Frame &F, const std::vector<MapPoint*> &vpMapPoints, const float th=3);

寻找最佳匹配点对的原则:

a.两个特征点所属的金字塔层一致;

b.最佳匹配点对距离>mfNNratio×次优匹配点对距离。

1 // Apply ratio to second match (only if best and second are in the same scale level)
2 if(bestDist<=TH_HIGH)
3 {
4     if(bestLevel==bestLevel2 && bestDist>mfNNratio*bestDist2)
5            continue;
6 
7      F.mvpMapPoints[bestIdx]=pMP;
8      nmatches++;
9  }

 

(2)将上一帧跟踪的地图点投影到当前帧,并且搜索匹配点。用于跟踪前一帧。

1 int SearchByProjection(Frame &CurrentFrame, const Frame &LastFrame, const float th, const bool bMono)

a.建立旋转直方图,用于检测旋转一致性;

b.先分别计算当前帧和前一帧的旋转矩阵和平移矩阵;

c.对于前一帧的每一个地图点,通过相机模型,映射得到当前帧的像素坐标;

d.在一个窗口内搜寻特征点,窗口尺寸根据尺度进行变化。因为前面ORB是进行了高斯金字塔分层,金字塔越上层的尺寸越小,搜索窗口相应就小;

e.计算特征点之间的距离,如果距离小于设定值TH_HIGH,则认为是匹配点对;

f.应用旋转一致性再一次筛选匹配点对。

(3)将关键帧的地图点投影到当前帧,并且搜索匹配点。用于重定位。

1 int SearchByProjection(Frame &CurrentFrame, KeyFrame* pKF, const std::set<MapPoint*> &sAlreadyFound, const float th, const int ORBdist);

a.计算旋转直方图,用于检测旋转一致性;

b.获取关键帧的地图点,对于每一个地图点:

  1° 将地图点根据相机模型投影到当前帧的像素坐标系上,且判断是否在CCD平面上;

  2° 计算预测的尺度层,地图点的深度值必须在图像尺度金字塔内;

  3° 在预测尺度层上获取特征点;

  4° 计算该层上的特征点与关键帧中的特征点的描述子距离,取距离最小的。若最小距离小于设定的ORBdist,则为匹配点对;同样需要检测方向(这个还没懂)。

  5° 用直方图检测描述子方向,和上面一样。

(4)用相似变换投影地图点,并且搜索匹配点。用于闭环检测。

1 int SearchByProjection(KeyFrame* pKF, cv::Mat Scw, const std::vector<MapPoint*> &vpPoints, std::vector<MapPoint*> &vpMatched, int th);

 a.获取相机标定参数,为之后的投影做准备;

b.分解相似变换矩阵:先计算得到尺度因子s,然后计算世界坐标系到相机坐标系pKF的旋转矩阵和平移向量(都是去掉尺度因子的)。

c.在已经找到的匹配点中,去除没有对应匹配点的地图点;

d.遍历每个地图点,去掉坏的地图点和已经找到的地图点。获取到地图点的3D世界坐标系,并通过上面计算得到的矩阵转换到相机坐标系下,且在Pc下的深度必须为正。再投影到像素坐标系下,坐标值必须在image范围内。计算世界坐标系下,该地图点的深度,该深度必须在该点的尺度方差区域内,且观察视角必须小于60°(通过向量内积来判断观察角度是否小于60°)。根据地图点的深度和关键帧预测一个高斯金字塔层,根据该层计算一个半径,在该半径内搜索最相似的特征点。

3.两个重载的SearchByBoW()函数,搜索关键帧的地图点和某一帧的ORB特征点的匹配点。暴力约束属于相同词典节点的ORB(在一个特定的金字塔层上)。用于重定位和闭环检测。

1 // Search matches between MapPoints in a KeyFrame and ORB in a Frame.
2 // Brute force constrained to ORB that belong to the same vocabulary node (at a certain level)
3 // Used in Relocalisation and Loop Detection
4 int SearchByBoW(KeyFrame *pKF, Frame &F, std::vector<MapPoint*> &vpMapPointMatches);
5 int SearchByBoW(KeyFrame *pKF1, KeyFrame* pKF2, std::vector<MapPoint*> &vpMatches12);

第一个函数通过词包对关键帧的特征点进行跟踪,用于重定位。

第二个函数通过词包,对关键帧的特征点进行跟踪,用于闭环检测时对两个关键帧间的特征点匹配。

4.地图初始化时的匹配,仅用于单目情况。

1 int SearchForInitialization(Frame &F1, Frame &F2, std::vector<cv::Point2f> &vbPrevMatched, std::vector<int> &vnMatches12, int windowSize=10); 

5.匹配用于三角化新的地图点,检查对极约束。

1 int SearchForTriangulation(KeyFrame *pKF1, KeyFrame* pKF2, cv::Mat F12, std::vector<pair<size_t, size_t> > &vMatchedPairs, const bool bOnlyStereo);

 1.利用基本矩阵F12,在两个关键帧之间未匹配的特征点中产生新的3D点,返回成功匹配的数量。

2.将左图像的每个特征点与右图像同一node节点的所有特征点依次检测,判断是否满足对极约束,满足就是匹配的特征点。

6.搜索在关键帧KF1和关键帧KF2中的地图点匹配,其中KF2是经过KF1相似变换得到,[s12*R12| t12]。在双目和RGB-D情况下,s12=1。

1 int SearchBySim3(KeyFrame* pKF1, KeyFrame* pKF2, std::vector<MapPoint *> &vpMatches12, const float &s12, const cv::Mat &R12, const cv::Mat &t12, const float th);

通过sim3变换,确定pKF1中的特征点在pKF2中的大致区域,同理,确定pKF2的特征点在pKF1中的大致区域。在该区域内通过描述子进行匹配捕获pKF1和pKF2之前漏匹配的特征点,更新vpMatches12(之前使用SearchByBow进行特征点匹配时会有漏匹配)。 

7.两个重载的Fuse()函数,用于融合地图点。

(1)将地图点投影至关键帧中,并且搜索复制的地图点。

1 int Fuse(KeyFrame* pKF, const vector<MapPoint *> &vpMapPoints, const float th=3.0); 

(2)将地图点投影至用相似变换得来的关键帧中,并且搜索复制的地图点。

1 int Fuse(KeyFrame* pKF, cv::Mat Scw, const std::vector<MapPoint*> &vpPoints, float th, vector<MapPoint *> &vpReplacePoint);

 

以上几个函数在检测特征点时,都会用到这个函数来计算直方图,还没搞明白。。。

 1 // Rotation Histogram (to check rotation consistency)
 2 vector<int> rotHist[HISTO_LENGTH];
 3 for(int i=0;i<HISTO_LENGTH;i++)
 4      rotHist[i].reserve(500);
 5 const float factor = 1.0f/HISTO_LENGTH;
 6 
 7 if(mbCheckOrientation)
 8 {
 9       float rot = LastFrame.mvKeysUn[i].angle-CurrentFrame.mvKeysUn[bestIdx2].angle;
10       if(rot<0.0)
11              rot+=360.0f;
12       int bin = round(rot*factor);
13       if(bin==HISTO_LENGTH)
14              bin=0;
15       assert(bin>=0 && bin<HISTO_LENGTH);
16       rotHist[bin].push_back(bestIdx2);
17 }

以及在删除错误匹配对时,会用到下面这部分的代码:

if(mbCheckOrientation)
{
    int ind1=-1;
    int ind2=-1;
    int ind3=-1;

    ComputeThreeMaxima(rotHist,HISTO_LENGTH,ind1,ind2,ind3);

    for(int i=0; i<HISTO_LENGTH; i++)
    {
        if(i==ind1 || i==ind2 || i==ind3)
             continue;
        for(size_t j=0, jend=rotHist[i].size(); j<jend; j++)
        {
             vMatches12[rotHist[i][j]]=-1;
             nmatches--;
         }
     }

}

 角度直方图是用来剔除不满足两帧之间角度旋转的外点的,也就是旋转一致性检测:

1.将关键帧与当前帧匹配点的angle相减,得到rot(0≤rot<360),放入一个直方图中,对于每一对匹配点的角度差,均可以放入一个bin的范围内(360/HISTO_LENGTH)。

2.统计直方图最高的三个bin保留,其他范围内的匹配点剔除。另外,若最高的比次高的高10倍以上,则只保留最高的bin中的匹配点。

 

protected中:

1.检测极线距离:

1 bool CheckDistEpipolarLine(const cv::KeyPoint &kp1, const cv::KeyPoint &kp2, const cv::Mat &F12, const KeyFrame *pKF);

(1)已知两帧图像的对极约束,E或者F,对于第一帧中的关键点x1,其在第二帧中对应的极线l=x1‘F12=[a b c]

(2)点到直线的距离:

设直线 L 的方程为Ax+By+C=0,点 P 的坐标为(Xo,Yo),则点 P 到直线 L 的距离为:

 技术分享图片
 1 bool ORBmatcher::CheckDistEpipolarLine(const cv::KeyPoint &kp1,const cv::KeyPoint &kp2,const cv::Mat &F12,const KeyFrame* pKF2)
 2 {
 3     // Epipolar line in second image l = x1‘F12 = [a b c]
 4     const float a = kp1.pt.x*F12.at<float>(0,0)+kp1.pt.y*F12.at<float>(1,0)+F12.at<float>(2,0);
 5     const float b = kp1.pt.x*F12.at<float>(0,1)+kp1.pt.y*F12.at<float>(1,1)+F12.at<float>(2,1);
 6     const float c = kp1.pt.x*F12.at<float>(0,2)+kp1.pt.y*F12.at<float>(1,2)+F12.at<float>(2,2);
 7 
 8     const float num = a*kp2.pt.x+b*kp2.pt.y+c;
 9 
10     const float den = a*a+b*b;
11 
12     if(den==0)
13         return false;
14 
15     const float dsqr = num*num/den;  //img2中的关键点到极线的距离的平方
16 
17     return dsqr<3.84*pKF2->mvLevelSigma2[kp2.octave];
18 }

 Q:3.84是什么值?

 

2.计算三个极大值点。

1 void ComputeThreeMaxima(std::vector<int>* histo, const int L, int &ind1, int &ind2, int &ind3);

 

以上是关于ORBmatacher的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段——CSS选择器

谷歌浏览器调试jsp 引入代码片段,如何调试代码片段中的js

片段和活动之间的核心区别是啥?哪些代码可以写成片段?

VSCode自定义代码片段——.vue文件的模板

VSCode自定义代码片段6——CSS选择器

VSCode自定义代码片段——声明函数