介绍
阈值化是一种简单而有效的技术,可以在图像中执行基本分割,并将其二值化(将其转换为二值图像),其中像素是 0
or 1
(或 255
如果您使用整数来表示它们)。
通常,您可以使用阈值在图像中执行简单的背景 - 前景分割,它归结为每个像素的简单技术的变体:
if pixel_value > threshold:
pixel_value = MAX
else:
pixel_value = 0
简单的阈值处理具有明显的问题,并且需要相当原始的输入,这使得它对于许多用例来说不太实用。 主要问题是应用于整个图像的全局阈值,而图像很少均匀到足以使总阈值起作用,除非它们是人为的。
全局阈值可以很好地在扫描页面上分离黑白书中的字符。 在同一页面的手机图片上,全局阈值很可能会失败,因为页面各部分之间的光照条件可能会发生变化,从而使全局截止点对真实数据过于敏感。
为了解决这个问题——我们可以雇用 本地 阈值,使用一种称为 自适应阈值. 我们可以改变每个部分的阈值,而不是用相同的规则处理图像的所有部分 当地 这似乎很合适。 这使得阈值对光照、噪声和其他因素的变化部分保持不变。 虽然比全局阈值化更有用,但阈值化本身是一种有限的、严格的技术,最适合用于帮助图像预处理(尤其是在识别要丢弃的图像时),而不是分割。
对于需要上下文的更精细的应用程序,您最好采用更先进的技术,包括深度学习,这一直在推动计算机视觉的最新进展。
使用 OpenCV 进行自适应阈值处理
让我们加载具有可变光照条件的图像,其中图像的一部分比另一部分更聚焦,并且图片是从一个角度拍摄的。 我给哈罗德·麦吉拍的一张照片 “关于食物和烹饪” 会很棒!
img = cv2.imread('book.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img)
现在,使用常规阈值,我们可以尝试从背景中分离出字母,因为它们之间存在明显的颜色差异。 所有纸张颜色将被视为背景。 由于我们真的不知道阈值应该是什么——让我们应用 Otsu 的方法来找到一个好的值,预计图像有点双模态(主要由两种颜色控制):
img = cv2.imread('book.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (7, 7), 0)
ret, mask = cv2.threshold(blurred, 0, 255, cv2.THRESH_OTSU)
print(f'Threshold: {ret}')
fig, ax = plt.subplots(1, 2, figsize=(12, 5))
ax[0].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
ax[1].imshow(cv2.cvtColor(mask, cv2.COLOR_BGR2RGB))
让我们看一下结果:
哎哟。 左边的文字主要是褪色,gutter周围的阴影完全吃掉了一部分图像,文字太饱和了! 这是一张“在野外”的图像,全局阈值等一揽子规则效果不佳。 门槛应该是多少? 这取决于图像的部分!
cv2.adaptiveThreshold()
方法允许我们这样做:
cv2.adaptiveThreshold(img,
max_value,
adaptive_method,
threshold_method,
block_size,
C)
adaptive_method
可以是一个 cv2.ADAPTIVE_THRESH_MEAN_C
or cv2.ADAPTIVE_THRESH_GAUSSIAN_C
,其中 C
是您设置的最后一个参数。 这两种方法都根据相关像素的邻居计算阈值,其中 block_size
规定要考虑的邻居的数量(邻居的面积)。
ADAPTIVE_THRESH_MEAN_C
取邻居的平均值并减去C
,而ADAPTIVE_THRESH_GAUSSIAN_C
取邻居的高斯加权和并扣除C
.
查看我们的 Git 学习实践指南,其中包含最佳实践、行业认可的标准以及随附的备忘单。 停止谷歌搜索 Git 命令,实际上 学习 它!
它还允许您设置二值化策略,但仅限于 THRESH_BINARY
和 THRESH_BINARY_INV
,并且在它们之间进行切换将有效地切换什么是“背景”和什么是“前景”。
该方法只返回图像的掩码——而不是返回码和掩码。 让我们尝试使用自适应阈值分割同一图像中的字符:
img = cv2.imread('book.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (7, 7), 0)
mask = cv2.adaptiveThreshold(blurred,
255,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY,
31,
10)
fig, ax = plt.subplots(1, 2, figsize=(12, 5))
ax[0].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
ax[1].imshow(cv2.cvtColor(mask, cv2.COLOR_BGR2RGB))
plt.tight_layout()
这会产生更清晰的图像:
请注意: block_size
参数必须是奇数。
以同样的方式,我们可以应用高斯阈值:
mask = cv2.adaptiveThreshold(blurred,
255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY,
31,
10)
这最终也产生了一个非常令人满意的图像:
块大小(相邻区域)和 C
是在这里调整的超参数。 尝试不同的值并选择最适合您的图像的值。 一般来说,高斯阈值对噪声不太敏感,并且会产生更暗淡、更清晰的图像,但这会有所不同并取决于输入。
自适应阈值的限制
使用自适应阈值,我们能够避免阈值的总体限制,但它仍然相对严格,不适用于彩色输入。 例如,如果我们加载一张剪刀图像和一个不同颜色的小工具包,即使是自适应阈值处理也会在真正正确分割它时遇到问题,勾勒出某些暗特征,但没有考虑整个对象:
如果我们调整块大小和 C
,我们可以让它认为更大的块是同一个对象的一部分,但是会遇到使邻居大小的问题 过于全球化,回到与全局阈值相同的首要问题:
结论
近年来,二进制分割(就像我们在这里所做的那样)和多标签分割(你可以对任意数量的类进行编码)已经成功地使用深度学习网络建模,这些网络更加强大和灵活。 此外,他们可以将全局和本地上下文编码到他们正在分割的图像中。 缺点是——你需要数据来训练他们,以及时间和专业知识。
对于动态的简单阈值,您可以使用 OpenCV,并使用自适应阈值而不是全局阈值策略来克服一些限制。 对于准确的生产级分割,您需要使用神经网络。
走得更远——计算机视觉的实用深度学习
你好奇的天性让你想走得更远? 我们建议查看我们的 套餐: “使用 Python 进行计算机视觉的实用深度学习”.
另一个计算机视觉课程?
我们不会对 MNIST 数字或 MNIST 时尚进行分类。 他们很久以前就发挥了作用。 在让高级黑盒架构承担性能负担之前,太多的学习资源专注于基本数据集和基本架构。
我们想专注于 揭秘, 实际性, 理解, 直觉 和 真实项目. 想学 形成一种 你可以有所作为? 我们将带您从大脑处理图像的方式到编写研究级的乳腺癌深度学习分类器,再到“产生幻觉”的深度学习网络,通过实际工作教您原理和理论,为您配备成为应用深度学习解决计算机视觉问题的专家的专业知识和工具。
里面是什么?
- 视觉的首要原则以及如何教计算机“看”
- 计算机视觉的不同任务和应用
- 让您的工作更轻松的交易工具
- 为计算机视觉寻找、创建和利用数据集
- 卷积神经网络的理论与应用
- 处理数据集中的域转移、共现和其他偏差
- 迁移学习并利用他人的训练时间和计算资源为您谋取利益
- 构建和训练最先进的乳腺癌分类器
- 如何将健康的怀疑态度应用于主流思想并理解广泛采用的技术的含义
- 使用 t-SNE 和 PCA 可视化 ConvNet 的“概念空间”
- 公司如何使用计算机视觉技术取得更好结果的案例研究
- 适当的模型评估、潜在空间可视化和识别模型的注意力
- 执行领域研究,处理您自己的数据集并建立模型测试
- 尖端架构、想法的发展、是什么让它们与众不同以及如何实现它们
- KerasCV – 用于创建最先进的管道和模型的 WIP 库
- 如何解析和阅读论文并自己实现它们
- 根据您的应用选择型号
- 创建端到端机器学习管道
- 使用 Faster R-CNN、RetinaNets、SSD 和 YOLO 进行对象检测的景观和直觉
- 实例和语义分割
- 使用 YOLOv5 进行实时对象识别
- 训练 YOLOv5 目标检测器
- 使用 KerasNLP(行业强大的 WIP 库)使用 Transformers
- 将 Transformers 与 ConvNet 集成以生成图像的标题
- 深梦
- 计算机视觉的深度学习模型优化