特征点就是角点或兴趣点,它具有旋转不变形,光照不变性,视角不变性等特性,可以在目标匹配、目标跟踪、三维重建等应用中使用
梯度可以看作是变量在某方向上变换大小的参考值。因为图像中的数值是离散的,所以不能用微分,而是得用一阶差分来表示梯度。
百度百科——一阶差分
通俗的理解,角点就是除边缘外多线的交点。
角点是多种形态的,如下
我们用一个模板进行全局的小幅度滑动:
1——无梯度。在这种情况下滑动,由于该区域平滑,均值不变,梯度为0
2——单个梯度值。在这种情况下滑动,由于有黑色的边缘,也就是有突变或灰度值变换较大的区域,水平方向的滑动会造成均值不同,梯度也就不为0。但是朝着垂直方向滑动,会跟情况1相同,均值没有变化。如果朝斜的方向移动,这时用行列式的思想,将居中的黑色数值所在的列移到了靠边的位置,差分值不受影响。
3——多个梯度值。在这种情况下滑动,因为水平方向和垂直方向都有突变区域,在对应的方向上滑动,就会产生梯度值。多方向有梯度值就说明有角点出现。
在第三种情况下,就会产生角点。图像四个边缘角不算角点。
作一下说明:
1.图像灰度是指图像窗口内的灰度值,窗口一般是3邻域,5邻域大小的
2.窗口函数的作用是通过赋予邻域内不同像素值的权重来调整对识别角点的贡献。如果窗口的中心是角点,就把中心的权重加大,周围的权重调小,如此调整后,均值就会加大,梯度相应的也就变大了。
其中I(x)表示X轴方向的梯度方向
提取出u,v,由于移动步幅小,可得到一个近似值
其中M是:
这里将M做了转换,将实对称矩阵用特征值λ以及特征向量R来表示。
疑惑点:
本人根据 线性代数中的实对称矩阵对角化公式,认为框出来的位置需要交换(学得死板)
用特征值及其对应的特征向量矩阵表示实对称矩阵。例子如下:
后来有人指导说,只要R可逆即可,框出来的地方无需调换。也可以从另外一个角度理解。R代表的是把整矢量空间旋转,旋转到以特征向量为基的空间,中间的矩阵是个由特征值组成的,只负责扩大或缩小,不改变方向,最后乘R**-1,再转回原来的矢量空间。
公式:
由此可知特征值特别重要,关系到行列式的值和矩阵的迹也决定了特征向量。
这里依旧会用到1.3中所讲,利用梯度判别。
(1)平面:窗口函数在X轴和Y轴的方向上的变换小——梯度I(x)和I(y)都很小,那么特征值λ1和λ2也很小。根据公式R的值也很小
(2)边缘:R值为负,窗口函数只在X轴或Y轴一个方向上的变换较为剧烈——I(x)和I(y)会出现一大一小的情况,那么特征值λ1和λ2,要么前者远大于后者,要么后者远大于前者
(3)角点:R值为正,较大,在X轴和Y轴两个方向上的变换都较为剧烈。I(x)和I(y)值都很大,那么特征值λ1和λ2也很大。
cv2.cornerHarris(src, blockSize, ksize, k[, dst[, borderType]])可以用来进行角点检测。参数如下:
import cv2 as cv
from matplotlib import pyplot as plt
import numpy as np
# detector parameters
block_size = 3
sobel_size = 3
k = 0.04
image = cv.imread('C:/Users/HP/Desktop/CLOES.jpg')
image = cv.resize(image,dsize=(800,600))
image_ori=image.copy()
image_ori= cv.cvtColor(image_ori, cv.COLOR_BGR2RGB)
gray_img = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
# modify the data type setting to 32-bit floating point
gray_img = np.float32(gray_img)
# detect the corners with appropriate values as input parameters
corners_img = cv.cornerHarris(gray_img, block_size, sobel_size, k)
# result is dilated for marking the corners, not necessary
kernel = cv.getStructuringElement(cv.MARKER_CROSS, (3, 3))
dst = cv.dilate(corners_img, kernel)
# Threshold for an optimal value, marking the corners in Green
# image[corners_img>0.01*corners_img.max()] = [0,0,255]
for r in range(height):
for c in range(width):
pix = dst[r, c]
if pix > 0.06 * dst.max():
cv.circle(image, (c, r), 3, (0, 250,0 ), 0)
image = cv.cvtColor(image, cv.COLOR_BGR2RGB)
plt.figure()
plt.subplot(2, 2, 1)
plt.imshow(image_ori)
plt.subplot(2, 2, 2)
plt.imshow(gray_img)
plt.subplot(2, 2, 3)
plt.imshow(corners_img)
plt.subplot(2, 2, 4)
plt.imshow(image)
plt.show()
图示检测结果:
左上是原图,右上是原图转换后的灰度图(读取图像时按BGR来的,而非RGB)
左下是Harris算法的结果图,右下是经过膨胀加circle后的结果图
参考资料