程序员

Python3 Pillow库图像(表情包)处理教程

2022-12-31  本文已影响0人  练习时长两年半的写作练习生

Pillow简介

Pillow 是 Python 图像处理的基础库,它是一个免费开源的第三方库,由一群 Python 社区志愿者使用 Python 语言开发而成(主要贡献者:Alex Clark)。
Pillow 的前身是 PIL 库。不过 PIL 仅支持 Python2 版本,后由 Python 社区志愿者移植到 Python3 版本,改名为 Pillow。Pillow 与 PIL 不能共存于同一环境中。

基础知识

这部分需要一定的 Python 基础知识,以及对于颜色模式如 RGB 等的了解。

Pillow 安装与导入

安装命令:pip install pillow
导入命令:from PIL import Image

Image 类

Image 类是 Pillow 中最重要的类,使用 Image 类可以实例化 Image 对象。

Image 对象的创建

在创建 Image 对象时,既可以创建新的图片,也可以选择打开已有文件。


使用 new() 方法可以创建新的对象,语法格式如下:

img = Image.new(mode, size, color)

参数说明如下:


使用 open() 方法可以从已有文件创建新的对象,语法格式如下:

img = Image.open(fp, mode='r', format=None)

参数说明如下:

Image 对象的属性

Image 对象常用属性如下:

Image 对象的操作函数

save()

save() 方法用于保存图像,当不指定文件格式时,它会以默认的图片格式来存储;如果指定图片格式,则会以指定的格式存储图片。save() 的语法格式如下:

img.save(fp, format=None)

参数说明如下:

convert()

有些情况下 save() 方法无法直接保存,例如 RGBA 颜色格式的图片无法保存为 JPG。而 convert() 方法可以转换图片的颜色格式。其语法格式如下:

img1 = img.convert('RGB')

resize() 与 thumbnail()

实际使用中经常遇到需要调整图像大小的情况,这时就要用 resize() 方法,该方法返回一个新的对象。其语法如下:

img1 = img.resize(size, resample=image.BICUBIC, 
                  box=None, reducing_gap=None)

参数说明:


当需要创建缩略图时,往往使用 thumbnail() 方法,该方法直接修改原对象。其语法如下:

img.thumbnail(size, resample)

参数与 resize() 方法含义相同。

split() 与 merge()

split() 与 merge() 方法分别起到分离与合并颜色通道的作用。用法如下:

r, g, b = img.split()               # r,g,b 是各颜色通道的黑白图像
img1 = Image.merge('RGB', [b,g,r])  # 合并

注意各颜色通道图片需要大小相同,否则不能合并。

crop(),copy() 与 paste()

crop() 的作用是以矩形区域的方式对原图像进行裁剪。其用法如下:

img_crop = img.crop(box);

box:表示裁剪区域,默认为 None,表示拷贝原图像。<br />注意:box 是一个四元组,各参数分别表示被裁剪矩形区域的左上角 x、y 坐标和右下角 x,y 坐标。默认 (0, 0) 表示坐标原点,宽度的方向为 x 轴,高度的方向为 y 轴,每个像素点代表一个单位。


copy() 与 paste() 的作用是复制粘贴。copy() 方法较简单,下面主要介绍 paste() 方法:

large_image.paste(image, box=None, mask=None)

该函数的作用是将一张图片粘贴至另一张图片中。注意,粘贴后的图片模式将自动保持一致,不需要进行额外的转换。参数说明如下:

GIF 图像

GIF 图像的特殊性在于它是一种动态图像,其中往往包含较多的帧。因此它相比于 JPG,PNG 等格式有一些特殊方法。

save() 的参数

GIF 图像在使用 save() 保存时有更多的可用参数。部分参数如下:

用例:

# 这里的 frame_list 存储了所有的帧信息
frame_list[0].save('example.gif', format='gif', save_all=True,
                    append_images=frame_list[1:], duration=50, transparency=0, loop=0, disposal=2)

tell() 与 seek()

访问 GIF 图像时在特定时刻只能访问一帧。使用 tell() 方法可以查看当前的帧数;使用 seek() 方法可以跳转到对应的帧。不过如果访问到不存在的帧,会产生 EOFError。常见用法如下:

try:
    while True:
        # 一些处理操作
        img.seek(img.tell() + 1)  # 下一帧
except EOFError:
    pass

该用法可以遍历 GIF 图像的每一帧。

使用示例

我们以表情包“滑稽” huaji.gif

为例来展示 Pillow 库的一些使用方式。
注:示例程序不一定是最优解。

表情包拼接

表情包拼接的大致思路是创建适合大小的背景,再将表情包粘贴到合适位置。代码如下:

# 本文件与 huaji.gif 在同一目录

from PIL import Image

img = Image.open('huaji.gif')
frame_list = []                         # 存储最终 GIF 文件的帧
multiple = 9                            # 放大倍数

# 遍历 gif 所有帧,当遍历到最后一帧之后就会抛出 EOFError, 使程序结束
try:
    while True:
        frame = img.convert('RGBA')             # 转换为 RGBA 颜色格式
        frame_large = Image.new('RGBA', (frame.width * multiple, frame.height * multiple),
                                '#00000000')    # 创建边长为原先 multiple 倍的背景图
        for i in range(multiple):
            for j in range(multiple):
                frame_large.paste(frame, (frame.width * i, frame.height * j))   # 在[i, j] 位置粘贴背景图
                 
        frame_list.append(frame_large)          # 将当前帧加入帧列表
        img.seek(img.tell() + 1)                # 遍历下一帧
