PIL:Python Imaging Library,已经是Python平台事实上的图像处理标准库了。PIL功能非常强大,但API却非常简单易用。

Pillow的前身是PIL。由于PIL仅支持到python2.7,于是一些志愿者在在PIL的基础上创建了兼容的版本,名字叫Pillow,支持最新Python 3.x,又加入了许多新特性,因此,我们可以直接安装使用Pillow。

Pillow一般有以下几种用法:

  • 图像存储。PIL设计用于图像归档和图像批量处理,你可以使用它建立缩略图,转换格式,打印图片等等。
  • 图像显示。PIL包含Tk PhotoImage 和 BitmapImage 接口, 以及 Windows DIB interface ,这有助于在 Windows 下使用。
  • 图像处理。PIL包含了基本的图像处理功能,包括点操作,使用内置卷积内核过滤,色彩空间转换;也还支持更改图像大小、旋转、自由变换;还可以用于对比度增强和全局统计分析。

因为我们已经安装了anaconda,所以可以直接使用pillow。当然你也可以通过‘pip install pillow’安装。

相关的概念

PIL处理光栅图像——即像素数据的矩形。意思就是说PIL把每一个图像都转换成一个像素矩阵,对图像处理就是对该像素矩阵进行操作。在进行学习之前,我们想先了解一些概念。

通道(Bands):图像可以由一个或多个数据带组成。PIL运行你在单个图像中存储多个通道,但前提是它们都具有相同的尺寸和深度。如果想获取图像中通道的数量和名称,可以使用getbands()方法。

模式(Mode):图像的模式定义了图像中像素的类型和深度。目前PIL支持以下标准模式:

  • 1(1位像素,黑白,每像素以一个字节存储)
  • L(8位像素,黑白)
  • P(8位像素,使用调色板可以映射到任何其他模式)
  • RGB(3x8位像素,真彩色)
  • RGBA(4x8位像素,带透明度的真彩色)
  • CMYK(4x8位像素,分色)
  • YCbCr(3x8位像素,彩色视频格式)
  • I(32位有符号整数像素)
  • F(32位浮点像素)

我们可以通过mode属性读取图像的模式。 返回一个包含上述值之一的字符串。 S 大小(ize):你可以通过size属性读取图像大小。 这是一个2元组,包含像素的水平和垂直尺寸。

坐标系(Coordinate System):PIL使用笛卡尔像素坐标系,左上角是(0,0)。请注意,坐标指的是隐含的像素角点; 寻址为(0,0)的像素的中心实际上位于(0.5,0.5)处。坐标通常作为2元组(x,y)传递给PIL。矩形表示为4元组,首先给出左上角。例如,覆盖全部800x600像素图像的矩形写为(0,0,800,600)。

调色板(Palette):调色板模式(P)使用调色板来定义每个像素的实际颜色。

信息(Info):你可以使用info属性将辅助信息附加到图像上,info是一个字典对象。加载和保存图像文件时如何处理这些信息取决于文件格式处理程序。大多数处理程序在加载图像时向info属性添加属性,但在保存图像时忽略它。

过滤器(Filters):对于可能将多个输入像素映射到单个输出像素的几何操作,P提供了四种不同的重采样滤波器。

  • NEAREST。从输入图像中选取最近的像素。 忽略所有其他输入像素。
  • BILINEAR。在输入图像的2x2环境中使用线性插值。
  • BICUBIC。在输入图像的4x4环境中使用三次插值。
  • ANTIALIAS。使用高质量重采样滤波器(截断sinc)计算输出像素值,所有像素都可能对输出值有贡献。

使用Image类

PIL最重要的类是Image class, 你可以通过多种方法创建这个类的实例;你可以从文件加载图像,或者处理其他图像, 或者从scratch创建。

from PIL import Image
im = Image.open("lena.png")
print(im.format, im.size, im.mode)

format这个属性标识了图像来源。如果图像不是从文件读取它的值就是None。size属性是一个二元tuple,包含width和height(宽度和高度,单位都是px)。 mode属性定义了图像通道的数量和名称,以及像素类型和深度。常见的modes 有 “L” (luminance) 表示灰度图像, “RGB” 表示真彩色图像, and “CMYK” 表示出版图像。

如果文件打开错误,返回 IOError 错误。如果读取成功,我们就可以处理图像。比如显示图像:

im.show()

读写图像

PIL模块支持大量图片格式。使用在Image模块的open()函数从磁盘读取文件。你不需要知道文件格式就能打开它,这个库能够根据文件内容自动确定文件格式。要保存文件,使用 Image 类的 save() 方法。保存文件的时候文件名变得重要了。除非你指定格式,否则这个库将会以文件名的扩展名作为格式保存。

转换文件格式到JPEG:

import os, sys
from PIL import Image

