ORB特征匹配

    接<SLAM基本概念>

    ORB特征关键点描述子两部分组成,关键点称为“Oriented FAST”,是一种改进的FAST 角点。它的描述子称为BRIEF(Binary Robust Independent Elementary Feature)。提取 ORB 特征分为如下两个步骤:

    1. FAST 角点提取:找出图像中的“角点”。相较于原始的 FAST,ORB 中计算了特征点的主方向,为BRIEF 描述子增加了旋转不变特性。

    2. BRIEF 描述子的计算:对前一步提取出特征点的周围图像区域进行描述。ORB 对 BRIEF 进行了改进,主要是在 BRIEF 中使用了先前计算的方向信息。


FAST关键点

    FAST 是一种角点,主要检测局部像素灰度变化明显的地方,以速度快著称。它的思想是:如果一个像素与邻域的像素差别较大(过亮或过暗),那么它可能是角点。检测步骤如下:

1. 在图像中选取像素 p,假设它的亮度为Ip

2. 设置一个阈值 T(比如,Ip的20%)。

3. 以像素 p 为中心,选取半径为3的圆上的16个像素点。

4. 假如选取的圆上有连续的 N 个点的亮度大于 Ip+T 或小于 Ip−T,那么像素p  可以被认为是特征点(N通常取12,即为 FAST-12。其他常用的N取值为9和11,它们分别被称为FAST-9和FAST-11)。

5. 循环以上四步,对每一个像素执行相同的操作。

    在FAST-12算法中,可以进行预测试操作,以快速地排除绝大多数不是角点的像素。具体操作为,对于每个像素,直接检测邻域圆上的第 1, 5, 9, 13 个像素的亮度。只有当这 4个像素中有 3 个同时大于 Ip+T 或小于 Ip−T 时,当前像素才有可能是一个角点,否则应该直接排除。这大大加速了角点检测。

    还需要用非极大值抑制(Non-maximal suppression),在一定区域内仅保留响应极大值的角点,避免角点集中的问题。

1.jpg

    FAST特征点的计算仅仅是比较像素间亮度的差异,所以速度非常快。它的缺点是重复性不强,分布不均匀,不具有方向信息。同时,由于它固定取半径为3的圆,存在尺度问题:远处看着像是角点的地方,接近后看可能就不是角点了。

    针对 FAST 角点不具有方向性和尺度的弱点,ORB 添加了尺度和旋转的描述。尺度不变性由构建图像金字塔解决,在金字塔的每一层上检测角点。特征的旋转是由灰度质心法(Intensity Centroid)实现。

图像金字塔

2.jpg

    图像金字塔如上图,金字塔底层是原始图像,每往上一层,就对图像进行一个固定倍率的缩放,这样就有了不同分辨率的图像。较小的图像可以看成是远处看过来的场景。在特征匹配算法中,我们可以匹配不同层上的图像,从而实现尺度不变性。例如,如果相机在后退,那么我们应该能够在上一个图像金字塔的上层和下一个图像的下层中找到匹配。

旋转不变性

    计算特征点附近的图像灰度质心来保持旋转不变性。所谓质心是指以图像块灰度值作为权重的中心。其具体操作步骤如下:

    1. 在一个小的图像块 B 中,定义图像块的矩为:

3.jpg

    2. 通过矩可以找到图像块的质心:C=(m10/m00, m01/m00)

    3.连接图像块的几何中心O 与质心C,得到一个方向向量OC,于是特征点的方向可以定义为 θ = arctan(m01 m10)

    通过以上方法,FAST 角点便具有了尺度与旋转的描述,从而大大提升了其在不同图像之间表述的鲁棒性。所以在 ORB 中,把这种改进后的 FAST 称为 Oriented FAST


BRIEF 描述子

    在提取 Oriented FAST 关键点后,对每个点计算其描述子,ORB 使用改进的BRIEF特征描述。BRIEF 是一种二进制描述子,其描述向量由许多个 0 和 1 组成,这里的 0 和 1 编码了关键点附近两个随机像素(比如p和q)的大小关系:如果 p 比 q 大,则取 1,反之就取 0。如果我们取了 128个这样的 p, q,最后就得到 128 维由 0、1 组成的向量。关于一对随机点的选择方法,ORB论文原作者测试了以下5种方法,发现方法(2)比较好:

4.jpg

    N=256时的结果(一条线段的两端是一对点):

5.jpg

    原始的 BRIEF 描述子不具有旋转不变性,因此在图像发生旋转时容易丢失。而 ORB 在 FAST 特征点提取阶段计算了关键点的方向,所以可以利用方向信息,计算了旋转之后的“Steer BRIEF”特征使 ORB 的描述子具有较好的旋转不变性。


