1.Harris角点
1.1基本原理
人眼对角点的识别通常是在一个局部的小区域或小窗口完成的。如果在各个方向上移动这个特征的小窗口,窗口内区域的灰度发生了较大的变化,那么就认为在窗口内遇到了角点。如果这个特定的窗口在图像各个方向上移动时,窗口内图像的灰度没有发生变化,那么窗口内就不存在角点;如果窗口在某一个方向移动时,窗口内图像的灰度发生了较大的变化,而在另一些方向上没有发生变化,那么,窗口内的图像可能就是一条直线的线段。
对于图像I(x,y),当在点(x,y)处平移(Δx,Δy)后的自相似性,可以通过自相关函数给出:
其中,W(x,y)是以点(x,y)为中心的窗口,w(u,v)为加权函数,它既可是常数,也可以是高斯加权函数。
根据泰勒展开,对图像I(x,y)在平移(Δx,Δy)后进行一阶近似:
其中,Ix,Iy是图像I(x,y)的偏导数,这样的话,自相关函数则可以简化为:
其中
也就是说图像I(x,y)在点(x,y)处平移(Δx,Δy)后的自相关函数可以近似为二项函数:
其中
二次项函数本质上就是一个椭圆函数。椭圆的扁率和尺寸是由M(x,y)的特征值λ1、λ2决定的,椭贺的方向是由M(x,y)的特征矢量决定的,如下图所示,椭圆方程为:
椭圆函数特征值与图像中的角点、直线(边缘)和平面之间的关系如下图所示。共可分为三种情况:
图像中的直线。一个特征值大,另一个特征值小,λ1≫λ2或λ2≫λ1。自相关函数值在某一方向上大,在其他方向上小。
图像中的平面。两个特征值都小,且近似相等;自相关函数数值在各个方向上都小。
图像中的角点。两个特征值都大,且近似相等,自相关函数在所有方向都增大。
根据二次项函数特征值的计算公式,我们可以求M(x,y)矩阵的特征值。但是Harris给出的角点差别方法并不需要计算具体的特征值,而是计算一个角点响应值R来判断角点。R的计算公式为:
式中,detM为矩阵M=[A B
B C]的行列式;traceM为矩阵M的直迹;α为经常常数,取值范围为0.04~0.06。事实上,特征是隐含在detM和traceM中,因为,
2. Harris角点算法实现
根据上述讨论,可以将Harris图像角点检测算法归纳如下,共分以下五步:
-
计算图像I(x,y)在X和Y两个方向的梯度Ix、Iy。
2.计算图像两个方向梯度的乘积。
3.使用高斯函数对I2x、I2y和Ixy进行高斯加权(取σ=1),生成矩阵M的元素A、B和C。
-
计算每个像素的Harris响应值R,并对小于某一阈值t的R置为零。
- 在3×3或5×5的邻域内进行非最大值抑制,局部最大值点即为图像中的角点。
Harris角点检测源码python版本的实现:
import cv2
import numpy as np
def detectHarrisCorners(imgSrc,imgDst):
if imgSrc is None:
print ("imgSrc is empty")
#imgDst = imgSrc
if(imgSrc.shape[-1] == 3):
grayImg = cv2.cvtColor(imgSrc,cv2.COLOR_BGR2GRAY)
else:
grayImg = imgSrc
grayImg_h = int(grayImg.shape[0])
grayImg_w = int(grayImg.shape[1])
ykernel = np.array(([-1],[0],[1]),dtype = "float32")
xkernel = np.array(([-1,0,1]),dtype = "float32")
Gradientx = cv2.filter2D(grayImg,-1,xkernel)
Gradienty = cv2.filter2D(grayImg,-1,ykernel)
Gradientx2 = np.multiply(Gradientx,Gradientx)
Gradienty2 = np.multiply(Gradienty,Gradienty)
Gradientxy = np.multiply(Gradientx,Gradienty)
A = cv2.GaussianBlur(Gradientx2,(3,3),1.5)
B = cv2.GaussianBlur(Gradienty2,(3,3),1.5)
C = cv2.GaussianBlur(Gradientxy,(3,3),1.5)
#计算R = detM - k(traceM)^2
R = np.zeros(grayImg.shape)
for i in range(grayImg_h):
for j in range(grayImg_w):
M = [[A[i,j],C[i,j]],[C[i,j],B[i,j]]]
#print("M",M)
R[i,j] = np.linalg.det(M) - 0.06*(np.trace(M))*(np.trace(M))
#函数计算输入矩阵的行列式 np.linalg.det
#对R进行处理得到角点图
for i in range(grayImg_h):
for j in range(grayImg_w):
#threshold
if R[i,j]<np.amin(R)*0.35:
#print("i",i,"j",j)
cv2.circle(imgDst,(j,i),8,(0,0,255),1)
if __name__ == "__main__":
img_path = "timg.jpg"
imgSrc = cv2.imread(img_path)
#cv2.namedWindow("src",cv2.WINDOW_NORMAL)
#cv2.imshow("src",imgSrc)
#cv2.waitKey(100)
imgDst = imgSrc
print(imgSrc.shape)
detectHarrisCorners(imgSrc,imgDst)
cv2.namedWindow("harris",cv2.WINDOW_NORMAL)
cv2.imshow("harris",imgDst)
cv2.waitKey(0)
3.Harris角点的性质
-
参数α对角点检测的影响
假设已经得到了矩阵M的特征值λ1≥λ2≥0,令λ2=kλ1,0≤k≤1。由特征值与矩阵M的直迹和行列式的关系可得:
从而可以得到角点的响应
假设R≥0,则有:
对于较小的k值,R≈λ2(k−α),α<k。
由此,可以得出这样的结论:增大α的值,将减小角点响应值R,降低角点检测的灵性,减少被检测角点的数量;减小α值,将增大角点响应值R,增加角点检测的灵敏性,增加被检测角点的数量。
-
Harris角点检测算子对亮度和对比度的变化不敏感
这是因为在进行Harris角点检测时,使用了微分算子对图像进行微分运算,而微分运算对图像密度的拉升或收缩和对亮度的抬高或下降不敏感。换言之,对亮度和对比度的仿射变换并不改变Harris响应的极值点出现的位置,但是,由于阈值的选择,可能会影响角点检测的数量。
Harris角点检测算子具有旋转不变性
Harris角点检测算子使用的是角点附近的区域灰度二阶矩矩阵。而二阶矩矩阵可以表示成一个椭圆,椭圆的长短轴正是二阶矩矩阵特征值平方根的倒数。当特征椭圆转动时,特征值并不发生变化,所以判断角点响应值R也不发生变化,由此说明Harris角点检测算子具有旋转不变性。Harris角点检测算子不具有尺度不变性
如下图所示,当右图被缩小时,在检测窗口尺寸不变的前提下,在窗口内所包含图像的内容是完全不同的。左侧的图像可能被检测为边缘或曲线,而右侧的图像则可能被检测为一个角点。