OpenCV4入门教程115:ORB特征检测与匹配

索引地址:系列索引

特征:图像特征与特征描述子

ORB(Oriented FAST and Rotated BRIEF)是一种快速特征点提取和描述的算法。这个算法是由Ethan Rublee, Vincent Rabaud, Kurt Konolige以及Gary R.Bradski在2011年一篇名为“ORB:An Efficient Alternative to SIFTor SURF”的文章中提出。ORB算法分为两部分,分别是特征点提取和特征点描述。特征提取是由FAST(Features from Accelerated Segment Test)算法发展来的,特征点描述是根据BRIEF(Binary Robust IndependentElementary Features)特征描述算法改进的。ORB特征是将FAST特征点的检测方法与BRIEF特征描述子结合起来,并在它们原来的基础上做了改进与优化。据说,ORB算法的速度是sift的100倍,是surf的10倍。

SIFT和SURF算法是有专利保护的 如果你使用它们就可能要花钱。但是ORB不需要

数据的方差大的一个好处是 使得特征更容易分辨。

对于描述符,ORB使用BRIEF描述符。但我们已经看到,这个BRIEF的表现在旋转方面表现不佳。因此,ORB所做的是根据关键点的方向来“引导”。

对于在位置(xi,yi)的n个二进制测试的任何特性集,定义一个包含这些像素坐标的2 n矩阵。然后利用补丁的方向,找到旋转矩阵并旋转S,以得到引导(旋转)版本s。

ORB将角度进行离散化,以增加2/30(12度),并构造一个预先计算过的简短模式的查找表。只要键点的方向是一致的,就会使用正确的点集来计算它的描述符。

BRIEF有一个重要的属性,即每个比特的特性都有很大的方差,而平均值接近0.5。但是一旦它沿着键点方向移动,它就会失去这个属性并变得更加分散。高方差使特征更有区别,因为它对输入的响应不同。另一个可取的特性是让测试不相关,因为每个测试都将对结果有所贡献。为了解决所有这些问题,ORB在所有可能的二进制测试中运行一个贪婪的搜索,以找到那些既有高方差又接近0.5的,同时又不相关的。结果被称为rBRIEF。

对于描述符匹配,在传统的LSH上改进的多探测LSH是被使用的。这篇文章说,ORB比冲浪快得多,而且比冲浪还好。对于全景拼接的低功率设备,ORB是一个不错的选择。

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <opencv2/core/core.hpp> 
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/xfeatures2d.hpp>

using namespace cv;

int main(int argc, char** argv)
{
Mat img_1 = imread("box.png");
Mat img_2 = imread("box_in_scene.png");

// -- Step 1: Detect the keypoints using STAR Detector
std::vector<KeyPoint> keypoints_1,keypoints_2;
Ptr<xfeatures2d::StarDetector> detector =xfeatures2d::StarDetector::create();
detector->detect(img_1, keypoints_1);
detector->detect(img_2, keypoints_2);

// -- Stpe 2: Calculate descriptors (feature vectors)
Ptr<ORB> orb=ORB::create();
Mat descriptors_1, descriptors_2;
orb->compute(img_1, keypoints_1, descriptors_1);
orb->compute(img_2, keypoints_2, descriptors_2);

//-- Step 3: Matching descriptor vectors with a brute force matcher
BFMatcher matcher(NORM_HAMMING);
std::vector<DMatch> mathces;
matcher.match(descriptors_1, descriptors_2, mathces);
// -- dwaw matches
Mat img_mathes;
drawMatches(img_1, keypoints_1, img_2, keypoints_2, mathces, img_mathes);
// -- show
imshow("Mathces", img_mathes);

waitKey(0);
return 0;
}

效果为

orb

视频实时提取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//---------------------------------【头文件、命名空间包含部分】----------------------------
// 描述:包含程序所使用的头文件和命名空间
//------------------------------------------------------------------------------------------------
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

//--------------------------------------【main( )函数】-----------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//-----------------------------------------------------------------------------------------------
int main() {
//【0】载入源图,显示并转化为灰度图
Mat srcImage = imread("1.jpg");
imshow("src", srcImage);
Mat grayImage;
cvtColor(srcImage, grayImage, COLOR_BGR2GRAY);

//------------------检测SIFT特征点并在图像中提取物体的描述符----------------------

//【1】参数定义
Ptr<ORB> orb = ORB::create();
vector<KeyPoint> keyPoints;
Mat descriptors;

//【3】计算描述符(特征向量)
orb->detectAndCompute(grayImage, Mat(),keyPoints, descriptors);

//【4】基于FLANN的描述符对象匹配
flann::Index flannIndex(descriptors, flann::LshIndexParams(12, 20, 2),
cvflann::FLANN_DIST_HAMMING);

//【5】初始化视频采集对象
VideoCapture cap("tifa.mp4");

unsigned int frameCount = 0; //帧数

//【6】轮询,直到按下ESC键退出循环
while (1) {
double time0 = static_cast<double>(getTickCount()); //记录起始时间
Mat captureImage, captureImage_gray; //定义两个Mat变量,用于视频采集
cap >> captureImage; //采集视频帧
if (captureImage.empty()) //采集为空的处理
break;

//转化图像到灰度
cvtColor(captureImage, captureImage_gray, COLOR_BGR2GRAY); //采集的视频帧转化为灰度图

//【7】检测SIFT关键点并提取测试图像中的描述符
vector<KeyPoint> captureKeyPoints;
Mat captureDescription;

//【8】调用detect函数检测出特征关键点,保存在vector容器中
orb->detectAndCompute(captureImage_gray,Mat(),captureKeyPoints,captureDescription);
if(captureDescription.empty()) continue;

//【10】匹配和测试描述符,获取两个最邻近的描述符
Mat matchIndex(captureDescription.rows, 2, CV_32SC1),
matchDistance(captureDescription.rows, 2, CV_32FC1);
flannIndex.knnSearch(captureDescription, matchIndex, matchDistance, 2,
flann::SearchParams()); //调用K邻近算法

//【11】根据劳氏算法(Lowe's algorithm)选出优秀的匹配
vector<DMatch> goodMatches;
for (int i = 0; i < matchDistance.rows; i++) {
if (matchDistance.at<float>(i, 0) < 0.6 * matchDistance.at<float>(i, 1)) {
DMatch dmatches(i, matchIndex.at<int>(i, 0), matchDistance.at<float>(i, 0));
goodMatches.push_back(dmatches);
}
}

//【12】绘制并显示匹配窗口
Mat resultImage;
drawMatches(captureImage, captureKeyPoints, srcImage, keyPoints, goodMatches, resultImage);
imshow("matched", resultImage);

//【13】显示帧率
cout << ">帧率= " << getTickFrequency() / (getTickCount() - time0) << endl;

//按下ESC键,则程序退出
if (char(waitKey(1)) == 27)
break;
}

return 0;
}

效果

orb video


OpenCV4入门教程115:ORB特征检测与匹配
https://feater.top/opencv/opencv-orb-feature-detect-and-match/
作者
JackeyLea
发布于
2020年9月20日
许可协议