except EOFError:                                # 已读到最后一帧之后
    pass


# save_all:         保存全部帧
# append_images:    接下来的图片
# duration:         两帧的时间间隔,单位 ms
# transparency:     透明
# loop:             循环次数,为 0 时永久循环
# disposal:         处理,即播放下一帧时清除上一帧

frame_list[0].save('huaji_enlarged.gif', format='gif', save_all=True,
                    append_images=frame_list[1:], duration=50, transparency=0, loop=0, disposal=2)

效果展示如下图:


huaji_enlarged.gif

表情包颜色修改

本程序中,表情包颜色修改的方式为对每个像素的颜色进行修改。代码如下:

# 本文件与 huaji.gif 在同一目录

from PIL import Image


# 将原颜色乘以目标颜色并返回
# RGBA 颜色以四元组 (r,g,b,a) 表示,r,g,b,a 均要混合
def to_color(color, target) -> tuple:
    'Change to target color.'
    
    temp = []
    for i in range(len(color)):
        temp.append(color[i] * target[i] // 255)    # 混合颜色方式为乘以目标颜色后除以其最大值
    return tuple(temp)


# 打开图片
img = Image.open('huaji.gif')
frame_list = []
green = (0, 255, 0, 255)

# 遍历所有帧并染色
try:
    while True:
        frame = img.convert('RGBA')
        pixels = frame.load()               # 原图片的各像素,将其修改后原图像也会修改
        for i in range(frame.width):
            for j in range(frame.height):   # 修改单个像素颜色
                pixels[i, j] = to_color(pixels[i, j], green)
        frame_list.append(frame)
        img.seek(img.tell() + 1)            # 下一帧
except EOFError:                            # 已全部遍历
    pass

# 保存图片
frame_list[0].save('huaji_green.gif', format='gif', save_all=True,
                   append_images=frame_list[1:], duration=50, transparency=0, loop=0, disposal=2)

效果展示:


huaji_green.gif

表情包像素画

将“表情包拼接”与“表情包颜色修改”结合起来,就可以得到“表情包像素画”功能。表情包像素画的效果就是将图片的每个像素都按照位置替换为相应颜色的一个表情包。<br />出于图片文件大小及展示效果的考虑,基底图片的大小定为 40x40,替换用的表情包大小定为 20x20。代码如下:

# 本文件与 huaji.gif 在同一目录

from PIL import Image


# 将原颜色乘以目标颜色并返回
# RGBA 颜色以四元组 (r,g,b,a) 表示,r,g,b,a 均要混合
def to_color(color, target) -> tuple:
    'Change to target color.'
    
    temp = []
    for i in range(len(color)):
        temp.append(color[i] * target[i] // 255)    # 混合颜色方式为乘以目标颜色后除以其最大值
    return tuple(temp)


# 转换单个形状的颜色
def shape_to_color(original_image, color):
    image = original_image.convert('RGBA')          # 这里需要 convert 来复制新对象,
                                                    # 否则修改的对象是 original_image
    shape_pixels = image.load()
    for i in range(image.width):
        for j in range(image.height):
            shape_pixels[i, j] = to_color(shape_pixels[i, j], color)
    return image


base_size = (40, 40)
shape_size = (20, 20)

img_base = Image.open('huaji.gif')      # 基底图片
img_shape = Image.open('huaji.gif')     # 用于替换的形状图片

shape_list = []                         # 存储替换形状的各帧
frame_list = []                         # 存储最终输出表情的各帧

# 获取替换形状各帧
try:
    while True:
        shape = img_shape.convert('RGBA').resize(shape_size)
        shape_list.append(shape)
        img_shape.seek(img_shape.tell() + 1)
except EOFError:
    pass

# 生成像素画各帧
try:
    while True:
        frame = img_base.convert('RGBA').resize(base_size)
        shape = shape_list[len(frame_list) % len(shape_list)]   # 按循环顺序获取 shape 的一帧 
        frame_large = Image.new('RGBA', (frame.width * shape.width, frame.height * shape.height),
                                '#00000000')
        pixels = frame.load()
        for i in range(frame.width):
            for j in range(frame.height):
                new_shape = shape_to_color(shape, pixels[i, j]) # 转换单个形状颜色
                frame_large.paste(new_shape, (i * new_shape.width, j * new_shape.width))
        frame_list.append(frame_large)                          # 将当前帧加入帧列表
        img_base.seek(img_base.tell() + 1)                      # 遍历下一帧
except EOFError:
    pass

# 保存图片
frame_list[0].save('huaji_pixels.gif', format='gif', save_all=True,
                   append_images=frame_list[1:], duration=50, transparency=0, loop=0, disposal=2)

效果展示如下图:


huaji_pixels.gif

参考资料

教程

Pillow(PIL)入门教程(非常详细)
Python 图像处理 Pillow 库 基础篇

文档

stable版本:Pillow stable版本
latest版本:Pillow latest版本

上一篇下一篇

猜你喜欢

热点阅读