图像处理入门010 | Pillow 与 OpenCV 对比:双库图像读写实战

系列:《图像处理算法入门 144 期》· 模块二:数字图像基础操作

标签:OpenCV / Pillow / PIL / 图像读写 / 性能对比 / 入门


一、前言

上一期(009)我们深入讲解了 OpenCV 的 imread / imshow 全解析,掌握了 flags 参数、BGR 陷阱和安全读取封装。本期进入 M02 的第二期 ,主题是 Pillow 与 OpenCV 的对比实战

Python 图像处理领域有两大主流库:

  1. OpenCVcv2):C++ 底座的计算机视觉库,性能强劲,生态庞大
  2. PillowPIL):Pythonic 的图像处理库,接口友好,格式支持广泛

很多初学者会问:我该学哪个? 答案是:两个都要学,且要知道何时用哪个。

本期通过同一操作双库实现、API 风格对比、格式支持矩阵、性能基准测试和互转方法,帮你建立清晰的选择框架。


二、API 风格对比:两种哲学

2.1 读取图像

python 复制代码
import cv2
from PIL import Image

# OpenCV 读取
img_cv = cv2.imread("photo.jpg", cv2.IMREAD_COLOR)
print(type(img_cv))      # <class 'numpy.ndarray'>
print(img_cv.shape)      # (H, W, 3) ------ 高、宽、通道
print(img_cv.dtype)      # uint8

# Pillow 读取
img_pil = Image.open("photo.jpg")
print(type(img_pil))     # <class 'PIL.JpegImagePlugin.JpegImageFile'>
print(img_pil.size)      # (W, H) ------ 宽、高(注意顺序!)
print(img_pil.mode)      # 'RGB'

核心差异一览:

对比项 OpenCV Pillow
返回类型 numpy.ndarray PIL.Image.Image
尺寸表示 shape=(H, W, C) size=(W, H)
通道顺序 BGR RGB
数据类型 NumPy 数组(直接数值计算) PIL 内部对象(需 np.array() 转换)
读取方式 一次性解码到内存 惰性加载(load() 才真正读取像素)

关键陷阱 :Pillow 的 size(W, H),而 OpenCV 的 shape(H, W, C)------宽度高度的顺序正好相反。

2.2 属性访问对比

操作 OpenCV Pillow
获取宽度 img.shape[1] img.width
获取高度 img.shape[0] img.height
获取通道数 img.shape[2] len(img.getbands())
获取像素 img[y, x] img.getpixel((x, y))
设置像素 img[y, x] = [B, G, R] img.putpixel((x, y), (R, G, B))
转 ndarray 已有 np.array(img)

2.3 可视化对比

子图 说明
上排左 OpenCV 读取结果(BGR ndarray,matplotlib 显示时已转 RGB)
上排中 Pillow 读取结果(RGB Image)
上排右 差异图(理论上全黑,证明两库读取的像素数据一致)
下排左 API 风格对比表
下排中 属性访问对比表
下排右 使用场景建议

三、格式支持矩阵

3.1 两库格式支持对比

格式 OpenCV Pillow 备注
JPEG/JPG 读+写 读+写 最通用格式,两库完全支持
PNG 读+写 读+写 支持透明通道
BMP 读+写 读+写 Windows 位图
TIFF/TIF 读+写 仅读 Pillow 支持多层 TIFF,OpenCV 读单层
GIF 读+写 仅读 OpenCV 只读首帧;Pillow 支持动画帧
WebP 读+写 读+写 Google 推出的现代格式
ICO 读+写 仅读 Windows 图标
PPM/PGM 仅读 读+写 便携式像素图
OpenEXR 仅读 读+写 HDR 高动态范围
RAW/DNG 仅读 需插件 Pillow 需 rawpy 插件
HEIC/HEIF 需插件 需插件 Pillow 需 pillow-heif
JPEG2000 读+写 仅读 医学/遥感常用
PDF 仅读 不支持 OpenCV 读为图像序列
PSD 仅读 不支持 Photoshop 源文件
SVG 不支持 不支持 矢量格式,需转换
AVIF 需插件 需插件 下一代图像格式

3.2 可视化矩阵

选择建议

  • 需要处理 GIF 动画 / 多层 TIFF → Pillow
  • 需要处理 RAW / PDF / PSD → OpenCV(有限支持)+ 专用工具
  • 通用格式(JPEG/PNG/WebP)→ 两库均可

四、性能基准测试

4.1 测试环境

  • Python 3.13.12
  • OpenCV 5.0.0
  • Pillow 12.3.0
  • NumPy 2.5.2
  • 测试图像:600×400 PNG, iterations = 50

4.2 测试结果

测试项 平均 (ms) 中位数 (ms) 最小 (ms) 最大 (ms)
读取 OpenCV 2.494 2.334 2.186 3.477
读取 Pillow 0.074 0.067 0.063 0.243
保存 (JPEG) OpenCV 0.979 0.931 0.605 1.324
保存 (JPEG) Pillow 1.294 1.254 0.786 4.174
颜色转换 OpenCV 0.021 0.013 0.008 0.131
颜色转换 Pillow 0.010 0.009 0.008 0.029

