博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
OpenCV 尺度不变特征检测:SIFT、SURF、BRISK、ORB
阅读量:6591 次
发布时间:2019-06-24

本文共 4284 字,大约阅读时间需要 14 分钟。

这个学期在上数字图像处理这门课。这门课没有考试,只有大作业,要求使用labwindows和NI Vision进行开发。我选的题目是全景图像的合成(图像拼接),其中要使用到一些特征点检测和匹配的算法。本文主要讨论一下opencv中一些尺度不变特征检测算法的实现。

img_70d2f25f118466339ff0e40404602f05.png
乔帮主

1.概述

在计算机视觉领域,兴趣点(也称关键点特征点)的概念已经得到了广泛的应用,包括目标识别、图像配准、视觉跟踪、三维重建等。这个概念的原理是,从图像中选取某些特征点并对图像进行局部分析(即提取局部特征),而非观察整幅图像。只要图像中有足够多的可检测的兴趣点,并且这些兴趣点各不相同且特征稳定、能被精确地定位,上述方法就十分有效。

因为要用于图像内容的分析,所以不管图像拍摄时使用了什么视角、尺度和方位,理想情况下同一场景或目标位置都要检测到特征点。视觉不变性是图像分析中一个非常重要的属性,目前有大量关于它的研究。

目前一些广为人知的特征点检测算法有:

  1. 不带尺度不变特征的检测,如Harris、FAST、AGAST等。在我上一篇中提到过,本文不进行讨论。
  2. 尺度不变特征的检测,如SIFT、SURF、BRISK、ORB等。本文主要讨论SIFT。

2.算法原理

根据我的理解,尺度不变特征检测的算子基本都基于这样一个假设

对图像进行缩放或者进行高斯滤波,可以模拟人眼在不同距离下观察物体时的场景

SIFT算法的第一个步骤,就是通过对原图像进行不断的缩小和高斯滤波,生成图像金字塔,以用来进行后续的分析.

img_7ce1a8092ac72f53ecdfbff0a3f41a1b.png
图像金字塔

SIFT算法使用了高斯差分(DOG)这个概念。高斯滤波器可以提取图像的低频成分,过滤的频率范围取决于参数σ。那么,用两个不同带宽的高斯滤波器对一幅图像做滤波,然后相减,就可以得到图像中的一定频段构成的图像。这种运算就叫高斯差分。而SIFT算法对每个Octave的图像进行不同程度的高斯滤波后生成高斯差分金字塔

img_512155deaa0db3e662f83a10cb4cfbaf.png
高斯差分金字塔
得到DOG金字塔之后,通过检测不同层之间的图像的极值点来进行关键点的初步探查。这个过程很简单,就是与周围的像素点进行比较,当大于或小于所有相邻点时,即为极值点。下图中的X不仅要跟周围的O进行比较,还要跟上一层和下一层的9x2个O进行比较,才能确定是否是极值点。
img_0fc7a67eee2d253cd21f8af7d7db4818.png
极值检测
我们找到的极值点是离散的,我们需要使用泰勒展开式来拟合真正的极值点

img_9e8c7a44078c3e46699a915a07c95822.png
泰勒展开式(二阶)

img_9689e4c0a5012417a4fe8bfeef5bd83b.png
精确定位
上述步骤找到的极值点很多都是处于边缘区域的点。我们要找的兴趣点是角点,角点的特征是纵向和横向的变化都很大,而边缘点只有一个方向变化比较大。于是,可以使用
Hessian矩阵进行筛选。这个实际上是计算两个方向上的二阶导数。

img_931dff00afd702c56799ecf12bdbba14.png
Hessian(海塞)矩阵

得到特征点的位置之后,我们需要求取它们的方向。对于在DOG金字塔检测到的关键点,采集所在图像3σ邻域窗口内像素的梯度和方向特征,并进行统计。取幅值最高的方向为主方向,超过峰值百分之80的方向为辅方向

img_db26bbbd5d5a385360af967fcbf28170.png
m为梯度幅值 Θ为幅角

img_b2a5f5e6db5fce95c520129944cf3c7b.png
确定主方向

我们已经得到了关键点的所需要的信息,接下来就是用一组向量将关键点描述出来。为了保证特征向量具有旋转不变性,需要将坐标轴旋转到关键点的方向。

img_6ae45385de46587cd36b4948a077f627.png
坐标轴旋转
最后就是生成特征匹配点。将特征点邻域分为几个区块,计算八个方向的梯度方向直方图。这里有16个区域,所以生成了16x8=128个维度的数据。
在进行统计之前,还要进行一次高斯加权,特征点附近的区域权值大,相反权值小。

img_603a86fcf76f03182288b4be731d17ac.png
采样
img_dc9037f127e970cfe8bc01bb2a75b404.png
加权并统计

SIFT是十分经典的算法,但有以下缺点:

  • 实时性不高
  • 特征点比较少
  • 对边缘光滑的图像有时无能为力

至此,SIFT的原理介绍到这里。这个算法比较复杂,我花了不少时间去理解,但笔者水平有限,理解十分粗浅,可能会有错误。所以还是以维基和论文为准比较稳妥。

至于SURF、BRISK、ORB基本都是在SIFT算法的基础上进行改进,只要理解SIFT,其他算法相对而言也比较好办。这里不对其他算法进行讨论,读者可以查看文末贴出的博文自行了解。

3.算法实现

/****************************************************** * 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: yangbangjie1998@qq.com * 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;}

结果如下

img_b1eeeb2b7d43229bac7d6c62e8c99d44.png
SURF
img_3e79abfa4274cd63aa37a32c84087d7e.png
SIFT、ORB、BRISK
img_f5c705b5a81e8680b6ed82b6f767e511.png
关键点的数量

References:

opencv计算机视觉编程攻略(第三版) —— Robert

转载地址:http://tuuio.baihongyu.com/

你可能感兴趣的文章
9-51单片机ESP8266学习-AT指令(测试TCP服务器--51单片机程序配置8266,C#TCP客户端发信息给单片机控制小灯的亮灭)...
查看>>
香港设计师带来仿生机器人,其身体 70% 构造均由3D打印完成
查看>>
不规则物体形状匹配综述
查看>>
自动化设计-框架介绍 TestCase
查看>>
CJ看showgirl已经out!VR体验才是王道
查看>>
postgresql 数组类型
查看>>
Vue+Webpack常见问题(持续更新)
查看>>
栈与递归的实现
查看>>
Manually Summarizing EIGRP Routes
查看>>
spring boot 1.5.4 整合webService(十五)
查看>>
modsecurity(尚不完善)
查看>>
获取.propertys文件获取文件内容
查看>>
Redis3.0.5配置文件详解
查看>>
Know about Oracle RAC Heartbeat
查看>>
JQuery——实现Ajax应用
查看>>
前端05.js入门之BOM对象与DOM对象。
查看>>
oracle kill所有plsql developer进程
查看>>
keepalived双机热备原理及实例部署LVS+keepalived
查看>>
曲线学习PyQt5方案一
查看>>
企业私有云之rabbitmq高可用
查看>>