第四章 OpenCV教程:图像梯度与边缘检测的Python实现

目录

一.Sobel算子

二.Scharr算子与laplacian算子

三.Canny边缘检测

1.高斯滤波器

2.梯度和方向

3.非极大值抑制

4.双阈值检测


此章节主要讲解图像梯度计算方法和边缘检测算法,分别主要是:sobel算子、scharr与lapkacian算子、canny边缘检测流程。

一.Sobel算子

A代表选取的卷积核。Gx,Gy分别代表水平梯度和垂直梯度

注:

图像的梯度算法就是卷积操作。公式如下:

这里与矩阵乘法有差异的地方在于求垂直梯度。

公式中的K代表卷积核

以下是水平梯度运算的方法。

Gx= 1*p3-1*p1+2*p6-2*p4+1*p9-1*p7

这个运算通过右边减去左边的区域,求出它的像素点的差异值。通过这个差异值,将求出的这个差异值视作水平方向的梯度

Gy = 1*p7-1*p1+2*p8-2*p2+1*p9-1*p3

G = Gx+Gy

垂直梯度的计算是从下减上的一个操作流程。以上是Sobel算子的一个运算规则方法。

Sobel算子的语法格式:

cv2.Sobel(src,ddepth,dx,dy,ksize)

  • src:当前图像
  • ddepth:图像的深度
  • dx和dy分别表示水平和竖直方向
  • ksize是Sobel算子的大小(指定核,一般是3×3或5×5)
  • 注:

    一般深度计算默认是-1,表示我输出的深度和输入的深度是一模一样的。

    Sobel算子的ddepth 的参数共有五种类型分别为:cv2.CV_8U,cv2.CV_16S,cv2.CV_32F,cv2.CV_64F,cv2.CV_16U这五种类型的图像,分别表示8位无符号整数、16位有符号整数、32位浮点数、64位浮点数和16位无符号整数。

    求水平梯度代码演示如下:

    原图如下

    代码如下:

    import cv2
    import numpy as np
    def cv_show(img,name):
        cv2.imshow(name,img)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    def cv_Xshow(image):
        # Sobel算子第二个参数一共有5种,分别是:cv2.CV_8U,cv2.CV_16S,cv2.CV_32F,cv2.CV_64F,cv2.CV_16U
    # 这五种类型的图像,分别表示8位无符号整数、16位有符号整数、32位浮点数、64位浮点数和16位无符号整数。
        sobelx = cv2.Sobel(image,cv2.CV_64F,1,0,ksize=3)
        cv_show(sobelx,'sobelx')
    if __name__ == '__main__':
        image = cv2.imread('10.png')
        cv_Xshow(image)

    运行代码后,求水平梯度图像如下:

    注:

    原本应该右边应该也出现白边但是这个图像并无这条边,原因是:白到黑是整数,黑到白就是负数,所有的负数会被截断成0,所以要取绝对值。

    调整后的代码如下:

    注:

    cv2.convertScaleAbs的作用是把图像的像素值转换成绝对值,并返回一个图像

    调整后代码的图像呈现如下:

    import cv2
    import numpy as np
    def cv_show(img,name):
        cv2.imshow(name,img)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    def cv_Xshow(image):
        # Sobel算子第二个参数一共有5种,分别是:cv2.CV_8U,cv2.CV_16S,cv2.CV_32F,cv2.CV_64F,cv2.CV_16U
    # 这五种类型的图像,分别表示8位无符号整数、16位有符号整数、32位浮点数、64位浮点数和16位无符号整数。
        sobelx = cv2.Sobel(image,cv2.CV_64F,1,0,ksize=3)
        # cv2.convertScaleAbs的作用是把图像的像素值转换成绝对值,并返回一个图像
        sobelx = cv2.convertScaleAbs(sobelx)
        cv_show(sobelx,'sobelx')
    if __name__ == '__main__':
        image = cv2.imread('10.png')
        cv_Xshow(image)

    求垂直梯度代码演示如下:

    原图如下:

    代码如下:

    import cv2
    import numpy as np
    def cv_show(img,name):
        cv2.imshow(name,img)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    def cv_Yshow(image):
        sobelY = cv2.Sobel(image,cv2.CV_64F,0,1,ksize=3)
        sobelY = cv2.convertScaleAbs(sobelY)
        cv_show(sobelY,'sobelY')
    if __name__ == '__main__':
        image = cv2.imread('10.png')
        cv_Yshow(image)

    代码过后的图像如下:

    完整图像梯度的代码如下:

    import cv2
    import numpy as np
    def cv_show(img,name):
        cv2.imshow(name,img)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    def cv_Xshow(image):
        # Sobel算子第二个参数一共有5种,分别是:cv2.CV_8U,cv2.CV_16S,cv2.CV_32F,cv2.CV_64F,cv2.CV_16U
        # 这五种类型的图像,分别表示8位无符号整数、16位有符号整数、32位浮点数、64位浮点数和16位无符号整数。
        sobelx = cv2.Sobel(image,cv2.CV_64F,1,0,ksize=3)
       # cv2.convertScaleAbs的作用是把图像的像素值转换成绝对值,并返回一个图像
        sobelx = cv2.convertScaleAbs(sobelx)
        return sobelx
    def cv_Yshow(image):
        sobelY = cv2.Sobel(image,cv2.CV_64F,0,1,ksize=3)
        sobelY = cv2.convertScaleAbs(sobelY)
        return sobelY
    def cv_XYshow(image,sobelX,sobelY):
        sobelXY = cv2.addWeighted(sobelX,0.5,sobelY,0.5,0)
        cv_show(sobelXY,'sobelXY')
        return None
    if __name__ == '__main__':
        image = cv2.imread('10.png')
        cv_XYshow(image,cv_Xshow(image),cv_Yshow(image))
    
    
    
    

    运行代码后的图像呈现如下:

    注:

    不建议直接进行计算,建议使用cv2.addWeighted进行梯度的计算。

    直接计算梯度的代码演示如下:

    import cv2
    import numpy as np
    src = cv2.imread('10.png')
    sobelX = cv2.Sobel(src,cv2.CV_64F,1,0)
    sobelY = cv2.Sobel(src,cv2.CV_64F,0,1)
    sobelXY = cv2.Sobel(src,cv2.CV_64F,1,1)
    cv2.imshow('sobelXY',sobelXY)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    注:

    从此可以看出来如果直接计算图像的呈现并不好。因此最好使用cv2.addWeighted进行水平和垂直的相加进行的一个比例分布。

    综合运行梯度计算的代码如下:

    原图如下:

    代码如下:

    import cv2
    import numpy as np
    def cv_show(img,name):
        cv2.imshow(name,img)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    img = cv2.imread('2.jpg')
    sobelX = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
    sobelX = cv2.convertScaleAbs(sobelX)
    sobelY = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
    sobelY = cv2.convertScaleAbs(sobelY)
    sobelXY = cv2.addWeighted(sobelX,0.5,sobelY,0.5,0)
    cv_show(sobelXY,'sobelXY')

    代码运行后的图像呈现如下:

    注:

    这种计算梯度的方式可以取出一个图像的基本框架。

    二.Scharr算子与laplacian算子

    Scharr算子与Sobel本质没什么太大区别,基本原理依旧如同Sobel算子一样。唯一区别是Scharr算子核的数据要比Sobel算子的处理的数据更敏感(较大)。

    接下来讲解讲解laplacian算子。

    从图可以看出laplacian算子与前面二种算子有很大区别。

    laplacian算子的计算方法如下:

    laplacian算子计算方式是G = 1*p2+ 1*p4+1*p6+1*p8-4*p5

    相当于拿中间的P6与周围边缘点进行比较,由于周围边缘点是4个都是正的,所以是中间的点为-4,这就是它与其他算子核不同的原因。

    三种算子的程序如下:

    原图:

    代码如下:

    import cv2
    import numpy as np
    # 不同算子的差异
    img = cv2.imread("5.jpg")
    gray = cv2.cvtColor(img,cv2.IMREAD_GRAYSCALE)
    # sobel算子
    sobelx = cv2.Sobel(gray,cv2.CV_64F,1,0,ksize=3)
    sobely = cv2.Sobel(gray,cv2.CV_64F,0,1,ksize=3)
    sobelx = cv2.convertScaleAbs(sobelx)
    sobely = cv2.convertScaleAbs(sobely)
    sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
    # Scharr算子
    scharrx = cv2.Scharr(gray,cv2.CV_64F,1,0)
    scharry = cv2.Scharr(gray,cv2.CV_64F,0,1)
    scharrx = cv2.convertScaleAbs(scharrx)
    scharry = cv2.convertScaleAbs(scharry)
    scharrxy = cv2.addWeighted(scharrx,0.5,scharry,0.5,0)
    #  laplacian算子
    laplacian = cv2.Laplacian(gray,cv2.CV_64F)
    laplacian = cv2.convertScaleAbs(laplacian)
    
    res = np.hstack((sobelxy,scharrxy,laplacian))
    cv2.imshow("res",res)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    运行代码后如下:

    三.Canny边缘检测

    以下是Canny边缘检测算法的步骤如下:

    1)使用高斯滤波器,以平滑图像,滤除噪声。

    2)计算图像中每个像素点的梯度强度和方向。

    3)应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。

    4)应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。

    5)通过抑制孤立的弱边缘最终完成边缘检测。

    1.高斯滤波器

    2.梯度和方向

    运用了Sobel算子

    3.非极大值抑制

    非极大值抑制有两种方法;

    方法一:

    方法二:为了简化计算,由于一个像素周围有八个像素,把一个像素的梯度方向离散为八个方向,这样就只需计算前后即可,不用插值了。

    4.双阈值检测

    Canny边缘检测算法演示代码如下:

    import cv2
    import numpy as np
    
    img = cv2.imread('9.jpg',cv2.IMREAD_GRAYSCALE)
    # 高斯滤波,降噪
    img = cv2.GaussianBlur(img,(5,5),0)
    # 边缘检测
    # 第一个参数是图像,第二个参数是低阈值,第三个参数是高阈值
    v1 = cv2.Canny(img,80,160)
    v2 = cv2.Canny(img,50,200)
    
    res = np.hstack((v1,v2))
    cv2.imshow('res',res)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    注:

    从此可以看出来Canny边缘检测,你设置的阈值越大检测的东西检测边界的东西越少,设置阈值越小检测边界的东西越丰富。

    Canny语法格式:cv2.Canny(图像,低阈值,高阈值)

    作者:从晓不会计算机

    物联沃分享整理
    物联沃-IOTWORD物联网 » 第四章 OpenCV教程:图像梯度与边缘检测的Python实现

    发表回复