OpenCv—特征检测
文章目录
1.特征检测的基本概念
特征检测是计算机视觉和图像处理中的一个概念。它指的是使用计算机提取图像信息,决定每个图像的点是否属于一个图像特征。特征检测的结果是把图像上的点
分为不同的子集,这些子集往往属于孤立的点、连续的曲线或者连续的区域。
特征检测是指从图像或视频中自动检测出一些具有特定几何形状或特定的灰度分布等特征的局部区域。
- 边缘检测
- 角点检测
- 区域检测
- 脊检测
特征具有哪些特点:
- 特征是唯一的
- 特征是可追踪的
- 特征是能比较的
也就是说,图像特征是指有意义的图像区域,具有独特性,易于识别性,比较角点,斑点以及高密度区域。
在图像特征中最重要的就是角点。
- 灰度梯度的最大值对应的像素
- 两条线的交点
- 极点值(一阶导数最大,二阶导数为0)
2.Harris角点检测
-
算法的核心是利用局部窗口在图像上进行移动,判断灰度是否发生较大的变化。如果窗口内的灰度值(在梯度图上)都有较大的变化,那么这个窗口所在区域就存在角点。
-
这样就可以将 Harris 角点检测算法分为以下三步:
-
当窗口(局部区域)同时向 x (水平)和 y(垂直) 两个方向移动时,计算窗口内部的像素值变化量 E(x,y) ;
-
对于每个窗口,都计算其对应的一个角点响应函数 R;
-
然后对该函数进行阈值处理,如果 R>threshold,表示该窗口对应一个角点特征。
-
-
数学原理:
-
让一个窗口的中心位于灰度图像的一个位置 ( x , y ) (x,y) (x,y) ,这个位置的像素灰度值为 I ( x , y ) I(x,y) I(x,y) ,如果这个窗口分别向 x x x和 y y y 方向移动一个小的位移 u u u和 v v v,到一个新的位置 ( x + u , y + v ) (x+u,y+v) (x+u,y+v),这个位置的像素灰度值就是 I ( x + u , y + v ) I(x+u,y+v) I(x+u,y+v)。
-
∣ I ( x + u , y + v ) − I ( x , y ) ∣ |I(x+u,y+v)-I(x,y)| ∣I(x+u,y+v)−I(x,y)∣就是窗口移动引起的灰度值的变化值。
-
设 w ( x , y ) w(x,y) w(x,y)为位置 ( x , y ) (x,y) (x,y)的窗口函数,表示窗口内各像素的权重,最简单的就是把窗口内所有像素的权重都设为1,即一个均值滤波核。
-
当然,也可以把 w ( x , y ) w(x,y) w(x,y)设定为以窗口中心为原点的高斯分布,即一个高斯核。如果窗口中心点像素是角点,那么窗口移动前后,中心点的灰度值变化非常强烈,
-
所以该点权重系数应该设大一点,表示该点对灰度变化的贡献较大;而离窗口中心(角点)较远的点,这些点的灰度变化比较小,于是将权重系数设小一点,表示该点对灰度变化的贡献较小。
-
-
则窗口在各个方向上移动 ( u , v ) (u,v) (u,v)所造成的像素灰度值的变化量公式如下:
- E = ∑ x , y w ( x , y ) [ I ( x + u , y + v ) − I ( x , y ) ] 2 E = \sum\limits_{x,y}w(x,y)[I(x+u,y+v)-I(x,y)]^2 E=x,y∑w(x,y)[I(x+u,y+v)−I(x,y)]2
-
若窗口内是一个角点,则E(u,v)的计算结果将会很大。
- E = ∑ x , y w ( x , y ) ( I x 2 I x I y I x I y I y 2 ) E = \sum\limits_{x,y}w(x,y)\begin{pmatrix} Ix^2&IxIy\\ IxIy&Iy^2\\ \end{pmatrix} E=x,y∑w(x,y)(Ix2IxIyIxIyIy2)
-
计算每个窗口对应的得分(角点响应函数R定义):
-
R = λ 1 λ 2 − k ( λ 1 + λ 2 ) 2 R=\lambda1\lambda2-k(\lambda1+\lambda2)^2 R=λ1λ2−k(λ1+λ2)2
-
λ 1 \lambda1 λ1和 λ 2 \lambda2 λ2是矩阵 的特征值,k是一个经验常数
-
-
因为特征值 λ1 和 λ2 决定了 R 的值,所以我们可以用特征值来决定一个窗口是平面、边缘还是角点:
-
平面:该窗口在平坦区域上滑动,窗口内的灰度值基本不会发生变化,所以 |R| 值非常小,在水平和竖直方向的变化量均较小,即 Ix和 Iy都较小,那么 λ1 和 λ2 都较小;
-
边缘:|R|值为负数,仅在水平或竖直方向有较大的变化量,即 Ix和 Iy只有一个较大,也就是 λ1>>λ2 或 λ2>>λ1;
-
角点: |R| 值很大,在水平、竖直两个方向上变化均较大的点,即 Ix和 Iy 都较大,也就是 λ1 和 λ2 都很大。
-
cornerHarris(src,blockSize,ksize,k,[,dst[,borderType]])
blockSize:
检测窗口大小ksize:
sobel的卷积核k:
权重系数,是个经验值,一般取0.04~0.06之间,一般默认是0.04
代码示例:
import cv2
img = cv2.imread('../my_work/01.png')
# 变成灰度图片
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# harris角点检测
dst = cv2.cornerHarris(gray,blockSize=2,ksize=3,k=0.04)
# 返回的东西叫角点响应,每一个像素点都能计算出一个角点响应来
print(dst.shape)
# 显示角点
# 我们认为角点响应大于0.01倍的dst.max()就可以认为是角点了
img[dst>0.01*dst.max()] = [0,0,255]
cv2.imshow('img',img)
cv2.waitKey()
cv2.destroyAllWindows()
3.Shi-Tomasi角点检测
-
Shi-Tomasi角点检测是Harris角点检测的改进。
-
Harris角点检测的稳定性与K有关,而K是一个经验值。
-
Shi-Tomasi发现,角点的稳定性其实和矩阵M的较小特征值有关,于是直接用那个较小的特征值作为分数,就不用调整K值了。
-
Shi-Tomasi将公式改为: R = m i n ( λ 1 , λ 2 ) R=min(\lambda1,\lambda2) R=min(λ1,λ2)。
-
和Harris一样,如果该分数大于设定的阈值,就认为是一个角点。
-
-
goodFeaturesToTrack(image,,qualityLevel,minDistance[,corner[,mask[,blockSize[,useHarrisDetector[,k]]]]])
-
maxCorners:
角点的最大数,值为0表示无限制; -
qualityLevel:
角点质量,小于1.0的整数,一般位于0.01~0.1之间; -
minDistance:
角之间的最小欧式距离,忽略小于此距离的点; -
mask:
感兴趣的区域; -
blockSize:
检测窗口的大小; -
useHarrisDetector:
是否使用Harris算法; -
k:
默认是0.04。
-
代码示例:
import cv2
import numpy as np
img = cv2.imread('../my_work/01.png')
# 参数设置
maxCorners = 1000
q1 = 0.01
minDistance = 10
# 变成灰度图片
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
corners = cv2.goodFeaturesToTrack(gray,maxCorners,q1,minDistance)
corners = np.int0(corners)
# 打印角点
for i in corners:
x,y = i.ravel()
cv2.circle(img,(x,y),3,(255,0,0),-1)
cv2.imshow('img',img)
cv2.waitKey()
cv2.destroyAllWindows()
4.SIFT关键点检测
-
SIFT算法,Scale-invariant feature transform,中文含义就是尺度不变特征变换。
-
在此之前的目标检测算法对图片的大小、旋转非常敏感,而SIFT算法是一种基于局部兴趣点的算法,因此不仅对图片大小和旋转不敏感,而且对光照、噪声等影响的抗击能力也非常优秀。
-
主要步骤分为:
-
尺度空间的极值检测
-
关键点精确定位
-
关键点主方向分配
-
关键点描述子的生成
-
4.1 尺度空间的极值检测
-
SIFT算法提出的目标是建立一个描述图像特征的完整体系,这个问题自然可以分解为寻找图像的特征 和 描述图像的特征两个部分。
-
特征应是区分不同内容的两幅图像和关联相同内容的两幅图像的量化指标。区分不同内容的图像时,人眼的做法是匹配一些图像的关键点。这一点成为SIFT特征的突破口,从而将图像特征的提取和描述的问题转化为寻找图像关键点(keypoints)和建立基于关键点的图像特征描述子(feature descriptor) 的问题。
4.1.1 高斯滤波
-
确定图像的关键点需要先排除图像的噪声,排除噪声最经典的办法是使用滤波器对图像进行滤波。使用 高斯滤波器(Gaussian Blur) 可以降低图像中的噪点。高斯滤波器滤波的过程相当于将一个特殊的模板对图像进行卷积。滤波后的结果图像中纹理和次要细节、噪声等部分得到了有效抑制,仅留下了图像的边缘和轮廓特征。
-
对原图像进行不同程度的降采样,再对降采样后的图像进行高斯滤波
4.1.2 高斯差分
- 得到不同尺度和不同高斯模板的降采样高斯滤波结果图像可以发现,虽然已经滤除了大部分的无用信息,但图像的轮廓等特征信息仍有待进一步凸显。为进一步削弱图像无用信息、凸显特征,考虑对得到的不同尺度不同滤波模板的图像组进行处理。算法对同一尺度下不同高斯模板滤波的图像进行差分(两两相减)。高斯滤波后再进行差分即进行高斯差分(Difference of Gaussian(DOG))。
4.1.3 局部极值
-
获得高斯差分金字塔后,算法应当利用高斯差分金字塔中的图像特征信息确定图像的特征点。SIFT算法选择高斯差分金字塔中的极值点作为代表图像特征的关键点。需要注意的是:这里所说的极值点不仅仅是在一幅图像中像素值为极值的点,而是该点像素值对于与其同图像的八邻域和上一张图像相同位置点以及八邻域、下一张图像相同位置点以及八邻域共计26个像素值为局部极值。
-
选出的高斯差分金字塔极值点只是候选的特征点。虽然高斯差分金字塔极值点已经能够较好地代表图像的特征并且具有尺度不变性,但在选取过程中没有考虑图像特征点对于图像噪声的鲁棒性,这样确定出的图像特征点在实际应用时易出现图像匹配不当等问题。
4.2 关键点的精确定位
-
为提高图像特征点对噪声的鲁棒性,SIFT算法的通常做法是对比测试和边缘测试。
-
对比测试是指为解决对比度较低的极值点,对所有极值点进行二阶泰勒展开(second-order Taylor expansion)。如果结果值小于0.03,则剔除该关键点。
-
边缘测试是指使用二阶Hessian矩阵来识别具有高边缘度但对少量噪声没有鲁棒性的关键点
4.3 关键点主方向分配
- 在建立高斯差分金字塔以选取图像特征点时,算法考虑了关键点的尺度不变性。而对于图像特征而言,与尺度不变性同等重要的还有旋转不变性。为使算法定义的图像特征具有旋转不变性,需要在定位特征点后对特征点定义 “主方向”,该“主方向”生成特征点周围的局部区域的梯度方向基准,使图像在旋转后仍能与旋转前保持相同的特征描述。
- 算法将关键点指定大小领域中的所有点计算梯度方向与赋值,并统计所有梯度方向对应的赋值和,作关键点周围i邻域梯度方向直方图。
4.4 关键点描述子的生成
- 得到特征点二维位置、尺度位置、主方向的具体信息后,算法需要解决的最后一个问题就是生成关键点信息的描述子,即用一个向量描述图像中的特征点信息。算法将特征点周围的1616邻域分为4个88的区域,再将88的区域划为22区域,即每个小区域为44的范围。统计每个44区域的梯度方向直方图(直方图为8个bin),故共计448=128个bin(每个1616邻域有44个44邻域,每个44邻域有8个bin)。对应生成128维向量(值为梯度方向的幅值)。该128维向量即该点的特征描述子。
使用SIFT步骤
-
创建SIFT对象,
sift = cv2.xfeatures2d.SIFT_create()
-
进行检测,
kp = sift.detect(img,…..)
-
绘制关键点,
drawKeypoints(gray,kp,img)
代码示例:
import cv2
img = cv2.imread('../my_work/01.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 创建sift对象
sift = cv2.SIFT_create()
# 进行检测
kp = sift.detect(gray)
# kp是一个列表,里面存放的是封装的KeyPoint对象
# 计算描述子
kp,des = sift.compute(gray,kp)
print(len(kp))
print(type(des))
print(des.shape)
print(des[0])
# 绘制关键点
cv2.drawKeypoints(gray,kp,img)
cv2.imshow('img',img)
cv2.waitKey()
cv2.destroyAllWindows()
5.ORB 特征检测
-
ORB可以做到实时检测
-
ORB 是 Oriented Fast and Rotated Brief 的简称,可以用来对图像中的关键点快速创建特征向量,这些特征向量可以用来识别图像中的对象。
-
FAST可以做到特征点的实时检测,brief是对已检测的特征点进行描述
import cv2
img = cv2.imread('../../my_work/01.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 创建对象
sift = cv2.ORB_create()
# 进行检测、des描述子、kp是一个列表,里面存放的是封装的KeyPoint对象
kp,des = orb.detectAndCompute(gray,None)
print(len(kp))
print(type(des))
print(des.shape)
print(des[0])
# 绘制关键点
cv2.drawKeypoints(gray,kp,img)
cv2.imshow('img',img)
cv2.waitKey()
cv2.destroyAllWindows()