4.3 结果分析

场景 胜者 原因
图像读取 Pillow Pillow 的惰性加载机制使其读取速度约为 OpenCV 的 34 倍
图像保存 OpenCV OpenCV 的 JPEG 编码器略快,且耗时更稳定
颜色转换 Pillow 通过 NumPy 数组操作,两者都极快(< 0.03ms)

重要说明 :Pillow 读取快是因为它采用惰性加载 ------Image.open() 只读取文件头信息,真正解码像素是在后续访问时。如果立即 np.array(img),耗时会与 OpenCV 接近。

4.4 可视化基准测试

左图 右图
平均耗时柱状图对比 50 次迭代耗时分布箱线图

五、两库互转方法

在实际项目中,经常需要两库混用。掌握互转方法是必备技能。

5.1 OpenCV → Pillow

python 复制代码
import cv2
from PIL import Image
import numpy as np

# OpenCV 读取的是 BGR ndarray
cv_img = cv2.imread("photo.jpg")  # shape=(H, W, 3), BGR

# 方法 1:cvtColor + fromarray(推荐)
pil_img = Image.fromarray(
    cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)
)

# 方法 2:通道反转 + fromarray(等价,略快但可读性差)
pil_img = Image.fromarray(cv_img[:, :, ::-1])

5.2 Pillow → OpenCV

python 复制代码
from PIL import Image
import cv2
import numpy as np

# Pillow 读取的是 RGB Image
pil_img = Image.open("photo.jpg")  # size=(W, H), RGB

# 方法:np.array + cvtColor(推荐)
cv_img = cv2.cvtColor(
    np.array(pil_img), cv2.COLOR_RGB2BGR
)

5.3 互转流程图

上半部分 下半部分
OpenCV → Pillow 流程 Pillow → OpenCV 流程

5.4 互转一致性验证

python 复制代码
# 验证:CV -> PIL -> CV 是否一致
cv_img = cv2.imread("photo.jpg")
pil_img = Image.fromarray(cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB))
cv_back = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)

assert np.allclose(cv_img, cv_back)  # ✅ 通过

实践建议 :在项目中封装一个 cv2pil()pil2cv() 工具函数,避免每次都写转换代码。


六、选择决策树

复制代码
                    开始
                      │
          ┌───────────┼───────────┐
          ▼           ▼           ▼
       处理视频    格式转换/    深度学习
       /摄像头    批量处理      预处理
          │           │           │
          ▼           ▼           ▼
       OpenCV      Pillow      OpenCV
       ✅          ✅          ✅
          │           │           │
          └───────────┴───────────┘
                      │
              需要像素级矩阵运算?
              /NumPy 生态集成?
                      │
              ┌───────┴───────┐
              是              否
              │               │
              ▼               ▼
           OpenCV          Pillow
           ✅ (ndarray)    ✅ (更轻量)
场景 推荐库 理由
视频/摄像头处理 OpenCV 原生支持 VideoCapture
实时计算机视觉 OpenCV C++ 底层,性能最优
深度学习预处理 OpenCV 直接输出 NumPy,与 PyTorch/TensorFlow 无缝衔接
图像格式批量转换 Pillow 格式支持更广,API 更简洁
Web 服务/后端 Pillow 更轻量,依赖少,启动快
文字渲染/字体 Pillow ImageDraw + ImageFont 原生支持
滤镜/艺术效果 Pillow ImageFilter 插件生态丰富
GIF 动画处理 Pillow 原生支持多帧
两库混用 两者 封装互转函数,各取所长

七、产品与工具推荐

7.1 移动端

产品 平台 价格 与本期的关联
InShot iOS/Android 免费 + 内购 视频+图像处理工具,底层使用类似 OpenCV 的管线处理每一帧
Snapseed iOS/Android 免费 Google 出品,同时支持 OpenCV 风格的专业调整和 Pillow 风格的滤镜叠加
美图秀秀 iOS/Android 免费 + 订阅 国内最流行,批量格式转换和滤镜应用体现 Pillow 的批量处理思想
VSCO iOS/Android 免费 + 订阅 滤镜系统基于 LUT(查找表),与 Pillow 的 ImageFilter 机制类似

7.2 专业级

工具 形态 价格 与本期的关联
GIMP 桌面(Win/Mac/Linux) 开源免费 图像处理功能的完整实现,底层同时集成类似 OpenCV 的 GEGL 和 Pillow 风格的 Python-Fu 脚本
ImageMagick 命令行 开源免费 格式转换之王(支持 200+ 格式),Pillow 底层部分调用 ImageMagick 解码器
FFmpeg 命令行 开源免费 视频处理标准工具,与 OpenCV 的 VideoCapture 底层互通
Adobe Bridge 桌面 订阅 批量格式转换和元数据管理,体现 Pillow 的批量处理优势
XnConvert 桌面 免费 批量格式转换 GUI 工具,支持 500+ 格式,适合测试格式兼容性

