Harris角点检测

1. 关于角点

图像中的特殊位置,是很常用的一类特征,点的局部特征也可以叫做“关键特征点”(keypoint feature),或“兴趣点”(interest point),或“角点”(conrner)。

关于角点的具体描述可以有几种:

一阶导数(即灰度的梯度)的局部最大所对应的像素点;

两条及两条以上边缘的交点;

图像中梯度值和梯度方向的变化速率都很高的点;

角点处的一阶导数最大,二阶导数为零,指示物体边缘变化不连续的方向。

直接看图的话会相对清晰一些

2. 预备知识

(1)高数:二元函数的泰勒展开

(2)线性代数:相似对角化、向量空间

3. 原理:Haris角点检测

角点检测方法不止一种,这里介绍Haris角点检测。

(1)变化

角点可以根据某个大小的框线中的图像在框线移动时灰度值的变化来判断,如果是角点那就应该在框线的x轴和y轴坐标变化时影响都比较大,如果是边界点则应该沿着某一个轴变化较大,而图像内部的点在坐标变化时不会发生明显变化,如下方绿色框线区域发生变化时对应的三种情况:

 (2)灰度变化的数学表达

为了简化计算,将彩色图直接转化为灰度图:检测效果一致,但是只需要考虑灰度变化即可。

 其中:

  • dx 和 dy:窗口偏移量
  • u 和 v :窗口内像素坐标
  • W(x,y) :窗口函数,内含权重信息,常用的有权重为1和呈二元高斯正太分布的权重,意在突出像素值变化明显的程度
  • 函数 I :像素密度函数,类比像素值(个人觉得这里直接当作取(u,v)处像素值并无不妥)

关于W(x,y):只是一个加权函数,如果想要中心点的权重更大一些,就使用高斯加权

关于窗口:就是前面讲的框线区域,如一个3*3的矩阵,这个矩阵就记为窗口,其中9个点的灰度值相关的函数就是I(u,v)而W(u,v)则是(u,v)点应有的权重,

如高斯加权长这样:

 (3)化简

我们所希望看到的化简结果是能够清楚地看到C与dx、dy关系的一个形式。


  •  泰勒展开

(dx,dy)在图像上是以像素为单位进行变化的,但是在数学计算中完全可以是一个小数,当dx、dy趋近于0的时候考虑使用二元函数的泰勒展开(由于几乎是恒等变形,所以可以直接用)。

当采用一阶展开的时候就可以化简掉I(u,v)了:


  •   相似对角化

上述的M'显然是一个对称阵,如果对其进行对角化,则会得到一个分别和dx、dy有关的式子,而dx、dy前的系数就是M'矩阵的特征向量λ1、λ2,也表示着dx、dy二者变化时对C影响的权重,即当λ1>>λ2时表明图像窗口在x轴上的变化比较明显,这里很可能是关于y轴的一条边,λ1>>λ2类似。


  •  判断角点

而不容易判断的就是什么时候二者都“比较大”从而对应角点,以及角点周围的点也是“权重较高的点”,如何找到正确的那个?

 【法一】

直接“规定”一个基于实验的来的“合适”的大小,作为λ1、λ2的参考,并通过NMS非极大值抑制来决定真正的角点:非极大值抑制(Non-Maximum Suppression,NMS) - 康行天下 - 博客园 (cnblogs.com)

 【法二】

opencv给出了一个计算公式来确定角点

,其中det为行列式,tr()为矩阵的迹,k是一个常数,可以根据需要自行调配

4. 程序:python

 opencv提供了相应的函数:cv2.cornerHarris()

  • image:数据类型为float32的输入图像
  • blockSize:窗口大小,就是上述的框线大小,如3*3矩阵的blockSize就是3
  • ksize:Sobel求导中使用的窗口大小
  • k:取值参数为[0, 0.04, 0.06],三个推荐值,别的也可以,不过效果不好

其中,Sobel是指Sobel算子,用于求导,即求解Ix、Iy:(49条消息) sobel算子原理与实现_写代码的胡歌的博客-CSDN博客_sobel算子原理

个人理解:Ix、Iy并非真正对x、y求偏导,而是通过一些运算过程(函数映射、算子)来得到近似或者等价的值。

# Harris角点检测:移动dx,dy后检查灰度变化
import cv2
import numpy

image = cv2.imread("./pics/blocks.jpg")
image_gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cValue = cv2.cornerHarris(image_gray, 2, 3, 0.04)# 计算图中每个点的C值

cv2.imshow("image",image)
cv2.moveWindow("image",400,200)

minVal = 0.005*cValue.max()
_,cValueFilter = cv2.threshold(cValue,minVal,255,cv2.THRESH_BINARY)

cv2.imshow("cValueFilter",cValueFilter)
cv2.moveWindow("cValueFilter",400+image.shape[1],200)

for row in range(cValueFilter.shape[0]):
    for column in range(cValueFilter.shape[1]):
        if(cValueFilter[row][column]==255):
            cv2.circle(image, (column, row), 3, (0,0,255), 2)
cv2.imshow("cornerHarris",image)
cv2.moveWindow("cornerHarris",400+image.shape[1]+image.shape[1],200)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

posted @ 2022-01-20 16:58  YIYUYI  阅读(230)  评论(0编辑  收藏  举报