• 日常搜索
  • 百度一下
  • Google
  • 在线工具
  • 搜转载

Python中的图像过滤

你有没有遇到过嘈杂的图像?我的意思是在查看时图像不是那么清晰?我想我们确实经常遇到这样的图像,尤其是现在很多图像是由我们的手机相机或低分辨率数码相机拍摄的。

如果您只有那个对您有意义的嘈杂图像,但问题是无法正确查看它,是否有解决方案可以从这种噪音中恢复?

这就是图像过滤发挥作用的地方,这也是我将在本教程中描述的内容。让我们开始吧!

图像过滤

图像过滤是图像处理中常用的工具。归根结底,我们使用图像过滤来去除图像中的噪声和任何不需要的特征,从而创建该图像的增强版本。存在两种类型的滤波器:线性和非线性。线性滤波器的示例是均值滤波器和拉普拉斯滤波器。非线性滤波器构成滤波器,如中值、最小值、最大值和 Sobel 滤波器。

这些过滤器中的每一个都有特定的用途,旨在消除噪声或改善图像的某些方面。但是过滤是如何进行的呢?这就是我们将在下一节中看到的内容。

了解图像过滤背后的基本数学

为了执行图像过滤过程,我们需要一个过滤器,也称为掩码。该过滤器通常是一个二维方形窗口,即具有相等尺寸(宽度和高度)的窗口。

过滤器将包括数字。这些数字称为系数,它们实际上决定了过滤器的效果以及输出图像的外观。

要应用过滤器,3x3窗口会滑过图像。这种在图像上滑动过滤器窗口的过程称为空间域中的卷积。窗口将放置在图像中的每个像素上(即,将其视为矩阵中的一个单元),过滤器的中心应与该像素重叠。

一旦发生这种重叠,滤波器位于其顶部的子图像中的像素将乘以滤波器的相应系数。在这种情况下,我们将有一个新矩阵,其新值类似于过滤器的大小(即3x3)。最后,中心像素值将根据所使用的滤波器(即中值滤波器)的类型使用特定的数学方程替换为新值。

我知道上面的段落有点罗嗦。让我们举个例子来展示如何在实际中应用图像过滤器。假设我们有以下子图像,其中我们的过滤器重叠(i并且j指的是子图像中的像素位置,并且I指的是图像):

Python中的图像过滤  第1张

第一张图中显示的我们的过滤器与上面的子图像的卷积将如下所示,其中I_new(i,j)表示位置处的结果(i,j)。

I_new(i,j) = v1 x I(i-1,j-1) + v2 x I(i-1,j) + v3 x I(i-1,j+1) + 
v4 x I(i,j-1) + v5 x I(i,j) + v6 x I(i,j+1) + v7 x I(i+1,j-1) + 
v8 x I(i+1,j) + v9 x I(i+1,j+1)

对图像中的每个像素重复该过程,包括图像边界处的像素。但是,正如您可以猜到的,当将过滤器放置在边界像素处时,部分过滤器将驻留在图像之外。在这种情况下,我们执行padding。

这个过程只是意味着我们在子图像中插入新的像素值,在卷积过程之前位于图像之外的滤波器部分下方,因为该部分显然不包含任何像素值。它在图像之外!那些填充的像素可以是零或恒定值。还有其他设置填充值的方法,但这些超出了本教程的范围。

我认为现在的理论已经足够了,所以让我们继续动手编写代码吧!在本教程中,我将解释中值滤波器(非线性)和均值滤波器(线性)以及我们如何在 python 中实现它们。

中值滤波器

在中值滤波器中,我们选择一个滑动窗口,它将在所有图像像素上移动。我们在这里所做的是收集过滤器下的像素值并取这些值的中值。结果将分配给中心像素。

假设我们的3x3过滤器在将其放置在子图像上后具有以下值:

Python中的图像过滤  第2张

让我们看看如何计算中位数。本质上,中位数是排序后的数字列表的中间数。因此,要找到上述过滤器的中值,我们只需将数字从最低到最高排序,这些数字的中间值就是我们的中值。对窗口中的值进行排序3x3将为我们提供以下信息:

17 29 43 57 59 63 65 84 98

要找到中间数字(中位数),我们只需计算我们拥有的值的数量,将该数字加 1,然后除以 2。这将为我们提供中间值在窗口中的位置,即我们的中值。所以中值将在位置9+1/2 = 5,即59。该值将是我们3x3窗口中心下方像素的新值。

这种类型的过滤器用于去除噪声,最适用于遭受椒盐噪声的图像。下图显示了遭受此类噪声的图片示例:

Python中的图像过滤  第3张

现在,让我们编写一个 Python 脚本,将中值滤波器应用于上述图像。对于这个例子,我们将使用 OpenCV 库。请查看此安装指南以了解如何在 Python 中安装 OpenCV 包。

要应用中值滤波器,我们只需使用 OpenCV 的cv2.medianBlur()函数。因此,我们的脚本如下所示:

import cv2, argparse
 
# create the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument('-i', '--image', required = True, help = 'Path to the input image')
args = vars(ap.parse_args())
 
 
image = cv2.imread(args['image'])
processed_image = cv2.medianBlur(image, 3)
 
cv2.imwrite('processed_image.png', processed_image)
cv2.waitKey(0)

请注意,我使用argparse了 ,因为在这里灵活使用是一个好习惯,并使用命令行将我们想要应用中值滤波器的图像作为参数传递给我们的程序。

