在OpenCV里Harris角检测

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/caimouse/article/details/102640082

前面学习了很多关于图像的内容,以及变换,那么你有没有问过自己这样一个问题,什么才是图像的特征?如果我们把一个图像进行分割成一小块一小块的,然后打乱之后再想拼到一起,会寻找什么的规律呢?一般情况之下是找对应的条纹,边缘线等,其实最容易确定一般是有十字交叉的线条,因为这样交叉线条组成了角,而角的两条边具有稳定性,它不能随便旋转,如果旋转了就与别的对应不上了。因此在一幅图像里,最具有代表性的特征就是角。

我们怎样找到角?或者说我们怎样找到角点?我们也已经用一种直观的方式做了回答,比如在图像中找一些区域,无论你想那个方向移动这些区域变化都很大。我们会用计算机语言来实现这个想法,所以找到图像特征的过程被称为特征检测(Feature Detection)。我们找到图像的一些特征,然后我们可以在其他图片中找到类似的特征,这是怎样实现的呢?我们获取特征区域,用我们的语言描述它们,比如“图的上方是天空,下面有一幢大楼,大楼上有好多玻璃”,然后我们可以在其他图片中寻找相似特征的区域。上面我们对图像的特征进行了描述,类似的,计算机也可以描述一些特征然后去其他图片上找相似的地方。这样一个描述被称作 特征描述Feature Description)。一旦你有一种特征并有它的描述,你就可以在其他图片中找出相似甚至相同的部分。(更多特征参考:https://www.jianshu.com/p/2cdf0eeeeef3

 

在1988年,Chris Harris & Mike Stephens发表了一篇论文《A Combined Corner and Edge Detector》,论文描述怎么样找角的原理,现在叫做Harris角检测(Harris Corner Detector)。在论文里用一个最基本的数学公式来表示:

在公式里,w(x,y)表示一个窗口函数,像前面的平滑窗口一样,可以使用矩形窗口或者高斯分布窗口,然后来处理每个像素的权重。寻找角点的特征就变换为找E(u,v)的值最大化问题,要想让它最大化,就相当于寻找第二项最大化,即是位移强度相减的最大化。要想严格地求解此函数有难度,那么退一步海阔天空,去求一个差不多的解,人与机器的区别,就在这里会变通,有时候还不那么严格要求,所以说人类社会是一个概率社会。既然遇到了问题就要解决,此时此地,感觉到数学知识的缺少,真应了“书到用时方恨少”。回忆一下多年大学的数学知识,发现有一个叫做泰勒定理,描述如下:

从这里可以看到泰勒定理可以做近似计算,因此又叫做泰勒展开式,比如多元的展开式就可以定义如下:

由于图像是二元函数,可以使用二元的泰勒展开式:

可见只取一阶导数的展开,就可以把E(u,v)公式近似地表示如下:

 

其中M矩阵如下:

在这里可以看到Ix是x轴方向的导数,Iy是y轴方向的导数,如果仔细看我前面的文章,就会发现它不就是cv.Sobel()函数所做的事情吗?这样图像求导问题就解决了。接着下来,就是求E的最大值,变换求矩阵M的问题了,可以采用下面等式来解决:

上面公式里的量定义:

所以这些特征值决定了这个区域是角点,还是边缘,还是光滑区:

当|R|比较小时,也就是lambda 1和lambda 2比较小,区域是光滑区

当R<0时, 也就是lambda 1远大于lambda 2或反之亦然,区域是边缘

当R比较大时,也就是lambda 1和lambda2都大且相似,区域是角点

可以把这三个条件用下图来表示:

从上图可以看到参数变换时,表现出来图像的特征变换。还有一个图可以表示:

从上图可以看到,当在不同的图形下像素的变化。

有了上面的算法,就可以采用python代码来实现它:

#python 3.7.4,opencv4.1
#蔡军生 https://blog.csdn.net/caimouse/article/details/51749579
#
import numpy as np
import cv2
from matplotlib import pyplot as plt
from scipy import signal as sig

def gradient_x(imggray):
    ##Sobel算子求导
    kernel_x = np.array([[-1, 0, 1],[-2, 0, 2],[-1, 0, 1]],np.float)
    return sig.convolve2d(imggray, kernel_x, mode='same')
def gradient_y(imggray):
    kernel_y = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]],np.float)
    return sig.convolve2d(imggray, kernel_y, mode='same')

#读取文件
img = cv2.imread('corner1.png',0)

newImg = img.copy() #拷贝文件
color_img = cv2.cvtColor(newImg, cv2.COLOR_GRAY2RGB) #转换为彩色图片,以便标记红色

window_size = 6 #窗口大小
k = 0.04 #K常数
thresh = 0

I_x = gradient_x(img)#
I_y = gradient_y(img)#

Ixx = I_x**2
Ixy = I_y*I_x
Iyy = I_y**2

offset = window_size//2
height = img.shape[0]
width = img.shape[1]
#
for y in range(offset, height-offset):
    for x in range(offset, width-offset):
        Sxx = np.sum(Ixx[y-offset:y+1+offset, x-offset:x+1+offset])
        Syy = np.sum(Iyy[y-offset:y+1+offset, x-offset:x+1+offset])
        Sxy = np.sum(Ixy[y-offset:y+1+offset, x-offset:x+1+offset])

        #查找角点
        det = (Sxx * Syy) - (Sxy**2)
        trace = Sxx + Syy
        r = det - k*(trace**2)

        if r > thresh:
                #print(x, y, r)
                color_img.itemset((y, x, 0), 0)
                color_img.itemset((y, x, 1), 0)
                color_img.itemset((y, x, 2), 255)
#显示图片
cv2.imshow('img',img)
cv2.imshow('color_img',color_img)
           
cv2.waitKey(0)
cv2.destroyAllWindows()

结果输出如下:

输入图片

输出图片

https://blog.csdn.net/caimouse/article/details/51749579

猜你喜欢

转载自blog.csdn.net/caimouse/article/details/102640082
今日推荐