Python-图像库 PIL
- Python 提供了 PIL(python image library)图像库,来满足开发者处理图像的功能,该库提供了广泛的文件格式支持,包括常见的 JPEG、PNG、GIF 等,它提供了图像创建、图像显示、图像处理等功能。
-
基本概念
要学习 PIL 图像库的使用,我们必须先来了解一些关于图像的基本概念,包括深度(depth),通道(bands),模式(mode),坐标系统(coordinate system)等。
-
图像的深度
图像中像素点占得 bit 位数,就是图像的深度,比如:
二值图像:图像的像素点不是0就是1 (图像不是黑色就是白色),图像像素点占的位数就是1位,图像的深度就是1,也称作位图。
灰度图像:图像的像素点位于0-255之间(0代表全黑,255代表全白,在0-255之间插入了255个等级的灰度)。2^8=255,图像像素点占的位数就是8位,图像的深度是8。
依次类推,我们把计算机中存储单个像素点所用的 bit 位称为图像的深度。
-
图像的通道
每张图像都是有一个或者多个数据通道构成的,如 RGB 是基本的三原色(红色、绿色和蓝色),如果我们用8位代表一种颜色,那么每种颜色的最大值是255,这样,每个像素点的颜色值范围就是(0-255, 0-255, 0-255)。这样的图像的通道就是3。而灰度图像的通道数是1。
-
图像的模式
图像实际上是像素数据的矩形图,图像的模式定义了图像中像素的类型和深度,每种类型代表不同的深度,在 PIL 中我们称之为图像的模式。常见的模式有以下几种:
-
图像的坐标系
PIL 中图像的坐标是从左上角开始,向右下角延伸,以二元组 (x,y)的形式传递,x 轴从左到右,y 轴从上到下,即左上角的坐标为 (0, 0)。那么矩形用四元组表示就行,例如一个450 x 450 像素的矩形图像可以表示为 (0, 0, 450, 450)。
-
PIL 的安装
和其他库一样,PIL 的安装也很简单:
-
pip3 install pillow
-
PIL 图像模块的功能
打开图像
我们可以从本地目录中打开文件,也可以从文件流中打开图像。打开文件的方法为:
-
Image.open(file,mode)
- 读取图像文件,mode 只能是 ‘r’,所以我们也可以省略这个参数。
-
from PIL import Image from io import BytesIO import requests # 打开图像文件 im = Image.open('cat.jpg') # 从文件流中打开图像 r = requests.get('http://f.hiphotos.baidu.com/image/pic/item/b151f8198618367aa7f3cc7424738bd4b31ce525.jpg') im2 = Image.open(BytesIO(r.content)) # 展示图像 im.show() im2.show() # 翻转90度展示 im.rotate(90).show()
- 我们首先打开本目录下的 cat.jpg 图像,接着从百度图片请求到一张图片,使用文件流的方式打开。使用 show 方法可以展示图像。我们也可以使用 rotate 方法来是图像翻转角度。运行程序,我们会看到弹出三张图片,一张是 cat.jpg 对应的图像,一张是百度图片中的图像,还有一种是将 cat.jpg 翻转90度后展示的图像。
-
创建图像
Image.new(mode,size,color)
我们可以使用给定的模式、大小和颜色来创建新图像。大小以(宽度,高度)的二元组形式给出,单位为像素;颜色以单波段图像的单个值和多波段图像的元组(每个波段的一个值)给出,可以使用颜色名如 ‘red’ ,也可以受用16进制 '#FF0000' 或者使用数字表示(255,0,0)。
from PIL import Image im = Image.new('RGB', (450, 450), (255, 0, 0)) im1 = Image.new('RGB', (450, 450), 'red') im2 = Image.new('RGB', (450, 450), '#FF0000') im.show() im1.show() im2.show()
上面例子中我们分别通过三种形式创建了 RGB 模式的大小为 450x450 ,颜色为红色的图像,最终的图像效果是一样的。
-
转换格式
Image.save(file)
我们直接使用保存方法,修改保存的文件名就可以转换图像的格式。
-
from PIL import Image # 加载 cat.jpg im = Image.open('cat.jpg', 'r') # 打印图片类型 print(im.format) # 保存为 png 类型图片 im.save('cat.png') # 加载新保存的 png 类型图片 im2 = Image.open('cat.png', 'r') # 打印新保存图片类型 print(im2.format) # 输出结果 JPEG PNG
- 例子中我们先打开 cat.jpg 图像,然后新保存一张类型为 png 的图像,通过打印我们可以看到两者的格式。
-
创建缩略图
Image.thumbnail(size, resample=3)
修改当前图像制作成缩略图,该缩略图尺寸不大于给定的尺寸。这个方法会计算一个合适的缩略图尺寸,使其符合当前图像的宽高比,调用方法 draft() 配置文件读取器,最后改变图像的尺寸。
size 参数表示给定的最终缩略图大小。
resample 参数是过滤器,只能是 NEAREST、BILINEAR、BICUBIC 或者 ANTIALIAS 之一。如果省略该变量,则默认为 NEAREST。
注意:在当前PIL的版本中,滤波器 BILINEAR 和 BICUBIC 不能很好地适应缩略图产生。用户应该使 用ANTIALIAS,图像质量最好。如果处理速度比图像质量更重要,可以选用其他滤波器。这个方法在原图上进行修改。
-
from PIL import Image
# 加载图像
im = Image.open('cat.png')
# 展示图像
im.show()
# 图像尺寸
size = 128, 128
# 缩放图像
im.thumbnail(size, Image.ANTIALIAS)
# 展示图像
im.show()
-
融合图像
Image.blend(image1, image2, alpha)
将图像 image1 和 图像 im2 根据 alpha 值进行融合,公式为:
out = image1 * (1.0 - alpha) + image2 * alpha
image1 和 image2 表示两个大小和模式相同的图像, alpha 是介于 0 和 1 之间的值。如果 alpha 为0,返回 image1 图像,如果 alpha 为1,返回 image2 图像。
-
from PIL import Image # 蓝色图像 image1 = Image.new('RGB', (128, 128), (0, 0, 255)) # 红色图像 image2=Image.new('RGB', (128, 128), (255, 0, 0)) # 取中间值 im = Image.blend(image1, image2, 0.5) image1.show() image2.show() # 显示紫色图像 im.show()
-
像素点处理
Image.eval(image, *args)
根据传入的函数对图像每个像素点进行处理。第一个参数 image 为需要处理的图像对象,第二个参数是函数对象,有一个整数作为参数。
如果变量image所代表图像有多个通道,那么函数作用于每一个通道。注意:函数对每个像素点只处理一次,所以不能使用随机组件和其他生成器。
-
from PIL import Image im = Image.open('cat.jpg') im.show() # 将每个像素值翻倍(相当于亮度翻倍) evl1 = Image.eval(im, lambda x: x*2) evl1.show() # 将每个像素值减半(相当于亮度减半) evl2 = Image.eval(im, lambda x: x/2) evl2.show()
-
合成图像
Image.composite(image1, image2, mask)
使用给定的两张图像及 mask 图像作为透明度,创建出一张新的图像。变量 mask 图像的模式可以为“1”,“L” 或者 “RGBA”。所有图像必须有相同的尺寸。
-
from PIL import Image # 打开 cat.png image1 = Image.open('cat.png') # 打开 flower.jpg image2 = Image.open('flower.jpg') # 分离image1的通道 r, g, b = image1.split() # 合成图像,获得 cat + flower im = Image.composite(image1, image2, mask=b) image1.show() image2.show() im.show()
- 上面例子中我们将一张图像猫(cat.png)和一张图像花(flower.jpg),以图像猫的一个通道构成的蒙版进行合成,就像 PS 一样,我们最终得到猫+花的图像,结果如下图所示:
-
通过单通道创建图像
Image.merge(mode,bands)
将一组单通道图像合并成多通道图像。参数 mode 为输出图像的模式,bands 为输出图像中每个通道的序列。
-
from PIL import Image im = Image.open('cat.png') # 将三个通道分开 im_split = im.split() # 分别显示三个单通道图像 im_split[0].show() im_split[1].show() im_split[2].show() # 将三个通道再次合并 im2 = Image.merge('RGB', im_split) im2.show() # 打开第二张图像 im3 = Image.open('flower.jpg') # 将第二张图像的三个通道分开 im_split2 = im3.split() # 将第二张图像的第1个通道和第一张图像的第2、3通道合成一张图像 rgbs = [im_split2[0], im_split[1], im_split[2]] im4 = Image.merge('RGB', rgbs) im4.show()
-
Image 模块的方法
convert
Image.convert(mode=None, matrix=None, dither=None, palette=0, colors=256)
参数说明:
- mode:转换的模式
- matrix:可选转变矩阵。如果给出,必须为包含浮点值长为 4 或 12 的元组。
- dither:抖动方法。RGB 转换为 P;RGB 或 L 转换为 1 时使用。有 matrix 参数可以无 dither。参数值 NONE 或 FLOYDSTEINBERG(默认)。
- palette:调色板,在 RGB 转换为 P 时使用, 值为 WEB 或 ADAPTIVE 。
- colors:调色板的颜色值,默认 256.
转换图片模式,它支持每种模式转换为"L" 、 "RGB"和 "CMYK"。有 matrix 参数只能转换为"L" 或 "RGB"。当模式之间不能转换时,可以先转换 RGB 模式,然后在转换。色彩模式转换为 L 模式计算公式 如下:
L = R * 299/1000 + G * 587/1000 + B * 114/1000
我们一般使用时,只用传需要转换的 mode 即可,其他的可选参数需要先理解图片深层次的原理后才可以理解,大家如果感兴趣可以去深入了解一下。下面我们来看一个简单实例:
-
from PIL import Image im = Image.open('cat.png') im.show() # 将图像转换成黑白色并返回新图像 im1 = im.convert('L') im1.show()
- copy
Image.copy()
复制图像方法,该方法完全复制一个一模一样的图像,很好理解,我们就不举例说明了。
crop
Image.crop(box)
参数说明:
- box:相对图像左上角坐标为(0,0)的矩形坐标元组, 顺序为(左, 上, 右, 下)
该方法从图像中获取 box 矩形区域的图像,相当于从图像中抠一个矩形区域出来。我们来看例子:
-
filter
Image.filter(filter)
参数说明:
- filter:过滤内核
使用给定的筛选器筛选此图像。有关可用筛选器的列表:
筛选器名称 说明 BLUR 模糊滤波,处理之后的图像会整体变得模糊。 CONTOUR 轮廓滤波,将图像中的轮廓信息全部提取出来。 DETAIL 细节增强滤波,会使得图像中细节更加明显。 EDGE_ENHANCE 边缘增强滤波,突出、加强和改善图像中不同灰度区域之间的边界和轮廓的图像增强方法。 EDGE_ENHANCE_MORE 深度边缘增强滤波,会使得图像中边缘部分更加明显。 EMBOSS 浮雕滤波,会使图像呈现出浮雕效果。 FIND_EDGES 寻找边缘信息的滤波,会找出图像中的边缘信息。 SHARPEN 锐化滤波,补偿图像的轮廓,增强图像的边缘及灰度跳变的部分,使图像变得清晰。 SMOOTH 平滑滤波,突出图像的宽大区域、低频成分、主干部分或抑制图像噪声和干扰高频成分,使图像亮度平缓渐变,减小突变梯度,改善图像质量。 SMOOTH_MORE 深度平滑滤波,会使得图像变得更加平滑。 看到这些,大家是不是联想到我们手机上一些 APP 的图像处理功能了,其实那些功能的实现方式跟我们这里讲的方法是一样的。我们来看个例子:
-
getbands
Image.getbands()
返回一个包含此图像中每个通道名称的元组。直接看实例:
-
from PIL import Image # 打开图像 im = Image.open('cat.jpg') # 创建新图像 im1 = Image.new('L', (450, 450), 50) # 获取图像的通道名称元组 print(im.getbands()) print(im1.getbands()) # 输出结果 ('R', 'G', 'B') ('L',)
-
getbbox
Image.getbbox()
计算图像中非零区域的边界框。将边界框作为定义左、上、右和下像素坐标的四元组返回。我们来看例子:
-
from PIL import Image # 打开图像(451x300) im = Image.open('cat.jpg') # 创建新图像(450x450) im1 = Image.new('L', (450, 450), 50) # 打印图像中非零区域的边界框 print(im.getbbox()) print(im1.getbbox()) # 输出结果 (0, 0, 451, 300) (0, 0, 450, 450)
- 这个方法很简单,很容易理解,那么这个方法有什么用处呢?最直接的一个用处就是迅速地获取图像的边界坐标。
-
getcolors
Image.getcolors(maxcolors=256)
参数说明:
- maxcolors:最大颜色数。默认限制为256色。
获取图像中颜色的使用列表,超过 maxcolors 设置值返回 None 。返回值为 (count, pixel) 的列表,表示(出现的次数,像素的值)
-
getdata
Image.getdata(band=None)
参数:
- band:获取对应通道值。如:RGB 图像像素值为 (r,g,b) 的元组,要返回单个波段,请传递索引值(例如0,从 RGB 图像中获取 R 波段)。
获取图像中每个像素的通道对象元组,像素获取从左至右,从上至下。
getextrema
Image.getextrema()
获取图像中每个通道的最小值与最大值。对于单波段图像,包含最小和最大像素值的2元组。对于多波段图像,每个波段包含一个2元组的元组。
getpixel
Image.getpixel(xy)
参数:
- xy:坐标,以(x,y)表示。
通过传入坐标返回像素值。如果图像是多层图像,则此方法返回元组。
point
Image.point(lut, mode=None)
参数说明:
-
lut:一个查找表,包含图像中每个波段的256个(或65536个,如果 self.mode==“I” 和 mode==“L”)值。可以改用函数,它应采用单个参数。对每个可能的像素值调用一次函数,结果表将应用于图像的所有带区。
-
mode:输出模式(默认与输入相同)。只有当源图像具有模式 “L” 或 “P” ,并且输出具有模式 “1” 或源图像模式为 “I” ,并且输出模式为 “L” 时,才能使用此选项。
对图像的的每个像素点进行操作,返回图像的副本。
-
resize
Image.resize(size, resample=0, box=None)
参数说明:
- size:以像素为单位的请求大小,作为2元组:(宽度、高度)。
- resample:可选的重新采样滤波器。可以是 PIL.Image.NEAREST(最近滤波) , PIL.Image.ANTIALIAS (平滑滤波), PIL.Image.BILINEAR (双线性滤波), PIL.Image.HAMMING , PIL.Image.BICUBIC (双立方滤波)。如果省略,或者图像具有模式 “1” 或 “P” ,则设置为 PIL.Image.NEAREST 。-box:一个可选的4元组的浮点数,给出了应该缩放的源图像区域。值应在(0,0,宽度,高度)矩形内。如果省略或没有,则使用整个源。
这个方法是获取调整大小后的图片。通俗地讲就是在原图中抠一个矩形区域(如果传入了 box 参数),然后对抠出来的区域进行滤波处理(如果传入了 resample 参数),最后以指定的 size 大小进行返回。
-
这几个滤波器的具体的原理比较理论,大家对照着程序运行返回自行去深入了解。
关于 PIL 的 Image 模块的方法我们只讲这么多,还有好多其他方法,大家可参照 https://www.osgeo.cn/pillow/reference/ 这个网站去尝试。
我们讲的 PIL 的 Image 模块只是 PIL 的一个基础模块而已,它还有好多其他的模块,诸如 ImageChops (通道操作模块)、ImageColor (颜色转换模块)、ImageDraw (二维图形模块)等,大家在需要的时候可以去查找 API 使用。