图像梯度计算的是图像变化的速度。对于图像的边缘部分,其灰度值变化较大,梯度值也较大;相反,对于图像中比较平滑的部分,其灰度值变化较小,相应的梯度值也较小。一般情况下,图像梯度计算的是图像的边缘信息。 严格来讲,图像梯度计算需要求导数,但是图像梯度一般通过计算像素值的差来得到梯度的近似值(近似导数值)。将上述运算关系进一步优化,可以得到更复杂的边缘信息。

1.1 原理介绍

        Sobel 算子是一种离散的微分算子,该算子结合了高斯平滑和微分求导运算。该算子利用局部差分寻找边缘,计算所得的是一个梯度的近似值。

        Sobel算子如图所示:

         假定有原始图像src,下面对Sobel算子的计算进行讨论。

        计算水平方向偏导数的近似值:将Sobel算子与原始图像src进行卷积计算,可以计算水平方向上的像素值变化情况。 例如,当Sobel算子的大小为3×3时,水平方向偏导数Gx的计算方式为:

         计算垂直方向偏导数的近似值同理,将Sobel算子换为垂直方向的算子。

1.2 函数语法

        在OpenCV内,使用函数cv2.Sobel()实现Sobel算子运算,其语法形式为:

        dst=cv2.Sobel(src,ddepth,dx,dy[,ksize[,scale[,delta[,borderType]]]])

        式中:

        ● dst代表目标图像。

        ● src代表原始图像。

        ● ddepth代表输出图像的深度。

        ● dx代表x方向上的求导阶数。

        ● dy代表y方向上的求导阶数。

        ● ksize代表Sobel核的大小。该值为-1时,则会使用Scharr算子进行运算。

        ● scale代表计算导数值时所采用的缩放因子,默认情况下该值是1,是没有缩放的。

        ● delta代表加在目标图像dst上的值,该值是可选的,默认为0。

        ● borderType代表边界样式。

        1.2.1 关于ddepth

        在函数cv2.Sobel()的语法中规定,可以将函数cv2.Sobel()内ddepth参数的值设置为-1,让处理结果与原始图像保持一致。但是,如果直接将参数ddepth的值设置为-1,在计算时得到的结果可能是错误的。 在实际操作中,计算梯度值可能会出现负数。如果处理的图像是8位图类型,则在 ddepth的参数值为-1时,意味着指定运算结果也是8位图类型,那么所有负数会自动截断为0,发生信息丢失。为了避免信息丢失,在计算时要先使用更高的数据类型 cv2.CV_64F,再通过取绝对值将其映射为cv2.CV_8U(8位图)类型。所以,通常要将函数cv2.Sobel()内参数ddepth的值设置为“cv2.CV_64F”。。

        在OpenCV中,使用函数cv2.convertScaleAbs()对参数取绝对值,该函数的语法格式为:         dst=cv2.convertScaleAbs(src[,alpha[,beta]])

        上式中:

        ● dst代表处理结果。

        ● src代表原始图像。

        ● alpha代表调节系数,该值是可选值,默认为1。

        ● beta代表调节亮度值,该值是默认值,默认为0。

        1.2.2 方向      

         在函数cv2.Sobel()中,参数dx表示x轴方向的求导阶数,参数dy表示y轴方向的求导阶数。参数dx和dy通常的值为0或者1,最大值为2。如果是0,表示在该方向上没有求导。 当然,参数dx和参数dy的值不能同时为0。 参数dx和参数dy可以有多种形式的组合,主要包含:

        ● 计算x方向边缘(梯度):dx=1,dy=0。

        ● 计算y方向边缘(梯度):dx=0,dy=1。

        ● 参数dx与参数dy的值均为1:dx=1,dy=1。

        ● 计算x方向和y方向的边缘叠加:通过组合方式实现。

        

1.3 程序实例

        

import cv2  as cv

def cv_show(name, img):
    cv.imshow(name, img)
    cv.waitKey(0)
    cv.destroyAllWindows()

img = cv.imread('D:\\qipan.jpg')
if img is None:
    print("Failed to read the image")

# 沿x方向的边缘检测
img1 = cv.Sobel(img, cv.CV_64F, 1, 0)
sobelx = cv.convertScaleAbs(img1)
# 展示未进行取绝对值的图片
cv_show('img1', img1)
cv_show('sobelx', sobelx)

# 沿y方向的边缘检测
img1 = cv.Sobel(img, cv.CV_64F, 0, 1)
sobely = cv.convertScaleAbs(img1)
cv_show('sobely', sobely)

# 沿x,y方向同时检测,效果不是特别好
img1 = cv.Sobel(img, cv.CV_64F, 1, 1)
sobelxy = cv.convertScaleAbs(img1)
cv_show('sobelxy', sobelxy)

# 一般在x,y方向分别检测,在进行与运算
# sobelxy1 = cv.bitwise_and(sobelx, sobely)
# cv_show('sobelxy1', sobelxy1)
# 这种方法也行

sobelxy1 = cv.addWeighted(sobelx, 0.5, sobely, 0.5, 0)
cv_show('sobelxy1', sobelxy1)

原图如下: 

 沿x方向未进行取绝对值操作:

 沿x方向的边缘检测:

         可以观察到,未进行取绝对值的图片有许多干扰,不利于图像分析。

沿y方向的边缘检测:

 沿x,y方向同时检测:

x,y方向叠加效果:

       在本例中,分别使用两种不同的方式获取边缘信息:

        ● 方式1:将参数dx和dy的值设为“dx=1,dy=1”,获取图像在两个方向的梯度。

        ● 方式2:分别使用“dx=1,dy=0”和“dx=0,dy=1”计算图像在水平方向和垂直方向的边缘信息,然后将二者相加,构成两个方向的边缘信息。

        可以看出,方式2的边缘信息提取的更好,一般我们是分别计算 x 方向的边缘、y 方向的边缘,接下来使用函 数cv2.addWeighted()对两个方向的边缘进行叠加来提取图像边缘信息。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