Python 图像处理实战

从表情包制作入手:Python 图像处理入门实战

想象一下:把自家猫主子的照片 一键缩成朋友圈缩略图自动打可爱水印加个猫脸识别框(后期可以接OpenCV但这次先用几何图形画个轮廓)、甚至 生成专属动态开头图的静态脚本——这些需求 Python 用一个库就能快速搞定,它就是 Pillow

Daoman Python AI 的落地项目里,Pillow 是数据预处理、视觉工具开发的「瑞士军刀级入门库」,先学它再碰 OpenCV、TensorFlow Lite 这类硬核视觉框架,会轻松很多。


1. 两分钟扫盲:图像的「最小零件」

在敲代码前,先搞懂两个核心概念(虽然简单,但踩过坐标坑的人都懂它们的重要性):

1.1 颜色模型(RGB/RGBA)

计算机显示器是「色光叠加」原理:红(Red)、绿(Green)、蓝(Blue)三种色光混合出所有可见色。

  • 每种色光的强度是 0-255 的整数(对应256级灰度),数值越大颜色越亮
  • RGBA 多了个 Alpha 通道,也是 0-255,0 完全透明,255 完全不透明
常用名称RGB 值常用名称RGB 值
纯白(255, 255, 255)纯红(255, 0, 0)
纯绿(0, 255, 0)纯蓝(0, 0, 255)
中灰(128, 128, 128)亮黄(255, 255, 0)
纯黑(0, 0, 0)暗紫(128, 0, 128)

1.2 像素(Pixel)

图像的「最小可编辑单元」——拿你现在看的这篇文章的手机/电脑屏幕来说,放大到极致,就是密密麻麻的小彩色方块,一张 1920×1080 的图片,就有 207.36 万个这样的方块


2. Pillow 快速上手:从安装到基础操作

Pillow 是 PIL(Python Imaging Library)的「现代化复活版」,兼容性好、API 简单,Python 3.x 首选。

安装一步到位

pip install pillow

2.1 基础三件套:读取→查看信息→裁剪→缩放→转存

核心是 PIL.Image 模块,操作步骤和你用 PS、手机修图APP的逻辑一模一样。

假设你现在有一张名为 cat_hero.jpg 的图片(建议用你自己的素材,效果更直观):

from PIL import Image

# 1. 打开本地/网络图片(注意:如果是网络图片,得先用requests.get()拉取二进制流)
try:
    img = Image.open("cat_hero.jpg")
    # 快速验证打开成功,弹出系统默认看图软件
    img.show()
except FileNotFoundError:
    print("❌ 图片不存在,请检查路径!")

# 2. 获取图片基本属性
print(f"✅ 图片格式:{img.format}")  # 如 JPEG、PNG、BMP
print(f"✅ 图片尺寸(宽, 高):{img.size}")  # 元组形式
print(f"✅ 颜色模式:{img.mode}")  # 如 RGB、RGBA、L(灰度)

# 3. 裁剪(要记住 Pillow 的坐标系:左上角是 (0,0),右→x轴增加,下→y轴增加!)
# 参数是 (左边界, 上边界, 右边界, 下边界)——注意右/下边界是「取不到」的
crop_area = (100, 50, 500, 400)
cat_face = img.crop(crop_area)
cat_face.show()
cat_face.save("cat_face_only.png")  # 顺便转成带透明通道的 PNG(不过原裁剪区域如果是 JPEG 就没用)

# 4. 缩放(两种常用方法)
## 方法一:thumbnail()——原地修改原图,**强制保持宽高比**,不会拉伸变形,适合生成缩略图
img_copy = img.copy()  # thumbnail会改原图,先复制一份保险
img_copy.thumbnail((200, 200))  # 这里的尺寸是「最大允许尺寸」
img_copy.save("cat_thumb.jpg")
## 方法二:resize()——返回新图,可以拉伸/压缩到指定尺寸
cat_face_resized = cat_face.resize((100, 100))
cat_face_resized.show()

# 5. 旋转与翻转(都是返回新图,不修改原图)
## 旋转45度(默认会补黑色背景,逆时针旋转)
img_rotated = img.rotate(45, expand=True)  # expand=True可以让画布自动放大,不切边角
img_rotated.show()
## 水平/垂直翻转
img_flip_h = img.transpose(Image.FLIP_LEFT_RIGHT)  # 水平镜像
img_flip_v = img.transpose(Image.FLIP_TOP_BOTTOM)  # 垂直镜像
img_flip_h.save("cat_mirror.jpg")

2.2 进阶小技巧:滤镜+粘贴+去水印(简单版)

Pillow 自带了很多预设滤镜,粘贴功能还能做表情包合成、水印叠加。

预设滤镜一键调用

from PIL import ImageFilter

# 用刚才的 cat_hero.jpg 或 cat_face_only.jpg 测试
# 注意:滤镜返回新图
img_blur = img.filter(ImageFilter.GaussianBlur(radius=5))  # 高斯模糊(可以调radius调模糊程度)
img_contour = img.filter(ImageFilter.CONTOUR)  # 轮廓提取(画表情包必备!)
img_sharpen = img.filter(ImageFilter.SHARPEN)  # 锐化
img_contour.show()

合成表情包(裁剪区域+粘贴)

# 假设我们有一张爱心贴纸的PNG图片(要带透明通道哦!)
try:
    heart_sticker = Image.open("heart.png").convert("RGBA")  # 强制转RGBA,避免兼容问题
