Python 图像处理实战:Pillow 与 OpenCV 从入门到精通
专栏:Python 常用工具库实战教程 | 第三篇
适合人群:前端开发者、设计师、计算机视觉初学者
目录
- [前言:Python 图像处理生态](#前言:Python 图像处理生态)
- [第一章:Pillow ------ Python 图像处理入门](#第一章:Pillow —— Python 图像处理入门)
- [1.1 Pillow 是什么](#1.1 Pillow 是什么)
- [1.2 图像基础概念](#1.2 图像基础概念)
- [1.3 图像的打开与创建](#1.3 图像的打开与创建)
- [1.4 图像的保存与格式转换](#1.4 图像的保存与格式转换)
- [1.5 图像裁剪与缩放](#1.5 图像裁剪与缩放)
- [1.6 旋转与翻转](#1.6 旋转与翻转)
- [1.7 绘图与文字](#1.7 绘图与文字)
- [1.8 滤镜效果大全](#1.8 滤镜效果大全)
- [1.9 颜色调整与增强](#1.9 颜色调整与增强)
- [1.10 水印与合成](#1.10 水印与合成)
- [1.11 实战案例:批量处理图片](#1.11 实战案例:批量处理图片)
- [第二章:OpenCV ------ 计算机视觉利器](#第二章:OpenCV —— 计算机视觉利器)
- [2.1 OpenCV 简介](#2.1 OpenCV 简介)
- [2.2 图像读取与基本操作](#2.2 图像读取与基本操作)
- [2.3 图像变换](#2.3 图像变换)
- [2.4 图像滤波](#2.4 图像滤波)
- [2.5 边缘检测](#2.5 边缘检测)
- [2.6 颜色空间转换](#2.6 颜色空间转换)
- [2.7 形态学操作](#2.7 形态学操作)
- [2.8 轮廓检测](#2.8 轮廓检测)
- [Pillow vs OpenCV 选择指南](#Pillow vs OpenCV 选择指南)
前言
在日常开发中,图像处理是一个非常实用的技能。无论是给照片加水印、批量调整图片尺寸,还是做人脸识别、图像分类,Python 都有对应的工具库。
Python 图像处理主要有两大工具:

| 对比维度 | Pillow | OpenCV |
|---|---|---|
| 定位 | 通用图像处理 | 计算机视觉 |
| 优势 | 简单易用、功能丰富 | 高性能、算法丰富 |
| 安装 | pip install pillow |
pip install opencv-python |
| 颜色顺序 | RGB | BGR (注意!) |
| 视频支持 | 弱 | 强 |
| GPU加速 | 无 | 有 |
| 学习难度 | 低 | 中高 |
本文将从 Pillow 开始,循序渐进地介绍图像处理的方方面面。
第一章:Pillow ------ Python 图像处理入门
1.1 Pillow 是什么
Pillow 是 Python Imaging Library (PIL) 的一个活跃分支。PIL 原版已经停止维护,Pillow 继承了 PIL 的所有功能并持续更新。
Pillow 支持:
- 读写 30+ 种图像格式(PNG、JPEG、BMP、GIF、TIFF、WebP 等)
- 图像缩放、旋转、裁剪、翻转
- 色彩模式转换(RGB、RGBA、灰度、L、1 等)
- 滤镜效果(模糊、锐化、边缘检测、浮雕等)
- 图像增强(亮度、对比度、色彩饱和度)
- 在图像上绘制文字和图形
- 图像合成与叠加
bash
pip install pillow
python
from PIL import Image, ImageDraw, ImageFont, ImageFilter, ImageEnhance
1.2 图像基础概念
在学习图像处理之前,需要理解几个核心概念:
像素(Pixel)
像素是图像的最小单位。每张图片都是由大量像素排列组成的网格:

颜色模式
| 模式 | 说明 | 每像素字节数 | 用途 |
|---|---|---|---|
1 |
黑白(1-bit) | 1/8 | 位图 |
L |
灰度 | 1 | 黑白照片 |
RGB |
红绿蓝三通道 | 3 | 彩色图片 |
RGBA |
RGB + 透明度 | 4 | 需要透明的图片 |
CMYK |
印刷四色 | 4 | 印刷输出 |
P |
调色板模式 | 1 | GIF 动画 |
图像尺寸与分辨率
python
# 图片尺寸 = 宽 × 高 (单位:像素)
# 1920 × 1080 = 2,073,600 像素 (约200万像素,即 2MP)
# DPI (Dots Per Inch) = 每英寸的像素数
# 72 DPI: 屏幕显示标准
# 150 DPI: 普通打印
# 300 DPI: 高质量打印
1.3 图像的打开与创建
打开已有图片
python
from PIL import Image
# 打开图片
img = Image.open('photo.jpg')
# 查看图片信息
print(f"格式: {img.format}") # JPEG, PNG 等
print(f"模式: {img.mode}") # RGB, RGBA, L 等
print(f"尺寸: {img.size}") # (宽度, 高度)
print(f"宽度: {img.width} 像素")
print(f"高度: {img.height} 像素")
# 查看 EXIF 信息(相机参数等)
exif = img._getexif()
if exif:
for tag_id, value in exif.items():
print(f" {tag_id}: {value}")
创建新图片
python
# 创建空白图片
img = Image.new('RGB', (800, 400), color='#2c3e50')
# 创建透明图片
img = Image.new('RGBA', (400, 300), (0, 0, 0, 0))
# 从像素数据创建
pixels = [(255, 0, 0), (0, 255, 0), (0, 0, 255)]
img = Image.new('RGB', (3, 1))
img.putdata(pixels)
运行效果 ------ 创建自定义图形:

1.4 图像的保存与格式转换
Pillow 支持在不同格式之间自由转换:
python
img = Image.open('source.png')
# 保存为不同格式
img.save('output.jpg', 'JPEG', quality=95) # JPEG, 质量95%
img.save('output.bmp', 'BMP') # BMP 无损
img.save('output.webp', 'WEBP') # WebP 现代格式
img.save('output.gif', 'GIF') # GIF
img.save('output.tiff', 'TIFF') # TIFF 专业格式
各格式的文件大小对比(同一张图片):
格式测试输出:
PNG: .png → 1.9 KB ← 无损压缩,体积中等
JPEG: .jpg → 6.5 KB ← 有损压缩,体积较大
BMP: .bmp → 351.6 KB ← 无压缩,体积巨大
WEBP: .webp → 0.9 KB ← 新一代格式,体积最小
| 格式 | 特点 | 适用场景 | 透明支持 |
|---|---|---|---|
| PNG | 无损压缩 | 网页图标、截图 | 支持 |
| JPEG | 有损压缩、高压缩率 | 照片存储 | 不支持 |
| BMP | 无压缩、原始数据 | 需要无损的场景 | 不支持 |
| WebP | 高压缩率、现代格式 | 网页优化 | 支持 |
| GIF | 动画、256色限制 | 表情包、动画 | 支持 |
| TIFF | 无损、多页 | 印刷、医疗影像 | 支持 |
1.5 图像裁剪与缩放
运行效果:



裁剪
python
img = Image.open('photo.jpg')
# 裁剪区域: (左, 上, 右, 下)
cropped = img.crop((100, 50, 500, 350))
cropped.save('cropped.jpg')
裁剪坐标示意:

缩放
python
# 指定尺寸缩放(可能变形)
resized = img.resize((400, 300))
# 高质量缩放(使用 LANCZOS 滤波器)
resized = img.resize((400, 300), Image.LANCZOS)
# 等比缩放(保持宽高比)
img.thumbnail((800, 800)) # 最大边不超过800
# 放大 2 倍
enlarged = img.resize((img.width * 2, img.height * 2), Image.LANCZOS)
缩放质量对比:
| 方法 | 说明 | 速度 | 质量 |
|---|---|---|---|
NEAREST |
最近邻插值 | 最快 | 最差(锯齿) |
BILINEAR |
双线性插值 | 较快 | 一般 |
BICUBIC |
双三次插值 | 较慢 | 较好 |
LANCZOS |
Lanczos 重采样 | 最慢 | 最好 |
1.6 旋转与翻转
运行效果:

python
img = Image.open('photo.jpg')
# 旋转
rotated_45 = img.rotate(45) # 逆时针旋转45度
rotated_90 = img.rotate(90, expand=True) # 旋转90度,扩展画布
rotated_free = img.rotate(30, expand=True, fillcolor='#2c3e50') # 自定义背景色
# 翻转
flipped_h = img.transpose(Image.FLIP_LEFT_RIGHT) # 水平翻转
flipped_v = img.transpose(Image.FLIP_TOP_BOTTOM) # 垂直翻转
1.7 绘图与文字
Pillow 提供了 ImageDraw 模块,可以在图片上绘制各种图形和文字:
python
from PIL import Image, ImageDraw, ImageFont
img = Image.new('RGB', (800, 400), '#ecf0f1')
draw = ImageDraw.Draw(img)
# 绘制矩形
draw.rectangle([50, 50, 200, 200], fill='#e74c3c', outline='white', width=3)
# 绘制椭圆
draw.ellipse([250, 50, 400, 200], fill='#3498db', outline='white', width=3)
# 绘制三角形
draw.polygon([(500, 200), (450, 50), (550, 50)], fill='#2ecc71', outline='white')
# 绘制圆角矩形
draw.rounded_rectangle([600, 50, 750, 200], radius=20, fill='#f39c12')
# 绘制线条
draw.line([(50, 250), (750, 250)], fill='#2c3e50', width=3)
# 绘制文字
font = ImageFont.truetype("msyh.ttc", 36) # 微软雅黑36号
draw.text((200, 300), 'Hello Pillow!', fill='#2c3e50', font=font)
绘制函数速查:
| 方法 | 说明 | 参数 |
|---|---|---|
rectangle(xy, fill, outline, width) |
矩形 | (左,上,右,下) |
ellipse(xy, fill, outline, width) |
椭圆/圆 | (左,上,右,下) |
polygon(points, fill, outline) |
多边形 | [(x1,y1), (x2,y2), ...] |
line(xy, fill, width) |
线段 | (x1,y1,x2,y2) |
text(xy, text, fill, font) |
文字 | (x,y), 文本, 字体 |
arc(xy, start, end, fill, width) |
弧线 | 角度(度) |
chord(xy, start, end, fill, outline) |
弦 | 角度 |
pieslice(xy, start, end, fill) |
扇形 | 角度 |
rounded_rectangle |
圆角矩形 | 半径参数 |
1.8 滤镜效果大全
运行效果:

Pillow 内置了多种滤镜:
python
from PIL import ImageFilter
img = Image.open('photo.jpg')
# 模糊
img_blur = img.filter(ImageFilter.GaussianBlur(radius=5))
img_blur = img.filter(ImageFilter.BLUR) # 默认模糊
# 锐化
img_sharpen = img.filter(ImageFilter.SHARPEN)
img_sharpen = img.filter(ImageFilter.UnsharpMask(radius=2, percent=150))
# 边缘检测
img_edges = img.filter(ImageFilter.FIND_EDGES)
# 浮雕
img_emboss = img.filter(ImageFilter.EMBOSS)
# 轮廓
img_contour = img.filter(ImageFilter.CONTOUR)
# 细节增强
img_detail = img.filter(ImageFilter.DETAIL)
# 平滑
img_smooth = img.filter(ImageFilter.SMOOTH)
img_smooth_more = img.filter(ImageFilter.SMOOTH_MORE)
# 多重滤镜叠加
result = img.filter(ImageFilter.GaussianBlur(2)).filter(ImageFilter.SHARPEN)
所有内置滤镜一览:
| 滤镜 | 效果 | 适用场景 |
|---|---|---|
BLUR |
模糊 | 背景虚化、隐私保护 |
CONTOUR |
轮廓 | 素描效果 |
DETAIL |
细节增强 | 照片锐化 |
EDGE_ENHANCE |
边缘增强 | 图像增强 |
EDGE_ENHANCE_MORE |
强边缘增强 | 更强的增强 |
EMBOSS |
浮雕 | 艺术效果 |
FIND_EDGES |
边缘检测 | 图像分析 |
SHARPEN |
锐化 | 照片修复 |
SMOOTH |
平滑 | 降噪 |
SMOOTH_MORE |
更强平滑 | 更强降噪 |
GaussianBlur(n) |
高斯模糊 | 可控模糊程度 |
UnsharpMask |
反锐化蒙版 | 专业锐化 |
1.9 颜色调整与增强
运行效果:

python
from PIL import ImageEnhance
img = Image.open('photo.jpg')
# 亮度调整 (1.0=原始, >1增强, <1减弱)
enhancer = ImageEnhance.Brightness(img)
bright = enhancer.enhance(1.5) # 亮度提高50%
# 对比度调整
enhancer = ImageEnhance.Contrast(img)
contrast = enhancer.enhance(1.8) # 对比度提高80%
# 色彩饱和度调整
enhancer = ImageEnhance.Color(img)
color = enhancer.enhance(2.0) # 饱和度翻倍
# 锐度调整
enhancer = ImageEnhance.Sharpness(img)
sharp = enhancer.enhance(2.0) # 锐度翻倍
# 综合调整
from PIL import ImageOps
img_auto = ImageOps.autocontrast(img) # 自动对比度
img_equalize = ImageOps.equalize(img) # 直方图均衡化
img_invert = ImageOps.invert(img.convert('RGB')) # 反色
img_gray = ImageOps.grayscale(img) # 转灰度
颜色调整效果表:
| 方法 | 调整值范围 | 效果 | 典型值 |
|---|---|---|---|
Brightness |
0.0 ~ ∞ | 明暗 | 1.2~1.5 |
Contrast |
0.0 ~ ∞ | 对比 | 1.3~1.8 |
Color |
0.0 ~ ∞ | 饱和度 | 1.5~2.5 |
Sharpness |
0.0 ~ ∞ | 清晰度 | 1.5~3.0 |
1.10 水印与合成
运行效果:

文字水印
python
from PIL import Image, ImageDraw, ImageFont
def add_text_watermark(image_path, text, output_path, opacity=128):
img = Image.open(image_path).convert('RGBA')
watermark = Image.new('RGBA', img.size, (0, 0, 0, 0))
draw = ImageDraw.Draw(watermark)
font = ImageFont.truetype("msyh.ttc", 36)
# 在右下角添加水印
bbox = draw.textbbox((0, 0), text, font=font)
x = img.width - bbox[2] - 20
y = img.height - bbox[3] - 20
draw.text((x, y), text, fill=(128, 128, 128, opacity), font=font)
result = Image.alpha_composite(img, watermark)
result.convert('RGB').save(output_path)
图片水印
python
def add_image_watermark(image_path, watermark_path, output_path, position='bottom-right'):
img = Image.open(image_path).convert('RGBA')
watermark = Image.open(watermark_path).convert('RGBA')
# 缩放水印
wm_size = (img.width // 5, img.height // 5)
watermark = watermark.resize(wm_size, Image.LANCZOS)
# 计算位置
if position == 'bottom-right':
x = img.width - wm_size[0] - 20
y = img.height - wm_size[1] - 20
elif position == 'center':
x = (img.width - wm_size[0]) // 2
y = (img.height - wm_size[1]) // 2
# 调整透明度
watermark.putalpha(watermark.split()[3])
# 合成
img.paste(watermark, (x, y), watermark)
img.convert('RGB').save(output_path)
图片拼接
python
def combine_images(images, cols=2, padding=10, bg_color='#2c3e50'):
rows_count = (len(images) + cols - 1) // cols
max_w = max(img.width for img in images)
max_h = max(img.height for img in images)
combined_w = cols * max_w + (cols + 1) * padding
combined_h = rows_count * max_h + (rows_count + 1) * padding
combined = Image.new('RGB', (combined_w, combined_h), bg_color)
for i, img in enumerate(images):
row = i // cols
col = i % cols
x = padding + col * (max_w + padding)
y = padding + row * (max_h + padding)
combined.paste(img, (x, y))
return combined
1.11 实战案例:批量处理图片
将前面学的技术组合起来,做一个实用的批量处理脚本:
python
"""
批量处理图片:调整大小 + 加水印 + 转换格式
"""
from PIL import Image, ImageDraw, ImageFont, ImageEnhance, ImageFilter
import os
def batch_process(input_dir, output_dir, max_size=(1920, 1080),
watermark_text=None, output_format='JPEG'):
"""批量处理图片"""
os.makedirs(output_dir, exist_ok=True)
supported = {'.jpg', '.jpeg', '.png', '.bmp', '.webp', '.tiff'}
for filename in os.listdir(input_dir):
ext = os.path.splitext(filename)[1].lower()
if ext not in supported:
continue
filepath = os.path.join(input_dir, filename)
try:
img = Image.open(filepath)
except Exception:
print(f"跳过无法打开的文件: {filename}")
continue
# 1. 转为 RGB(去除透明通道)
if img.mode in ('RGBA', 'P'):
img = img.convert('RGB')
# 2. 缩放到最大尺寸
img.thumbnail(max_size, Image.LANCZOS)
# 3. 轻微锐化
img = img.filter(ImageFilter.SHARPEN)
# 4. 自动对比度
from PIL import ImageOps
img = ImageOps.autocontrast(img)
# 5. 添加水印
if watermark_text:
draw = ImageDraw.Draw(img)
try:
font = ImageFont.truetype("msyh.ttc", max(20, img.width // 30))
except:
font = ImageFont.load_default()
text = watermark_text
bbox = draw.textbbox((0, 0), text, font=font)
x = img.width - (bbox[2] - bbox[0]) - 20
y = img.height - (bbox[3] - bbox[1]) - 20
draw.text((x, y), text, fill=(255, 255, 255), font=font)
# 6. 保存
name = os.path.splitext(filename)[0]
output_path = os.path.join(output_dir, f"{name}.{output_format.lower()}")
img.save(output_path, output_format, quality=90)
print(f"已处理: {filename} → {output_path}")
# 使用
batch_process(
input_dir='./photos',
output_dir='./photos_processed',
max_size=(1920, 1080),
watermark_text='© 2025 MyBlog'
)
第二章:OpenCV ------ 计算机视觉利器
2.1 OpenCV 简介
OpenCV(Open Source Computer Vision Library)是世界上最大的计算机视觉库,支持 2500+ 种算法,覆盖图像处理、物体检测、人脸识别、OCR、姿态估计等领域。
bash
pip install opencv-python
pip install opencv-contrib-python # 包含额外模块
python
import cv2
import numpy as np
重要区别 :OpenCV 使用 BGR 顺序(蓝-绿-红),而不是 PIL 的 RGB 顺序。这是一个常见的坑!
2.2 图像读取与基本操作
python
import cv2
# 读取图片 (BGR 格式)
img = cv2.imread('photo.jpg')
img_color = cv2.imread('photo.jpg', cv2.IMREAD_COLOR) # 彩色(默认)
img_gray = cv2.imread('photo.jpg', cv2.IMREAD_GRAYSCALE) # 灰度
# 查看信息
print(f"形状: {img.shape}") # (高度, 宽度, 通道数)
print(f"数据类型: {img.dtype}") # uint8
print(f"像素总数: {img.size}") # 高 × 宽 × 通道
# 显示图片
cv2.imshow('Image', img)
cv2.waitKey(0) # 按任意键关闭
cv2.destroyAllWindows()
# 保存图片
cv2.imwrite('output.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 95])
像素操作
python
# 读取单个像素 (BGR)
pixel = img[100, 200] # [B, G, R] 值
# 修改像素
img[100, 200] = [255, 0, 0] # 蓝色
# ROI(感兴趣区域)
roi = img[50:200, 100:300] # (y1:y2, x1:x2)
img[50:200, 100:300] = 0 # 将该区域涂黑
# 使用掩码
mask = np.zeros(img.shape[:2], dtype=np.uint8)
mask[50:200, 100:300] = 255
result = cv2.bitwise_and(img, img, mask=mask)
2.3 图像变换
python
import cv2
import numpy as np
img = cv2.imread('photo.jpg')
# 缩放
resized = cv2.resize(img, (400, 300)) # 指定尺寸
scaled = cv2.resize(img, None, fx=0.5, fy=0.5) # 按比例
# 旋转
h, w = img.shape[:2]
center = (w // 2, h // 2)
matrix = cv2.getRotationMatrix2D(center, angle=45, scale=1.0)
rotated = cv2.warpAffine(img, matrix, (w, h))
# 仿射变换
pts1 = np.float32([[0, 0], [w-1, 0], [0, h-1]]) # 原始3个点
pts2 = np.float32([[0, 0], [w-1, 0], [int(w*0.33), h-1]]) # 目标3个点
matrix = cv2.getAffineTransform(pts1, pts2)
warped = cv2.warpAffine(img, matrix, (w, h))
# 透视变换
pts1 = np.float32([[56, 65], [368, 52], [28, 387], [389, 390]])
pts2 = np.float32([[0, 0], [300, 0], [0, 300], [300, 300]])
matrix = cv2.getPerspectiveTransform(pts1, pts2)
perspective = cv2.warpPerspective(img, matrix, (300, 300))
2.4 图像滤波
python
img = cv2.imread('photo.jpg')
# 均值模糊
blur = cv2.blur(img, (5, 5))
# 高斯模糊(更自然的模糊效果)
gaussian = cv2.GaussianBlur(img, (5, 5), 0)
# 中值滤波(去除椒盐噪声效果最好)
median = cv2.medianBlur(img, 5)
# 双边滤波(保边去噪)
bilateral = cv2.bilateralFilter(img, 9, 75, 75)
滤波效果对比:
| 方法 | 效果 | 噪声类型 | 边缘保留 |
|---|---|---|---|
| 均值模糊 | 整体模糊 | 高斯噪声 | 差 |
| 高斯模糊 | 平滑模糊 | 高斯噪声 | 一般 |
| 中值滤波 | 清晰保留细节 | 椒盐噪声 | 好 |
| 双边滤波 | 边缘清晰 | 所有噪声 | 最好 |
2.5 边缘检测
python
img = cv2.imread('photo.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Canny 边缘检测(最常用)
edges = cv2.Canny(gray, threshold1=50, threshold2=150)
# Sobel 算子
sobel_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
sobel_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
sobel = cv2.magnitude(sobel_x, sobel_y)
# Laplacian 算子
laplacian = cv2.Laplacian(gray, cv2.CV_64F)
# 边缘检测 + 二值化
_, binary = cv2.threshold(edges, 127, 255, cv2.THRESH_BINARY)
Canny 边缘检测的两个阈值参数:
threshold1: 低阈值 ------ 弱边缘threshold2: 高阈值 ------ 强边缘- 两者之间的像素如果与强边缘相连,也会被保留
2.6 颜色空间转换
python
img = cv2.imread('photo.jpg')
# BGR → 灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# BGR → RGB(与PIL互转时需要)
rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# BGR → HSV(色相-饱和度-明度,颜色分割常用)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# BGR → LAB(感知均匀的颜色空间)
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
# HSV 颜色分割示例
lower_red = np.array([0, 100, 100])
upper_red = np.array([10, 255, 255])
mask = cv2.inRange(hsv, lower_red, upper_red)
red_only = cv2.bitwise_and(img, img, mask=mask)
常用颜色空间:
| 颜色空间 | 组成 | 优势 | 适用场景 |
|---|---|---|---|
| BGR | 蓝-绿-红 | OpenCV 默认 | 通用 |
| RGB | 红-绿-蓝 | 通用标准 | PIL/Web 互转 |
| HSV | 色相-饱和度-明值 | 颜色分割方便 | 颜色检测 |
| Grayscale | 灰度 | 单通道、计算快 | 边缘检测、OCR |
| LAB | 明度-红绿-蓝黄 | 感知均匀 | 颜色对比 |
| YCrCb | 亮度-红差-蓝差 | 肤色检测 | 人脸识别 |
2.7 形态学操作
形态学操作基于数学形态学理论,常用于二值图像处理:
python
import cv2
import numpy as np
# 先二值化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 定义结构元素
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 也可用圆形: cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
# 腐蚀 ------ 收缩白色区域
eroded = cv2.erode(binary, kernel, iterations=1)
# 膨胀 ------ 扩张白色区域
dilated = cv2.dilate(binary, kernel, iterations=1)
# 开运算 ------ 先腐蚀后膨胀(去噪点)
opened = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
# 闭运算 ------ 先膨胀后腐蚀(填孔洞)
closed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
# 形态学梯度 ------ 膨胀 - 腐蚀(提取边缘)
gradient = cv2.morphologyEx(binary, cv2.MORPH_GRADIENT, kernel)
# 顶帽 ------ 原图 - 开运算(提取小亮区域)
tophat = cv2.morphologyEx(binary, cv2.MORPH_TOPHAT, kernel)
# 黑帽 ------ 闭运算 - 原图(提取小暗区域)
blackhat = cv2.morphologyEx(binary, cv2.MORPH_BLACKHAT, kernel)
形态学操作效果图解:

2.8 轮廓检测
python
import cv2
img = cv2.imread('shapes.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 查找轮廓
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
# 绘制所有轮廓
result = img.copy()
cv2.drawContours(result, contours, -1, (0, 255, 0), 2)
# 分析每个轮廓
for i, contour in enumerate(contours):
# 面积
area = cv2.contourArea(contour)
# 周长
perimeter = cv2.arcLength(contour, True)
# 多边形近似
epsilon = 0.02 * perimeter
approx = cv2.approxPolyDP(contour, epsilon, True)
# 外接矩形
x, y, w, h = cv2.boundingRect(contour)
# 外接圆
(cx, cy), radius = cv2.minEnclosingCircle(contour)
print(f"轮廓 {i}: 面积={area:.0f}, 周长={perimeter:.0f}, "
f"顶点数={len(approx)}, 类形={'矩形' if len(approx)==4 else '其他'}")
# 绘制外接矩形和圆
cv2.rectangle(result, (x, y), (x+w, y+h), (255, 0, 0), 2)
cv2.circle(result, (int(cx), int(cy)), int(radius), (0, 0, 255), 2)
轮廓分析速查:
| 函数 | 说明 | 返回值 |
|---|---|---|
contourArea(c) |
轮廓面积 | float |
arcLength(c, True) |
轮廓周长 | float |
approxPolyDP(c, e, True) |
多边形近似 | 顶点数组 |
boundingRect(c) |
外接矩形 | (x, y, w, h) |
minEnclosingCircle(c) |
外接圆 | (center, radius) |
moments(c) |
图像矩 | 字典 |
isContourConvex(c) |
是否凸多边形 | bool |
选择指南
| 你的需求 | 推荐工具 | 原因 |
|---|---|---|
| 给图片加水印 | Pillow | 简单几行代码 |
| 批量调整图片大小 | Pillow | API 简洁、速度快 |
| 图片格式转换 | Pillow | 支持 30+ 格式 |
| 给图片加文字/图形 | Pillow | ImageDraw 完美支持 |
| 图片加滤镜效果 | Pillow | 内置丰富滤镜 |
| 人脸识别/检测 | OpenCV | 内置 Haar/DNN 模型 |
| 实时视频处理 | OpenCV | VideoCapture 支持 |
| 边缘检测/轮廓 | OpenCV | Canny/轮廓检测算法 |
| 图像分类/目标检测 | OpenCV + 深度学习 | DNN 模块支持模型推理 |
| OCR 文字识别 | OpenCV + Tesseract | 预处理 + OCR |
两者互转
在实际项目中,经常需要在 Pillow 和 OpenCV 之间转换:
python
from PIL import Image
import cv2
import numpy as np
# Pillow → OpenCV
pil_img = Image.open('photo.jpg')
cv_img = np.array(pil_img)[:, :, ::-1] # RGB → BGR
# OpenCV → Pillow
cv_img = cv2.imread('photo.jpg')
pil_img = Image.fromarray(cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB))
下一篇预告:《Python 网络请求与爬虫实战:Requests + BeautifulSoup + Scrapy 从入门到实战》------ 学会从互联网上获取数据!