特征匹配

    经过上面两步,对于一幅图中的每一个特征点,都得到了一个128的二进制编码。接下来对有相似或重叠部分的两幅图像进行配准。特征配对是利用的汉明距离进行判决:

    1、两个特征编码对应bit位上相同元素的个数小于64的,一定不是配对的。

    2、一幅图上特征点与另一幅图上特征编码对应bit位上相同元素的个数最多的特征点配成一对。

    考虑两个时刻的图像。如果在图像 It 中提取到特征点 xtm,m = 1, 2, ..., M,在图像 It+1 中提取到特征点 xt+1n, n =1, 2, ..., N,如何寻找这两个集合元素的对应关系呢?

    最简单的特征匹配方法就是暴力匹配(Brute Force Matcher)。即对每一个特征点 xtm 与所有的 xt+1n 测量描述子的距离,然后排序,取最近的一个作为匹配点。描述子距离表示了两个特征之间的相似程度,在实际运用中还可以取不同的距离度量范数:对于浮点类型的描述子,使用欧氏距离进行度量即可,而对于二进制的描述子(比如BRIEF),往往使用汉明距离进行度量。

    (汉明距离 Hamming distance,即通过比较向量每一位是否相同,若不同则汉明距离加1。向量相似度越高,对应的汉明距离越小)

    然而,当特征点数量很大时,暴力匹配法的运算量将变得很大。此时快速近似最近邻(FLANN)算法更加适合于匹配点数量极多的情况。



代码实现

CMakeLists.txt

cmake_minimum_required(VERSION 2.6)
project(orbtest)
set( CMAKE_CXX_FLAGS "-std=c++11" )
#opencv
find_package( OpenCV REQUIRED )
include_directories( ${OpenCV_INCLUDE_DIRS} )
add_executable(orbtest main.cpp)
target_link_libraries(orbtest ${OpenCV_LIBS} )
install(TARGETS orbtest RUNTIME DESTINATION bin)


main.cpp

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;

int main(int argc, char **argv) 
{
    std::cout << "Hello, world!" << std::endl;    
    //图片读取
    Mat img_1 = imread("1.png", CV_LOAD_IMAGE_COLOR);
    Mat img_2 = imread("2.png", CV_LOAD_IMAGE_COLOR);
    imshow("img_1", img_1);
    imshow("img_2",img_2);
    waitKey(0);
    
    //初始化
    std::vector<KeyPoint> keypoints_1, keypoints_2;
    Mat descriptors_1, descriptors_2;
    Ptr<FeatureDetector> detector = ORB::create();
    Ptr<DescriptorExtractor> descriptor = ORB::create();
    /*匹配算法可选:
        BruteForce (it uses L2 )
        BruteForce-L1
        BruteForce-Hamming
        BruteForce-Hamming(2)
        FlannBased
    */
    Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");
    
    //第一步:检测 Oriented FAST 角点位置
    detector->detect(img_1, keypoints_1);
    detector->detect(img_2, keypoints_2);
    
    //第二步:根据角点位置计算 BRIEF 描述子
    descriptor->compute(img_1, keypoints_1, descriptors_1);
    descriptor->compute(img_2, keypoints_2, descriptors_2);
    
    //绘制特征点
    Mat outimg1;
    drawKeypoints(img_1, keypoints_1, outimg1, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
    imshow("ORB features", outimg1);
    waitKey(0);
    
    //第三步:对两幅图像中的BRIEF描述子进行匹配,使用 Hamming 距离
    vector<DMatch> matches;
    matcher->match(descriptors_1, descriptors_2, matches);
    
    //-- 第四步:匹配点对筛选
    // 计算最小距离    
    double min_d=matches[0].distance;
    for(int i=0;i<matches.size();i++){
        if(min_d>matches[i].distance)
            min_d=matches[i].distance;
    }
    cout<<"min_d= "<<min_d<<endl;

    //当描述子之间的距离大于两倍的最小距离时,即认为匹配有误.但有时候最小距离会非常小,设置一个经验值30作为下限.
    std::vector<DMatch> good_matches;
    for (int i = 0; i < descriptors_1.rows; i++) {
        if (matches[i].distance <= max(2 * min_d, 30.0)) 
            good_matches.push_back(matches[i]);
    }
    
    //-- 第五步:绘制匹配结果
    Mat img_match;
    Mat img_goodmatch;
    
    drawMatches(img_1, keypoints_1, img_2, keypoints_2, matches, img_match);
    drawMatches(img_1, keypoints_1, img_2, keypoints_2, good_matches, img_goodmatch);
    
    imshow("all matches", img_match);
    imshow("good matches", img_goodmatch);
    waitKey(0);
    return 0;
}

    提取的ORB特征:

6.jpg

    所有匹配和过滤后的匹配:

7.jpg





参考文献

[0]高翔.视觉SLAM14讲

[1]hujingshuang.【特征检测】BRIEF特征点描述算法. https://blog.csdn.net/hujingshuang/article/details/46910259  .2015-07-16


上一篇:
下一篇:

首页 所有文章 机器人 计算机视觉 自然语言处理 机器学习 编程随笔 关于