在OpenCV中进行图像预处理

今天,我们进一步深入,并处理在图像处理中常用的形态学操作。形态学操作用于提取区域、边缘、形状等。

什么是形态学操作?

形态学操作是在二值图像上进行的。二值图像可能包含许多不完美之处。特别是由一些简单的阈值操作产生的二值图像(如果你对阈值不熟悉,现在不用担心)可能包含许多噪声和畸变。OpenCV库中提供了不同的形态学操作来处理这些噪声和缺陷。

形态学操作生成与原始图像相同形状的图像。形态学操作将结构元素应用于输入图像。结构元素可以是任何形状。在今天的所有形态学操作中,将比较输入图像的每个像素与相邻像素以生成输出图像。

对于不同的形态学操作,比较方式有所不同。我们将详细讨论这一点。

在深入讨论问题之前,这是我们将在本教程中使用的图片:

b49938df5a12b0dabafd3422ca9e0601.jpeg

在这里,我们导入必要的包,将图像读取为数组,并将其转换为二值图像,正如前面提到的,形态学操作应用于二值图像:

import cv2 
import matplotlib.pyplot as plt
#Reading the image to an array
image = cv2.imread('practice.jpg')

#converting it to a binary image
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

这是'gray'的样子:

e04c4c03188c8ebe1133a9502967015f.jpeg

我们将使用这个灰度图像来观察形态学操作的工作原理。

在深入示例之前,我想创建一个3x3的内核和一个6x3的内核:

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT, (6, 3))

我们将在下面的形态学操作中使用这两个内核。如果需要,稍后我们将创建更多的内核。

内核可以是任何形状。你也可以尝试一些其他不同形状的内核,如1x4、4x4、5x5、7x18或更多。根据你的项目,内核的形状和大小可能会产生重大差异。

腐蚀

腐蚀做了它听起来的事情。它侵蚀图像的方式就像水侵蚀河岸一样。在腐蚀操作中,它将一个结构元素从输入图像的左到右、从上到下滑动。如果结构元素内的所有像素都大于0,则保留原始像素值。否则,将像素设置为0。

腐蚀用于去除被视为噪声的小斑点。

以下是腐蚀的语法:

eroded1 = cv2.erode(gray.copy(), kernel, iterations = 1)
cv2.imwrite('erode1.jpg', eroded1)

腐蚀函数cv2.erode()使用图像、结构元素和迭代次数。这里的'iterations'参数是可选的。如果你不提供'iterations'值,它会自动执行一次迭代。

这是'erode1'的样子:

6d3276e00db4babeab5fa6d381afeb9a.jpeg

将其与原始图像进行比较。它已经被腐蚀掉了。此外,原始图像中的其他小元素也被去除了。

在任何OCR(光学字符识别)项目中,我们只想识别字母或数字。但图像中可能还有其他更小的字母和元素,可能会混淆你的算法。腐蚀可以消除这些噪声。

如果我们尝试2或3次迭代,它将被更加腐蚀:

eroded2 = cv2.erode(gray.copy(), kernel, iterations = 2)
cv2.imwrite('erode2.jpg', eroded2)

eroded3 = cv2.erode(gray.copy(), kernel, iterations = 3)
cv2.imwrite('erode3.jpg', eroded3)

这是2次和3次迭代的结果:

86351c8b3a1dce2f269858c48461d3c3.jpeg dc402c6cea47a4478368ce75340be056.jpeg

你可以看到,随着迭代次数的增加,图像变得越来越腐蚀。因此,如果你需要提取粗体并且周围有很多噪声的字母,可以通过侵蚀图像来消除噪声。

膨胀

膨胀与腐蚀的作用恰恰相反。它增加前景,从而有助于连接断裂部分。在膨胀中,如果结构元素中的任何像素大于0,则将像素值设置为白色或255。这里我们使用1和3次迭代进行膨胀,以查看差异并了解其工作原理。

dilated1 = cv2.dilate(gray.copy(), kernel, iterations=1)
cv2.imwrite('dilate1.jpg', dilated1)

dilated3 = cv2.dilate(gray.copy(), kernel, iterations=3)
cv2.imwrite('dilate3.jpg', dilated3)

这是膨胀1次迭代(第二个图像)和3次迭代(第三个图像)的图像。原始灰度图像在顶部。

7773f101794173b34cb95dc51ccc24ba.jpeg 470ab7eb87bb688a530cea829595af14.jpeg 40c79879d2657f80be6edd59d1ea288d.jpeg

如果我们将顶部的原始图像与经过一次膨胀的第二个图像进行比较,会有一些微小的差异,经过3次迭代后,差异变得更加显著。根据你的项目,你可以使用尽可能多的迭代次数。

