Go...

当前位置: 首页>>世界杯太太团

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 orb = cv::ORB::create(50); //创建orb对象,后为得到的特征点数目

cv::Mat imageL1,imageR2,imageL2,imageR2,despL1,despR1,despL2,despR2; //只是举例,建设图像已传入

std::vector 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 orb = cv::ORB::create(50); //创建orb对象,后为得到的特征点数目 std::vector 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 matches; cv::Ptr matcher=cv::DescriptorMatcher::create("FlannBased"); //进行匹配 matcher->match(despL, despR, matches); std::vector point_l,std::vector point_r; for(int i=0;i

Demo:

#include

#include

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 orb = cv::ORB::create(50);

//特征点

std::vector 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 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 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

{

if(matches[i].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 create(const string& descriptorMatcherType)

如: cv::Ptr 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& matches, const Mat& mask ) const

* match函数的参数中位置在前面的为query descriptor,后面的是 train descriptor

* 例如:query descriptor的数目为20,train descriptor数目为30,则DescriptorMatcher::match后的vector的size为20

* 若反过来,则vector的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 orb = cv::ORB::create(50); //创建orb对象,后为得到的特征点数目 cv::Mat imageL1,imageR2,imageL2,imageR2,despL1,despR1,despL2,despR2; //只是举例,建设图像已传入 std::vector 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 orb = cv::ORB::create(50); //创建orb对象,后为得到的特征点数目

std::vector 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 matches;

cv::Ptr matcher=cv::DescriptorMatcher::create("FlannBased"); //进行匹配

matcher->match(despL, despR, matches);

std::vector point_l,std::vector point_r;

for(int i=0;i

{

point_l.push_back(matches[i].queryIdx);

point_r.push_back(matches[i].trainIdx);

}

实战展示:

#include #include 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 orb = cv::ORB::create(50);//特征点 std::vector 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 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 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=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& keypoints) ;

operator()( InputArray image, InputArray mask, vector& keypoints,

OutputArray descriptors, useProvidedKeypoints= ) ;

AlgorithmInfo* info() ;

:

computeImpl( Mat& image, vector& keypoints, Mat& descriptors ) ;

detectImpl( Mat& image, vector& 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& _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 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 > allKeypoints;

( do_keypoints )

{

computeKeyPoints(imagePyramid, maskPyramid, allKeypoints,

nfeatures, firstLevel, scaleFactor,

edgeThreshold, patchSize, scoreType);

for (int level = 0; level < n_levels; ++level)

vector& keypoints = all_keypoints[level];

keypoints.clear();

keypoint_end = temp.end(); keypoint != keypoint_end; ++keypoint)

}

{

KeyPointsFilter::runByImageBorder(_keypoints, image.size(), edgeThreshold);

allKeypoints.resize(levelsNum);

(vector::iterator keypoint = _keypoints.begin(),

keypointEnd = _keypoints.end(); keypoint != keypointEnd; ++keypoint)

allKeypoints[keypoint->octave].push_back(*keypoint);

( level = 0; level < levelsNum; ++level)

{

(level == firstLevel)

;

vector & keypoints = allKeypoints[level];

scale = 1/getScale(level, firstLevel, scaleFactor);

(vector::iterator keypoint = keypoints.begin(),

keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)

keypoint->pt *= scale;

}

}

Mat descriptors;

vector 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& 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::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& imagePyramid,

vector& maskPyramid,

vector >& 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 & 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::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& keypoints,

halfPatchSize, vector<>& umax)

{

(vector::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 (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& keypoints, Mat& descriptors,

vector& 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(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