我正在参加「掘金·启航计划」
相信大部分人都尝试过在Photoshop中或借助移动应用程序模糊或锐化图像,本文将解释如何在OpenCV中使用卷积来进行图像过滤。
让我们首先看一下用于过滤图像的代码。每行代码都会详细讨论,以便充分理解。
图像处理中的卷积核介绍
在图像处理中,卷积核是用于过滤图像的二维矩阵。卷积核也称为卷积矩阵,通常是一个MxN方阵,其中M和N都是奇数(例如3×3、5×5、7×7 等)。请参阅下面给出的 3×3 示例矩阵。
(1)
3×3 2D 卷积核
此类内核可用于对图像的每个像素执行数学运算,以实现所需的效果(例如模糊或锐化图像)。但为什么要模糊图像呢?这里有两个重要原因:
- 因为它减少了图像中某些类型的噪声。因此,模糊通常称为平滑。
- 要删除分散注意力的背景,您可能会故意模糊图像的某些部分,就像在移动设备相机上的“人像”模式下所做的那样。
作为计算机视觉中的一项基本处理技术,使用内核过滤图像具有更多应用。
如何使用内核锐化或模糊图像
源图像的过滤是通过将内核与图像进行卷积来实现的。简单来说,图像与内核的卷积表示内核与其在图像中的对应元素之间的简单数学运算。
- 假设内核的中心位于图像中的特定像素 ( p ) 上。
- 然后将内核中每个元素的值(在本例中为 1)与源图像中相应的像素元素(即其像素强度)相乘。
- 现在,将这些乘法的结果相加并计算平均值。
- 最后,将像素 ( p )的值替换为您刚刚计算的平均值。
使用上述 3×3 内核对源图像中的每个像素执行此操作后,生成的过滤图像将显得模糊。这是因为该内核的卷积运算具有平均效果,往往会使图像变得平滑或模糊。您很快就会亲眼看到内核中各个元素的值如何决定过滤的性质。例如,通过改变内核元素的值,还可以达到锐化的效果。这个概念简单但非常强大,因此被用于众多图像处理管道中。
现在您已经学会了使用卷积核,让我们探讨一下它是如何在 OpenCV 中实现的。
我们将使用下图进行所有编码操作。
将身份内核应用于 OpenCV 中的图像
在我们描述如何实现模糊和锐化内核之前,让我们先了解一下恒等内核。恒等核是一个方阵,中间元素为1,其他元素全部为0,如下图。
(2)
3×3 恒等核
单位矩阵的特殊之处在于,将其与任何其他矩阵相乘将返回原始矩阵。现在让我们演示如何将这个恒等内核与 OpenCV 过滤函数结合使用。在第一个示例中,我们将使用上面的恒等内核来表明过滤操作使原始图像保持不变。
首先导入 OpenCV 和 Numpy,如下面的代码所示。
下面的代码中执行了以下步骤:
- 读取测试图像
- 使用 3×3 NumPy 数组定义恒等核
- 使用**
filter2D()
** OpenCV中的函数进行线性滤波操作 - 显示原始图像和过滤后的图像,使用**
imshow()
** - 将过滤后的图像保存到磁盘,使用**
imwrite()
**
filter2D(src, ddepth, kernel)
该**filter2D()
** 函数需要三个输入参数:
- 第一个参数是源图像
- 第二个参数是**
ddepth
**,表示生成图像的深度。值为 -1 表示最终图像也将具有与源图像相同的深度 - 最后的输入参数是**
kernel
**,我们将其应用于源图像
以下是 Python代码
如下图所示,过滤后的图像(右侧)与原始图像(左侧)完全相同。
使用自定义 2D 卷积内核模糊图像
接下来,我们将演示如何模糊图像。在这里,我们也将定义一个自定义内核,并使用**filter2D()
** OpenCV 中的函数对源图像应用过滤操作。
首先定义一个 5×5 内核,仅由 1 组成。请注意,我们还将内核除以 25。这是为什么呢? 那么,在使用二维卷积矩阵对图像应用任何卷积之前,您需要确保所有值都已归一化。这是通过将内核的每个元素除以内核中元素的数量(在本例中为 25)来完成的。这可确保所有值都保持在 [0,1] 范围内。
现在使用**filter2D()
** 函数过滤图像。如您所见,filter2D()
可用于使用任何用户定义的内核对图像进行卷积。
Python
使用 OpenCV 的内置函数模糊图像
您还可以使用 OpenCV 的内置**blur()
** 函数对图像进行模糊处理。本质上是一个方便的功能,用它来模糊图像,你不需要专门定义一个内核。只需使用输入参数指定内核大小**ksize
**,如下面的代码所示。然后模糊函数将在内部创建一个 5×5 的模糊内核,并将其应用于源图像。
下面使用该**blur()
** 函数的示例将生成与上面使用该函数的示例完全相同的输出**filter2d()
** 。
Python
在 OpenCV 中对图像应用高斯模糊
现在,我们将使用 OpenCV 对图像应用高斯模糊。该技术使用高斯滤波器,它执行加权平均,而不是第一个示例中描述的均匀平均。在这种情况下,高斯模糊根据像素值与内核中心的距离对像素值进行加权。距离中心越远的像素对加权平均值的影响越小。以下代码使用**GaussianBlur()
** OpenCV 中的函数对图像进行卷积。
GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]])
该**GaussianBlur()
** 函数需要四个输入参数:
- 第一个参数**
src
**指定要过滤的源图像。 - 第二个参数是**
ksize
**,它定义了高斯核的大小。在这里,我们使用 5×5 内核。 - 最后两个参数是**
sigmaX
和sigmaY
,它们都设置为 0。这些是 X(水平)和 Y(垂直)方向的高斯核标准差。的默认设置sigmaY
为零。如果您简单地设置sigmaX
**为零,则标准偏差是根据内核大小(分别为宽度和高度)计算的。您还可以将每个参数的大小显式设置为大于零的正值。
Python
结果如下图所示。如您所见,右侧过滤后的图像中有少量模糊。
在 OpenCV 中对图像应用双边滤波
虽然模糊是减少图像噪声的有效方法,但通常不希望模糊整个图像,因为可能会丢失重要的细节和锐利的边缘。在这种情况下,***bilateral filtering
***可以让你的生活更轻松。
- 该技术有选择地应用滤波器来模糊邻域中相似强度的像素。尽可能保留锋利的边缘。
- 它不仅可以让您控制过滤器的空间大小,还可以控制相邻像素包含在过滤输出中的程度。这是根据颜色强度的变化以及距过滤像素的距离来完成的。
双边滤波实质上对图像应用 2D 高斯(加权)模糊,同时还考虑相邻像素强度的变化,以最大限度地减少边缘附近的模糊(我们希望保留)。这意味着内核的形状实际上取决于每个像素位置的局部图像内容。
这是一个具体的例子。假设您正在过滤图像中靠近边缘的区域。简单的高斯模糊滤波器会使边缘模糊,因为它位于过滤区域附近(靠近高斯滤波器的中心)。但双边滤波器可以感知边缘,因为它还考虑了像素强度的差异。因此,它将为跨越边缘的像素计算一个低得多的权重,从而减少它们对过滤区域的影响。强度更均匀的区域模糊得更重,因为它们与强边缘无关。
值得庆幸的是,OpenCV提供了**bilateralFilter()
** 过滤图像的功能。
bilateralFilter(src, d, sigmaColor, sigmaSpace)
该函数有四个必需参数:
-
该函数的第一个参数是源图像。
-
下一个参数 d 定义用于过滤的像素邻域的直径。
-
接下来的两个参数**
sigmaColor
和sigmaSpace
**分别定义 (1D) 颜色强度分布和 (2D) 空间分布的标准偏差。- 该**
sigmaSpace
**参数定义了内核在 x 和 y 方向上的空间范围(就像前面描述的高斯模糊滤波器一样)。 - 该**
sigmaColor
**参数定义一维高斯分布,指定可以容忍像素强度差异的程度。
- 该**
滤波图像中像素的最终(加权)值是其空间权重和强度权重的乘积。因此,
- 相似且靠近过滤像素的像素会产生影响
- 远离过滤像素的像素影响很小(由于空间高斯)
- 具有不同强度的像素几乎没有影响(由于颜色强度高斯),即使它们靠近内核的中心。
Python
查看下图双边滤波的结果。查看像素强度更均匀的区域如何被平滑(模糊),同时保留木材中的细裂纹(边缘)。双边过滤是一种非常有效的技术,但计算成本高昂(特别是对于大内核)。因此,请根据您的特定应用明智地选择。
概括
从卷积核的概念以及如何使用它们来过滤图像开始。然后学习了如何使用它们对图像的每个像素执行数学运算以达到所需的效果,例如模糊或锐化。了解如何使用 OpenCV 实现 2D 滤波。了解身份内核后, 我们继续创建更多可与**filter2D()
** OpenCV 中的函数一起使用的自定义内核。探索了 OpenCV 中一些重要的内置过滤函数,例如**MedianBlur()
** 和**GaussianBlur()
** 。最后,在 OpenCV 中进行演示**bilateralFilter()
** ,看看它如何在保持清晰边缘的同时平滑图像。