开运算

开运算也是从图像中去除噪声的另一种方法。它在一次迭代中执行腐蚀后进行膨胀。这里有两个示例,我使用了之前准备好的内核和内核1:

opening1 = cv2.morphologyEx(gray, cv2.MORPH_OPEN, kernel)
cv2.imwrite('open1.jpg', opening1)

opening2 = cv2.morphologyEx(gray, cv2.MORPH_OPEN, kernel1)                         )
cv2.imwrite('open2.jpg', opening2)

这里,我将原始图像放在顶部,然后是'opening1'操作的图像,底部是'opening2'操作的图像。

515fca54de04fe95991c2f34af4c328d.jpeg dfdbd7eb91b0bf8f31cd86641849bcac.jpeg afae2e36328c1596f7ade5a91b4a30a8.jpeg

正如你所看到的,在使用3x3内核的中间图片中,左下角的小文本消失了,在使用大小为6x3的kernel1的底部图片中,添加了一个黑色阴影。

闭运算

闭运算与开运算相反。在闭运算中,首先进行膨胀,然后进行腐蚀。

让我们看一些示例:

closing1 = cv2.morphologyEx(gray.copy(), cv2.MORPH_CLOSE, kernel, iterations=1)
cv2.imwrite('close1.jpg', closing1)

closing3 = cv2.morphologyEx(gray.copy(), cv2.MORPH_CLOSE, (3, 3), iterations=3)
cv2.imwrite('close3.jpg', closing3)

与之前一样,我将原始灰度图像放在顶部进行比较,然后是'closing1'和'closing2'的输出图像。

106452f51ae12dd137acf82433e27acd.jpeg 5d457ca431c6f45e786f92c62702a29c.jpeg 8187c8b2a259ba35fa202bd2916ad6fd.jpeg
形态梯度

形态梯度对于检测对象的轮廓非常有用。它可用于边缘检测。基本上,它是膨胀操作和腐蚀操作之间的差异。

这是使用kernel和kernel1的两个示例:

grad1 = cv2.morphologyEx(gray.copy(), cv2.MORPH_GRADIENT, kernel)
cv2.imwrite('grad1.jpg', grad1)

grad2 = cv2.morphologyEx(gray.copy(), cv2.MORPH_GRADIENT, kernel1)
cv2.imwrite('grad3.jpg', grad2)

这是'grad1'和'grad2'的输出图像:

bfc9de4783089336af6117ebdfcdc92e.jpeg 20d0c0b0c3b1e137d3c75e661ea8bf61.jpeg

正如你所看到的,不同形状的内核提供了两种不同类型的输出。

顶帽/白帽

顶帽操作是原始二值图像与开运算之间的差异。当你需要从黑色背景中找到亮区域时,它很有帮助。

对于这个操作,我们将使用不同的输入图像和其他操作:

这是输入图像:

5d49f0c00c10f60e0de6405536c15efb.jpeg

这辆车的车牌比车身更亮。让我们看看如何提取出那个白色区域。和往常一样,我们应该将其转换为灰度图像,并定义两个不同的内核。

image = cv2.imread('car.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (23, 5))

kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT, (35, 8))

我尝试了一些不同形状的内核,然后使用了这两个内核大小。请随意尝试一些其他内核大小并比较结果。

tophat1 = cv2.morphologyEx(gray.copy(), cv2.MORPH_TOPHAT, kernel1)
cv2.imwrite('tophat1.jpg', tophat1)

这是输出图像:

1e1edec5adf7ccb56eeb0a4de6430187.jpeg

看,它检测到了车牌从车身上的亮区域。

黑帽

黑帽操作与顶帽相反。

blackhat1 = cv2.morphologyEx(gray.copy(), cv2.MORPH_BLACKHAT, kernel)
cv2.imwrite('blackhat1.jpg', blackhat1)

这是黑帽操作的输出:

e583e3d65157c62261ba1e75cac3b5c9.jpeg

如果你注意到,它聚焦于车牌上的字母。

经过顶帽操作后,检测到了车牌区域,经过黑帽操作后,突出显示了白色车牌上的黑色字母。

因此,如果我们想要检测车牌上的数字,我们将执行顶帽操作,然后是黑帽操作。

结论

本文试图通过示例解释一些众所周知的形态学操作。在将来的一些文章中,我将使用它们来解决一些实际问题。那将更有趣。

☆ END ☆

如果看到这里,说明你喜欢这篇文章,请转发、点赞。微信搜索「uncle_pn」,欢迎添加小编微信「 woshicver」,每日朋友圈更新一篇高质量博文。

扫描二维码添加小编↓

91422f37ebbb885b720d8dc7f4f62286.jpeg

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值