角点引入
如图所示,从图像的灰度级考虑,A,B为平面无论沿任何方向灰度级都不会发生过大的变化。C,D沿某一方向会发生巨大变化。E,F沿着任何方向都发生巨大变化。
结论
其中,,
当R》》0时,为角点,
当R0时,为边界,
当R《0时,平坦区。
推导
本质上是根据图像滑动前后灰度的变化进行判断,所以是一个减法:
其中,是窗口当前的坐标为当前的灰度值,为进行微小移动后的灰度值。是窗口的权重项。
基于泰勒展开,对图像在平移后进行一阶近似:
其中,是的偏导数
近似可得
二次函数可以理解为一个不在原点的椭圆它的等高线未定,设 且因为M是是对称阵,椭圆可以化简为,如图
所以这个椭圆=灰度值差值,它的某个方向的变化大小(形状)体现它当前位置的情况(平坦、边、脚),如图
所以推出判断标准:
其中,,
当R》》0时,为角点,
当R0时,为边界,
当R《0时,平坦区。
补充:非极大值抑制,NMS。有的边灰度值变化也大,但并不是角点。
代码实现
import cv2
img = cv2.imread('./data/test2.png')
print ('img.shape:',img.shape)
#转换为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#cv2.cornerHarris(img, blockSize, ksize, k)
# img:数据类型为float32的图像
# blockSize: 角点检测中指定区域的大小
# ksize: Sobel求导中使用的窗口大小,边缘检测的算子
# k :参数,一般取[0.04]
dst = cv2.cornerHarris(gray, 2, 3, 0.04)
print ('dst.shape:',dst.shape)
# dst.max是最大值,一定是角点
# 只要dst中满足0.01倍的最大值,就判定为角点,要求较高的也可以设置为其他倍数
img[dst>0.01*dst.max()]=[0,0,255]
cv2.imshow('dst',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.imwrite('./data/test_1_dst.jpg',img)