Python OpenCV Canny 边缘检测知识补充

举报
梦想橡皮擦 发表于 2021/09/23 13:33:57 2021/09/23
【摘要】 Python OpenCV 365 天学习计划,与橡皮擦一起进入图像领域吧。本篇博客是这个系列的第 47 篇。 学在前面在 Canny 边缘提取相关知识学习,图像处理第 32 篇博客 这篇博客中,我们已经对 Canny 边缘检测进行了基本的学习,今天这篇文章主要用于对其进行补充,当然知识难度不大,1 个小时就能学到。 Canny 边缘检测流程参照互联网大家发布最多的流程高斯模糊去噪;计算图像...

Python OpenCV 365 天学习计划,与橡皮擦一起进入图像领域吧。本篇博客是这个系列的第 47 篇。

学在前面

Canny 边缘提取相关知识学习,图像处理第 32 篇博客 这篇博客中,我们已经对 Canny 边缘检测进行了基本的学习,今天这篇文章主要用于对其进行补充,当然知识难度不大,1 个小时就能学到。

Canny 边缘检测流程

参照互联网大家发布最多的流程

  1. 高斯模糊去噪;
  2. 计算图像梯度幅值和方向,这里一般用的是 Sobel 算子;
  3. 非极大值抑制,简单理解就是“瘦边”;
  4. 双阈值检测,也叫做滞后阈值。

按照上面的步骤进行翻译,就能写出一个 Canny 朴实的案例了

import cv2 as cv
import numpy as np

src = cv.imread("./t7.jpg", 0)
# 高斯模糊
gaussian = cv.GaussianBlur(src, (3, 3), 0)
ret, thresh = cv.threshold(gaussian, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
# 计算图像梯度幅值和方向,这里一般用的是 Sobel 算子;
sobel_x = cv.Sobel(thresh, cv.CV_16SC1, 1, 0)
sobel_y = cv.Sobel(thresh, cv.CV_16SC1, 0, 1)
sobel_x = cv.convertScaleAbs(sobel_x)
sobel_y = cv.convertScaleAbs(sobel_y)
sobel_xy = cv.addWeighted(sobel_x, 0.5, sobel_y, 0.5, 0)

canny1 = cv.Canny(sobel_xy, 50, 150)

image = np.hstack((thresh, canny1))
cv.imshow('img', image)
cv.waitKey(0)
cv.destroyAllWindows()

代码中一些要说明的地方,双阈值检测采用的是高低阈值检测,高阈值使用 maxVal、低阈值使用 minVal,有以下结论。

  1. 梯度值 > maxVal ,边界保留;
  2. minVal < 梯度值 < maxVal 时,如果像素梯度值在边界上,保留,否则,舍弃;
  3. 梯度值 < minVal,舍弃。

以上三点的大白话,非常容易记忆,如果你想要更多的边界,调小 minVal 值,否则调高它。

在实操的时候还尝试出一个问题,如果在进行边缘提取之前进行了二值化操作,那得到的图像受上面参数影响较小,例如下述代码,双阈值设置不同值,得到的结果基本无差异。

import cv2 as cv
import numpy as np

src = cv.imread("./t77.jpg", 0)
# 高斯模糊
gaussian = cv.GaussianBlur(src, (3, 3), 0)
ret, thresh = cv.threshold(gaussian, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
# 计算图像梯度幅值和方向,这里一般用的是 Sobel 算子;
sobel_x = cv.Sobel(thresh, cv.CV_16SC1, 1, 0)
sobel_y = cv.Sobel(thresh, cv.CV_16SC1, 0, 1)
sobel_x = cv.convertScaleAbs(sobel_x)
sobel_y = cv.convertScaleAbs(sobel_y)
sobel_xy = cv.addWeighted(sobel_x, 0.5, sobel_y, 0.5, 0)

canny1 = cv.Canny(sobel_xy, 10, 30)
canny2 = cv.Canny(sobel_xy, 30, 90)
canny3 = cv.Canny(sobel_xy, 50, 150)
image = np.hstack((canny1, canny2,canny3))
cv.imshow('img', image)
cv.waitKey(0)
cv.destroyAllWindows()

如果去掉二值化,那得到的图像边缘有明显区别,现在也理解了为何刚才在说明 Canny 边缘提取步骤,并未有二值化这一步,看来是多此一举了。

# 高斯模糊
gaussian = cv.GaussianBlur(src, (3, 3), 0)
# ret, thresh = cv.threshold(gaussian, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
# 计算图像梯度幅值和方向,这里一般用的是 Sobel 算子;

还有一个小问题,是在测试的时候发现,使用下面两段代码得到的边缘不一致,具体如下,你可以分开测试。差别在于方法 1 通过 Sobel 算子分别计算了 x 方向和 y 方向的梯度,在使用合并之后的图像进行 Canny 边缘检测,得到的边缘是双线,方法 2 效果更佳理想。

# 方法1
import cv2 as cv
import numpy as np

src = cv.imread("./t77.jpg", 0)
# 高斯模糊
gaussian = cv.GaussianBlur(src, (3, 3), 0)

# 计算图像梯度幅值和方向,这里一般用的是 Sobel 算子;
sobel_x = cv.Sobel(gaussian, cv.CV_16SC1, 1, 0)
sobel_y = cv.Sobel(gaussian, cv.CV_16SC1, 0, 1)
sobel_x = cv.convertScaleAbs(sobel_x)
sobel_y = cv.convertScaleAbs(sobel_y)
sobel_xy = cv.addWeighted(sobel_x, 0.5, sobel_y, 0.5, 0)


canny1 = cv.Canny(sobel_xy, 50, 150)
# canny2 = cv.Canny(sobel_x,sobel_y, 50, 150)
image = np.hstack((gaussian, canny1))
cv.imshow('img', image)
cv.waitKey(0)
cv.destroyAllWindows()


### 方法2
import cv2 as cv
import numpy as np

src = cv.imread("./t77.jpg", 0)
# 高斯模糊
gaussian = cv.GaussianBlur(src, (3, 3), 0)

# 计算图像梯度幅值和方向,这里一般用的是 Sobel 算子;
sobel_x = cv.Sobel(gaussian, cv.CV_16SC1, 1, 0)
sobel_y = cv.Sobel(gaussian, cv.CV_16SC1, 0, 1)

canny2 = cv.Canny(sobel_x, sobel_y, 50, 150)
image = np.hstack((gaussian, canny2))
cv.imshow('img', image)
cv.waitKey(0)
cv.destroyAllWindows()

很遗憾是 非极大值抑制 相关知识的学习,还需要在延期一下,现在的知识储备量不足,学起来有些吃力,我们后面见。

橡皮擦的小节

希望今天的 1 个小时你有所收获,我们下篇博客见~

相关阅读


技术专栏

  1. Python 爬虫 100 例教程,超棒的爬虫教程,立即订阅吧
  2. Python 爬虫小课,精彩 9 讲

今天是持续写作的第 89 / 100 天。
如果你想跟博主建立亲密关系,可以关注同名公众号 梦想橡皮擦,近距离接触一个逗趣的互联网高级网虫。
博主 ID:梦想橡皮擦,希望大家点赞评论收藏

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。