一个窗口在一检测点处上下、左右或者沿对角线移动:
其实Harris是在Moravec基础上改进而来的,所以在讨论Harris之前,先来看一下Moravec
其实从线性代数的角度很好理解,就是要矩阵M的两个特征值都很大,为了表示“两个特征值都很大”这一层意思,给出下列角点响应公式,角点响应R大的话,则表明两个特征值都很大,则该点判断为角点。从数学的角度来看,这个角点响应取得真好,先是乘积打头,让两个值都尽可能地大,同时减去矩阵迹(trace)的平方,排除出现一个远大于另一个的情况(这种情况是边缘点),太妙了~
传统的Harris角点检测(由Harris等在1988年提出)虽然对旋转改变有很好的检测不变性,但是却不具有尺度不变性及仿射不变性。为了获得尺度不变性,就需要在传统的Harris角点检测中引入多尺度空间。Mikolajczyk和Schmid于2004年提出具有尺度不变性的Harris-Laplace检测方法[1]。
import cv2
import math
import numpy as np
from scipy import ndimage as ndim
import matplotlib.pyplot as plt
%matplotlib inline
# 取最近的正奇数
def odd(val):
if int(val)%2 == 0:
return int(val)+1
else:
return int(val)
# 生成归一化后的高斯内核
def gauss_kernel(size,sigma):
'''
size表示高斯内核的大小,为正的奇数
sigma表述随机变量X和Y的标准差,此处假设X和Y具有相同的标准差
'''
xy = range(-(size-1)//2,(size+1)//2)
X,Y = np.meshgrid(xy,xy)
G = 1/(2*np.pi*sigma**2)*np.exp(-0.5*(X**2+Y**2)/sigma**2)
return G/np.sum(G)
# 生成归一化后的LOG内核
def log_kernel(size,sigma):
'''
size表示高斯内核的大小,为正的奇数
sigma表述随机变量X和Y的标准差,此处假设X和Y具有相同的标准差
'''
xy = range(-(size-1)//2,(size+1)//2)
X,Y = np.meshgrid(xy,xy)
LOG = (X**2+Y**2-2*sigma**2)/(sigma**4)*np.exp(-0.5*(X**2+Y**2)/sigma**2)
return LOG
# 非极大值抑制
def find_local_maximum(val):
'''功能:查找领域极大值;
输入:val-矩阵
输出:row-领域极大值的行坐标 col-领域极大值的列坐标 max_local-领域极大值'''
footprint = np.ones((3,3))
footprint[1,1] = 0
maxima_around = ndim.maximum_filter(val,footprint = footprint) # 8领域内最大值滤波
mask = val > maxima_around # 找出极大值点所在的位置
points = np.argwhere(mask==1)
return points
# plt.imshow(img,cmap = 'gray')
# plt.axis('off')
# plt.show
def Har_Lap_corners_detect(img):
'''功能:Harris Laplace角点检测
参数:img-输入的图像
输出:points = [l,c,radius] l,c-角点的横纵坐标,radius-特征尺度值所对应的圆形区域的半径
'''
# 图像参数
height,width = img.shape
# 尺度参数
sigma_begin = 1.5
sigma_step = 1.2
sigma_nb = 13
sigma_array = (sigma_step**np.arange(sigma_nb))*sigma_begin
# 第一部分:提取Harris角点
harris_pts = np.zeros((0,3),dtype = float)
k = 0.06
for i in range(sigma_nb):
# 尺度
s_I = sigma_array[i]
ksize = odd(6*s_I+1)
s_D = 0.7*s_I
# # 微分掩模
# x = np.arange(-round(3*s_D),round(3*s_D)+1)
# Ix = np.array([x*np.exp(-x*x/(2*s_D*s_D))/(s_D*s_D*s_D*(2*np.pi)**0.5)])
# Iy = Ix.T
# # 分别计算x,y方向上的梯度
# dx = cv2.filter2D(img,cv2.CV_32F,Ix)
# dy = cv2.filter2D(img,cv2.CV_32F,Iy)
# dx
x = np.array([-1,0,1])
g = np.array([np.exp(-x*x/(2*s_D**2))/(s_D*(2*np.pi)**0.5)])
g = g/np.sum(g) # 归一化
fx = np.array([[-1,0,1]])
dx = cv2.filter2D(img,cv2.CV_32F,g*fx)
dy = cv2.filter2D(img,cv2.CV_32F,g.T*fx.T)
# 自相关矩阵
g = gauss_kernel(max(1,ksize),s_I) # 生成高斯核
dx2 = cv2.filter2D(dx**2,cv2.CV_32F,g)
dy2 = cv2.filter2D(dy**2,cv2.CV_32F,g)
dxy = cv2.filter2D(dx*dy,cv2.CV_32F,g)
cim = dx2*dy2-dxy**2-k*(dx2+dy2)**2
# 阈值
tmp = 0.01*np.max(cim)
cim[cim<tmp] = 0
# 3*3领域非极大值抑制
points = find_local_maximum(cim)
n = points.shape[0]
points = np.c_[points,i*np.ones((n,1))]
harris_pts = np.r_[harris_pts,points]
# 第二部分:Laplace变换
# 计算尺度空间
log = np.zeros((height,width,sigma_nb))
for i in range(sigma_nb):
s_L = sigma_array[i]
#img_g = cv2.filter2D(img,-1,gauss_kernel(max(1,int(6*s_I+1)),s_L))
#log[:,:,i] = s_L*s_L*cv2.filter2D(img,-1,log_kernel(odd(6*s_L+1),s_L))
log[:,:,i] = s_L*s_L*cv2.Laplacian(cv2.GaussianBlur(img,(odd(6*s_L+1),odd(6*s_L+1)),s_L,s_L),cv2.CV_32F,ksize = 3)
# 检测每个特征点在某一尺度LoG相应是否最大
n = harris_pts.shape[0]
cpt = -1
points_t = np.zeros((n,3))
for i in range(n):
l = int(harris_pts[i,0])
c = int(harris_pts[i,1])
s = int(harris_pts[i,2])
val = log[l,c,s]
if s>0 and s<sigma_nb-1:
if val>log[l,c,s-1] and val>log[l,c,s+1]:
cpt += 1;
points_t[cpt,:] = harris_pts[i,:]
points_t[cpt,2] = 3*sigma_array[s]
elif s == 0:
if val > log[l,c,1]:
cpt += 1;
points_t[cpt,:] = harris_pts[i,:]
points_t[cpt,2] = 3*sigma_array[s]
else:
if val > log[l,c,s-1]:
cpt += 1;
points_t[cpt,:] = harris_pts[i,:]
points_t[cpt,2] = 3*sigma_array[s]
points = np.zeros((cpt,3))
for i in range(cpt):
points[i,:] = points_t[i,:]
return points
def draw(img,points):
'''功能:在原图上标注出角点并显示
输入:img-图像
points = [l,c,radius] l,c-角点的横纵坐标,radius-特征尺度值所对应的圆形区域的半径
'''
for i in range(points.shape[0]):
cv2.circle(img,(int(points[i,1]),int(points[i,0])),int(points[i,2]),(255,0,0),2)
# 原来这里要横纵坐标互换,害我改了半天才发现错误所在,哭了。
plt.figure(figsize=(7,7))
plt.imshow(img)
plt.axis('off')
plt.show
使用上述代码对检测图像角点,结果如下:
img = cv2.imread('wujiaoxing.jpg')
img1 = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
img2 = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
draw(img2,Har_Lap_corners_detect(img1))
Perfect!!!这次大作业有底了,逃出生天,哈哈哈哈哈~
下面换一个难一点的图试试看
img = cv2.imread('door.jpg')
img1 = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
img2 = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
draw(img2,Har_Lap_corners_detect(img1))
。。。。这画的什么鬼
此处是编程过程中的小发现,与主题无关可不看
* 破案了,其实在MATLAB中卷积计算conv2(A,G,‘same’)与图像滤波fitler2(G,A)的计算差不多,就是相差了一个G矩阵旋转180度。但是对于高斯核G而言,其旋转180度不变,所以在这种情况下,此两者等价。
* 矩阵的卷积运算和我想的差不多,矩阵对应元素的相乘之后求和。
* matlab中的filter2计算和CV2中的cv2.filter2D 差不多,除了CV2中会把小于0的点直接设置为0
设计实验,对图像进行缩放和旋转,比较Harris-Laplace和Harris角点检测算法的稳定性
实验数据来自图像数据集网站,Affine Covariant Features,选择6张存在旋转和放缩的图片,如下图红框所示:
可见,Harris-Laplace算法检测出的角点数量要比Harris算法少得多。
由上图可见,在同样的旋转和缩放条件下,Harris-Laplace检测算子的稳定性要高于Harris角点检测算子。