基于Python和OpenCV的零参数的自动Canny边缘检测这个技巧真的很棒——而且在很多情况下,它完全减轻了调优Canny边缘检测器参数的需要。但在我们开始之前,让我们先讨论一下Canny边缘检测器。
1.Canny边缘检测器
Canny边缘检测器是在1986年由John F. Canny发明的。它至今仍被广泛使用,是图像处理中默认的边缘检测器之一。
Canny边缘检测算法可以分为5个步骤:
- 1.使用高斯滤波器平滑图像以去除高频噪声。
- 2.计算图像的梯度
- 3.应用非最大抑制来消除对边缘检测的“假”响应。
- 4.使用梯度值的下限和上限应用阈值。
- 5.通过抑制未连接到强边的弱边,使用滞后跟踪边。
如果你熟悉Canny边缘检测器的OpenCV实现,你会知道函数签名是这样的:cv2.canny(image, lower, upper)
。其中image
是我们想要检测边缘的图像;lower和upper
分别是步骤4的整数阈值。
问题在于确定这些下限和上限。
阈值的最佳值是什么?
当你处理在不同光照条件下拍摄的不同内容的多幅图像时,这个问题特别重要。
在剩余部分,我将向您展示一个基于基本统计数据的小技巧,您可以应用它来摒弃Canny边缘检测阈值的手动调优。
这个技巧将节省你的参数调整时间-应用该功能后你仍然会得到一个不错的Canny边缘结果。
2.基于Python和OpenCV的零参数自动Canny边缘检测
在你最喜欢的代码编辑器中打开一个新文件,命名为auto_canny.py
,让我们开始吧:
# 导入相关库
import numpy as np
import argparse
import glob
import cv2
def auto_canny(image, sigma=0.33):
# 计算单个通道像素强度的中值
v = np.median(image)
# 根据计算的中值,实现自动Canny边缘检测
lower = int(max(0, (1.0 - sigma) * v))
upper = int(min(255, (1.0 + sigma) * v))
edged = cv2.Canny(image, lower, upper)
# 返回结果
return edged
我们要做的第一件事是导入我们需要的包。我们将使用NumPy
来进行数值运算,argparse
来解析命令行参数,glob
来获取从磁盘得到图像的路径。
然后我们定义auto_canny
,我们的自动Canny边缘检测函数。这个函数需要一个参数image
,它是单通道图像。作为一个可选参数sigma
,可以用来改变基于简单统计确定的百分比阈值。
在auto_canny
函数中,首先得到图像中像素强度的中值。然后我们取这个中值,构造两上、下两个阈值。这些阈值是建立在由sigma
参数控制的+/-百分比的基础上的。
sigma
值越低,临界值越紧,而sigma
值越大,临界值越宽。一般来说,你不需要经常改变这个值。简单地选择一个单一的,默认的sigma
,并将其应用到您的整个图像数据集。
注:在实践中,sigma=0.33往往在我使用的大多数数据集上给出好的结果,所以我选择提供33%作为默认的sigma值。
现在我们有了下限和上限,然后应用Canny边缘检测器,并将结果返回。
# 构造参数parse并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--images", required=True,
help="path to input dataset of images")
args = vars(ap.parse_args())
# 循环操作图像
for imagePath in glob.glob(args["images"] + "/*.jpg"):
# 加载图像,将其转换为灰度,并稍微模糊它
image = cv2.imread(imagePath)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (3, 3), 0)
# 应用Canny边缘检测,采用宽阈值、紧阈值和自动确定阈值
wide = cv2.Canny(blurred, 10, 200)
tight = cv2.Canny(blurred, 225, 250)
auto = auto_canny(blurred)
# 显示图片
cv2.imshow("Original", image)
cv2.imshow("Edges", np.hstack([wide, tight, auto]))
cv2.waitKey(0)
我们解析命令行参数。这里我们只需要一个参数——images,它是包含我们想要处理的图像的目录的路径。
然后我们循环查看目录中的图像,从磁盘加载图像,将图像转换为灰度,并应用带有3 x 3核的高斯模糊来去除高频噪声。
然后采用三种方法进行Canny边缘检测:
+宽阈值。
- 紧阈值。
- 使用auto_canny函数自动确定的阈值。
3.结果展示
如你所见,宽Canny边缘阈值不仅能检测到海豚,还能检测到图像中的许多云。紧的阈值没有探测到云,但错过了海豚的尾巴。最后,自动方法能够找到所有的海豚,同时去除许多云的边缘。
让我们试试另一个图像:
左边的宽Canny阈值包括基于相机拉丝金属上的光线反射的高频噪声,而中间的窄阈值忽略了相机上的许多结构边缘。最后,右边的自动方法能够在不包括高频噪声的情况下找到许多结构边缘。
又一个例子:
这里的结果相当引人注目。虽然宽(左)和自动(右)Canny边缘检测方法的性能相似,但紧阈值(中)几乎遗漏了杯子的所有结构边缘。
通过上面的例子,很明显,自动的,零参数版本的Canny边缘检测以最少的努力获得最好的结果。
结论
在这篇博客文章中,我向你展示了一个简单的技巧,可以使用Canny边缘检测器(可靠地)自动检测图像中的边缘,而不需要为函数提供阈值。
这个技巧只是取图像的中值,然后根据中值的百分比构造上下限阈值。在实践中,sigma=0.33往往能获得较好的结果。
一般情况下,您会发现Canny边缘检测的自动、零参数版本能够获得相当不错的结果,而您几乎不需要付出任何努力。