1,图像梯度的概念
梯度简单来说就是求导,在图像上表现出来的就是提取图像的边缘(无论是横向的、纵向的、斜方向的等等),所需要的是一个核模板。模板的不同结果也不同。所以能够看到,全部的这些个算子函数,归结究竟都能够用函数cv2.filter2D()来表示,不同的方法给予不同的核模板,然后演化为不同的算子。
在微积分中,一维函数的一阶微分的基本定义是这样的:
而图像是一个二维函数f(x,y),其微分当然就是偏微分。因此有:
因为图像是一个离散的二维函数,ϵ不能无限小,我们的图像是按照像素来离散的,最小的ϵ就是1像素。因此,上面的图像微分又变成了如下的形式(ϵ=1):
这分别是图像在(x, y)点处x方向和y方向上的梯度,从上面的表达式可以看出来,图像的梯度相当于2个相邻像素之间的差值。x方向和y方向上的梯度可以用如下式子表示在一起:
这里又是平方,又是开方的,计算量比较大,于是一般用绝对值来近似平方和平方根的操作,来降低计算量:
2,经典的图像梯度算法
经典的图像梯度算法考虑图像的每个像素的某个邻域内的灰度变化,利用边缘临近的一阶或二阶导数变化规律,对原始图像中像素某个邻域设置梯度算子,通常我们用小区域模板进行卷积来计算,OpenCV提供了三种不同的梯度滤波器,或者说高通滤波器:Sobel,Scharr和Lapacian。Sobel,Scharr其实就是求一阶或二阶导。Scharr是对Sobel的部分优化。Laplacian是求二阶导。 假如对一幅数字图像,求出M之后与原来每个像素点对应值相加,则图像边缘将被大大加强,轮廓更加明显,是一个很典型的sharp filter的效果。
关于Sobel算子与Scharr算子:
关于拉普拉斯(Laplacian)算子:
源代码示例:
import cv2 as cv
import numpy as np
def lapalian_demo(image):
#dst = cv.Laplacian(image, cv.CV_32F)#默认为4领域拉普拉斯算子
#lpls = cv.convertScaleAbs(dst)
kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]])#自定义的8领域拉普拉斯算子
dst = cv.filter2D(image, cv.CV_32F, kernel=kernel)
lpls = cv.convertScaleAbs(dst)
cv.imshow("lapalian_demo", lpls)
def sobel_demo(image):
#grad_x = cv.Sobel(image, cv.CV_32F, 1, 0)
#grad_y = cv.Sobel(image, cv.CV_32F, 0, 1)
grad_x = cv.Scharr(image, cv.CV_32F, 1, 0)#cv.Sobel的增强版,对噪声比较敏感
grad_y = cv.Scharr(image, cv.CV_32F, 0, 1)
gradx = cv.convertScaleAbs(grad_x) #求绝对值并转化为8位的图像上
grady = cv.convertScaleAbs(grad_y)
cv.imshow("gradient-x", gradx)
cv.imshow("gradient-y", grady)
gradxy = cv.addWeighted(gradx, 0.5, grady, 0.5, 0)
cv.imshow("gradient", gradxy)
src = cv.imread("F:/images/lena.png")
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", src)
sobel_demo(src)
lapalian_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()
运行结果:
关于canny边缘检測算子,细究的话比较的复杂,将在下一篇文章中具体介绍。