OpenCV实践之ORB算法

ORB算法简述

  ORB:An Efficient Alternative to SIFT or SURF是2011年ICCV上作者Rublee所提出,主要针对目前主流的SIFT或者SURF等算法的实时性进行改进。当然在实时性大为提升的基础上,匹配性能也在一定程度较SIFT与SURF算法降低。但是,在图像Two Views匹配对之间变换关系较小时,能够匹配性能逼近SIFT算法,同时计算耗时极大降低。ORB算法实时性在移动端设备上提供很好的应用,当下比较流行SLAM中采用较多的ORB-SLAM算法主要就是青睐于ORB算法实时性同时匹配精度并不差。

论文对ORB算法主要贡献如下:

  1 在FAsT角点算子快速提取角点基础上,添加主方向信息。

  2 通过方向BRIEF描述算子高效计算FAsT角点提取的特征点描述符。

  3 分析方向BRIEF描述符的方差与相关性。

  4 提出一种在旋转不变情况下去除相关性的BRIEF描述子的学习方式,在最近邻应用中获得更好的效果。

OpenCV中ORB源码接口

  ORB算法并没有申请专利,所以在OpenCV开源视觉库里面可以免费使用同时继承基础类Feature2D。调用OpenCV算法中ORB算法初始化create()函数基本涵盖所有ORB算法的参数配置,一般采用默认方式,只是简单设置下ORB提取角点的初始数量。下面为ORB外部接口类:

class CV_EXPORTS_W ORB : public Feature2D
{
public:
    enum { kBytes = 32, HARRIS_SCORE=0, FAST_SCORE=1 };
/*
     Create()函数初始化
     参数说明:
                nfeatures:    保留提取的最大特征数
                  scaleFactor:  金字塔抽取比率,大于1。经典金字塔默认值为2,每个层级  之间为4个像素插值关系。尺度变化较大会影响匹配性能,同时尺度范围太接近要覆盖一定的尺度,需要更多的金字塔层数,但是速度会降低。
                nlevels: 金字塔层次数量。
                edgeThreshold: 没有检测到特征的边界大小。
                firstLevel: 在当下实现中值为0.
                WTA_K: 方向BRIEF描述子产生每个元素点的数量。
                  scoreType: 默认采取Harris_Score来对特征进行排序,采取Fast_Score可能 会产生特征点不是太稳定,但是计算速度快。
                matchSize:方向BRIEF描述符的区域大小
                fastThreahold: FAST角点阈值
*/
    CV_WRAP static Ptr<ORB> create(int nfeatures=500, float scaleFactor=1.2f, int nlevels=8, int edgeThreshold=31,int firstLevel=0, int WTA_K=2, int scoreType=ORB::HARRIS_SCORE, int patchSize=31, int fastThreshold=20);
    // 设置与获取最大特征数
    CV_WRAP virtual void setMaxFeatures(int maxFeatures) = 0;
    CV_WRAP virtual int getMaxFeatures() const = 0;
    // 设置与获取尺度参数
    CV_WRAP virtual void setScaleFactor(double scaleFactor) = 0;
    CV_WRAP virtual double getScaleFactor() const = 0;
    // 设置层数
    CV_WRAP virtual void setNLevels(int nlevels) = 0;
    CV_WRAP virtual int getNLevels() const = 0;
    // 设置与获取边缘阈值
    CV_WRAP virtual void setEdgeThreshold(int edgeThreshold) = 0;
    CV_WRAP virtual int getEdgeThreshold() const = 0;
    // 
    CV_WRAP virtual void setFirstLevel(int firstLevel) = 0;
    CV_WRAP virtual int getFirstLevel() const = 0;

    CV_WRAP virtual void setWTA_K(int wta_k) = 0;
    CV_WRAP virtual int getWTA_K() const = 0;
    // 设置与获取当前特征排序方式
    CV_WRAP virtual void setScoreType(int scoreType) = 0;
    CV_WRAP virtual int getScoreType() const = 0;
    // 设置与获取patch大小
    CV_WRAP virtual void setPatchSize(int patchSize) = 0;
    CV_WRAP virtual int getPatchSize() const = 0;
    // 设置与获取FAsT角点阈值
    CV_WRAP virtual void setFastThreshold(int fastThreshold) = 0;
    CV_WRAP virtual int getFastThreshold() const = 0;
    CV_WRAP virtual String getDefaultName() const;
};

OpenCV实现ORB算法:

  在贴上ORB算法上简单介绍一下2014年ECCV会议华科杨欣教授提出的(Local Different Binary, LDB)描述子。该算法相对与经典二进制描述符(只是简单考虑区域强度信息)上进行改进,在此基础上增加水平与垂直方向的梯度信息来构建描述子,同时采用在特征点区域划分不同大小的子块,小子块能够获取足够多的局部细节信息,提升描述子的区分性。大子块能够有效的去除噪声,但是对微小变化敏感度不够,降低描述子的唯一性,具体可以见论文LDB描述子。下图摘自LDB论文简述描述子过程:

图1 LDB描述子过程(多重网格划分、增加梯度信息描述等策略)

ORB算法进行Two Views匹配代码
#include <iostream>
#include <opencv2\opencv.hpp>
#include <opencv2\features2d.hpp>
#include <ldb.h>
using namespace std;
using namespace cv;

