特征匹配

1. 初始化时两帧之间的特征匹配 SearchForInitialization()

  在单目初始化时,对用于初始化的连续两帧特征点数大于 100 的图像处理,取出图像金字塔第 0 层(即原图)的特征点进行匹配,返回匹配的点对数量,实现在 ORBmatcher::SearchForInitialization() 函数中

1
2
3
4
5
int ORBmatcher::SearchForInitialization(Frame &F1,          // 参考帧.
                                        Frame &F2,          // 当前帧.
                                        vector<cv::Point2f> &vbPrevMatched, // 存储匹配点.
                                        vector<int> &vnMatches12,           // 匹配标志位.
                                        int windowSize)     // 搜索窗口大小.
  • 步骤一:参考帧特征点的坐标在当前帧坐标附近区域同层搜索可能匹配的特征点,实现在函数 Frame::GetFeaturesInArea() 中,作用是找到在以 (x,y) 为中心,半径为 r 的圆形内且在金字塔层 [minLevel, maxLevel] 之间的特征点,返回满足条件的特征点的序号。

    1
    2
    3
    
    vector<size_t> Frame::GetFeaturesInArea(const float &x, const float  &y, 
                                            const float  &r, 
                                            const int minLevel, const int maxLevel) const
    
    • 步骤 1: 检查该特征点周围的圆形区域是否在图像中,具体做法是分别求圆形搜索区域的上下左右四个边界是否能够满足图像的边界条件;
      • 这里的边界条件以圆的左边界为例,就是首先求出左边界所在的图像网格列,然后判断这个网格列位置是否超过了图像网格的上限;
      • 同理求其他三个边界是否在图像中,如果不符合要求则说明找不到特征点。
    • 步骤 2: 遍历圆形区域内的所有网格
      • 步骤 ①: 逐次获取每个网格中的特征点
      • 步骤 ②: 如果该网格中有特征点则遍历该网格中所有特征点
        • 步骤 A: 逐个读取网格中的特征点;
        • 步骤 B: 如果给定的搜索图层范围合法,则检查这个特征点是否是在给定搜索图层范围内生成的
        • 步骤 C: 计算这个特征点到指定的搜索中心的距离(x方向和y方向),查看是否是在这个圆形区域之内,如果符合要求则保存该特征点的索引到 vector<size_t> vIndices 中。
    • 结束,返回对应于参考帧中某个特征点在当前帧中一定大小圆形区域内的特征点索引
  • 步骤二: 对上一步获取的候选特征点依次计算描述子距离,并找到最小的两个距离

    • 步骤 1: 获取两个特征点在对应帧中的特征描述符
      1
      2
      
      cv::Mat d1 = F1.mDescriptors.row(i1); // 参考帧中的特征点.
      cv::Mat d2 = F2.mDescriptors.row(i2); // 当前帧中的候选特征点.
      
    • 步骤 2: 计算描述子距离
      1
      
      int dist = DescriptorDistance(d1,d2);
      
    • 步骤 3: 保存下所有候选特征点中描述子距离最小的两个;
    • 步骤 4: 确保最小距离小于阈值(50),并确保最小距离小于 mfNNratio(取 0.7 或 0.9 ) 倍的次小距离,保证该匹配的鲁棒性
      • 记录匹配状态
        1
        2
        3
        4
        
        vnMatches12[i1]=bestIdx2;       // 参考帧中的特征点 i1 对应当前帧的 bestIdx2.
        vnMatches21[bestIdx2]=i1;       // 当前帧中的特征点 bestIdx2 对应参考帧的 i1.
        vMatchedDistance[bestIdx2]=bestDist;
        nmatches++;
        
  • 步骤三: 直方图角度统(旋转一致性)计,剔除误匹配:每个特征点在提取描述子时的旋转主方向角度,如果图像旋转了,这个角度将发生改变,所有的特征点的角度变化应该是一致的,通过直方图统计得到最准确的角度变化值

    • 步骤 1: 计算没对匹配的角度差 【0, 360) 度之间
      1
      2
      3
      
      float rot = F1.mvKeysUn[i1].angle-F2.mvKeysUn[bestIdx2].angle;
      if(rot<0.0)
          rot+=360.0f;
      
    • 步骤 2: 将 360° 30 等分,没对匹配的角度差存储到一个 bin 组中,得到直方图
      1
      2
      3
      4
      5
      6
      7
      
      int bin = round(rot*factor);
      if(bin==HISTO_LENGTH)
          bin=0;
      assert(bin>=0 && bin<HISTO_LENGTH);
      
      // 得到直方图.
      rotHist[bin].push_back(i1);
      
    • 步骤 3: 取出直方图 rotHist 中最大的三个索引 ind1, ind2, ind3,实现在 ORBmatcher::ComputeThreeMaxima() 函数中
    • 步骤 4: 误匹配剔除,对于角度差在这三个索引中特征匹配不进行处理,剩下方向不在这三个索引中匹配进行剔除
  • 结束。至此完成了初始两帧之间的特征匹配


参考资料


2019.06.16
wuyanminmax@gmail.com