python的opencv操作记录12——Canny算子使用

Canny算子

上一篇说到,我在一个小项目里需要在一幅图像中提取一根试管里的两种液体的截面。为了达到这个目的使用传统图像里的区域分割技术,实际上就是想把这个图像分成两类,然后再找到这个两个类的边界。
上一张最后提到,我是使用一种拟合的方法来做的边界的判断,后来突然想到,opencv里面提供了现成的方法:边缘检测的Canny算子,直接就可以提取图像的边界。

Canny算子在官网上有介绍:

  • 调用方式:edges = cv2.Canny(image, threshold1, threshold2, apertureSize, L2gradient)
  • 参数一:输入图像,为二值图像。
  • 参数二:阈值1,和阈值2一样,是用于控制边缘检测的度的,后面在详细过程说明中来描述。
  • 参数三:阈值2
  • 参数四:apertureSize,sobel算子的卷积大小。sobel算子用于计算图像的梯度,参考sobel算子的文章(https://blog.csdn.net/pcgamer/article/details/127942102?spm=1001.2014.3001.5502)
  • 参数五:L2gradient,是否使用二级梯度计算。实际上就是使用Laplacian算子进行二阶梯度计算,同样可以参考上面的那片文章。
  • 返回值就是一个只有边缘信息的二值图像。

Canny算子的一般介绍中,主要提到了一下几个步骤:

  • 噪声去除
  • 计算梯度
  • 非极大值抑制
  • 滞后阈值

非极大值抑制

噪声去除这个步骤一般是采用高斯模糊在做的,这个在滤波的那一篇中已经提到过,这里就不多说了,可以参考:
https://blog.csdn.net/pcgamer/article/details/124989015?spm=1001.2014.3001.5502

计算梯度之前也提过了,这里也不多说。
所以从非极大值抑制这里继续。

非极大值抑制,用人话说就是只留下最大值。那么,这里有几个问题:

  • 为什么留下最大值?
  • 留下什么最大值?就是这个最大值怎么定义?
  • 怎么留下最大值?

首先回答为什么的问题,在图像处理中,一般来说,边缘就是像素值变化最大的地方,这个很容易理解。
所以第二个问题也很简单,这里的最大值就是上文提到的梯度最大。而且是在梯度方向上梯度最大。
在写清晰度的那片文章中提到了梯度方向的计算方式,其实可以理解成当前边缘方向的法线放下那个,有点绕吧,用几个图来说明一下:
首先,梯度方向定义为 a r c t a n ( g x g y ) arctan(\frac{g_x}{g_y}) arctan(gygx),也就是
在这里插入图片描述

放大到一整张图中:
在这里插入图片描述

黑线指向的方向就是梯度的方向。

那么上面说到非极大值抑制,就是要确定某一个点在这条梯度方向是是附近(局部)的最大值。一般来说,这个附近就是旁边的一个像素值。

非极大值抑制中的插值

虽然就是比较这个梯度方向上的三个像素点,但是这不是能直接比较的。

  • 首先,上面的梯度方向不是固定的,只有当这个梯度方向恰好是45度的整数倍时,才恰好对应到一个像素点:
    在这里插入图片描述

    • 可以从图中看到,如果是梯度方向是45,225的时候,中心像素点就是和对角线上的两个像素点比较(右上和左下)
    • 如果是梯度方向是135,315的时候,中心像素点就是和对角线上的两个像素点比较(左上和右下)
    • 如果是0,180,就是和左右两个像素点做比较
    • 如果是90和270度,就是和上下两个像素点做比较。
    • 上面的几种情况都是比较巧合的情况,但是如果不是这些角度呢?
  • 如果不是这些角度,就需要进行插值了,见下图:
    在这里插入图片描述

    比如上面的dTemp1这个点,很明显不是一个实际存在的像素点,这个像素点可以被称作一个亚像素(sub pixel),这个点的像素值可以根据旁边的两个实际像素点来插值计算,在canny算子中,插值方法就是普通的线性插值:
    这个亚像素的dTemp1的像素值就是:
    d T e m p 1 = p 1 + L d T e m p 1 − p 1 p 2 − p 1 dTemp1 = p1 + \frac{L_{dTemp1} - p1}{p2-p1} dTemp1=p1+p2p1LdTemp1p1
    上面的 L d T e m p 1 L_{dTemp1} LdTemp1可以根据梯度方向的 g x g y \frac{g_x}{g_y} gygx来计算出来

    同样,下面的dTemp2也可以通过插值方法计算出来。

  • 插值计算完成后,就可以完成非极大值抑制的比较计算了。如果中心像素是最大的,那么就保留,不是极大值,则赋值为0。

  • 以上面的逻辑完成整张图像的计算,就完成了非极大值抑制这个过程的计算。

滞后阈值

完成非极大值抑制后,边缘已经精细了很多了,但是还不保证所有留下的像素点都是边缘,所以最后一步是通过阈值来控制这些留下的梯度值(从非极大值抑制出来的数据已经不是像素值,而是梯度值矩阵),单独通过一个阈值来控制过于简单粗暴,所以Canny算子用了两个。。。。

  • 如果高于T1,也就是两个阈值中较高的阈值,则保留。
  • 如果低于T2,也就是两个阈值中较低的阈值,则丢弃。
  • 中间的怎么办呢?从所有的高于T1的梯度值出发,如果能连接的上的中间值,则保留,否则就丢弃。
  • 一般来说T1 = 2T2

完成上面的滞后阈值计算后,就完成了Canny算子的边缘检测(当然最后是输出像素值的二值图像)

实际应用

直接使用Canny算子

不管三七二十一,我把之前的图转换成灰度图后,直接调用Canny算子:

    img = cv2.imread("./images/tubeImg.jpeg")

    grey = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    edge = cv2.Canny(grey, 120, 54)

    cv2.imshow("origin", grey)
    cv2.imshow("result", edge)

效果非常的糟糕:
在这里插入图片描述

使用膨胀

因为Canny算子中的高斯模糊针对的是微小的高斯噪声,这个图像中的噪声都是大块的噪声,所以我就想着用膨胀函数试一下:

img = cv2.imread("./images/tubeImg.jpeg")

    grey = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 做一把膨胀
    kernel_e = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
    bin_clo = cv2.dilate(grey, kernel_e, iterations=10)

    edge = cv2.Canny(bin_clo, 120, 54)

    cv2.imshow("origin", grey)
    cv2.imshow("result", edge)

效果好了一点:
在这里插入图片描述

先阈值分割

但是还是去的不干净,而且最上面的分界线没有弄完成,突然想到上一次用大津法的阈值分割基本已经完成了前景和背景的分割,再加上一次阈值分割,用膨胀再清除一把,最后再做边缘检测:

img = cv2.imread("./images/tubeImg.jpeg")

    grey = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 如果大于阈值,赋值为255,小于阈值,赋值为0
    # ret, binary = cv2.threshold(grey, 60, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    #
    # 做一把腐蚀
    kernel_e = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
    bin_clo = cv2.dilate(grey, kernel_e, iterations=10)

    edge = cv2.Canny(bin_clo, 120, 54)

    cv2.imshow("origin", grey)
    cv2.imshow("result", edge)

效果妥妥的:
在这里插入图片描述

大功告成!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

新兴AI民工

码字不易,各位看客随意

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值