这是系列文章的第三篇,参见系列中的相关内容。
这篇文章介绍如何利用 PIL 库,获取图像中的像素内容、修改后生成新的图像。此外,在修改的过程中,我们会引入卷积滤镜,进而引出 PIL 中的图像滤镜库 ImageFilter
。
获取像素内容
位图是有一个个像素组成的。因此,读入一张图片,实际上就是读入了一系列的像素内容。这些像素内容,按照不同的模式具有不同的格式。对于三通道的 RGB 位图来说,每个像素是一个 8-bit 整数的三元组。例如 rgb(0, 0, 0)
表示纯黑色,而 rgb(255, 255, 255)
则表示纯白色。
前文介绍过,Image.open()
可以打开一张图片,返回一个 Image
类的对象。那么,我们怎样获得这一图片的像素内容呢?
PIL 提供了 PIL.Image.getdata(band = None)
方法,用来获取 Image
类的对象中的像素内容。
该方法会将图片中的像素内容,逐行逐行地拼接起来(俗称降维打击),作为一个完整的序列返回。方法的返回类型,是 PIL 库的内部类型。我们可以用 list(im.getdata())
得到标准的 Python list
对象。
该方法的参数中,band
意味「通道」。当 band = None
时,方法返回所有通道的像素内容;当 band = 0
时,则返回第一个通道的像素内容。例如,对于 RGB 模式的位图,band = 0
返回 R 通道的内容;band = 2
返回 B
通道的内容。
示例代码:
1 | from PIL import Image |
可能的输出:
1 | [(130, 82, 8), (132, 84, 8), (136, 87, 10), (141, 90, 11), (143, 90, 10), (145, 90, 7), (144, 88, 3), (144, 87, 0), (147, 85, 0), (148, 84, 0)] |
写入像素内容
上一节介绍了如何从一个 Image
类的对象中获得像素内容。现在我们考虑它的镜像问题:如何将已知的像素内容写入一个新的 Image
类的对象。
在介绍 PIL.Image.getdata()
的过程中,我们提到,该方法返回的内容是一个一维的序列。这个过程,实际上丢失了图像的模式、尺寸等信息。那么在从像素内容恢复到 Image
类的对象的过程中,我们就必须补足这些信息。因此,我们首先需要获取原图像的模式和尺寸。
1 | from PIL import Image |
如此,我们就创建了一个新的 Image
类的对象。它的模式与 im
保持一致,尺寸则相对 im
长宽颠倒。现在,我们可以向 imn
中写入像素内容了。
PIL.Image.putdata(data, scale=1.0, offset=0.0)
方法允许我们将像素内容写入 Image
类的对象。
该方法将序列类型 data
拷贝进 Image
类的对象,直到 Image
类的对象容纳不下更多的像素或 data
内容已耗尽。scale
和 offset
则是针对每一个像素值进行调整:pixel = value * scale + offset
。
据此,我们可以写出完整的代码。首先来看看原图。
示例代码:
1 | from PIL import Image |
结果:
实际操作看看——实现卷积滤镜
用固定的矩阵扫描更大的矩阵,这个操作称为卷积。若后者是一张图片,那么这一操作,就是对图像的滤镜操作了。我们在前作中介绍了这种操作,此处我们来实践看看。
示例代码:
1 | from PIL import Image |
此处 read_image
函数从一个图像文件中读入其模式、尺寸及 RGB 三通道矩阵。convolve2d
函数利用了 scipy
库中的 signal.convolve2d
函数,对图像的单通道进行卷积操作——滤镜。filter_image
函数则是对 convolve2d
的封装,从上述模式、尺寸及 RGB 三通道矩阵开始,使用 kerner
作为卷积核进行滤镜操作,并将图片保存下来。
此处我们选择了三个卷积核。blur_5
将目标像素周围的 5*5 的像素平均起来,起到 box-模糊化的作用。shrp_3
加强了目标像素的作用,同时减弱了上下左右四个像素的干扰,起到了锐化的作用。dtct_3
则凸显了哪些与周围 8 个像素具有明显差异的像素,起到了边缘检测的作用。三个滤镜的效果可以参见:原图/模糊化/锐化/边缘检测。
使用 ImageFilter
预定义的滤镜
PIL 库在 ImageFilter
模块中已经为我们预定义好了一些滤镜。同时 Image
模块也提供了 filter
方法应用滤镜:Image.filter(filter_object)
。因此,我们可以用 ImageFilter
提供的 Kernel
滤镜,很方便地复现上一节中的滤镜效果。(Kernel
当前仅支持 33 或 55 的滤镜,sad)。
示例代码:
1 | from PIL import Image |
效果:
你可以在官方文档中找到更多的滤镜。