介绍
边缘检测是我们自然而然会做的事情,但在为计算机定义规则时并不那么容易。 虽然已经设计了各种方法,但统治方法是由 John F. Canny 在 1986 年开发的,并被恰当地命名为 Canny 方法。
它速度快、相当健壮,并且几乎可以在它所采用的技术类型中发挥最佳效果。 在本指南结束时,您将了解如何对视频执行实时边缘检测,并产生以下内容:
坎尼边缘检测
什么是 Canny 方法? 它由四个不同的操作组成:
- 高斯平滑
- 计算梯度
- 非最大抑制
- 迟滞阈值
高斯平滑 被用作“熨平”输入图像的第一步,并软化噪声,使最终输出更清晰。
图像渐变 已在早期应用中用于边缘检测。 最值得注意的是,Sobel 和 Scharr 滤波器依赖于图像梯度。 Sobel 滤波器归结为两个内核 (Gx 和 Gy),哪里 Gx 检测水平变化,而 Gy 检测垂直变化:
G
x
=
[
-
1
0
+
1
-
2
0
+
2
-
1
0
+
1
]
G
y
=
[
-
1
-
2
-
1
0
0
0
+
1
+
2
+
1
]
当您将它们滑过图像时,它们将各自“拾取”(强调)各自方向的线条。 Scharr 内核以相同的方式工作,但具有不同的值:
G
x
=
[
+
3
0
-
3
+
10
0
-
10
+
3
0
-
3
]
G
y
=
[
+
3
+
10
+
3
0
0
0
-
3
-
10
-
3
]
这些过滤器一旦在图像上进行卷积,就会产生特征图:
图片来源:大卫肯尼迪
对于这些特征图,您可以计算 梯度幅度 和 梯度方向 – 即变化有多强烈(某事物是边缘的可能性有多大)以及变化指向哪个方向。 由于 Gy 表示垂直变化(Y 梯度),而 Gx 表示水平变化(X 梯度)——您可以通过简单地应用勾股定理来计算幅度,得到由“左”和“正确”方向:
$$
{G} ={sqrt {{{G} _{x}}^{2}+{{G} _{y}}^{2}}}
$$
使用幅度和方向,您可以生成边缘突出显示的图像:
图片来源:大卫肯尼迪
然而——你可以看到砖块的结构也捕捉到了多少噪音! 图像梯度对噪声非常敏感。 这就是为什么使用 Sobel 和 Scharr 过滤器作为组件,但不是 Canny 方法中的唯一方法。 高斯平滑在这里也有帮助。
非最大抑制
Sobel 滤波器的一个显着问题是边缘不是很清晰。 这不像有人拿铅笔画一条线来创建图像的线稿。 图像中的边缘通常不是那么清晰,因为光线会逐渐扩散。 但是,我们可以在边缘找到公共线,并抑制它周围的其余像素,从而产生一条干净、细的分隔线。 这被称为非最大抑制! 非最大像素(比我们在小的局部场(例如 3×3 内核)中比较它们的像素小)被抑制。 这个概念适用于比这更多的任务,但现在让我们将它绑定到这个上下文。
迟滞阈值
由于光照条件、图像中的材料等原因,许多非边缘可以并且很可能会被评估为边缘。由于发生这些错误计算的各种原因——很难自动评估边缘肯定是什么和现在是什么不。 假设“真实”边缘比“假”边缘更强烈,您可以阈值梯度,并且只包括更强的梯度。
阈值的工作方式与往常几乎相同——如果梯度低于一个较低的阈值,则将其移除(将其归零),如果它高于给定的最高阈值,则保留它。 下限和上限之间的所有内容都在“灰色地带”。 如果阈值之间的任何边缘连接到 确定的边缘 (高于阈值的)——它们也被认为是边缘。 如果它们没有连接,它们很可能是计算错误边缘的假象。
这就是滞后阈值! 实际上,它有助于清理最终输出并删除错误边缘,具体取决于您将什么分类为错误边缘。 要找到好的阈值,您通常会尝试使用不同的阈值下限和上限,或者采用自动方法,例如 Otsu 方法或 Triangle 方法。
让我们加载一个图像并对其进行灰度化(Canny,就像 Sobel/Scharr 需要对图像进行灰度化一样):
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('finger.jpg', cv2.IMREAD_GRAYSCALE)
img_blur = cv2.GaussianBlur(img, (3,3), 0)
plt.imshow(img_blur, cmap='gray')
手指的特写图像将作为边缘检测的良好试验场——从图像中辨别指纹并不容易,但我们可以近似。
使用 cv2.Canny() 对图像进行边缘检测
Canny 算法可以使用 OpenCV 应用 Canny()
方法:
cv2.Canny(input_img, lower_bound, upper_bound)
查看我们的 Git 学习实践指南,其中包含最佳实践、行业认可的标准以及随附的备忘单。 停止谷歌搜索 Git 命令,实际上 学习 它!
在下限和上限之间找到适当的平衡可能很棘手。 如果两者都很低 - 你将没有什么优势。 如果下限低而上限高 - 你会有噪音。 如果两者都很高并且彼此靠近 - 你将没有什么优势。 正确的位置在边界之间有足够的差距,并且在正确的范围内。 实验!
输入图像会被 Canny 方法模糊,但通常情况下,你会从模糊中受益 before 它也进去了。 该方法在进行其余操作之前对输入应用 5×5 高斯模糊,但即使使用这种模糊,仍然会渗入一些噪声,因此我们在将图像输入算法之前对其进行了模糊处理:
edge = cv2.Canny(img_blur, 20, 30)
fig, ax = plt.subplots(1, 2, figsize=(18, 6), dpi=150)
ax[0].imshow(img, cmap='gray')
ax[1].imshow(edge, cmap='gray')
结果是:
的价值观 20
和 30
这里不是任意的——我已经在各种参数上测试了该方法,并选择了一个似乎产生不错结果的集合。 我们可以尝试自动化吗?
cv2.Canny() 的自动阈值化?
你能找到一组最佳阈值吗? 是的,但它并不总是有效。 您可以自己计算一些好的值,然后调整范围 sigma
围绕该阈值:
lower_bound = (1-sigma)*threshold
upper_bound = (1+sigma)*threshold
什么时候 sigma
,也就是说, 0.33
– 界限将是 0.66*threshold
和 1.33*threshold
,允许在它周围有 ~1/3 的范围。 虽然,找到 threshold
更难的是什么。 OpenCV 为我们提供了 Otsu 的方法(适用于双模图像)和 Triangle 方法。 让我们尝试一下它们,并将像素值的简单中值作为第三个选项:
otsu_thresh, _ = cv2.threshold(img_blur, 0, 255, cv2.THRESH_OTSU)
triangle_thresh, _ = cv2.threshold(img_blur, 0, 255, cv2.THRESH_TRIANGLE)
manual_thresh = np.median(img_blur)
def get_range(threshold, sigma=0.33):
return (1-sigma) * threshold, (1+sigma) * threshold
otsu_thresh = get_range(otsu_thresh)
triangle_thresh = get_range(triangle_thresh)
manual_thresh = get_range(manual_thresh)
print(f"Otsu's Threshold: {otsu_thresh} nTriangle Threshold: {triangle_thresh} nManual Threshold: {manual_thresh}")
结果是:
Otsu's Threshold: (70.35, 139.65)
Triangle Threshold: (17.419999999999998, 34.58)
Manual Threshold: (105.18999999999998, 208.81)
这些很不一样! 从我们之前看到的值来看,我们可以预期 Triangle 方法在这里工作得最好。 手动阈值不是很清楚,因为它只取中间像素值,并最终具有较高的基本阈值,该阈值进一步乘以该图像的广泛范围。 大津的方法受此影响较小,但仍然受苦。
如果我们运行 Canny()
具有这些阈值范围的方法:
edge_otsu = cv2.Canny(img_blur, *otsu_thresh)
edge_triangle = cv2.Canny(img_blur, *triangle_thresh)
edge_manual = cv2.Canny(img_blur, *manual_thresh)
fig, ax = plt.subplots(1, 3, figsize=(18, 6), dpi=150)
ax[0].imshow(edge_otsu, cmap='gray')
ax[1].imshow(edge_triangle, cmap='gray')
ax[2].imshow(edge_manual, cmap='gray')
请注意: 该函数需要多个参数,我们的阈值是单个元组。 我们可以 解构 通过在元组前面加上前缀,将元组转换为多个参数 *
. 这也适用于列表和集合,并且是在通过编程方式获取多个参数后提供多个参数的好方法。
结果是:
三角形方法在这里工作得很好! 这不能保证它在其他情况下也能正常工作。
使用 cv2.Canny() 对视频进行实时边缘检测
最后,让我们将 Canny 边缘检测实时应用于视频! 我们将显示正在处理的视频(完成后的每一帧) cv2.imshow()
它显示了一个带有我们想要显示的框架的窗口。 不过,我们还将视频保存到 MP4 文件中,以便以后检查和共享。
要使用 OpenCV 加载视频,我们使用 VideoCapture()
方法。 如果我们通过 0
– 它将从当前的网络摄像头录制,因此您也可以在网络摄像头上运行代码! 如果您传入文件名,它将加载文件:
def edge_detection_video(filename):
cap = cv2.VideoCapture(filename)
fourcc = cv2.VideoWriter_fourcc(*'MP4V')
out = cv2.VideoWriter('output.mp4', fourcc, 30.0, (int(cap.get(3)), int(cap.get(4))), isColor=False)
while cap.isOpened():
(ret, frame) = cap.read()
if ret == True:
frame = cv2.GaussianBlur(frame, (3, 3), 0)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
edge = cv2.Canny(frame, 50, 100)
out.write(edge)
cv2.imshow('Edge detection', edge)
else:
break
if cv2.waitKey(10) & 0xFF == ord('q'):
break
cap.release()
out.release()
cv2.destroyAllWindows()
edge_detection_video('secret_video.mp4')
VideoWriter
接受几个参数——输出文件名、FourCC(四个编解码器代码,表示用于编码视频的编解码器)、帧速率和作为元组的分辨率。 为了不猜测或调整视频大小——我们使用了原始视频的宽度和高度,通过 VideoCapture
包含有关视频本身的数据的实例,例如宽度、高度、总帧数等。
打开捕获时,我们尝试读取下一帧 cap.read()
,它返回一个结果代码和下一帧。 结果代码是 True
or False
,表示存在或缺少下一帧。 只有当有帧时,我们才会尝试进一步处理它,否则,我们将打破循环。 对于每个有效帧,我们通过高斯模糊运行,将其转换为灰度,运行 cv2.Canny()
在它上面并使用 VideoWriter
到磁盘,并使用显示 cv2.imshow()
实时取景。
最后,我们发布了捕获和视频编写器,因为它们都在处理磁盘上的文件,并破坏所有现有的窗口。
当您使用 secret_video.mp4
输入 - 你会看到一个窗口弹出,一旦它完成,你的工作目录中的一个文件:
结论
在本指南中,我们了解了 Canny 边缘检测的工作原理及其组成部分——高斯平滑、Sobel 滤波器和图像梯度、非最大值抑制和滞后阈值。 最后,我们探索了 Canny 边缘检测的自动阈值范围搜索方法 cv2.Canny()
,并将该技术应用于视频,提供实时边缘检测并将结果保存在视频文件中。
走得更远——计算机视觉的实用深度学习
你好奇的天性让你想走得更远? 我们建议查看我们的 套餐: “使用 Python 进行计算机视觉的实用深度学习”.
另一个计算机视觉课程?
我们不会对 MNIST 数字或 MNIST 时尚进行分类。 他们很久以前就发挥了作用。 在让高级黑盒架构承担性能负担之前,太多的学习资源专注于基本数据集和基本架构。
我们想专注于 揭秘, 实际性, 理解, 直觉 和 真实项目. 想学 形成一种 你可以有所作为? 我们将带您从大脑处理图像的方式到编写研究级的乳腺癌深度学习分类器,再到“产生幻觉”的深度学习网络,通过实际工作教您原理和理论,为您配备成为应用深度学习解决计算机视觉问题的专家的专业知识和工具。
里面是什么?
- 视觉的首要原则以及如何教计算机“看”
- 计算机视觉的不同任务和应用
- 让您的工作更轻松的交易工具
- 为计算机视觉寻找、创建和利用数据集
- 卷积神经网络的理论与应用
- 处理数据集中的域转移、共现和其他偏差
- 迁移学习并利用他人的训练时间和计算资源为您谋取利益
- 构建和训练最先进的乳腺癌分类器
- 如何将健康的怀疑态度应用于主流思想并理解广泛采用的技术的含义
- 使用 t-SNE 和 PCA 可视化 ConvNet 的“概念空间”
- 公司如何使用计算机视觉技术取得更好结果的案例研究
- 适当的模型评估、潜在空间可视化和识别模型的注意力
- 执行领域研究,处理您自己的数据集并建立模型测试
- 尖端架构、想法的发展、是什么让它们与众不同以及如何实现它们
- KerasCV – 用于创建最先进的管道和模型的 WIP 库
- 如何解析和阅读论文并自己实现它们
- 根据您的应用选择型号
- 创建端到端机器学习管道
- 使用 Faster R-CNN、RetinaNets、SSD 和 YOLO 进行对象检测的景观和直觉
- 实例和语义分割
- 使用 YOLOv5 进行实时对象识别
- 训练 YOLOv5 目标检测器
- 使用 KerasNLP(行业强大的 WIP 库)使用 Transformers
- 将 Transformers 与 ConvNet 集成以生成图像的标题
- 深梦
- 计算机视觉的深度学习模型优化