一、说明
Pillow 具有被 Python 社区广泛使用的优势,并且它不像其他一些图像处理库那样具有陡峭的学习曲线。应用PIL库的Image对象,益处很多,首先它可以处理网上URL文件,其次,图片可以方面转化成int32、64或float类型,因而可以方便实现变换和显示uint8。尤其和Tkinter搭配,用起来非常方便顺手。
二、内容提要
- 使用 Python Pillow 库进行基本图像操作
- Pillow 中的图像模块和图像类
- 基本图像处理
- Python Pillow 库中图像的波段和模式
- 在 Python 中使用 Pillow 进行图像处理
- 使用卷积核的图像过滤器
- 图像模糊、锐化和平滑
- 边缘检测、边缘增强和压花
- 图像分割和叠加:一个例子
- 图像阈值处理
- 腐蚀和膨胀
- 使用阈值分割图像
- 使用 Image.paste() 叠加图像
- 创建水印
- 使用 NumPy 和 Pillow 进行图像处理
- 使用 NumPy 相互减去图像
- 使用 NumPy 创建图像
- 创建动画
- 结论
三、使用 Python Pillow 库进行基本图像操作
Python Pillow 库是一个名为 PIL 的旧库的分支。PIL 代表 Python Imaging Library,它是使 Python 能够处理图像的原始库。PIL 于 2011 年停产,仅支持 Python 2。按照开发人员自己的描述,Pillow 是友好的 PIL 分支,它使库保持活力并包含对 Python 3 的支持。
Python 中有不止一个模块来处理图像并执行图像处理。如果您想通过操作像素直接处理图像,那么您可以使用NumPy和SciPy。其他流行的图像处理库是OpenCV、scikit-image和Mahotas。其中一些库比 Pillow 更快、更强大。
然而,Pillow 仍然是处理图像的重要工具。它提供的图像处理功能类似于 Photoshop 等图像处理软件中的功能。Pillow 通常是不需要更高级图像处理专业知识的高级图像处理任务的首选。它也经常用于处理图像时的探索性工作。
您需要先安装该库才能使用它。您可以pip在虚拟环境中使用以下命令安装 Pillow :
- 视窗
- Linux + macOS
PS> python -m venv venv
PS> .\venv\Scripts\activate
(venv) PS> python -m pip install Pillow
现在您已经安装了该软件包,您可以开始熟悉Python Pillow 库并执行基本的图像操作。
3.1 Pillow 中的模块Image
和Image
类
Pillow 中定义的主要类是Image类。当您使用 Pillow 读取图像时,图像将存储在 类型的对象中Image
。
对于本部分中的代码,您将需要名为buildings.jpg
( imagecredit ) 的图像文件,您可以在本教程的图像存储库中找到该文件:
获取图像: 单击此处访问您将使用 Pillow 操作和处理的图像。
您可以将此图像文件放置在您正在使用的项目文件夹中。
使用 Pillow 探索图像时,最好使用交互式 REPL 环境。首先打开刚刚下载的图像:
>>>
>>> from PIL import Image
>>> filename = "buildings.jpg"
>>> with Image.open(filename) as img:
... img.load()
...
>>> type(img)
<class 'PIL.JpegImagePlugin.JpegImageFile'>
>>> isinstance(img, Image.Image)
True
您可能希望从 Pillow 导入,而不是从 PIL 导入。Pillow
毕竟你确实安装了,而不是PIL
。然而,Pillow 是 PIL 库的一个分支。PIL
因此,在导入代码时您仍然需要使用。
您调用该open()函数从文件中读取图像并将.load()图像读入内存,以便现在可以关闭文件。您可以使用with
语句创建上下文管理器,以确保文件在不再需要时立即关闭。
在此示例中,对象是 JPEG 图像特定类型,它是该类的子类Image
,正如您通过调用 来确认的那样isinstance()
。请注意,类和定义该类的模块共享相同的名称Image
。您可以使用以下命令显示图像.show():
>>>
>>> img.show()
该.show()
方法将图像保存为临时文件,并使用操作系统的本机软件处理图像来显示它。当您运行上面的代码时,您将看到显示以下图像:
在某些系统上,调用.show()
将阻止 REPL,直到您关闭映像。这取决于操作系统和您使用的默认图像查看软件。
在处理 Python Pillow 库中的图像时,您需要熟悉三个关键属性。您可以使用Image
类属性.format、.size和来探索这些.mode:
>>>
>>> img.format
'JPEG'
>>> img.size
(1920, 1273)
>>> img.mode
'RGB'
图像的格式显示了您正在处理的图像类型。在本例中,图像的格式为'JPEG'
. 尺寸显示图像的宽度和高度(以像素为单位)。该图像的模式是'RGB'
. 您很快就会了解有关模式的更多信息。
通常,您可能需要裁剪图像并调整图像大小。该类有两个Image
方法可用于执行这些操作.crop():.resize()
>>>
>>> cropped_img = img.crop((300, 150, 700, 1000))
>>> cropped_img.size
(400, 850)
>>> cropped_img.show()
>>> low_res_img = cropped_img.resize(
... (cropped_img.width // 4, cropped_img.height // 4)
... )
>>> low_res_img.show()
的参数.crop()
必须是一个 4 元组,用于定义要裁剪的区域的左边缘、上边缘、右边缘和下边缘。Pillow 中使用的坐标系将坐标 (0, 0) 分配给左上角的像素。这与通常用于二维数组的坐标系相同。4 元组代表图像的以下部分:
.crop()
上面代码中返回的新图像的大小为400x850
像素。裁剪后的图像仅显示原始图片中的一栋建筑物:
在上面的代码中,您还可以使用 更改裁剪图像的分辨率.resize()
,这需要一个元组作为必需参数。用作参数的元组定义图像的新宽度和高度(以像素为单位)。
在上面的示例中,您使用楼层除法运算符( //
) 以及Image
属性.width和,将新的宽度和高度设置为其原始值的四分之一.height。最后调用show()
显示裁剪和调整大小的图像:
您可以使用其他可选参数来.resize()控制图像的重新采样方式。或者,您可以使用以下方法实现类似的缩放.reduce():
>>>
>>> low_res_img = cropped_img.reduce(4)
该参数决定了缩小图像的因子。如果您更喜欢设置最大尺寸而不是缩放因子,那么您可以使用.thumbnail()。缩略图的大小将小于或等于您设置的大小。
注意:该.thumbnail()
方法会Image
就地更改对象,并且不会返回新对象。但是,.crop()
、.resize()
和.reduce()
都返回一个新Image
对象。并非 Pillow 库中的所有方法都具有相同的行为方式。
Image
一旦您对返回的图像感到满意,您可以使用以下命令将任何对象保存到文件中.save():
>>>
>>> cropped_img.save("cropped_image.jpg")
>>> low_res_img.save("low_resolution_cropped_image.png")
调用该方法后,它会在项目文件夹中创建图像文件。在此示例中,其中一个图像是 JPEG 图像,另一个是 PNG 图像。用作文件名的扩展名会自动确定文件格式,或者您可以将格式指定为附加可选参数。
3.2 基本图像处理
除了裁剪和调整大小之外,您还可以对图像进行操作。另一个常见的要求是旋转或翻转图像。您可以使用该.transpose()方法进行一些转换。继续执行您在上一节中开始的相同 REPL 会话:
>>>
>>> converted_img = img.transpose(Image.FLIP_TOP_BOTTOM)
>>> converted_img.show()
此代码显示以下图像:
您可以将七个选项作为参数传递给.transpose()
:
Image.FLIP_LEFT_RIGHT
:将图像从左向右翻转,形成镜像Image.FLIP_TOP_BOTTOM
:将图像从上到下翻转Image.ROTATE_90
:将图像逆时针旋转 90 度Image.ROTATE_180
:将图像旋转 180 度Image.ROTATE_270
:将图像逆时针旋转 270 度,与顺时针旋转 90 度相同Image.TRANSPOSE
:以左上角像素为原点转置行和列,转置图像中的左上角像素与原始图像中的相同Image.TRANSVERSE
:使用左下像素作为原点转置行和列,左下像素是原始版本和修改版本之间保持固定的像素
上面的所有旋转选项都以 90 度为步长定义旋转。如果您需要将图像旋转另一个角度,那么您可以使用.rotate():
>>>
>>> rotated_img = img.rotate(45)
>>> rotated_img.show()
此方法调用将图像逆时针旋转 45 度,得到以下图像:
返回的对象Image
与原始对象大小相同Image
。因此,该显示中缺少图像的角点。您可以使用命名参数更改此行为expand
:
>>>
>>> rotated_img = img.rotate(45, expand=True)
>>> rotated_img.show()
此方法返回完全包含旋转图像的更大图像:
您可以使用其他可选参数进一步自定义旋转。您现在可以更改图像的大小和方向。在下一节中,您将了解 Python Pillow 库中不同类型的图像。
3.3 Python Pillow 库中图像的波段和模式
图像是像素的二维阵列,其中每个像素对应一种颜色。每个像素可以由一个或多个值表示。例如,在RGB图像中,每个像素由与该像素的红色、绿色和蓝色值相对应的三个值表示。
因此,Image
RBG 图像的对象包含三个波段,每个波段对应一种颜色。像素大小的 RGB 图像由值数组100x100
表示。100x100x3
RGBA图像还包括 alpha 值,其中包含有关每个像素透明度的信息。RGBA 图像有四个色带,一个色带代表每种颜色,第四个色带包含 Alpha 值。每个带具有与图像尺寸相同的尺寸。因此,尺寸为像素的 RGBA 图像由值数组100x100
表示。100x100x4
图像的模式描述了您正在使用的图像类型。Pillow 支持大多数标准模式,包括黑白(二进制)、灰度、RGB、RGBA 和CMYK。您可以在有关模式的 Pillow 文档中查看支持的模式的完整列表。
Image
您可以使用该方法找出对象中有多少个带.getbands(),并且可以使用在模式之间进行转换.convert()。现在,您将在本教程中使用图像存储库中名为strawberry.jpg
( image Credit )的图像:
该图像的模式也是 RGB。您可以将此图像转换为其他模式。此代码使用您在前面部分中启动的相同 REPL 会话:
>>>
>>> filename = "strawberry.jpg"
>>> with Image.open(filename) as img:
... img.load()
...
>>> cmyk_img = img.convert("CMYK")
>>> gray_img = img.convert("L") # Grayscale
>>> cmyk_img.show()
>>> gray_img.show()
>>> img.getbands()
('R', 'G', 'B')
>>> cmyk_img.getbands()
('C', 'M', 'Y', 'K')
>>> gray_img.getbands()
('L',)
您调用.convert()
两次将 RGB 图像转换为 CMYK 和灰度版本。CMYK 图像看起来与原始图像相似,但使用印刷材料而不是数字显示的常见模式进行编码。转换为灰度给出以下输出:
调用的输出.getbands()
确认 RGB 图像中有 3 个波段、CMYK 图像中有 4 个波段、灰度图像中有 1 个波段。
您可以使用 将图像分成多个波段.split(),并Image
使用 将单独的波段重新组合回对象中merge()。当您使用 时.split()
,该方法将所有带区作为单独的Image
对象返回。您可以通过显示返回的对象之一的字符串表示形式来确认这一点:
>>>
>>> red, green, blue = img.split()
>>> red
<PIL.Image.Image image mode=L size=1920x1281 at 0x7FDD80C9AFA0>
>>> red.mode
'L'
返回对象的模式.split()
为,表示这是一张灰度图像,或者是只显示每个像素的亮度'L'
值的图像。
merge()
现在,您可以使用模块中的函数创建三个新的 RGB 图像,分别显示红色、绿色和蓝色通道Image
:
>>>
>>> zeroed_band = red.point(lambda _: 0)
>>> red_merge = Image.merge(
... "RGB", (red, zeroed_band, zeroed_band)
... )
>>> green_merge = Image.merge(
... "RGB", (zeroed_band, green, zeroed_band)
... )
>>> blue_merge = Image.merge(
... "RGB", (zeroed_band, zeroed_band, blue)
... )
>>> red_merge.show()
>>> green_merge.show()
>>> blue_merge.show()
第一个参数merge()
确定您要创建的图像的模式。第二个参数包含要合并到单个图像中的各个波段。
单独存储在变量 中的红色波段red
是模式为 L 的灰度图像。要创建仅显示红色通道的图像,请将原始图像中的红色波段与仅包含零的绿色和蓝色波段合并。要创建一个到处都包含零的带,可以使用该.point()
方法。
该方法需要一个函数作为参数。您使用的函数决定每个点的变换方式。在本例中,您使用lambda函数将每个点映射到0
。
当您将红色波段与包含零的绿色和蓝色波段合并时,您会得到一个名为 的 RGB 图像red_merge
。因此,您创建的 RGB 图像仅在红色通道中具有非零值,但由于它仍然是 RGB 图像,因此它将以彩色显示。
您还可以重复类似的过程来获取green_merge
和blue_merge
,其中包含带有来自原始图像的绿色和蓝色通道的 RGB 图像。该代码显示以下三张图像:
红色图像在代表草莓的像素中包含强信号,因为这些像素大部分是红色的。绿色和蓝色通道将这些像素显示为黑色,因为它们的值较小。例外的是那些代表草莓表面上的光反射的像素,因为这些像素几乎是白色的。
创建本教程中所示的并排显示显示隐藏.