except FileNotFoundError:
    # 如果没有贴纸,自己临时生成一个带透明通道的圆当爱心替代品
    from PIL import ImageDraw
    heart_sticker = Image.new("RGBA", (100, 100), (0, 0, 0, 0))  # 最后一个0是完全透明
    heart_draw = ImageDraw.Draw(heart_sticker)
    heart_draw.ellipse((0, 0, 100, 100), fill=(255, 0, 0, 200))  # 半透明红色圆

# 把爱心贴纸缩小一点
heart_sticker_resized = heart_sticker.resize((80, 80))
# 拿到贴纸的尺寸
w, h = heart_sticker_resized.size
# 准备粘贴的画布(先复制原图并转RGBA,方便后续处理透明)
canvas = img.convert("RGBA")
# 粘贴!第三个参数是「蒙版」——可以用贴纸本身的Alpha通道当蒙版,这样只贴爱心不贴背景
canvas.paste(heart_sticker_resized, (img.size[0] - w - 20, img.size[1] - h - 20), mask=heart_sticker_resized)
# 最后要转回RGB才能存JPG(JPG不支持透明)
canvas.convert("RGB").save("cat_with_heart.jpg")
canvas.show()

3. 静态绘图:做个简单的验证码/节日贺卡

Pillow 不仅能改现有图片,还能从零创建画布,用 PIL.ImageDraw 画几何图形、写文字——做验证码、批量生成带日期的节日卡片都能用。

从零做贺卡

from PIL import Image, ImageDraw, ImageFont

# 1. 创建画布(宽, 高, 背景色)
canvas_width, canvas_height = 600, 400
canvas = Image.new("RGB", (canvas_width, canvas_height), (240, 248, 255))  # 浅蓝背景
drawer = ImageDraw.Draw(canvas)

# 2. 绘制装饰图形
## 画一条从上到下的渐变?(这里简化成画几条彩色竖线吧)
for x in range(0, canvas_width, 20):
    color = (x % 256, (x + 50) % 256, (x + 100) % 256)
    drawer.line((x, 0, x, canvas_height), fill=color, width=10)

## 画几个半透明的小圆圈当气球
# 注意:半透明需要先转RGBA再画,或者用Image.new创建单独的RGBA图层粘贴
balloon_layer = Image.new("RGBA", canvas.size, (0, 0, 0, 0))
balloon_draw = ImageDraw.Draw(balloon_layer)
balloon_positions = [(100, 150), (300, 100), (500, 200)]
for (x, y) in balloon_positions:
    balloon_draw.ellipse((x-30, y-40, x+30, y+20), fill=(255, 192, 203, 180))  # 半透明粉色
    balloon_draw.line((x, y+20, x, y+100), fill=(128, 128, 128), width=2)

# 把气球图层贴到主画布上
canvas.paste(balloon_layer, mask=balloon_layer)

# 3. 写文字(关键是找到合适的字体!)
# Windows常用字体路径:C:/Windows/Fonts/simhei.ttf(黑体)、C:/Windows/Fonts/msyh.ttc(微软雅黑)
# Mac常用字体路径:/System/Library/Fonts/STHeiti Light.ttc(华文细黑)
# Linux常用字体路径:/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
try:
    # 可以用sys.platform判断操作系统,自动选字体,这里简化
    font_title = ImageFont.truetype("msyh.ttc", 60)
    font_subtitle = ImageFont.truetype("msyh.ttc", 30)
except IOError:
    # 如果找不到指定字体,用Pillow自带的默认字体(只能显示英文和数字,中文会乱码)
    print("⚠️ 未找到指定中文字体,将使用默认英文字体")
    font_title = ImageFont.load_default()
    font_subtitle = ImageFont.load_default()

# 居中写标题(需要先获取文字的宽度和高度)
text_title = "Happy Birthday!"
# Pillow 9.2.0+ 推荐用 textbbox 获取文字区域,textsize 已经过时
bbox_title = drawer.textbbox((0, 0), text_title, font=font_title)
text_width_title = bbox_title[2] - bbox_title[0]
text_height_title = bbox_title[3] - bbox_title[1]
x_title = (canvas_width - text_width_title) // 2
y_title = 50
drawer.text((x_title, y_title), text_title, fill=(255, 20, 147), font=font_title)

# 居中写副标题
text_subtitle = "To My Lovely Cat 🐱"
bbox_subtitle = drawer.textbbox((0, 0), text_subtitle, font=font_subtitle)
text_width_subtitle = bbox_subtitle[2] - bbox_subtitle[0]
x_subtitle = (canvas_width - text_width_subtitle) // 2
y_subtitle = y_title + text_height_title + 30
drawer.text((x_subtitle, y_subtitle), text_subtitle, fill=(0, 0, 139), font=font_subtitle)

# 4. 保存并展示
canvas.save("cat_birthday_card.jpg")
canvas.show()

总结与进阶方向

本次实战总结

  1. 核心坐标系:左上角 (0,0),切记切记!
  2. 最常用模块Image(基础读写)、ImageFilter(滤镜)、ImageDraw(绘图)、ImageFont(文字)
  3. 透明图层处理技巧:转成 RGBA,单独创建图层再用 mask 参数粘贴

下一步学什么?

  • 简单进阶:用 Pillow 批量处理图片(比如批量转灰度、批量加水印)
  • 硬核视觉:如果你要做人脸识别、目标检测、视频流处理,建议学 OpenCV-Python
  • 深度学习预处理:Pillow 是 TensorFlow/Keras、PyTorch 官方推荐的预处理工具之一,可以结合这些框架玩更高级的内容