Python中Pillow(PIL)的使用

安装

1
pip install Pillow

卸载

1
pip uninstall -y Pillow

常用方法

打开与保存

1
2
3
4
5
6
7
from PIL import Image

# 打开图像
img = Image.open('example.jpg')

# 保存图像
img.save('output.jpg')

Pillow 在保存图像时,会根据文件名的后缀自动选择编码格式。

Pillow 支持多种图像格式(如 PNG、JPEG、BMP、GIF 等),并且会根据文件扩展名自动推断并应用相应的编码器。

加载二进制

1
2
3
4
5
6
import PIL.Image

bmp_bytes = b'...' # 替换为你的实际字节数据
img = PIL.Image.open(BytesIO(bmp_bytes), formats=["bmp"])
full_path = os.path.join(save_folder, file_name)
img.save(full_path, format="jpeg")

显示图像

1
2
# 显示图像
img.show()

获取图像信息

1
2
3
4
5
6
7
8
9
10
11
# 获取图像尺寸
width, height = img.size

# 获取图像模式(如 'RGB', 'L' 等)
mode = img.mode

# 获取图像格式
format = img.format

# 获取图像元数据
info = img.info

剪裁图像

1
2
3
4
5
# 定义裁剪区域 (左, 上, 右, 下)
box = (100, 100, 400, 400)

# 裁剪图像
cropped_img = img.crop(box)

调整大小

1
2
# 调整图像大小
resized_img = img.resize((new_width, new_height))

图片旋转

旋转是按中心旋转的。

旋转的方向是逆时针。

expand

  • expand=True:旋转图像时自动扩展图像的尺寸,确保旋转后的图像不会被裁剪。
  • expand=False(默认值):旋转图像时不会扩展图像的尺寸,旋转后的图像可能会被裁剪。

示例

1
2
3
4
5
img = img.rotate(90, expand=True)
img = img.rotate(180, expand=True)
img = img.rotate(270, expand=True)

img = img.rotate(180)

正时针旋转90度

1
2
img = img.rotate(-90, expand=True)
img = img.rotate(270, expand=True)

因为

1
-90 % 360 = 270

翻转图像

1
2
3
4
5
# 左右翻转
flipped_img = img.transpose(Image.FLIP_LEFT_RIGHT)

# 上下翻转
flipped_img = img.transpose(Image.FLIP_TOP_BOTTOM)

绘制图形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from PIL import ImageDraw

# 创建一个绘图对象
draw = ImageDraw.Draw(img)

# 画线
draw.line((0, 0, 100, 100), fill='red', width=3)

# 画矩形
draw.rectangle((100, 100, 200, 200), fill='blue', outline='green')

# 画圆
draw.ellipse((200, 200, 300, 300), fill='yellow', outline='black')

# 绘制矩形的边框,不填充内部
draw.rectangle((100, 100, 200, 200), outline='green')

色值的方式

1
2
3
4
5
6
7
8
9
10
11
# 使用字符串颜色名称
draw.rectangle((10, 10, 50, 50), outline='red')

# 使用 RGB 元组
draw.rectangle((60, 10, 100, 50), outline=(0, 255, 0))

# 使用 RGBA 元组
draw.rectangle((110, 10, 150, 50), outline=(0, 244, 0, 128))

# 使用十六进制颜色字符串
draw.rectangle((10, 60, 50, 100), outline='#00FF00')

绘制中文

Pillow默认就支持中文,而OpenCV是不支持的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from PIL import Image, ImageDraw, ImageFont

# 创建一个新的图像
image = Image.new("RGB", (400, 200), color=(73, 109, 137))

# 创建一个绘图对象
draw = ImageDraw.Draw(image)

# 选择一个中文字体文件
font = ImageFont.truetype(
"simhei.ttf", 40
) # 请确保你有simhei.ttf字体文件,或者替换为其他中文字体文件

# 要绘制的中文文本
text = "你好,世界!"

