这个学期在上数字图像处理这门课。这门课没有考试,只有大作业,要求使用labwindows和NI Vision进行开发。我选的题目是全景图像的合成(图像拼接),其中要使用到一些特征点检测和匹配的算法。本文主要讨论一下opencv中一些尺度不变特征检测算法的实现。
在计算机视觉领域,兴趣点(也称关键点或特征点)的概念已经得到了广泛的应用,包括目标识别、图像配准、视觉跟踪、三维重建等。这个概念的原理是,从图像中选取某些特征点并对图像进行局部分析(即提取局部特征),而非观察整幅图像。只要图像中有足够多的可检测的兴趣点,并且这些兴趣点各不相同且特征稳定、能被精确地定位,上述方法就十分有效。
因为要用于图像内容的分析,所以不管图像拍摄时使用了什么视角、尺度和方位,理想情况下同一场景或目标位置都要检测到特征点。视觉不变性是图像分析中一个非常重要的属性,目前有大量关于它的研究。
目前一些广为人知的特征点检测算法有:
根据我的理解,尺度不变特征检测的算子基本都基于这样一个假设
对图像进行缩放或者进行高斯滤波,可以模拟人眼在不同距离下观察物体时的场景
SIFT算法的第一个步骤,就是通过对原图像进行不断的缩小和高斯滤波,生成图像金字塔,以用来进行后续的分析.
SIFT算法使用了高斯差分(DOG)这个概念。高斯滤波器可以提取图像的低频成分,过滤的频率范围取决于参数σ。那么,用两个不同带宽的高斯滤波器对一幅图像做滤波,然后相减,就可以得到图像中的一定频段构成的图像。这种运算就叫高斯差分。而SIFT算法对每个Octave的图像进行不同程度的高斯滤波后生成高斯差分金字塔。
得到特征点的位置之后,我们需要求取它们的方向。对于在DOG金字塔检测到的关键点,采集所在图像3σ邻域窗口内像素的梯度和方向特征,并进行统计。取幅值最高的方向为主方向,超过峰值百分之80的方向为辅方向。
我们已经得到了关键点的所需要的信息,接下来就是用一组向量将关键点描述出来。为了保证特征向量具有旋转不变性,需要将坐标轴旋转到关键点的方向。
SIFT是十分经典的算法,但有以下缺点:
至此,SIFT的原理介绍到这里。这个算法比较复杂,我花了不少时间去理解,但笔者水平有限,理解十分粗浅,可能会有错误。所以还是以维基和论文为准比较稳妥。
至于SURF、BRISK、ORB基本都是在SIFT算法的基础上进行改进,只要理解SIFT,其他算法相对而言也比较好办。这里不对其他算法进行讨论,读者可以查看文末贴出的博文自行了解。
/******************************************************
* Created by 杨帮杰 on 9/29/18
* Right to use this code in any way you want without
* warranty, support or any guarantee of it working
* E-mail: [email protected]
* Association: SCAU 华南农业大学
******************************************************/
#include
#include
#include
#define IMAGE1_PATH "/home/jacob/下载/church01.jpg"
#define IMAGE2_PATH "/home/jacob/下载/church02.jpg"
using namespace cv;
using namespace cv::xfeatures2d;
using namespace std;
int main()
{
Mat image= imread(IMAGE1_PATH,0);
if (!image.data)
return 0;
//SURF
vector keypoints;
//创建一个Hessian矩阵的阈值为2000的SURF检测器
//这个值越大,检测到的点越少
Ptr ptrSURF = SurfFeatureDetector::create(2000.0);
ptrSURF->detect(image, keypoints);
Mat featureImage;
drawKeypoints(image,keypoints,featureImage,
Scalar(255,255,255),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
imshow("IMAGE1_SURF",featureImage);
cout << "Number of IMAGE1_SURF keypoints: " << keypoints.size() << endl;
//读取第二个图像进行比较
image = imread(IMAGE2_PATH,0);
keypoints.clear();
ptrSURF->detect(image,keypoints);
drawKeypoints(image,keypoints,featureImage,
Scalar(255,255,255),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
imshow("IMAGE2_SURF",featureImage);
cout << "Number of IMAGE2_SURF keypoints: " << keypoints.size() << endl;
//SIFT
image = imread(IMAGE1_PATH,0);
keypoints.clear();
Ptr ptrSIFT = SiftFeatureDetector::create();
ptrSIFT->detect(image, keypoints);
drawKeypoints(image,keypoints,featureImage,
Scalar(255,255,255),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
imshow("SIFT",featureImage);
cout << "Number of SIFT keypoints: " << keypoints.size() << endl;
//BRISK
keypoints.clear();
cv::Ptr ptrBRISK = cv::BRISK::create(
60, // AGAST(FAST的加速版)检测的阈值,阈值越大检测到的点越小
5); // 金字塔的层数
ptrBRISK->detect(image,keypoints);
drawKeypoints(image,keypoints,featureImage,
Scalar(255,255,255),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
imshow("BRISK", featureImage);
cout << "Number of BRISK keypoints: " << keypoints.size() << endl;
//ORB
keypoints.clear();
cv::Ptr ptrORB = cv::ORB::create(
75, // 关键点的数量
1.2, // 金字塔每一层的缩放比例
8); // 金字塔的层数
ptrORB->detect(image, keypoints);
drawKeypoints(image,keypoints,featureImage,
Scalar(255,255,255),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
imshow("ORB",featureImage);
cout << "Number of ORB keypoints: " << keypoints.size() << endl;
waitKey();
return 0;
}
结果如下
References:
SIFT算法详解
SIFT特征匹配算法介绍
SURF特征提取分析
BRISK特征提取算法
ORB特征提取与匹配
opencv计算机视觉编程攻略(第三版) —— Robert