在将我们的图像作为命令行参数传递后,我们使用该cv2.imread()函数读取该图像。然后我们使用该medianBlur()函数应用中值滤波器,将我们的图像和滤波器大小作为参数传递。使用该cv2.imshow()功能显示图像,并使用 保存到磁盘cv2.imwrite()。

上述脚本的结果如下:

Python中的图像过滤  第4张

那么你觉得呢?非常漂亮——一个漂亮、干净的图像,没有噪音。

均值过滤器

均值滤波器是线性滤波器的一个例子。它基本上用邻域的平均值(平均值)替换输出图像中的每个像素。这具有平滑图像的效果(减少一个像素与下一个像素之间的强度变化量),从图像中去除噪声,并使图像变亮。

因此,在均值滤波中,图像的每个像素都将被其邻居的平均值替换,包括像素本身。3x3内核通常用于均值滤波,但也可以使用其他内核大小(即 5x5)。

这里要提到的重要一点是,平均内核的所有元素都应该:

  • 总和为 1

  • 是相同的

让我们举个例子让事情更清楚。假设我们有以下子图像:

Python中的图像过滤  第5张

应用均值滤波器时,我们将执行以下操作:

(7+9+23+76+91+7+64+90+32)/9 = 44

确切的结果是44.3,但我将结果四舍五入为44。所以中心像素的新值44不是91.

现在到编码部分。假设我们有以下嘈杂的图像:

Python中的图像过滤  第6张

此时我们要做的是在上面的图像上应用均值滤波器,看看应用这种滤波器的效果。

执行此操作的代码如下:

import cv2, argparse
  
# create the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument('-i', '--image', required = True, help = 'Path to the input image')
args = vars(ap.parse_args())
 
image = cv2.imread(args['image'])
processed_image = cv2.blur(image, (5, 5))
 
cv2.imwrite('processed_image.jpg', processed_image)
cv2.waitKey(0)

从代码中可以看出,我们5x5为均值滤波器使用了内核。我们还使用了该blur()方法来应用均值滤波器。这个函数的第一个参数是我们的输入图像,第二个参数是我们的内核。

在我们嘈杂的图像上运行代码后,这是我得到的结果:

Python中的图像过滤  第7张

如果您观察输出图像,我们可以看到它比噪声图像更平滑。任务完成!

高斯模糊

我们可以用来减少图像噪声的另一种重要技术称为高斯模糊。用于高斯模糊的实际数学很复杂,超出了本教程的范围。但是,您可以通过简单地使用名为GaussianBlur().

此方法接受源图像作为其第一个参数,一个具有内核宽度和高度的元组作为第二个参数,以及一个标准差值作为第三个参数。您可以单独指定标准偏差的sigmaX或sigmaY值。但是,当仅指定一个值时,假定两者相等。还要记住,内核大小必须是正数和奇数。

import cv2, argparse
  
# create the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument('-i', '--image', required = True, help = 'Path to the input image')
args = vars(ap.parse_args())
 
image = cv2.imread(args['image'])
processed_image = cv2.GaussianBlur(image, (5, 5), 0)
 
cv2.imwrite('processed_image.png', processed_image)
cv2.waitKey(0)

该GaussianBlur()方法在去除高斯噪声方面最有效。下图应用了一些高斯噪声。

Python中的图像过滤  第8张

现在,让我们看看我们的GaussianBlur()方法从这张图片中去除噪点的效果如何。下图显示了在上面的猫图像上应用我们的高斯蓝滤镜的结果。

Python中的图像过滤  第9张

如您所见,噪音明显降低。

双边过滤

我们上面使用的高斯滤波器依赖于所有附近的像素来过滤噪声。这也导致图像中像素强度变化很大的任何边缘模糊。该bilateralFilter()方法通过使用另一个高斯滤波器克服了这一限制,该滤波器是像素差的函数。此过滤器可确保仅考虑将具有相似强度的像素进行模糊处理。

该方法接受多个参数。它的第一个参数指定源图像,第二个参数确定用于过滤的像素邻域的大小。第三个和第四个参数指定像素的颜色或距离在它们停止影响中心像素的值之前可以有多远。第三个和第四个参数的 sigma 值一般应该在 70-80 左右。但是,它们可以低至 10 和高达 150。

以下代码片段将双边过滤器应用于图像:

import cv2, argparse
  
# create the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument('-i', '--image', required = True, help = 'Path to the input image')
args = vars(ap.parse_args())
 
image = cv2.imread(args['image'])
processed_image = cv2.bilateralFilter(image, 9, 80, 80)
 
cv2.imwrite('processed_image.png', processed_image)
cv2.waitKey(0)

为了清楚地观察高斯模糊和双边滤波器之间的差异,我们需要将其应用于具有大量纹理和锐利边缘的图像。木板的图像是用于此目的的理想选择。这是原始的木板图像:

Python中的图像过滤  第10张

这是应用了常规高斯模糊的同一张图像:

Python中的图像过滤  第11张


现在,这是应用了双边滤波器的木板图像:

Python中的图像过滤  第12张

差异是微妙的,但您应该能够在第二张图像中注意到更清晰的线条,而纹理仍然模糊。

结论

正如我们在本教程中所见,Python 允许我们以简单的方式执行图像过滤等高级任务,尤其是通过其 OpenCV 库。


文章目录
  • 图像过滤
  • 了解图像过滤背后的基本数学
  • 中值滤波器
  • 均值过滤器
  • 高斯模糊
  • 双边过滤
  • 结论
  • 发表评论