Pillow概况
PIL是Python的一种图像处理工具。
PIL支持大部分的图像格式,高效并强大。
核心库设计用来高速访问基于基于像素的数据存储,给这个通用的图像处理工具提供了坚实的基础。
来看下这个库的一般用途:
图像归档
PIL是较为理想的图片归档和批处理应用。你可以使用这个库去生成缩略图、转换图片格式、打印图像等。
当前版本可以识别和读取大量的格式。写操作被限制用于大多数通用的转换处理和显示格式上。
图像展示
当前发行版本包含TkPhotoImage
和BitmapImage
接口,这个和Windows DIB interface
一样,可以用于PythonWin
和其他基于窗口的程序。许多其他的GUI程序都是基于PIL。
为了调试方便,PIL提供了show()
,用来保存图片到硬盘和调用外部图片查看器。
图形处理
PIL包含基本的图像处理功能,包括像素操作、一套内置卷积核的滤镜、色空间转换。
PIL支持图像缩放、旋转、任意角度的转换。
PIL支持用于统计图像的直方图。这个可以用来自动自动对比增强、全局统计分析。
安装
使用PIP安装
pip install Pillow
使用
使用Image类
Image类是PIL最核心的类,定义在同名的模块中。你可以创建这个类实例,可以通过加载图片文件、处理其他Image实例、或新建一个空的Image实例。
为了从图片文件加载Image类,需要使用Image
模块中的open()
方法。
>>> from PIL import Image
>>> im = Image.open("hopper.ppm")
如果加载成功,这个函数会返回一个Image对象。你可以使用实例属性去查看文件内容。
>>> from __future__ import print_function
>>> print(im.format, im.size, im.mode)
PPM (512, 512) RGB
format
属性定义了图片来源格式。如果实例没有读取文件,format
就是None
。size属性是一个两个元素的元组,包含像素计的宽高。mode属性定义了图像通道的编号和名字,以及像素类型和深度。常用模式有用于灰度图的”L”(luminance)、用于真彩图的”RGB”、用于预印刷图的”CMYK”模式。
如果你没有打开文件,会触发一个IOError
异常。
一旦你有了一个Image
实例,你就可以使用类里的方法来处理和维护图像了。例如,让我们显示图像,我们仅仅加载如下:
>>> im.show()
这个版本的
show()
不是非常高效,因为他是保存图像到一个临时文件,然后调用xv协助去显示图像。如果你没有安装xv,他将不会工作。尽管他工作了,也会非常难以调试和测试。
下一节介绍PIL提供的几种不用的方法。
读写图像
PIL支持广泛的图片格式。为了从硬盘读取文件,需要使用Image
模块中的open()
方法。你不必知道文件格式就可以打开文件。PIL会根据文件内容自动判断文件格式。
可以使用Image
模块里的save()
方法来保存文件。保存文件的时候,命名会很重要。除非你规定了格式,PIL会用文件扩展名来存储。
转换图片为JPEG格式
from __future__ import print_function
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缩略图
from __future__ import print_function
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)
PIL不解码加载光栅数据,除非必须这样。当你打开一个文件,在文件头里就可以判断出文件格式,并抽取出像模式、大小和其他需要解码文件的属性,但是文件的其他部分不会被处理,除非以后需要。
这个意思就是打开图像文件是一个很快速的操作,这个跟文件大小和压缩方式无关。这里举几个简单的例子,快速识别一批图像文件。
识别图像文件
from __future__ import print_function
import sys
from PIL import Image
for infile in sys.argv[1:]:
try:
with Image.open(infile) as im:
print(infile, im.format, "%dx%d" % im.size, im.mode)
except IOError:
pass
剪切粘贴和合并图像
Image
类包含维护图像里区块的方法。为了从图像里抽取一个子矩形,使用crop()
方法。
复制一个子矩形
box = (100, 100, 400, 400)
region = im.crop(box)
使用包含4个元素的元组来定义区块,相匹配的坐标是(左上右下)。PIL使用一个坐标系统,原点位于左上角。坐标系统使用像素来引用位置,所以上例中的区块大小是300x300像素。
现在这个区块可以以某种方式处理和粘贴了。
处理子矩形并粘贴
region = region.transpose(Image.ROTATE_180)
im.paste(region, box)
当粘贴回的时候,区块大小必须精确匹配已给的区块大小。另外,这个区块不能扩展到外部图像。但是,原始图像的模式必须和区块的模式匹配。如果他们比匹配,区块会提前动转换为匹配的模式。
附件例子:
旋转图像
def roll(image, delta):
"Roll an image sideways"
xsize, ysize = image.size
delta = delta % xsize
if delta == 0: return image
part1 = image.crop((0, 0, delta, ysize))
part2 = image.crop((delta, 0, xsize, ysize))
part1.load()
part2.load()
image.paste(part2, (0, 0, xsize-delta, ysize))
image.paste(part1, (xsize-delta, 0, xsize, ysize))
return image
当从crop()
粘贴回的时候,要先调用load()
。这是因为剪切是一个惰性操作。如果load()
没有被调用,剪切操作就不会执行,直到有粘贴命令执行。这就意味着part1
会被首先修改的image
剪切。
对于更高级的技巧,粘贴命令可以将透明遮罩作为可选参数。在这个遮罩里面,255表明在这个位置上粘贴的图像是不透明的。0表明粘贴的图像是完全透明的。在这两个值之间的数值表明不同级别的透明度。例如,粘贴一张RGBA图像,并使用它作为遮罩,将会粘贴图像不透明的部分,而不是他的透明背景。
PIL也可以让你处理单个或多个通道图像,例如RGB图像。split
方法创建了一套新的图像,每一个包含一个来源于原始多通道图像里的单个通道。merge
方法采用一种模式和一组图像,然后合并他们魏一张新图像。下面的样本代码交换了RGB图像的三种通道。
分割合并通道
r, g, b = im.split()
im = Image.merge("RGB", (b, g, r))
对于单通道图像 ,split()
会返回图像本身。为作用于单色通道,你可以先转换图像为”RGB”模式。
几何变换
PIL.Image.Image
类包含一些变形和旋转图像方法resize()
、rotate()
。前者以一个元组为新尺寸,后者以逆时针的角度为角度。
简单的几何变换
out = im.resize((128, 128))
out = im.rotate(45) # degrees counter-clockwise
旋转图像90度的步骤里,你可以使用rotate()
方法或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)
也可以执行相同于rotate()
的功能,如果expand
标签为真,则图像尺寸有相同的更改。
更详细的图像转换,可以查阅transform()
方法说明。
颜色转换
PIL可以使用convert()
方法在不同的像素表示之间转换。
转换图像模式
im = Image.open("hopper.ppm").convert("L")
该库支持每个受支持的模式、“L”和“RGB”模式之间的转换。
要在其他模式之间转换,您可能需要使用中间图像(通常是“RGB”图像)。
图象增强
PIL提供了许多方法和模块,可用于增强图像。
滤镜
ImageFilter
模块包含一些预定义的增强滤镜,可以与filter()
方法一起使用。
应用滤镜
from PIL import ImageFilter
out = im.filter(ImageFilter.DETAIL)
点运算
point()
方法可以用来转换图像的像素值(例如图像对比操作)。在大多数情况下,期望一个参数的函数对象可以传递给这个方法。根据这个函数处理每个像素:
应用点转换
# multiply each pixel by 1.2
out = im.point(lambda i: i * 1.2)
使用上面的技术,您可以快速地将任何简单的表达式应用于图像。您还可以组合point()
和paste()
方法来选择性地修改图像:
处理单通道
# split the image into individual bands
source = im.split()
R, G, B = 0, 1, 2
# select regions where red is less than 100
mask = source[R].point(lambda i: i < 100 and 255)
# process the green band
out = source[G].point(lambda i: i * 0.7)
# paste the processed band back, but only where red was < 100
source[G].paste(out, None, mask)
# build a new multiband image
im = Image.merge(im.mode, source)
注意用于创建掩码的语法:
imout = im.point(lambda i: expression and 255)
Python只对逻辑表达式的部分进行评估,以确定结果,并返回所检查的最后一个值作为表达式的结果。因此,如果上面的表达式是假(0),那么Python不会查看第二个操作数,从而返回0。否则,它将返回255。
增强
对于更高级的图像增强,您可以使用ImageEnhance
模块中的类。一旦从图像中创建了,就可以使用增强对象快速尝试不同的设置。
你可以通过这种方式调整对比度、亮度、色彩平衡和清晰度。
增强图像
from PIL import ImageEnhance
enh = ImageEnhance.Contrast(im)
enh.enhance(1.3).show("30% more contrast")
图像序列
PIL包含一些对图像序列的基本支持(也可以叫做动画格式)。支持的序列格式包括FLI/FLC、GIF和几个实验格式。TIFF文件还可以包含多个帧。
当您打开一个序列文件时,它会自动加载序列中的第一个帧。您可以使用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)
# do something to im
except EOFError:
pass # end of sequence
如本例中所示,当序列结束时,您将得到一个EOFError异常。
注意,当前版本库中的大多数驱动程序只允许您寻找下一帧(如上面示例中所示)。要重新修改文件,您可能需要重新打开它。
下面的类让您使用for语句来对序列进行循环:
使用ImageSequence迭代器类
from PIL import ImageSequence
for frame in ImageSequence.Iterator(im):
# ...do something to frame...
Postscript打印
PIL包括打印图像、文字和在Postscript打印机上绘图的功能。这是一个简单的例子:
画Postscript
from PIL import Image
from PIL import PSDraw
im = Image.open("hopper.ppm")
title = "hopper"
box = (1*72, 2*72, 7*72, 10*72) # in points
ps = PSDraw.PSDraw() # default is sys.stdout
ps.begin_document(title)
# draw the image (75 dpi)
ps.image(box, im, 75)
ps.rectangle(box)
# draw title
ps.setfont("HelveticaNarrow-Bold", 36)
ps.text((3*72, 4*72), title)
ps.end_document()
更多关于读取图像
如前所述,Image
模块的open()
函数用于打开图像文件。在大多数情况下,您只需将文件名作为参数传过去:
im = Image.open("hopper.ppm")
如果一切顺利,结果就是一个PIL.Image.Image
对象。否则,将抛出IOError异常。
您可以使用类似文件的对象而不是文件名。对象必须实现read()
、seek()
和tell()
方法,并以二进制模式打开。
从一个打开的文件读取
fp = open("hopper.ppm", "rb")
im = Image.open(fp)
要从字符串数据中读取图像,使用StringIO
类:
从字符串读取
import StringIO
im = Image.open(StringIO.StringIO(buffer))
请注意,在读取图像头之前,库会重新调整文件(使用seek(0)
)。此外,还可以在读取图像数据时使用seek(通过load方法)。
如果图像文件嵌入到较大的文件中,如tar文件,则可以使用ContainerIO
或TarIO
模块来访问它。
从tar压缩包里读取
from PIL import Image, TarIO
fp = TarIO.TarIO("Tests/images/hopper.tar", "hopper.jpg")
im = Image.open(fp)
控制解码器
一些解码器允许您在从文件中读取图像时对图像进行操作。在创建缩略图(通常速度比质量更重要)和打印到一个单色激光打印机(当需要只需要一个灰度版本的图像时),这通常可以用于加速解码。
draft()
方法操作一个已打开但尚未加载的图像,以便尽可能地匹配给定的模式和大小。这是通过重新配置图像解码器来完成的。
以草稿模式读取
这只适用于JPEG和MPO文件。
from PIL import Image
from __future__ import print_function
im = Image.open(file)
print("original =", im.mode, im.size)
im.draft("L", (100, 100))
print("draft =", im.mode, im.size)
这个打印之类的:
original = RGB (512, 512)
draft = L (128, 128)
注意,生成的图像可能与请求的模式和大小不完全匹配。为了确保图像不大于给定的大小,可以使用缩略图方法。
举例
微信验证码地址
https://mp.weixin.qq.com/mp/verifycode?cert=1494918122899.514
im=Image.open('dog.jpg')
draw=ImageDraw.Draw(im)
newfont=ImageFont.truetype('simkai.ttf',40) #设置字体,simkai为楷体,字体大小40,truetype相关知识可百度
draw.text((200,100),'you are so good!',(255,255,0),font=newfont) #第一个tuple表示要写在哪里,(left,up),之后写的#文字,颜色为黄色,三通道设置可以百度,第<span style="white-space:pre"> </span> #一个是红色,第二个绿色,第三个是蓝色,从0到255,最后设置字体
im.show()
im.save('target.jpg')