int main(void)
{
    // 配置图像路径
    Mat img1 = imread("../orb_Algorithms/image/img1_boat.jpg", 0);
    Mat img2 = imread("../orb_Algorithms/image/img5_boat.jpg", 0);
    // 判断输入图像是否读取成功
    if (img1.empty() || img2.empty() || img1.channels() != 1 || img2.channels() != 1)
    {
        cout << "Input Image is nullptr or the image channels is not gray!" << endl;
        system("pause");
    }
    // ORB算法继承Feature2D基类
    Ptr<ORB> orb = ORB::create(1000, 1.2, 8, 31, 0, 2, 0, 31, 20);  
    // 调整精度,值越小点越少,越精准
    vector<KeyPoint> kpts1, kpts2;
    // 特征点检测算法...
    orb->detect(img1, kpts1);
    orb->detect(img2, kpts2);

    // 特征点描述算法...
    Mat desc1, desc2;

    bool SelectiveDescMethods = false;
    // 默认选择BRIEF描述符
    if (SelectiveDescMethods) 
    {
        // ORB 算法中默认BRIEF描述符
        orb->compute(img1, kpts1, desc1);
        orb->compute(img2, kpts2, desc2);
    }
    else
    {
      //LDB描述子描述FAsT特征点 
      bool flag = true;
      LDB ldb(48);
      ldb.compute(img1, kpts1, desc1, flag);
      ldb.compute(img2, kpts2, desc2, flag);
    }
    // 粗精匹配数据存储结构
    vector< vector<DMatch>> matches;
    vector<DMatch> goodMatchKpts;
    // Keypoint Matching...
    DescriptorMatcher *pMatcher = new BFMatcher(NORM_HAMMING, false);
    pMatcher->knnMatch(desc1, desc2, matches, 2);
    // 欧式距离度量  阈值设置为0.8
    for (unsigned int i = 0; i < matches.size(); ++i)
    {
        if (matches[i][0].distance < 0.8*matches[i][1].distance)
        {
            goodMatchKpts.push_back(matches[i][0]);
        }
    }
    // 显示匹配点对
    Mat show_match;
    drawMatches(img1, kpts1, img2, kpts2, goodMatchKpts, show_match);

    // 显示输出
    ostringstream s_time;
    s_time << time;
    imshow("ORB_Algorithms_" + s_time.str(), show_match);

    cout << "(kpts1: " << kpts1.size() << ") && (kpts2:" \
         << kpts2.size() << ") = goodMatchesKpts: " << goodMatchKpts.size() << endl;

    waitKey(0);

    // RANSAC Geometric Verification
    if (goodMatchKpts.size() < 4)
    {
        cout << "The Match Kpts' Size is less than Four to estimate!" << endl;
        return 0;
    }

    vector<Point2f> obj, scene;
    for (unsigned int i = 0; i < goodMatchKpts.size(); ++i)
    {
        obj.push_back(kpts1[goodMatchKpts[i].queryIdx].pt);
        scene.push_back(kpts2[goodMatchKpts[i].trainIdx].pt);
    }
    // 估计Two Views变换矩阵
    Mat H = findHomography(obj, scene, CV_RANSAC);
    vector<Point2f> obj_corners(4), scene_corners(4);
    obj_corners[0] = cvPoint(0, 0); obj_corners[1] = cvPoint(img1.cols, 0);
    obj_corners[2] = cvPoint(img1.cols, img1.rows); obj_corners[3] = cvPoint(0, img1.rows);
    // 点集变换标出匹配重复区域
    perspectiveTransform(obj_corners, scene_corners, H);

    line(show_match, scene_corners[0] + Point2f(img1.cols, 0), scene_corners[1] + Point2f(img1.cols, 0), Scalar(0, 255, 0), 4);
    line(show_match, scene_corners[1] + Point2f(img1.cols, 0), scene_corners[2] + Point2f(img1.cols, 0), Scalar(0, 255, 0), 4);
    line(show_match, scene_corners[2] + Point2f(img1.cols, 0), scene_corners[3] + Point2f(img1.cols, 0), Scalar(0, 255, 0), 4);
    line(show_match, scene_corners[3] + Point2f(img1.cols, 0), scene_corners[0] + Point2f(img1.cols, 0), Scalar(0, 255, 0), 4);

    imshow("Match End", show_match);
    imwrite("img_boat15.jpg", show_match);
    waitKey(0);
    system("pause");
    return 0;
}

个人总结想法

  在特征匹配算法很多的今天,如何有效的组合算法进行适应场景的应用最为重要。如上面代码,如果你觉得ORB算法鲁棒性较差同时又希望不要降低ORB算法的实时性。可以采取ORB特征提取+LDB描述子进行匹配,当然构建完描述子之后匹配方式也有多种选择,例如不采用BF暴力匹配方法,采用FLANN匹配方式。进行粗匹配之后,对初步匹配的特征点集如何进一步提纯仍然有许多算法:最为广泛的随机抽样一致性算法及其许多相关改进算法,或者稀疏向量场算法等等。这些环节中,如何选定最优组合似乎目前还没有超越SIFT算法的综合评价,只有在某个特别范围超越SIFT算法:例如时间、仿射等等。在此抛出一个问题,SIFT算法或者SURF算法综合性能是否就达到最佳应用场景状态,(虽然深度学习大火的今天,也会存在经典方式+神经网络等相互结合方式提出)今后相关算法该如何组合起来获取最佳效果?如有各位见解或思路,还请不吝赐教。

参考

https://opencv.org/
http://lbmedia.ece.ucsb.edu/research/binaryDescriptor/web_home/web_home/index.html
https://ieeexplore.ieee.org/document/6126544/?reload=true&arnumber=6126544

猜你喜欢

转载自blog.csdn.net/Small_Munich/article/details/80071137
今日推荐