7.3 推荐组合

身份 移动端 专业级 理由
自学者 Snapseed + 美图秀秀 OpenCV + Pillow + ImageMagick 免费组合,覆盖全场景
Web 开发者 (无) Pillow + ImageMagick 轻量后端,格式转换
视觉算法工程师 Snapseed(看实现) OpenCV + FFmpeg 算法开发 + 视频处理
批量处理需求 (无) Pillow + ImageMagick + XnConvert 命令行 + GUI 互补

八、常见问题排查(FAQ)

# 症状 根本原因 解决方案
1 Pillow 图像给 OpenCV 处理,颜色变怪 RGB → BGR 顺序未转换 cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
2 OpenCV 图像给 Pillow 保存,颜色变怪 BGR → RGB 顺序未转换 Image.fromarray(cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB))
3 img_pil.sizeimg_cv.shape 不一致 (W, H) vs (H, W, C) 注意顺序:shape[1] == widthshape[0] == height
4 Pillow 读取后无法直接数值运算 PIL.Image 不是 ndarray np.array(pil_img) 转换后再运算
5 OpenCV 保存 PNG 比 Pillow 大 默认压缩参数不同 OpenCV 用 cv2.imwrite(path, img, [cv2.IMWRITE_PNG_COMPRESSION, 9])
6 Pillow 保存 JPEG 质量不可控 默认 quality=75 pil_img.save(path, quality=95, optimize=True)
7 需要处理 GIF 动画 OpenCV 只读首帧 用 Pillow:Image.open(gif).seek(frame_index)
8 需要处理多层 TIFF OpenCV 只读单层 用 Pillow:Image.open(tiff).seek(layer_index)

九、扩展练习

  1. 入门 :编写 cv2pil(cv_img)pil2cv(pil_img) 两个工具函数,并验证互转后像素值完全一致。

  2. 进阶:读取 10 张不同格式图像(JPG/PNG/BMP/GIF/TIFF),分别用 OpenCV 和 Pillow 读取,比较读取耗时和内存占用差异。

  3. 挑战:编写一个批量格式转换脚本,支持以下功能:

    • 输入:文件夹路径 + 源格式 + 目标格式
    • 输出:转换后的图像保存到 output/ 目录
    • 要求:同时支持 OpenCV 和 Pillow 两种后端,根据格式自动选择最优后端
    • 提示:用本期格式支持矩阵做决策规则

十、本期小结

知识点 核心内容
返回类型差异 OpenCV 返回 ndarray,Pillow 返回 PIL.Image
尺寸顺序差异 OpenCV (H, W, C) vs Pillow (W, H)
通道顺序差异 OpenCV BGR vs Pillow RGB
读取性能 Pillow 惰性加载更快(约 34 倍),OpenCV 立即全解码
保存性能 OpenCV JPEG 编码略快且更稳定
格式支持 Pillow 更广(GIF/TIFF多层),OpenCV 更专(视频/PPM/OpenEXR)
互转模板 Image.fromarray(cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB))
选择原则 视频/实时/深度学习 → OpenCV;格式/Web/批量 → Pillow

下期(第 011 期):图像基本属性:尺寸、通道、数据类型 ------shape / dtype / size 全解析,图像属性查询方法、类型转换与边界处理。

相关推荐
vivo互联网技术2 小时前
Octopus:基于无历史数据的梯度正交化的学习框架|CVPR 2026
深度学习·计算机视觉·llm
牧羊人.3332 小时前
计算机视觉基础 第 9 章|实战:银行卡号识别
图像处理·人工智能·opencv·计算机视觉·图搜索算法
欧特克_Glodon4 小时前
OpenCV计算机视觉开发入门与实践<十三>:图像转换之灰度图、二值图
c++·人工智能·opencv·计算机视觉
欧特克_Glodon4 小时前
OpenCV计算机视觉开发入门与实践<十二>:XML 和 YAML 文件读写
xml·c++·opencv·计算机视觉
TAN-90°-5 小时前
Deep Learning for Computer Vision——Training CNNs and CNN Architectures
人工智能·深度学习·神经网络·算法·机器学习·计算机视觉·cnn
小玮看世界14 小时前
从“画图”到“Mermaid”:AI语义理解与架构绘制的演进之路
人工智能·深度学习·计算机视觉
Golden小狗种自己的花[哇]1 天前
书接上回(二)
计算机视觉
西柚研究生1234561 天前
论文分析16:多尺度特征增强与动态采样的轻量化目标检测
人工智能·计算机视觉·目标跟踪
Ricardo-Yang1 天前
无人机单目深度估计测试:ZipDepth 与 AerialMetric
人工智能·算法·机器学习·计算机视觉·无人机