ORB特征匹配原理及源代码

 

  这篇往后,会暂时先更ORB、SITF、SURF三篇特征算子,在代码部分,会在本篇介绍下OPENCV特征匹配的特征点KeyPoint、特征描述子和匹配算子Match等的构成。

1ORB简介

        ORB算法是一种特征匹配算法,可用于目标追踪、图像匹配等多个方面,在实时图像处理上,有较好的效果。目前比较流行的特征匹配算子有SIFT、SURF、ORB等,三者有不同的优缺点,SIFT是90年代提出的一种特征匹配算子,在机器学习流行前,十分火热,SURF是SIFT的改进,相对于SIFT,运行速度更快。ORB是一种为满足实时特征匹配提出的算子,运行速度较前两者有很大提升。

2ORB特征匹配原理

2.1概述

   ORB特征匹配步骤可分为3步:

     (1).特征点检测,(2)计算特征点描述子,(3)进行图像的特征点的匹配。(特征点的匹配部分由OPENCV提供的API实现,不再介绍)

 

 

2.2特征点检测:

  在特征点检测部分,ORB采用FAST算法,在介绍ORB算法内FAST的实现前,先介绍下FAST算法的原理,详细如下:

          

 

 

 

       如上图,任选图像中的一点P,以该点为圆形,r为半径确定一个圆,在圆上均匀取m个像素点,设定一个阈值t,如果m个像素点中,有连续N个像素点的大小均大于或小于t,则这个点就是角点。(其中r、m、t和N参数的设置 可设为 3 、16 、未知、9会有更好的效果,t可根据自己需要进行设置)。

       如果对于像分辨率为1440*1080尺寸比较大的图形,对每个像素点进行上述步骤处理,会需要很大的计算量,不满足ORB实时的要求,故有一种通过排除非角点的算法,提高了FAST特征点检测的效率:

       先进性提取像素点周围1,5,9,13四个位置的像素点大小,后通过1,9,5,13的顺序进行比较,若其中至少三个大于或小于t,则该点可能为角点,进一步按照上面不走进行判断;否则,该点不符合角点条件。

上面是FAST算法原理,下面介绍FAST在ORB内的实现。

      ORB算法作者使用FAST-9算法进行特征点提取(FAST-9算法有更好的效果,其中9代表m值为9即周围均匀的9个点)。在进行FAST进行角点检测时,边缘位置的部分易混淆,如对一个圆形物体,圆上的每一点按照FAST算法,均可视为角点。为最小化这种影响,ORB算法作何通过Harris角点检测器把N个关键点进行等级排序,使用者可提取前n个自己需要的点(排序方法根据Harris角点响应的大小,响应大小计算可看Harris角点检测一节中的介绍)。在进行特征点匹配时,检测出的角点需要满足尺度不变形和旋转不变性。ORB作者通过增加图像金字塔和计算角度的方法达到效果。详细如下面介绍。

      对于尺度不变形,ORB算法通过对初始图像的按1/2的比例不断下采样(即按1/2的比例不断缩放),得到一系列图像,形成图像金字塔。对每层图像,进行FAST角点检测,得到一系列角点,具备尺度不变性。(尺度不变性:在一幅图像提取角点后,对图像进行缩放,该角点仍能被检测出,且与第一幅检测角点进行匹配。)

      对于旋转不变形,ORB采用灰度质心法进行计算每个特征点的主方向。灰度质心法的定义如下:

                                                     

   其中x,y分别表示像素点周围圆上所选取点的横坐标和纵坐标,I(x,y)表示灰度值大小,p和q表示指数。角度计算的方法如下:

              θ=atan2(m01,m10)。

2.3         特征描述子计算

2.3.1 描述子概念

  描述子用于在提取角点后,两幅图之间的匹配,相当于角点的特征描述。同时,描述子应具备尺度、角度和光照等条件的不变形。

  ORB内描述子的计算采用BRIEF描述子计算算法实现,下面先介绍BRIEF算法原理。

2.3.2 BRIEF算法原理

  BRIEF算法在2010年由一篇《BRIEF:Binary Robust Independent Elementary Features》论文提出,是一种二进制编码的描述子,因没有采用直方图描述点的方式,加快特征提取速度,降低匹配时间。

 

 

  BRIEF算法可分为两步:1特征点大小的对比。2.选取多对特征点对比。下面具体介绍。

  第一步中,以特征点为中心,取SXS的邻域窗口,在窗口上通过某种规律(下面会有介绍)选择两个点p(x)和p(y),比较两个点像素值的大小,进行如下赋值:

 

 

 

 

  第二步中,在窗口中通过某种规律选取N对像素点,重复第一步进行像素值大小的比较,最后形成一个二进制编码(编码为比较的结果,如01010101.. N值大小一般为256)。

 

  上文中提到的某种规律,BRIEF作者尝试了5中方法,具体如下文,其中法(2)效果较好:

       

 

 

 