for infile in sys.argv[1:]:
    f, e = os.path.splitext(infile)
    outfile = f + ".jpg"
    if infile != outfile:
        try:
            Image.open(infile).save(outfile)
        except IOError:
            print("cannot convert", infile)

save() 方法的第二个参数可以指定文件格式,如果你使用非标准的扩展名你必须这样做:创建JPEG缩略图

import os, sys
from PIL import Image

size = (128, 128)

for infile in sys.argv[1:]:
    outfile = os.path.splitext(infile)[0] + ".thumbnail"
    if infile != outfile:
        try:
            im = Image.open(infile)
            im.thumbnail(size)
            im.save(outfile, "JPEG")
        except IOError:
            print("cannot create thumbnail for", infile)

剪切、粘贴和合并图像

Image 类包含的方法允许你操作图像部分选区。我们可以使用crop()方法获取图像的一个子矩形选区。

box = (100, 100, 400, 400)
region = im.crop(box)
region.show()

矩形选区有一个4元元组定义,分别表示左、上、右、下的坐标。这个库以左上角为坐标原点,单位是px,所以上述代码复制了一个300x300px的矩形选区。这个选区现在可以被处理并且粘贴到原图。

region = region.transpose(Image.ROTATE_180)
im.paste(region, box)
im.show()

粘贴时需要注意:必须保持尺寸一致,同时,矩形选区不能在图像外。不过我们不用保证矩形选区和原图的颜色模式一致,为矩形选区会被自动转换颜色。

PIL还可以操作图像通道,下面是一个分离和合并颜色通道的例子:

r, g, b = im.split()
im = Image.merge("RGB", (b, g, r))

注意:对于单通道图像,split()返回的是图像本身。如果我们想使用单独的通道,我们可能首先要将图像转换为‘RGB’。

几何变换

PIL提供resize()和rotate()方法操作一个图像。传入resize()的是一个2元组表式返回图像大小,rotate传入的是一个逆时针度数。

out = im.resize((128,128))
out = im.rotate(45)

不知道你是否留言到,在这之前我们也使用过一个旋转的函数transpose():

out = im.transpose(Image.FLIP_LEFT_RIGHT)
out = im.transpose(Image.FLIP_TOP_BOTTOM)
out = im.transpose(Image.ROTATE_90)
out = im.transpose(Image.ROTATE_180)
out = im.transpose(Image.ROTATE_270)

transpose()和rotate()转换效率和效果都差不多

颜色变换

PIL允许使用convert()转换图像模式。

im = Image.open("lena.png")
cm = im.convert("L")

PIL支持“L”和“RGB”模式之间的转换。如果要在其他模式之间进行转换,有可能需要中间图像(通常是“RGB”)。

颜色增强

PIL也提供了一系列方法和模块用来增强图片。

过滤器

ImageFilter模块包含许多可以通过filter()方法使用的预定义增强过滤器。

from PIL import ImageFilter
out = im.filter(ImageFilter.DETAIL)

点操作

point()方法可以对每一个像素点进行操作(例如图像对比度操作)。在大多数情况下,需要一个参数的函数对象可以传递给此方法。 每个像素都根据该函数进行处理,有点想我们之前讲的map(),还记得吗?

out = im.point(lambda i: i * 1.2)

使用point()函数,我们可以快速的将一些简单的表达式应用于图像上。当然我们还可以结合使用point()和paste()方法来选择性地修改图像。

point()也可以处理单独的通道:

# 分离通道
source = im.split()

R, G, B = 0, 1, 2

# 选择红色小于100的区域
mask = source[R].point(lambda i: i < 100 and 255)

# 处理绿色通道
out = source[G].point(lambda i: i * 0.7)

# 将处理过的通道粘贴回去
source[G].paste(out, None, mask)

# 建立一个新的多通道图像
im = Image.merge(im.mode, source)

增强图像

对于更高级的图像增强,我们可以使用ImageEnhance模块中提供的类。我们可以用这种方式调整对比度,亮度,色彩平衡和清晰度。

from PIL import ImageEnhance

enh = ImageEnhance.Contrast(im)
enh.enhance(1.3).show("30% more contrast")

多帧图像序列

PIL包含对图像序列(也称为动画格式)的一些基本支持。支持的序列格式包括FLI/FLC,GIF和一些实验格式。TIFF文件也可以包含多个帧。

当我们打开一个序列文件时,PIL自动加载序列中的第一帧。 我们可以使用seek和tell方法在不同的帧之间移动:

from PIL import Image

im = Image.open("animation.gif")
im.seek(1)   # skip to the second frame

try:
    while 1:
        im.seek(im.tell()+1)
        im.show()   # do something to im
except EOFError:
    pass   # end of sequence

如果读取序列结束就会抛出EOFError。