一、说明
该文是《在 Python 中使用 Pillow 进行图像处理》的第二部分,主要介绍pil库进行一般性处理:如:图像卷积、钝化、锐化、阈值分割。
二、在 Python 中使用 Pillow 进行图像处理
您已经学习了如何裁剪和旋转图像、调整图像大小以及从彩色图像中提取色带。但是,到目前为止您所采取的所有操作都没有对图像的内容进行任何更改。在本部分中,您将了解 Python Pillow 库中的图像处理功能。您将在 Pillow 中使用该ImageFilter模块。
2.1 使用卷积核的图像过滤器
图像处理中使用的方法之一是使用内核的图像卷积。本教程的目的不是详细解释图像处理理论。如果您对图像处理科学感兴趣,您可以使用的最佳资源之一是Gonzalez 和 Woods 的《数字图像处理》。
在本节中,您将学习如何使用卷积核执行图像处理的基础知识。但什么是卷积核呢?核是一个矩阵:
您可以考虑一个简单的图像来理解使用内核进行卷积的过程。该图像具有像素大小30x30
并包含一条垂直线和一个点。该线有四个像素宽,点由一个4x4
像素正方形组成。出于显示目的,下图已放大:
您可以将内核放置在图像上的任何位置,并使用内核中心单元的位置作为参考。下图是图像左上角部分的表示:
该图中的元素代表图像和内核的不同方面:
- 白色方块表示图像中值为 的像素
0
。 - 红色方块表示图像中值为 的像素
255
。这些构成了上图中的点。 - 每个紫色区域代表内核。该内核由一个
3x3
区域组成,内核中的每个单元格的值为1/9
。该图显示了标记为 1、2 和 3 的三个不同位置的内核。
图像与内核卷积的结果可以创建新图像。可以通过以下步骤来理解卷积过程:
- 定位内核:考虑内核位置之一并查看内核的九个单元覆盖的图像像素。
- 乘以内核和像素值:将每个内核单元中的值与图像中相应的像素值相乘。九次乘法将得到九个值。
- 乘法结果求和:将这九个值加在一起。结果将是新图像中与内核中心像素具有相同坐标的像素值。
- 对所有像素重复:对图像中的每个像素重复该过程,每次移动内核,以便内核的中心单元每次对应不同的图像像素。
您可以通过上图中标记为 1、2 和 3 的三个内核位置来查看此过程。考虑标记为 1 的内核位置。该内核的位置是(3, 2)
,这是其中心单元的位置,因为它位于第四行 (index = 3
) 和第三列 (index = 2
)。内核覆盖区域中的每个图像像素的值都为零。
因此,步骤 2 中的所有乘法都将为零,并且它们的加法也将为零。新图像的像素值为零(3, 2)
。
对于所示的其他内核位置,情况有所不同。接下来,考虑标记为 2 的内核,位于(4, 7)
。与此重叠的图像像素之一不为零。该像素值与核值相乘将得到255 x (1/9) = 28.33
。剩余的八次乘法仍然为零,因为图像像素为零。(4, 7)
因此,新图像中位置处的像素值为28.33
。
上面所示的第三个内核位置位于(8, 11)
。有四个非零图像像素与该内核重叠。每个像素位置的值为255
,因此乘法结果将再次28.33
针对每个像素位置。该内核位置的总体结果是28.33 x 4 = 113.33
。新图像的该值将为(8, 11)
。
该图和上面的讨论仅考虑了三个内核位置。卷积过程对图像中每个可能的内核位置重复此过程。这给出了新图像中每个像素位置的值。
卷积的结果如下图右侧所示,左侧为原始图像:
您使用的内核是框模糊内核。因子1/9
存在,因此内核的总体权重为1
。卷积的结果是原始图像的模糊版本。还有其他内核执行不同的功能,包括不同的模糊方法、边缘检测、锐化等。
Python Pillow 库有几个内置内核和函数,可以执行上述卷积。您无需了解通过卷积进行过滤的数学原理即可使用这些过滤器,但了解使用这些工具时幕后发生的情况总是有帮助的。
接下来的部分将介绍ImageFilterPillow 模块中可用的内核和图像过滤功能。
2.2 图像模糊、锐化和平滑
您将返回使用在本教程开始时使用的建筑物图像。您可以为此部分启动一个新的 REPL 会话:
>>>
>>> from PIL import Image, ImageFilter
>>> filename = "buildings.jpg"
>>> with Image.open(filename) as img:
... img.load()
...
除了 之外Image
,您还可以ImageFilter
从 Pillow 导入该模块。您可以使用该.filter()方法对图像应用过滤。此方法需要一个卷积核作为其参数,您可以使用 Pillow 模块中可用的几个内核之一ImageFilter。您将了解的第一组滤镜用于处理图像的模糊、锐化和平滑。
您可以使用预定义的滤镜模糊图像ImageFilter.BLUR
:
>>>
>>> blur_img = img.filter(ImageFilter.BLUR)
>>> blur_img.show()
显示的图像是原始图像的模糊版本。您可以使用放大以更详细地观察差异.crop()
,然后使用以下命令再次显示图像.show()
:
>>>
>>> img.crop((300, 300, 500, 500)).show()
>>> blur_img.crop((300, 300, 500, 500)).show()
两张裁剪后的图像显示了两个版本之间的差异:
ImageFilter.BoxBlur()您可以使用或自定义所需的模糊类型和数量ImageFilter.GaussianBlur():
>>>
>>> img.filter(ImageFilter.BoxBlur(5)).show()
>>> img.filter(ImageFilter.BoxBlur(20)).show()
>>> img.filter(ImageFilter.GaussianBlur(20)).show()
您可以看到下面的三个模糊图像,其显示顺序与上面代码中的顺序相同:
该.BoxBlur()
滤波器与上一节介绍卷积核时描述的滤波器类似。参数是框模糊滤镜的半径。在前面讨论内核的部分中,您使用的框模糊滤镜是一个3x3
滤镜。这意味着它的半径为1
,因为滤镜从中心延伸了一个像素。
模糊图像显示,半径为 的框模糊滤镜20
生成的图像比半径为 的框模糊滤镜生成的图像更模糊5
。
您还可以使用.GaussianBlur()
过滤器,它使用高斯模糊内核。高斯核对核中心像素的权重比边缘像素的权重更大,这会导致比框模糊获得的模糊更平滑。因此,高斯模糊在很多情况下可以给出更好的结果。
如果你想锐化图像怎么办?在这种情况下,您可以使用ImageFilter.SHARPEN
过滤器并将结果与原始图像进行比较:
>>>
>>> sharp_img = img.filter(ImageFilter.SHARPEN)
>>> img.crop((300, 300, 500, 500)).show()
>>> sharp_img.crop((300, 300, 500, 500)).show()
您正在比较两个图像的裁剪版本,显示建筑物的一小部分。锐化后的图像如右图所示:
也许您需要平滑图像,而不是锐化图像。ImageFilter.SMOOTH
您可以通过作为参数传递来实现此目的.filter()
:
>>>
>>> smooth_img = img.filter(ImageFilter.SMOOTH)
>>> img.crop((300, 300, 500, 500)).show()
>>> smooth_img.crop((300, 300, 500, 500)).show()
下面,您可以看到左侧为原始图像,右侧为平滑后的图像:
您将在下一节中看到平滑过滤器的应用,其中您将了解模块中的更多过滤器ImageFilter
。这些滤镜作用于图像中对象的边缘。
2.3 边缘检测、边缘增强和压花
当您查看图像时,确定该图像中对象的边缘相对容易。算法也可以使用边缘检测内核自动检测边缘。
Pillow 中的模块ImageFilter
有一个预定义的内核来实现此目的。在本部分中,您将再次使用建筑物图像并将其转换为灰度,然后再应用边缘检测滤镜。您可以继续上一节中的 REPL 会话:
>>> img_gray = img.convert("L")
>>> edges = img_gray.filter(ImageFilter.FIND_EDGES)
>>> edges.show()
结果是显示原始图像边缘的图像:
该过滤器识别图像中的边缘。ImageFilter.SMOOTH
在查找边缘之前应用过滤器可以获得更好的结果:
>>>
>>> img_gray_smooth = img_gray.filter(ImageFilter.SMOOTH)
>>> edges_smooth = img_gray_smooth.filter(ImageFilter.FIND_EDGES)
>>> edges_smooth.show()
您可以在下面看到原始灰度图像和两个边缘检测结果的比较。边缘检测之前进行平滑的版本显示在底部:
您还可以使用滤镜增强原始图像的边缘ImageFilter.EDGE_ENHANCE
:
>>>
>>> edge_enhance = img_gray_smooth.filter(ImageFilter.EDGE_ENHANCE)
>>> edge_enhance.show()
您使用灰度图像的平滑版本来增强边缘。下面并排显示了原始灰度图像的一部分和边缘增强的图像。右侧是经过边缘增强的图像:
另一个ImageFilter
处理对象边缘的预定义过滤器是ImageFilter.EMBOSS
。您可以将其作为参数传递给.filter()
本节中的其他过滤器:
>>>
>>> emboss = img_gray_smooth.filter(ImageFilter.EMBOSS)
>>> emboss.show()
您使用平滑的灰度版本作为此过滤器的起点。您可以看到下面的浮雕图像,它使用图像中的边缘显示了不同的效果:
在本节中,您了解了ImageFilter
模块中可应用于图像的几个可用过滤器。您还可以使用其他过滤器来处理图像。ImageFilter您可以在文档中查看所有可用过滤器的列表。
三、图像分割和叠加
3.1 先给出一个例子
cat.jpg
在本部分中,您将使用名为( imagecredit ) 和monastery.jpg
( imagecredit )的图像文件,您可以在本教程的图像存储库中找到这些文件:
获取图像: 单击此处访问您将使用 Pillow 操作和处理的图像。
这是两张图片:
您可以使用 Python Pillow 库从第一张图像中提取猫并将其放置在修道院庭院的地板上。您将使用多种图像处理技术来实现此目的。
3.2 图像阈值处理
您将从工作开始cat.jpg
。您需要使用图像分割技术从背景中删除猫的图片。在此示例中,您将使用阈值技术分割图像。
首先,您可以将图像裁剪为较小的图像以删除一些背景。您可以为此项目启动一个新的 REPL 会话:
>>> from PIL import Image
>>> filename_cat = "cat.jpg"
>>> with Image.open(filename_cat) as img_cat:
... img_cat.load()
...
>>> img_cat = img_cat.crop((800, 0, 1650, 1281))
>>> img_cat.show()
裁剪后的图像包含猫和一些距离猫太近而无法裁剪的背景:
彩色图像中的每个像素都由与该像素的红色、绿色和蓝色值相对应的三个数字来数字表示。阈值处理是将所有像素转换为最大值或最小值的过程,具体取决于它们是否高于或低于某个数字。在灰度图像上执行此操作更容易:
>>> img_cat_gray = img_cat.convert("L")
>>> img_cat_gray.show()
>>> threshold = 100
>>> img_cat_threshold = img_cat_gray.point(
... lambda x: 255 if x > threshold else 0
... )
>>> img_cat_threshold.show()
您可以通过调用将.point()
灰度图像中的每个像素转换为 或 来255
实现阈值化0
。转换取决于灰度图像中的值是大于还是小于阈值。本例中的阈值是100
。
下图显示了灰度图像和阈值处理的结果:
在此示例中,灰度图像中像素值大于 的所有点都100
转换为白色,所有其他像素都更改为黑色。您可以通过改变阈值来更改阈值处理的灵敏度。
当要分割的对象与背景不同时,可以使用阈值来分割图像。您可以使用具有更高对比度的原始图像版本获得更好的结果。在此示例中,您可以通过对原始图像的蓝色通道而不是灰度图像进行阈值化来实现更高的对比度,因为背景中的主色是棕色和绿色,其中蓝色成分较弱。
您可以像之前一样从彩色图像中提取红色、绿色和蓝色通道:
>>> red, green, blue = img_cat.split()
>>> red.show()
>>> green.show()
>>> blue.show()
下面从左到右显示了红色、绿色和蓝色通道。所有三个都显示为灰度图像:
蓝色通道在代表猫的像素和代表背景的像素之间具有更高的对比度。您可以使用蓝色通道图像来阈值:
>>> threshold = 57
>>> img_cat_threshold = blue.point(lambda x: 255 if x > threshold else 0)
>>> img_cat_threshold = img_cat_threshold.convert("1")
>>> img_cat_threshold.show()
在此示例中,您使用阈值57
。"1"
您还可以使用 的参数将图像转换为二进制模式.convert()
。二值图像中的像素只能具有0
或的值1
。
注意:处理某些依赖有损压缩的图像格式(例如 JPEG)时,图像可能会略有不同,具体取决于您使用的 JPEG 解码器。不同的操作系统通常具有不同的默认 JPEG 解码器。因此,处理图像时获得的结果可能会有所不同,具体取决于您使用的操作系统和 JPEG 解码器。
如果您的结果与本教程中显示的结果不匹配,您可能需要稍微调整阈值。
阈值化的结果如下:
您可以在这张黑白图像中识别出这只猫。但是,您希望图像中与猫对应的所有像素都是白色,而所有其他像素都是黑色。在此图像中,与猫相对应的区域仍然有黑色区域,例如眼睛、鼻子和嘴巴所在的位置,并且图像中的其他位置仍然有白色像素。
您可以使用称为腐蚀和膨胀的图像处理技术来创建更好的代表猫的蒙版。您将在下一节中了解这两种技术。