2.3.3ORB内的BRIEF算法

   OBR算法对BRIEF有两种改变,分别为steer BRIEF和rBRIEF。下面做介绍。

   相对于BRIEF,steer BRIEF具备旋转不变形的特征。通过2.2可计算出角点的角度大小θ,将该点周围的点旋转θ度,得到新的点对,公式如下:(R.表示旋转矩阵)

                 

 

 

 

         旋转后,在新的位置上比较像素值的大小,得到描述子。

            相对于BRIEF,rBRIEF的提出,是为了弥补在通过steer BRIEF进行旋转后的缺陷。下图为三者的一个对比:

         

 

 

     

           其中横坐标表示均值(即二进制形成的描述子的均值)距离0.5的距离,纵坐标表示数量。

           通过上图可发现,BRIEF描述子主要分布在0附近,表明通过BRIEF计算出的描述子内0和1的数目大致相等,而通过steer BRIEF计算出的描述子在0.3附近较多,表明通过steer BRIEF计算出的描述子内出现0多于1、或1多于0的情况,不易于两个特征点的描述子的区分,增大了描述符之间的相关性。

           rBRIEF算法通过改变描述子的计算方法,进一步减弱同一图像中特征点的描述子的相关性,具体如下:

           对每个角点,考虑其31X31的邻域,使用领域中每个点周围的5X5的邻域的像素值平均值作为该点的像素值,进而比较点对的大小。上面计算可得到(31-5+1)*(31-5+1)=729个子窗口,提取点对的方法有729*728=265356种,通过在这265356中方法中选取256种取法,形成描述子。下面是具体选择步骤:

    (1)  在300k特征点的每个31X31邻域内,M=265356种方法取点对,比较点对大小,形成一个300kXM的矩阵Q。矩阵种的每一列代表一种提取方法得到的二进制数。

    (2)  对Q矩阵的每一列求取平均值,按照平均值到0.5的距离大小,重新对Q进行列向量的排序形成矩阵T。

    (3)  将T的第一列向量放到R中。

    (4)   将T的下一列向量和R中的所有列向量计算相关性,如果相关系数小于设定的阈值,则将T中的该列向量移入R中。

    (5)    重复步骤(4)的操作,直至R中列向量数目为256.

   上面即为rBRIEF的算法原理。

 

 

3ORB在OPENCV内的API和一个demo:

 

                  //一点个人教训:在使用orb、sift和surf时,发现第一次进行特征点检测、描述子计算耗费时间远大于通过已用的接口进行第二次特征点
检测和描述子计算。(至于原因,博主暂时也未搞懂)举个简单的例子:


  

   cv::Ptr<cv::ORB> orb = cv::ORB::create(50); //创建orb对象,后为得到的特征点数目
  cv::Mat imageL1,imageR2,imageL2,imageR2,despL1,despR1,despL2,despR2;   //只是举例,建设图像已传入
  std::vector<cv::KeyPoint> KeyPointL1,KeyPointR1,KeyPointL2,KeyPointR2;
  orb->detectAndCompute(imageL1,cv::Mat(),KeyPointL1,despL1);  //第一次特征点检测和描述子计算
  orb->detectAndCompute(imageR1,cv::Mat(),KeyPointR1,despR1);
  orb->detectAndCompute(imageL2,cv::Mat(),KeyPointL2,despL2);  //第二次特征点检测和描述子计算
  orb->detectAndCompute(imageR2,cv::Mat(),KeyPointR2,despR2);

 


  //博主在使用中,发现第二次用时远少于第一次用时。同样情况出现在SIFT和SURF中。ps:使用opencv和opencv-contrib版本为4.0+(这是个大坑,记得博主使用时,因为这个坑,自己都写出起算法进行特征匹配,还好最后脱坑)
分为三部分:

      

      cv::Ptr<cv::ORB> orb = cv::ORB::create(50); //创建orb对象,后为得到的特征点数目 std::vector<cv::KeyPoint> keyPointL, keyPointR; //创建关键点对象 orb->detect(imageR, keyPointR); //关键点检测 orb->detect(imageL, keyPointL); //关键点检测 cv::Mat despL,despR;//创建描述子 orb->compute(imageR, cv::Mat(),keyPointR,despL);//cv::Mat()表示无roi区域 计算描述子 orb->detect(imageL, cv::Mat(), keyPointL,despR); //cv::Mat()表示无roi区域 计算描述子 
    orb->detectAndCompute(imageL,cv::Mat(),KeyPointL,despL);  //进行检测和计算,相当于上面detect和compute两步
    orb->detectAndCompute(imageR,cv::Mat(),KeyPointR,despR); std::vector<cv::DMatch> matches; cv::Ptr<cv::DescriptorMatcher> matcher=cv::DescriptorMatcher::create("FlannBased"); //进行匹配 matcher->match(despL, despR, matches); std::vector point_l,std::vector point_r; for(int i=0;i<matches.size();i++) //得到匹配点 { point_l.push_back(matches[i].queryIdx); point_r.push_back(matches[i].trainIdx); }

  Demo:

#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
int main()
{
cv::Mat imageL = cv::imread(".I/like/Study.png"); //图片位置
cv::Mat imageR = cv::imread("./Study/make/me/happy.png");//图片2位置
cv::cvtColor(imageL,imageL,cv::COLOR_BGR2GRAY);
cv::cvtColor(imageR,imageR,cv::COLOR_BGR2GRAY);
cv::resize(imageL,imageL,cv::Size(120,90));
cv::resize(imageR,imageR,cv::Size(120,90));

// ORB
cv::Ptr<cv::ORB> orb = cv::ORB::create(50);

//特征点
std::vector<cv::KeyPoint> keyPointL, keyPointR;
//单独提取特征点
orb->detect(imageL, keyPointL);
orb->detect(imageR, keyPointR);
//画特征点
cv::Mat keyPointImageL;
cv::Mat keyPointImageR;
drawKeypoints(imageL, keyPointL, keyPointImageL, cv::Scalar::all(-1), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
drawKeypoints(imageR, keyPointR, keyPointImageR, cv::Scalar::all(-1), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

//显示窗口
cv::namedWindow("KeyPoints of imageL");
cv::namedWindow("KeyPoints of imageR");

//显示特征点
cv::imshow("KeyPoints of imageL", keyPointImageL);
cv::imshow("KeyPoints of imageR", keyPointImageR);

//特征点匹配
cv::Mat despL, despR;
//提取特征点并计算特征描述子
orb->detectAndCompute(imageL, cv::Mat(), keyPointL, despL);
orb->detectAndCompute(imageR, cv::Mat(), keyPointR, despR);

std::vector<cv::DMatch> matches;

//如果采用flannBased方法 那么 desp通过orb的到的类型不同需要先转换类型
if (despL.type() != CV_32F || despR.type() != CV_32F)
{
despL.convertTo(despL, CV_32F);
despR.convertTo(despR, CV_32F);
}

cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create("FlannBased");
matcher->match(despL, despR, matches);

// //计算特征点距离的最大值
double maxDist = 0;
for (int i = 0; i < despL.rows; i++)
{
double dist = matches[i].distance;
if (dist > maxDist)
maxDist = dist;
}

//挑选好的匹配点
std::vector< cv::DMatch > good_matches;
int size = matches.size();
for (int i = 0; i < despL.rows; i++)
{
int low = 0;
for(int n=0;n<despL.rows;n++)
{
if(matches[i].distance<matches[n].distance)
{
low+=1;
}

}

if(low>=size-15)
{
good_matches.push_back(matches[i]);
}
}

cv::Mat imageOutput;
cv::drawMatches(imageL, keyPointL, imageR, keyPointR, good_matches, imageOutput);

cv::namedWindow("picture of matching",cv::WINDOW_NORMAL);
cv::imshow("picture of matching", imageOutput);
cv::waitKey(0);
return 0;
}

 

 

 

三、代码部分:

    首先介绍匹配部分DescriptorMatcher:

 

OPENCV特征匹配部分通过DescriptorMatcher实现,DescriptorMatcher是匹配器的抽象基类,其具体类有:
        class FlannBasedMatcher
        class BFMatcher
      源代码暂时未找到,不再介绍其内部如何构造,在此仅介绍其使用方法,匹配器的创建有两种方式(建议用1,方便,接口多):
      一:
          static Ptr<DescriptorMatcher> create(const string& descriptorMatcherType)  
          如:   cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create("FlannBased");
      二:
          DescriptorMatcher  *pMatcher = newBFMatcher;
       DescriptorMatcher的接口函数有以下几种(对于一般的使用,仅crete()和match()两个函数足可):
        create():创建匹配器,支持的匹配器类型有:BruteForce 、BruteForce、BruteForce-Hamming、BruteForce-Hamming(2)和FlannBased  (博主目前使用FlannBased,效果还可,其它暂未尝试)
        match(desL,desR,match) :通过描述子进行匹配,desL和desR为两个Mat类型的描述子,match为DMatch类型的vector,下面会介绍DMatch类型
        knnMatch();找到最好的k个匹配
        add();添加匹配器,用来连接匹配器集合,若匹配器集合不是空的,新加的匹配器会加到集合的后面。
        getTrainDescriptors();获得匹配器集合
        isMaskSupported();匹配器是否支持掩模,使得话就返回ture
        radiusMatch();找到距离不超过规定的匹配

 

介绍DMatch类型:      

 简化版(针对于不想看下面源代码的人):DMatch包含四个有用信息,queryIdx(要匹配的描述子的索引(序列)),trainIdx(被匹配的描述子索引(序列)),imageIdx(匹配图像的索引)distance表示两个描述子之间的距离,越小匹配效果越好。queryIdx
为match()函数第一个参数描述子对应图像的关键点的索引,trainIdx为第二个参数描述子对应图像的关键点的索引。imageIdx仅在多张图像间进行匹配时,用于表示匹
图像的序列。请注意,queryIdx为要匹配,trainIdx为被匹配。下面有实战展示如何使用。


/*
* DMatch主要用来储存匹配信息的结构体,query是要匹配的描述子,train是被匹配的描述子,在Opencv中进行匹配时 * void DescriptorMatcher::match( const Mat& queryDescriptors, const Mat& trainDescriptors, vector<DMatch>& matches, const Mat& mask ) const * match函数的参数中位置在前面的为query descriptor,后面的是 train descriptor * 例如:query descriptor的数目为20,train descriptor数目为30,则DescriptorMatcher::match后的vector<DMatch>的size为20 * 若反过来,则vector<DMatch>的size为30 */ struct CV_EXPORTS_W_SIMPLE DMatch { //默认构造函数,FLT_MAX是无穷大 //#define FLT_MAX 3.402823466e+38F /* max value */ CV_WRAP DMatch() : queryIdx(-1), trainIdx(-1), imgIdx(-1), distance(FLT_MAX) {} //DMatch构造函数 CV_WRAP DMatch( int _queryIdx, int _trainIdx, float _distance ) : queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(-1), distance(_distance) {} //DMatch构造函数 CV_WRAP DMatch( int _queryIdx, int _trainIdx, int _imgIdx, float _distance ) : queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(_imgIdx), distance(_distance) {} //queryIdx为query描述子的索引,match函数中前面的那个描述子 CV_PROP_RW int queryIdx; // query descriptor index //trainIdx为train描述子的索引,match函数中后面的那个描述子 CV_PROP_RW int trainIdx; // train descriptor index //imgIdx为进行匹配图像的索引 //例如已知一幅图像的sift描述子,与其他十幅图像的描述子进行匹配,找最相似的图像,则imgIdx此时就有用了。 CV_PROP_RW int imgIdx; // train image index //distance为两个描述子之间的距离 CV_PROP_RW float distance; //DMatch比较运算符重载,比较的是DMatch中的distance,小于为true,否则为false // less is better bool operator<( const DMatch &m ) const { return distance < m.distance; } };

 介绍KeyPoint类型(未找到文本样式,盗几张图):

简化版(懒人通道):KeyPoint主要使用的类型有pt(二维坐标点),size(该关键点领域直径大小),angle(关键点的方向:0~360°),response(关键点的相应强度,可用于排序),octve(关键点的金字塔层数),class_id(当要对图片进行分类时,用class_id对每个关键点进行区分,默认为-1。暂时不使用,使用时你也就已明白它的含义。),

        

 

KeyPointsFilter类型(属于对KeyPoint处理的封装好的类,已在算法内实现。一般仅在自己想写特征匹配算法时,才会用到这些):

      KeyPointsFilter包含5个功能函数:分别为runByImageBorder()、runByKeyPointSzie()、runByPixelsMask()、removeDuplicated()、retainBest()。其源代码结构如下:        

 

ORB API:

//一点个人教训:在使用orb、sift和surf时,发现第一次进行特征点检测、描述子计算耗费时间远大于通过已用的接口进行第二次特征点
检测和描述子计算。(至于原因,博主暂时也未搞懂)举个简单的例子:
   cv::Ptr<cv::ORB> orb = cv::ORB::create(50); //创建orb对象,后为得到的特征点数目
cv::Mat imageL1,imageR2,imageL2,imageR2,despL1,despR1,despL2,despR2;   //只是举例,建设图像已传入
std::vector<cv::KeyPoint> KeyPointL1,KeyPointR1,KeyPointL2,KeyPointR2;

  orb->detectAndCompute(imageL1,cv::Mat(),KeyPointL1,despL1);  //第一次特征点检测和描述子计算
  orb->detectAndCompute(imageR1,cv::Mat(),KeyPointR1,despR1);

orb->detectAndCompute(imageL2,cv::Mat(),KeyPointL2,despL2);  //第二次特征点检测和描述子计算
orb->detectAndCompute(imageR2,cv::Mat(),KeyPointR2,despR2);
  //博主在使用中,发现第二次用时远少于第一次用时。同样情况出现在SIFT和SURF中。ps:使用opencv和opencv-contrib版本为4.0+(这是个大坑,记得博主使用时,因为这个坑,自己都写出起算法进行特征匹配,还好最后脱坑)

分为三部分: cv::Ptr
<cv::ORB> orb = cv::ORB::create(50); //创建orb对象,后为得到的特征点数目 std::vector<cv::KeyPoint> keyPointL, keyPointR; //创建关键点对象 orb->detect(imageR, keyPointR); //关键点检测 orb->detect(imageL, keyPointL); //关键点检测 cv::Mat despL,despR;//创建描述子 orb->compute(imageR, cv::Mat(),keyPointR,despL);//cv::Mat()表示无roi区域 计算描述子 orb->detect(imageL, cv::Mat(), keyPointL,despR); //cv::Mat()表示无roi区域 计算描述子
    orb->detectAndCompute(imageL,cv::Mat(),KeyPointL,despL);  //进行检测和计算,相当于上面detect和compute两步
    orb->detectAndCompute(imageR,cv::Mat(),KeyPointR,despR); std::vector<cv::DMatch> matches; cv::Ptr<cv::DescriptorMatcher> matcher=cv::DescriptorMatcher::create("FlannBased"); //进行匹配 matcher->match(despL, despR, matches); std::vector point_l,std::vector point_r; for(int i=0;i<matches.size();i++) //得到匹配点 { point_l.push_back(matches[i].queryIdx); point_r.push_back(matches[i].trainIdx); }

 

实战展示:


#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
int main()
{
cv::Mat imageL = cv::imread(".I/like/Study.png"); //图片位置
cv::Mat imageR = cv::imread("./Study/make/me/happy.png");//图片2位置
cv::cvtColor(imageL,imageL,cv::COLOR_BGR2GRAY);
cv::cvtColor(imageR,imageR,cv::COLOR_BGR2GRAY);
cv::resize(imageL,imageL,cv::Size(120,90));
cv::resize(imageR,imageR,cv::Size(120,90));

// ORB
cv::Ptr<cv::ORB> orb = cv::ORB::create(50);

//特征点
std::vector<cv::KeyPoint> keyPointL, keyPointR;
//单独提取特征点
orb->detect(imageL, keyPointL);
orb->detect(imageR, keyPointR);
//画特征点
cv::Mat keyPointImageL;
cv::Mat keyPointImageR;
drawKeypoints(imageL, keyPointL, keyPointImageL, cv::Scalar::all(-1), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
drawKeypoints(imageR, keyPointR, keyPointImageR, cv::Scalar::all(-1), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

//显示窗口
cv::namedWindow("KeyPoints of imageL");
cv::namedWindow("KeyPoints of imageR");

//显示特征点
cv::imshow("KeyPoints of imageL", keyPointImageL);
cv::imshow("KeyPoints of imageR", keyPointImageR);

//特征点匹配
cv::Mat despL, despR;
//提取特征点并计算特征描述子
orb->detectAndCompute(imageL, cv::Mat(), keyPointL, despL);
orb->detectAndCompute(imageR, cv::Mat(), keyPointR, despR);

std::vector<cv::DMatch> matches;

//如果采用flannBased方法 那么 desp通过orb的到的类型不同需要先转换类型
if (despL.type() != CV_32F || despR.type() != CV_32F)
{
despL.convertTo(despL, CV_32F);
despR.convertTo(despR, CV_32F);
}

cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create("FlannBased");
matcher->match(despL, despR, matches);

// //计算特征点距离的最大值
double maxDist = 0;
for (int i = 0; i < despL.rows; i++)
{
double dist = matches[i].distance;
if (dist > maxDist)
maxDist = dist;
}

//挑选好的匹配点
std::vector< cv::DMatch > good_matches;
int size = matches.size();
for (int i = 0; i < despL.rows; i++)
{
int low = 0;
for(int n=0;n<despL.rows;n++)
{
if(matches[i].distance<matches[n].distance)
{
low+=1;
}

}

if(low>=size-15)
{
good_matches.push_back(matches[i]);
}
}

cv::Mat imageOutput;
cv::drawMatches(imageL, keyPointL, imageR, keyPointR, good_matches, imageOutput);

cv::namedWindow("picture of matching",cv::WINDOW_NORMAL);
cv::imshow("picture of matching", imageOutput);
cv::waitKey(0);
return 0;
}

 

 ORB源代码:

CV_EXPORTS_W ORB :  Feature2D  
{  
:  
      
     { kBytes = 32, HARRIS_SCORE=0, FAST_SCORE=1 };  
  
    CV_WRAP  ORB( nfeatures = 500,  scaleFactor = 1.2f,  nlevels = 8,  edgeThreshold = 31,  
         firstLevel = 0,  WTA_K=2,  scoreType=ORB::HARRIS_SCORE,  patchSize=31 );  
  
      
     descriptorSize() ;     
      
     descriptorType() ;  
  
      
     operator()(InputArray image, InputArray mask, vector<KeyPoint>& keypoints) ;  
  
      
     operator()( InputArray image, InputArray mask, vector<KeyPoint>& keypoints,      
                     OutputArray descriptors,  useProvidedKeypoints= ) ;  
  
    AlgorithmInfo* info() ;  
  
:  
  
     computeImpl(  Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) ;  
     detectImpl(  Mat& image, vector<KeyPoint>& keypoints,  Mat& mask=Mat() ) ;  
  
    CV_PROP_RW  nfeatures;  
    CV_PROP_RW  scaleFactor;  
    CV_PROP_RW  nlevels;  
    CV_PROP_RW  edgeThreshold;  
    CV_PROP_RW  firstLevel;  
    CV_PROP_RW  WTA_K;  
    CV_PROP_RW  scoreType;  
    CV_PROP_RW  patchSize;  
};  
/** Compute the ORB features and descriptors on an image 
 * @param keypoints the resulting keypoints 
 * @param do_keypoints if true, the keypoints are computed, otherwise used as an input 
 */ ORB::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _keypoints,  
                      OutputArray _descriptors,  useProvidedKeypoints)   
{  
    CV_Assert(patchSize >= 2);  
  
     do_keypoints = !useProvidedKeypoints;  
     do_descriptors = _descriptors.needed();  
  
    ( (!do_keypoints && !do_descriptors) || _image.empty() )  
        ;  
  
      
      HARRIS_BLOCK_SIZE = 9;  
     halfPatchSize = patchSize / 2;.  
     border = std::max(edgeThreshold, std::max(halfPatchSize, HARRIS_BLOCK_SIZE/2))+1;  
  
    Mat image = _image.getMat(), mask = _mask.getMat();  
    ( image.type() != CV_8UC1 )  
        cvtColor(_image, image, CV_BGR2GRAY);  
  
     levelsNum = ->nlevels;  
  
    ( !do_keypoints )     
    {  
          
          
          
          
          
          
          
          
          
        levelsNum = 0;  
        (  i = 0; i < _keypoints.size(); i++ )  
            levelsNum = std::max(levelsNum, std::max(_keypoints[i].octave, 0));  
        levelsNum++;  
    }  
  
      
    vector<Mat> imagePyramid(levelsNum), maskPyramid(levelsNum);  
     ( level = 0; level < levelsNum; ++level)  
    {  
         scale = 1/getScale(level, firstLevel, scaleFactor);    
                 static inline float getScale(int level, int firstLevel, double scaleFactor) 
                   return (float)std::pow(scaleFactor, (double)(level - firstLevel)); 
        */        Size sz(cvRound(image.cols*scale), cvRound(image.rows*scale));  
        Size wholeSize(sz.width + border*2, sz.height + border*2);  
        Mat temp(wholeSize, image.type()), masktemp;  
        imagePyramid[level] = temp(Rect(border, border, sz.width, sz.height));  
        ( !mask.empty() )  
        {  
            masktemp = Mat(wholeSize, mask.type());  
            maskPyramid[level] = masktemp(Rect(border, border, sz.width, sz.height));  
        }  
  
          
        ( level != firstLevel )      
        {  
            ( level < firstLevel )  
            {  
                resize(image, imagePyramid[level], sz, 0, 0, INTER_LINEAR);  
                 (!mask.empty())  
                    resize(mask, maskPyramid[level], sz, 0, 0, INTER_LINEAR);  
            }  
              
            {  
                resize(imagePyramid[level-1], imagePyramid[level], sz, 0, 0, INTER_LINEAR);  
                 (!mask.empty())  
                {  
                    resize(maskPyramid[level-1], maskPyramid[level], sz, 0, 0, INTER_LINEAR);  
                    threshold(maskPyramid[level], maskPyramid[level], 254, 0, THRESH_TOZERO);  
                }  
            }  
  
            copyMakeBorder(imagePyramid[level], temp, border, border, border, border,  
                           BORDER_REFLECT_101+BORDER_ISOLATED);  
             (!mask.empty())  
                copyMakeBorder(maskPyramid[level], masktemp, border, border, border, border,  
                               BORDER_CONSTANT+BORDER_ISOLATED);  
        }  
          
        {  
            copyMakeBorder(image, temp, border, border, border, border,  
                           BORDER_REFLECT_101);  
            ( !mask.empty() )  
                copyMakeBorder(mask, masktemp, border, border, border, border,  
                               BORDER_CONSTANT+BORDER_ISOLATED);  
        }  
    }  
  
      
    vector < vector<KeyPoint> > allKeypoints;  
    ( do_keypoints )  
    {  
          
        computeKeyPoints(imagePyramid, maskPyramid, allKeypoints,    
                         nfeatures, firstLevel, scaleFactor,  
                         edgeThreshold, patchSize, scoreType);  
  
          
                 for (int level = 0; level < n_levels; ++level) 
            vector<KeyPoint>& keypoints = all_keypoints[level]; 
            keypoints.clear(); 
 
 
             keypoint_end = temp.end(); keypoint != keypoint_end; ++keypoint) 
  
    }  
        
    {  
          
        KeyPointsFilter::runByImageBorder(_keypoints, image.size(), edgeThreshold);  
  
          
        allKeypoints.resize(levelsNum);  
         (vector<KeyPoint>::iterator keypoint = _keypoints.begin(),  
             keypointEnd = _keypoints.end(); keypoint != keypointEnd; ++keypoint)  
            allKeypoints[keypoint->octave].push_back(*keypoint);      
  
          
         ( level = 0; level < levelsNum; ++level)     
        {  
             (level == firstLevel)  
                ;  
  
            vector<KeyPoint> & keypoints = allKeypoints[level];  
             scale = 1/getScale(level, firstLevel, scaleFactor);  
             (vector<KeyPoint>::iterator keypoint = keypoints.begin(),  
                 keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)  
                keypoint->pt *= scale;     
        }  
    }  
  
    Mat descriptors;          
    vector<Point> pattern;  
  
    ( do_descriptors )   
    {  
         nkeypoints = 0;  
         ( level = 0; level < levelsNum; ++level)  
            nkeypoints += ()allKeypoints[level].size();  
        ( nkeypoints == 0 )  
            _descriptors.release();  
          
        {  
            _descriptors.create(nkeypoints, descriptorSize(), CV_8U);  
            descriptors = _descriptors.getMat();  
        }  
  
          npoints = 512;  
        Point patternbuf[npoints];  
         Point* pattern0 = ( Point*)bit_pattern_31_;  
  
        ( patchSize != 31 )  
        {  
            pattern0 = patternbuf;  
            makeRandomPattern(patchSize, patternbuf, npoints);  
        }  
  
        CV_Assert( WTA_K == 2 || WTA_K == 3 || WTA_K == 4 );  
  
        ( WTA_K == 2 )    
            std::copy(pattern0, pattern0 + npoints, std::back_inserter(pattern));  
          
        {  
             ntuples = descriptorSize()*4;  
            initializeOrbPattern(pattern0, pattern, ntuples, WTA_K, npoints);  
        }  
    }  
  
    _keypoints.clear();  
     offset = 0;  
     ( level = 0; level < levelsNum; ++level)  
    {  
          
        vector<KeyPoint>& keypoints = allKeypoints[level];  
         nkeypoints = ()keypoints.size();  
  
          
         (do_descriptors)  
        {  
            Mat desc;  
             (!descriptors.empty())  
            {  
                desc = descriptors.rowRange(offset, offset + nkeypoints);  
            }  
  
            offset += nkeypoints;    
              
            Mat& workingMat = imagePyramid[level];  
              
            GaussianBlur(workingMat, workingMat, Size(7, 7), 2, 2, BORDER_REFLECT_101);  
            computeDescriptors(workingMat, keypoints, desc, pattern, descriptorSize(), WTA_K);  
        }  
  
          
         (level != firstLevel)    
        {  
             scale = getScale(level, firstLevel, scaleFactor);  
             (vector<KeyPoint>::iterator keypoint = keypoints.begin(),  
                 keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)  
                keypoint->pt *= scale;   
        }  
          
        _keypoints.insert(_keypoints.end(), keypoints.begin(), keypoints.end());  
    }  
}  
/** Compute the ORB keypoints on an image 
 * @param keypoints the resulting keypoints, clustered per level 
  
  computeKeyPoints( vector<Mat>& imagePyramid,  
                              vector<Mat>& maskPyramid,  
                             vector<vector<KeyPoint> >& allKeypoints,  
                              nfeatures,  firstLevel,  scaleFactor,  
                              edgeThreshold,  patchSize,  scoreType )  
{  
     nlevels = ()imagePyramid.size();    
    vector<> nfeaturesPerLevel(nlevels);  
  
      
     factor = ()(1.0 / scaleFactor);  
     ndesiredFeaturesPerScale = nfeatures*(1 - factor)/(1 - ()pow(()factor, ()nlevels));  
  
     sumFeatures = 0;  
    (  level = 0; level < nlevels-1; level++ )     
    {  
        nfeaturesPerLevel[level] = cvRound(ndesiredFeaturesPerScale);  
        sumFeatures += nfeaturesPerLevel[level];  
        ndesiredFeaturesPerScale *= factor;  
    }  
    nfeaturesPerLevel[nlevels-1] = std::max(nfeatures - sumFeatures, 0);  
  
      
      
  
      
     halfPatchSize = patchSize / 2;             
    vector<> umax(halfPatchSize + 2);  
     v, v0, vmax = cvFloor(halfPatchSize * sqrt(2.f) / 2 + 1);  
     vmin = cvCeil(halfPatchSize * sqrt(2.f) / 2);  
     (v = 0; v <= vmax; ++v)             
        umax[v] = cvRound(sqrt(()halfPatchSize * halfPatchSize - v * v));  
      
     (v = halfPatchSize, v0 = 0; v >= vmin; --v)  
    {  
         (umax[v0] == umax[v0 + 1])  
            ++v0;  
        umax[v] = v0;  
           ++v0;  
    }  
  
    allKeypoints.resize(nlevels);  
  
     ( level = 0; level < nlevels; ++level)  
    {  
         featuresNum = nfeaturesPerLevel[level];  
        allKeypoints[level].reserve(featuresNum*2);  
  
        vector<KeyPoint> & keypoints = allKeypoints[level];  
  
          
        FastFeatureDetector fd(20, );        
        fd.detect(imagePyramid[level], keypoints, maskPyramid[level]);  
  
          
        KeyPointsFilter::runByImageBorder(keypoints, imagePyramid[level].size(), edgeThreshold);  
  
        ( scoreType == ORB::HARRIS_SCORE )  
        {  
              
            KeyPointsFilter::retainBest(keypoints, 2 * featuresNum);  
  
              
            HarrisResponses(imagePyramid[level], keypoints, 7, HARRIS_K);   
        }  
  
          
        KeyPointsFilter::retainBest(keypoints, featuresNum);  
  
         sf = getScale(level, firstLevel, scaleFactor);  
  
          
         (vector<KeyPoint>::iterator keypoint = keypoints.begin(),  
             keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)  
        {  
            keypoint->octave = level;    
            keypoint->size = patchSize*sf;   
        }  
  
        computeOrientation(imagePyramid[level], keypoints, halfPatchSize, umax);    
    }  
}  
static computeOrientation( Mat& image, vector<KeyPoint>& keypoints,  
                                halfPatchSize,  vector<>& umax)  
{  
      
     (vector<KeyPoint>::iterator keypoint = keypoints.begin(),    
         keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)  
    {  
        keypoint->angle = IC_Angle(image, halfPatchSize, keypoint->pt, umax);  
    }  
}  
static IC_Angle( Mat& image,   half_k, Point2f pt,  
                       vector<> & u_max)  
{  
     m_01 = 0, m_10 = 0;  
  
     uchar* center = &image.at<uchar> (cvRound(pt.y), cvRound(pt.x));  
  
      
     ( u = -half_k; u <= half_k; ++u)  
        m_10 += u * center[u];  
  
      
     step = ()image.step1();  
     ( v = 1; v <= half_k; ++v)      
    {  
          
         v_sum = 0;  
         d = u_max[v];  
         ( u = -d; u <= d; ++u)  
        {  
             val_plus = center[u + v*step], val_minus = center[u - v*step];  
            v_sum += (val_plus - val_minus);   
            m_10 += u * (val_plus + val_minus);  
        }  
        m_01 += v * v_sum;  
    }  
  
     fastAtan2(()m_01, ()m_10);  
}  
static computeDescriptors( Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors,  
                                vector<Point>& pattern,  dsize,  WTA_K)  
{  
      
    CV_Assert(image.type() == CV_8UC1);  
      
    descriptors = Mat::zeros(()keypoints.size(), dsize, CV_8UC1);  
  
     ( i = 0; i < keypoints.size(); i++)  
        computeOrbDescriptor(keypoints[i], image, &pattern[0], descriptors.ptr(()i), dsize, WTA_K);  
}  
static computeOrbDescriptor( KeyPoint& kpt,  
                                  Mat& img,  Point* pattern,  
                                 uchar* desc,  dsize,  WTA_K)  
{  
     angle = kpt.angle;   
      
    angle *= ()(CV_PI/180.f);  
     a = ()cos(angle), b = ()sin(angle);  
  
     uchar* center = &img.at<uchar>(cvRound(kpt.pt.y), cvRound(kpt.pt.x));  
     step = ()img.step;  
  
  
  
        center[cvRound(pattern[idx].x*b + pattern[idx].y*a)*step + \  
               cvRound(pattern[idx].x*a - pattern[idx].y*b)]  
  
     x, y;  
     ix, iy;  
  
        (x = pattern[idx].x*a - pattern[idx].y*b, \  
        y = pattern[idx].x*b + pattern[idx].y*a, \  
        ix = cvFloor(x), iy = cvFloor(y), \  
        x -= ix, y -= iy, \  
        cvRound(center[iy*step + ix]*(1-x)*(1-y) + center[(iy+1)*step + ix]*(1-x)*y + \  
                center[iy*step + ix+1]*x*(1-y) + center[(iy+1)*step + ix+1]*x*y))  
  
  
    ( WTA_K == 2 )  
    {  
         ( i = 0; i < dsize; ++i, pattern += 16)  
        {  
             t0, t1, val;  
            t0 = GET_VALUE(0); t1 = GET_VALUE(1);  
            val = t0 < t1;  
            t0 = GET_VALUE(2); t1 = GET_VALUE(3);  
            val |= (t0 < t1) << 1;  
            t0 = GET_VALUE(4); t1 = GET_VALUE(5);  
            val |= (t0 < t1) << 2;  
            t0 = GET_VALUE(6); t1 = GET_VALUE(7);  
            val |= (t0 < t1) << 3;  
            t0 = GET_VALUE(8); t1 = GET_VALUE(9);  
            val |= (t0 < t1) << 4;  
            t0 = GET_VALUE(10); t1 = GET_VALUE(11);  
            val |= (t0 < t1) << 5;  
            t0 = GET_VALUE(12); t1 = GET_VALUE(13);  
            val |= (t0 < t1) << 6;  
            t0 = GET_VALUE(14); t1 = GET_VALUE(15);  
            val |= (t0 < t1) << 7;  
  
            desc[i] = (uchar)val;  
        }  
    }  
     ( WTA_K == 3 )  
    {  
         ( i = 0; i < dsize; ++i, pattern += 12)  
        {  
             t0, t1, t2, val;  
            t0 = GET_VALUE(0); t1 = GET_VALUE(1); t2 = GET_VALUE(2);  
            val = t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0);  
  
            t0 = GET_VALUE(3); t1 = GET_VALUE(4); t2 = GET_VALUE(5);  
            val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 2;  
  
            t0 = GET_VALUE(6); t1 = GET_VALUE(7); t2 = GET_VALUE(8);  
            val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 4;  
  
            t0 = GET_VALUE(9); t1 = GET_VALUE(10); t2 = GET_VALUE(11);  
            val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 6;  
  
            desc[i] = (uchar)val;  
        }  
    }  
     ( WTA_K == 4 )  
    {  
         ( i = 0; i < dsize; ++i, pattern += 16)  
        {  
             t0, t1, t2, t3, u, v, k, val;  
            t0 = GET_VALUE(0); t1 = GET_VALUE(1);  
            t2 = GET_VALUE(2); t3 = GET_VALUE(3);  
            u = 0, v = 2;  
            ( t1 > t0 ) t0 = t1, u = 1;  
            ( t3 > t2 ) t2 = t3, v = 3;  
            k = t0 > t2 ? u : v;  
            val = k;  
  
            t0 = GET_VALUE(4); t1 = GET_VALUE(5);  
            t2 = GET_VALUE(6); t3 = GET_VALUE(7);  
            u = 0, v = 2;  
            ( t1 > t0 ) t0 = t1, u = 1;  
            ( t3 > t2 ) t2 = t3, v = 3;  
            k = t0 > t2 ? u : v;  
            val |= k << 2;  
  
            t0 = GET_VALUE(8); t1 = GET_VALUE(9);  
            t2 = GET_VALUE(10); t3 = GET_VALUE(11);  
            u = 0, v = 2;  
            ( t1 > t0 ) t0 = t1, u = 1;  
            ( t3 > t2 ) t2 = t3, v = 3;  
            k = t0 > t2 ? u : v;  
            val |= k << 4;  
  
            t0 = GET_VALUE(12); t1 = GET_VALUE(13);  
            t2 = GET_VALUE(14); t3 = GET_VALUE(15);  
            u = 0, v = 2;  
            ( t1 > t0 ) t0 = t1, u = 1;  
            ( t3 > t2 ) t2 = t3, v = 3;  
            k = t0 > t2 ? u : v;  
            val |= k << 6;  
  
            desc[i] = (uchar)val;  
        }  
    }  
      
        CV_Error( CV_StsBadSize,    
}  

 

 

 

 

 

如有错误,欢迎指正和批评。有部分参考,因忘记记录,未标注下面,抱歉。

参考:

https://blog.csdn.net/Small_Munich/article/details/80866162

https://www.cnblogs.com/TransTown/p/7396996.html

 http://docs.opencv.org/3.3.0/d2/d29/classcv_1_1KeyPoint.html

https://blog.csdn.net/Darlingqiang/article/details/79404869

https://blog.csdn.net/frozenspring/article/details/78146076

https://blog.csdn.net/zcg1942/article/details/83824367

https://www.cnblogs.com/wyuzl/p/7856863.html

posted @ 2020-03-12 02:50  无趣的鱼  阅读(6620)  评论(0编辑  收藏  举报