0%

Python - 图片处理 - OpenCV图片与PIL.Image对象互相转化

0. 起因

因项目需要对视频封面图片作以下处理:宽高比不为一的图片处理成两端模糊填充的正方形,使得呈现如下效果:

target_image.jpg

1. 思路

  • 使用PIL库对图片作裁减和放大处理;
  • 上一步得到的图片转化到OpenCV图像做高斯模糊处理(因为PIL模块的图片模糊滤镜强度不够);
  • 上一步处理好的图片转化为PIL.Image,将原图居中合成进来;
  • 处理好的图片保存到本地或者转化为内存中的文件对象作其它操作: 上传下载等.

2. 代码实现

涉及到的代码库 Pillow==4.3.0, opencv-python==4.1.0.25, numpy==1.16.0

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import requests
from io import BytesIO
from PIL import Image
import numpy as np
import cv2


def crop_img_on_ratio(img, ratio):
"""按比例裁减图片"""
ratio_w, ratio_h = ratio
img_w, img_h = img.size
if img_h/img_w >= ratio_h/ratio_w:
h = int(img_w*ratio_h/ratio_w)
box = (0, int((img_h-h)/2), img_w, int((img_h+h)/2))
else:
w = int(img_h*ratio_w/ratio_h)
box = (int((img_w-w)/2), 0, int((img_w+w)/2), img_h)
return img.crop(box)


def mix_img_icon(img, icon):
"""图片图标合成,居中"""
img_w, img_h = img.size
icon_w, icon_h = icon.size
box = (int((img_w-icon_w)/2), int((img_h-icon_h)/2))
if icon.format == 'PNG':
img.paste(icon, box=box, mask=icon)
else:
img.paste(icon, box=box)


def get_blur_image(img):
"""宽高比不为1的图片处理成两端模糊填充的正方形"""
w, h = img.size # 获取图片宽高
a = max(w, h) # 获取正方形边长
crop_img = crop_img_on_ratio(img, (1, 1)) # 裁减为正方形
scale_img = crop_img.resize((a, a), resample=Image.ANTIALIAS) # 等比放大
cv_img = cv2.cvtColor(np.asarray(scale_img), cv2.COLOR_RGB2BGR) # 转化为 opencv image
cv_gb_img = cv2.GaussianBlur(cv_img, (0, 0), 7) # opencv 高斯模糊
gb_img = Image.fromarray(cv2.cvtColor(cv_gb_img, cv2.COLOR_BGR2RGB)) # 转化为PIL.Image
mix_img_icon(gb_img, img) # 将原图合成进来
return gb_img


if __name__ == "__main__":
# 从本地读取图片
source_img = Image.open('/path/to/source_image.jpg')

# 从url读取图片
source_img = Image.open(BytesIO(requests.get('http://url/for/source_image.jpg').content))

# 获取模糊后的图片
blur_img = get_blur_image(source_img)

# 图片保存到本地
gb_img.save('/path/to/target_image.jpg', "JPEG")

# 图片保存为内存中的类文件对象
img_file = BytesIO()
image.save(img_file, "JPEG")


3. 引申

PIL模块可以对图片作很多滤镜操作达到不同效果:

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

target_img = source_img.filter(ImageFilter.BLUR) # 模糊
target_img = source_img.filter(ImageFilter.CONTOUR) # 轮廓
target_img = source_img.filter(ImageFilter.EDGE_ENHANCE) # 边界加强
target_img = source_img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 边界加强(阀值更大)
target_img = source_img.filter(ImageFilter.EMBOSS) # 模糊滤镜
target_img = source_img.filter(ImageFilter.FIND_EDGES) # 边界
target_img = source_img.filter(ImageFilter.SMOOTH) # 平滑
target_img = source_img.filter(ImageFilter.SMOOTH_MORE) # 平滑(阀值更大)
target_img = source_img.filter(ImageFilter.SHARPEN) # 锐化