文章目录
- 一、SIFT特征检测
- 二、SURF特征检测
- 演示SURF、SIFT特征检测
OpenCV—python 角点特征检测之二(SIFT、SURF、ORB)
一、SIFT特征检测
SIFT(Scale-Invariant Feature Transform)特征,是一种基于尺度空间的,对图像缩放、旋转、甚至仿射变换保持不变性的图像局部特征描述算子。
关于算法详情查看:点击
-
特征点提取
Sift的特征点是在DOG金字塔尺度空间中提取的,构建图像高斯金字塔(高斯平滑,降采样),求取DOG( DoG 近似 LoG)
构建的高斯金字塔,每一层根据sigma的值不同,可以分为几个等级,最少有4个
在尺度空间中先初步提取出在尺度空间和二维图像空间上都是局部极值点的兴趣点,再滤除掉能量低的不稳定的和错误的兴趣点(通过Hassian 矩阵特征值实现,小于阈值自动舍弃),得到最终稳定的特征点。
-
特征点描述
特征点描述包括特征点方向分配和128维向量描述两个步骤。
特征的的方向分配:Sift求取特征点周围邻域内所有像素的梯度方向,生成梯度方向直方图,并归一化为0~360°的梯度方向直方图到36个方向内( 计算每个高斯权重,sigma=scalex1.5, 0~360之间建立36个直方图Bins)。
取梯度直方图的主要分量所代表的方向作为特征点的方向。找最高峰对应的Bin, 大于max*80% 的都保留,这样就实现了旋转不变性,提高了匹配时候的稳定性。
128维向量描述:这个仍然是基于梯度方向直方图展开的,去特征点周围邻域4*4个快,每块提取出8个梯度方向,共计128个方向作为特征的描述子。
-
特征点的匹配
特征点的匹配是通过计算两组特征点的128维的特征点的欧氏距离实现的。欧氏距离越小,则相似度越高,当欧式距离小于设定阈值时,可以判定为匹配成功。
Sift算法的优点是特征稳定,对旋转、尺度变换、亮度保持不变性,对视角变换、噪声也有一定程度的稳定性;缺点是实时性不高,并且对于边缘光滑目标的特征点提取能力较弱。
函数参数
OpenCV 4.4.0 以下版本,
static Ptr<SIFT> cv::xfeatures2d::SIFT::create
static Ptr<SIFT>
int
nfeatures = 0, 要保留的最佳特征数量
int
nOctaveLayers = 3, 每个组中的层数。 3 是 Lowe 论文中使用的值。 组的数量是根据图像分辨率自动计算的。
double
contrastThreshold = 0.04, 滤掉低对比度区域中的弱特征的阈值。 阈值越大,特征越少。
double
edgeThreshold = 10, 用于过滤边缘特征的阈值。 阈值越大,滤除的特征越少(保留的特征越多)。
double
sigma = 1.6 高斯的参数 σ \sigmaσ ,输入图像的第 0 个组使用的。
)
函数演示如下文。
二、SURF特征检测
Speeded Up Robust Features(SURF,加速稳健特征),是一种稳健的局部特征点检测和描述算法。
关于算法详情查看:点击 最初由Herbert Bay发表在2006年的欧洲计算机视觉国际会议(Europen Conference on Computer Vision,ECCV)上,并在2008年正式发表在Computer Vision and Image Understanding期刊上。
Surf是对sift算法的改进,提升了算法的执行效率。与Sift算法一样,Surf算法的基本路程可以分为三大部分:局部特征点的提取、特征点的描述、特征点的匹配。
SURF特征的关键特性
- 特征检测
- 尺度空间
- 选择不变性(旋转不变性,光照不变性)
- 特征向量
工作原理
-
选择图像中POI(Points of Interest) Hessian Matrix
构建Hessian矩阵的目的是为了生成图像稳定的边缘点(突变点),拉普拉斯边缘检测的作用类似,构建Hessian矩阵的过程对应于Sift算法中的高斯卷积过程。Hessian Matrix是一个多元函数的二阶偏导数构成的方阵,描述了函数的局部曲率。
设图像,分别对x,y方向求二阶偏导,Hessian Matrix如下:
基于高斯的LoG滤波
- 加速模式
- 构建尺度空间,在不同的尺度空间发现关键点
-
发现特征点方法、特征点主方向分配
将经过Hessian矩阵处理的每个像素点与二维图像空间和尺度空间邻域内的26个点进行比较,初步定位出关键点,再经过滤除能量比较弱的关键点以及错误定位的关键点,筛选出最终的稳定的特征点。
采用的是统计特征点圆形邻域内的harr小波特征。即在特征点的圆形邻域内,统计60度扇形内所有点的水平、垂直harr小波特征总和,然后扇形以0.2弧度大小的间隔进行旋转并再次统计该区域内harr小波特征值之后,最后将值最大的那个扇形的方向作为该特征点的主方向。
SURF为一个类,如下为其创建方法:
static Ptr<SURF>
double
hessianThreshold=100, 一般取值在300~500之间(值越大,获得的关键点就越少)
int
nOctaves = 4, 表示在四个尺度空间
int
nOctaveLayers = 3, 表示每个尺度的层数
bool
extended = false, 0表示应计算基本描述符(每个64个元素),1表示应计算扩展描述符(每个128个元素)
bool
upright = false 0- 表示计算选择不变性,1表示不计算,速度更快
)
绘制特征描述点
InputArray
image, 原始图像,三通道或单通道图像
const std::vector&
keypoints, 特征点向量,向量每一个元素(KeyPoint对象),包含了特征点的各种属性信息;
InputOutputArray
outImage, 特征点绘制的画布图像,可以是原图像
const Scalar& color=
Scalar::all(-1), 绘制的特征点的颜色,默认随机彩色
DrawMatchesFlags flags=
DrawMatchesFlags::DEFAULT
)
- DEFAULT=0 只绘制特征点的坐标点,画小圆点
- DRAW_OVER_OUTIMG=1 函数不创建输出的图像,而是直接在输出图像变量空间绘制
- NOT_DRAW_SINGLE_POINTS=2 单点的特征点不被绘制
- DRAW_RICH_KEYPOINTS=4 绘制特征点圆(圆坐标,size和方向),是最能显示特征的一种绘制方式
头文件
image_feature_all.h
:声明类与公共函数
#pragma once
#include <opencv2/opencv.hpp>
#include <iostream>
#include <opencv2/xfeatures2d.hpp> //新增引入库
using namespace cv;
using namespace std;
class ImageFeature {
public:
void surf_demo(Mat& image);
void sift_demo(Mat& image);
};
主函数
main.cpp
调用该类的公共成员函数
#include "image_feature_all.h"
int main(int argc, char** argv) {
const char* img_path = "D:\\Desktop\\jianzhu.jpg";
Mat image = imread(img_path, IMREAD_GRAYSCALE); //灰度图读入
if (image.empty()) {
cout << "图像数据为空,读取文件失败!" << endl;
}
ImageFeature imgfeature;
imgfeature.surf_demo(image);
imgfeature.sift_demo(image);
imshow("image", image);
waitKey(0);
destroyAllWindows();
return 0;
}
演示SURF、SIFT特征检测
#include "image_feature_all.h"
void ImageFeature::surf_demo(Mat& image) {
int minHessian = 400;
vector<KeyPoint> keypoints;
Ptr<xfeatures2d::SURF> detector = xfeatures2d::SURF::create(minHessian);
detector->detect(image, keypoints, Mat());
Mat keypointsImg;
drawKeypoints(image, keypoints, keypointsImg, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
imshow("keypointsImg", keypointsImg);
}
void ImageFeature::sift_demo(Mat& image) {
int minFeature = 300;
vector<KeyPoint> keypoints;
Ptr<SIFT> detector = SIFT::create(minFeature);
detector->detect(image, keypoints, Mat());
Mat keypointsImg;
drawKeypoints(image, keypoints, keypointsImg, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
imshow("Sift_img", keypointsImg);
}