# 获取文本的边界框
text_bbox = draw.textbbox((0, 0), text, font=font)
text_width = text_bbox[2] - text_bbox[0]
text_height = text_bbox[3] - text_bbox[1]

# 计算文本位置
position = ((image.width - text_width) // 2, (image.height - text_height) // 2)

# 绘制文本
draw.text(position, text, font=font, fill=(255, 255, 255))

# 显示图像
image.show()

其他方法

图像滤镜

1
2
3
4
5
6
7
8
9
10
from PIL import ImageFilter

# 应用模糊滤镜
blurred_img = img.filter(ImageFilter.BLUR)

# 应用锐化滤镜
sharpened_img = img.filter(ImageFilter.SHARPEN)

# 应用边缘增强滤镜
edges_img = img.filter(ImageFilter.EDGE_ENHANCE)

图像转换

1
2
3
4
5
# 将图像转换为灰度图
gray_img = img.convert('L')

# 将图像转换为 RGBA 模式
rgba_img = img.convert('RGBA')

图像合成

1
2
3
4
5
# 打开另一张图像
logo = Image.open('logo.png')

# 将 logo 粘贴到原图像上
img.paste(logo, (100, 100))

图像分割和合并通道

1
2
3
4
5
# 分离图像的 RGB 通道
r, g, b = img.split()

# 合并 RGB 通道
merged_img = Image.merge('RGB', (r, g, b))

应用点操作

1
2
# 反转图像颜色
inverted_img = img.point(lambda p: 255 - p)

创建新图像

1
2
# 创建一个新的空白图像
new_img = Image.new('RGB', (width, height), color='white')

处理像素

1
2
3
4
5
# 获取单个像素的值
pixel_value = img.getpixel((x, y))

# 设置单个像素的值
img.putpixel((x, y), (r, g, b))

Pillow和OpenCV

Pillow使用起来比较便捷,但是性能上不及OpenCV。

体积上Pillow只有5.8M,而OpenCV71.8M+NumPy51.1M总和将近123M。

Pillow侧重于基本的图像处理操作,如图像格式转换、裁剪、缩放、旋转、颜色模式转换等。

OpenCV是用C++编写的,并且经过了高度优化,因此在处理速度上通常比Pillow快得多。

OpenCV在内部使用NumPy数组来表示图像数据,这使得数据处理更加高效,特别是在与其他科学计算库(如NumPy和SciPy)结合使用时。

所以

在做简单的图片处理可以使用Pillow。

如果项目已经有OpenCV的前提下,还是使用OpenCV会更好点,Pillow能实现的OpenCV都能实现。

加载二进制

Pillow

1
2
3
4
import PIL.Image

bmp_bytes = b'...' # 替换为你的实际字节数据
img = PIL.Image.open(BytesIO(bmp_bytes), formats=["bmp"])

OpenCV

1
2
3
4
5
6
7
8
import cv2
import numpy as np

bmp_bytes = b'...' # 替换为你的实际字节数据
# 将字节数据转换为 NumPy 数组
nparr = np.frombuffer(bmp_bytes, np.uint8)
# 使用 cv2.imdecode 从字节数据中解码图像
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)

获取宽高

Pillow

1
width, height = img.size

OpenCV

1
height, width = img.shape[:2]

旋转

Pillow

1
2
3
4
5
6
7
# 逆时针90度
img = img.rotate(90, expand=True)
# 顺时针90度
img = img.rotate(-90, expand=True)
img = img.rotate(270, expand=True)
# 旋转180度
img = img.rotate(180, expand=True)

OpenCV

1
2
3
4
5
6
# 逆时针90度
img = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)
# 顺时针90度
img = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
# 旋转180度
img = cv2.rotate(img, cv2.ROTATE_180)

保存

Pillow

1
img.save(full_path)

OpenCV

1
cv2.imencode(".jpg", img)[1].tofile(full_path)