OpenCV4开发入门教程111:SIFT特征检测与匹配

索引地址:系列索引

SIFT特征是非常稳定的图像特征,在图像搜索、特征匹配、图像分类检测等方面应用十分广泛,但是它的缺点也是非常明显,就是计算量比较大,很难实时,所以对一些实时要求比较高的常见SIFT算法还是无法适用。如今SIFT算法在深度学习特征提取与分类检测网络大行其道的背景下,已经越来越有鸡肋的感觉,但是它本身的算法知识还是很值得我们学习,对我们也有很多有益的启示,本质上SIFT算法是很多常见算法的组合与巧妙衔接,这个思路对我们自己处理问题可以带来很多有益的帮助。

特别是SIFT特征涉及到尺度空间不变性与旋转不变性特征,是我们传统图像特征工程的两大利器,可以扩展与应用到很多图像特征提取的算法当中,比如SURF、HOG、HAAR、LBP等。夸张一点的说SIFT算法涵盖了图像特征提取必备的精髓思想,从特征点的检测到描述子生成,完成了对图像的准确描述,早期的ImageNet比赛中,很多图像分类算法都是以SIFT与HOG特征为基础,所有SIFT算法还是值得认真详细解读一番的。SIFT特征提取归纳起来SIFT特征提取主要有如下几步:

  • 构建高斯多尺度金字塔
  • 关键点精准定位与过滤
  • 关键点方向指派
  • 描述子生成

测试代码

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
// 功能:代码 8-3 SIFT 特征检测及匹配
// 作者:朱伟 zhu1988wei@163.com
// 来源:《OpenCV图像处理编程实例》
// 博客:http://blog.csdn.net/zhuwei1988
// 更新:2016-8-1
// 说明:版权所有,引用或摘录请联系作者,并按照上面格式注明出处,谢谢。
// 注解:需重新编译OpenCV3.1 contrib,见本书附录2
#include <opencv2/opencv.hpp>
#include <opencv2/features2d.hpp>
#include <iostream>

using namespace std;
using namespace cv;
// 计算图像的SIFT特征及匹配
float cacSIFTFeatureAndCompare(cv::Mat srcImage1,
cv::Mat srcImage2, float paraHessian)
{
CV_Assert(srcImage1.data != NULL && srcImage2.data != NULL);
// 转换为灰度
cv::Mat grayMat1, grayMat2;
cv::cvtColor(srcImage1, grayMat1, COLOR_RGB2GRAY);
cv::cvtColor(srcImage2, grayMat2, COLOR_RGB2GRAY);
// 初始化SIFT检测描述子
cv::Ptr<SIFT> sift = SIFT::create();

// 关键点及特征描述矩阵声明
vector<cv::KeyPoint> keyPoints1, keyPoints2;
cv::Mat descriptorMat1, descriptorMat2;

sift->detectAndCompute(grayMat1, Mat(), keyPoints1, descriptorMat1);
sift->detectAndCompute(grayMat2, Mat(), keyPoints2, descriptorMat2);
float result = 0;
// 特征点匹配
if (keyPoints1.size() > 0 && keyPoints2.size() > 0)
{
// 计算特征匹配点
cv::FlannBasedMatcher matcher;
vector< cv::DMatch > matches;
std::vector<cv::DMatch> viewMatches;
matcher.match(descriptorMat1, descriptorMat2, matches);
// 最优匹配判断
double minDist = 100;
for (int i = 0; i < matches.size(); i++)
{
if (matches[i].distance < minDist)
minDist = matches[i].distance;
}
// 计算距离特征符合要求的特征点
int num = 0;
std::cout << "minDist: " << minDist << std::endl;
for (int i = 0; i < matches.size(); i++)
{
// 特征点匹配距离判断
if (matches[i].distance <= 2 * minDist)
{
result += matches[i].distance * matches[i].distance;
viewMatches.push_back(matches[i]);
num++;
}
}
// 匹配度计算
result /= num;
// 绘制特征点匹配结果
cv::Mat matchMat;
cv::drawMatches(srcImage1, keyPoints1,
srcImage2, keyPoints2, matches, matchMat);
cv::imshow("matchMat", matchMat);
cv::waitKey(0);
}
return result;
}
int main()
{
// 读取源图像及待匹配图像
cv::Mat srcImage1 =
cv::imread("box.png", 1);
if (srcImage1.empty())
return -1;
cv::Mat srcImage2 =
cv::imread("box_in_scene.png", 1);
if (srcImage2.empty())
return -1;
float matchRate = cacSIFTFeatureAndCompare(srcImage1, srcImage2, 1000);
std::cout << "matchRate: " << matchRate << std::endl;
return 0;
}

测试输出为

1
2
minDist: 66.2571
matchRate: 10781.6

测试效果

sift

实时检测

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
#include <opencv2/features2d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

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

//【2】检测SIFT关键点、提取训练图像描述符
vector<KeyPoint> train_keyPoint;
Mat trainDescription;
Ptr<SIFT> sift = SIFT::create();
sift->detectAndCompute(trainImage_gray,Mat(),train_keyPoint, trainDescription);

// 【3】进行基于描述符的暴力匹配
BFMatcher matcher;
vector<Mat> train_desc_collection(1, trainDescription);
matcher.add(train_desc_collection);
matcher.train();

//【4】创建视频对象、定义帧率
VideoCapture cap("tifa.mp4");
unsigned int frameCount = 0; //帧数

//【5】不断循环,直到q键被按下
while (char(waitKey(1)) != 'q') {
//<1>参数设置
double time0 = static_cast<double>(getTickCount()); //记录起始时间
Mat captureImage, captureImage_gray;
cap >> captureImage; //采集视频到testImage中
if (captureImage.empty())
continue;

//<2>转化图像到灰度
cvtColor(captureImage, captureImage_gray, COLOR_BGR2GRAY);

//<3>检测SURF关键点、提取测试图像描述符
vector<KeyPoint> test_keyPoint;
Mat testDescriptor;
sift->detectAndCompute(captureImage_gray,Mat(),test_keyPoint, testDescriptor);

//<4>匹配训练和测试描述符
vector<vector<DMatch>> matches;
matcher.knnMatch(testDescriptor, matches, 2);

// <5>根据劳氏算法(Lowe's algorithm),得到优秀的匹配点
vector<DMatch> goodMatches;
for (unsigned int i = 0; i < matches.size(); i++) {
if (matches[ i ][ 0 ].distance < 0.6 * matches[ i ][ 1 ].distance)
goodMatches.push_back(matches[ i ][ 0 ]);
}

//<6>绘制匹配点并显示窗口
Mat dstImage;
drawMatches(captureImage, test_keyPoint, trainImage, train_keyPoint, goodMatches, dstImage);
imshow("matched", dstImage);

//<7>输出帧率信息
cout << "\t>当前帧率为:" << getTickFrequency() / (getTickCount() - time0) << endl;
}

return 0;
}

测试效果

sift

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