这是系列文章的第三篇,参见系列中的相关内容。
这篇文章介绍如何利用 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 | 
效果:

你可以在官方文档中找到更多的滤镜。
 
         
        