4. 图像预处理技术:从原始数据到可用素材

做图像处理这么多年,我越来越觉得预处理才是真正的「功夫在诗外」。你想想看,一张手机拍的照片,直接扔进算法里,十有八九要翻车。我刚开始带项目时,就吃过这个亏——模型在实验室跑得好好的,一到用户手里就崩,后来发现是输入图像的尺寸、角度、噪声全都不一样。

说白了,预处理就是给算法「喂」它习惯吃的东西。这一章,我把最常用的几招都拆开来讲。

4.1 几何变换:让图像对齐到标准坐标系

几何变换解决的是「图像位置不对」的问题。人脸歪了、尺寸不对、角度偏了,都得靠它来校正。

4.1.1 图像缩放

缩放是最基础的操作。但这里有个坑——插值方法选不对,效果天差地别。

插值方法 适用场景 质量 速度
最近邻插值 像素风格、放大倍数极小 低(有锯齿) 最快
双线性插值 日常缩放、缩略图 中等 快
双三次插值 人像美化、高质量输出 高(边缘平滑) 较慢
Lanczos 插值 专业级图像处理 极高(保留细节) 慢
我的习惯:做人像预处理时,我一般用双三次插值。它比双线性稍微慢一点,但人脸边缘的锯齿感明显更少。如果是做缩略图预览,双线性就够了,别浪费算力。
import cv2

img = cv2.imread('portrait.jpg')
# 双三次插值缩放
resized = cv2.resize(img, (512, 512), interpolation=cv2.INTER_CUBIC)

4.1.2 图像旋转与裁剪

旋转和裁剪经常一起用。比如人脸检测后,需要把歪着的脸「摆正」再裁剪出人脸区域。

旋转有个关键点:旋转后的图像会丢失边角信息。我建议先计算旋转后的边界框,再决定是否要填充背景色。

我曾经踩过的坑:直接旋转整张图,结果人脸是正了,但耳朵被切掉了一半。后来我改用「先扩展画布再旋转」的策略,才解决了这个问题。
# 旋转 + 自动扩展画布
h, w = img.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, angle=15, scale=1.0)
# 计算新边界
cos = abs(M[0, 0])
sin = abs(M[0, 1])
new_w = int(h * sin + w * cos)
new_h = int(h * cos + w * sin)
M[0, 2] += (new_w - w) / 2
M[1, 2] += (new_h - h) / 2
rotated = cv2.warpAffine(img, M, (new_w, new_h))

4.1.3 仿射变换与透视变换

这两个概念容易搞混。我简单说:

  • 仿射变换:保持平行线平行。平移、旋转、缩放、错切都属于它。用 2×3 矩阵表示。
  • 透视变换:不保持平行线平行。模拟「近大远小」的效果。用 3×3 矩阵表示。

做人像美化时,仿射变换用来做人脸对齐——把两只眼睛拉到同一水平线。透视变换则用来做证件照背景替换,或者矫正从侧面拍的人脸。

# 仿射变换:三点对应
src_pts = np.float32([[50,50], [200,50], [50,200]])
dst_pts = np.float32([[10,100], [200,50], [100,250]])
M_affine = cv2.getAffineTransform(src_pts, dst_pts)
affine_img = cv2.warpAffine(img, M_affine, (w, h))

# 透视变换:四点对应
src_pts = np.float32([[56,65], [368,52], [28,387], [389,390]])
dst_pts = np.float32([[0,0], [300,0], [0,300], [300,300]])
M_persp = cv2.getPerspectiveTransform(src_pts, dst_pts)
persp_img = cv2.warpPerspective(img, M_persp, (300, 300))

4.2 图像滤波:去噪与保边

滤波说白了就是「让像素和邻居商量着来」。但怎么商量、跟谁商量,决定了你是去噪还是糊图。

4.2.1 均值滤波

最简单粗暴。把核内所有像素取平均。速度快,但边缘也会被平均掉。

blurred = cv2.blur(img, (5, 5))  # 5x5 核

我一般只在做粗略降噪时用它,比如先均值滤波再二值化。做人像美化?基本不用——它会让人脸变成「磨皮过度」的塑料感。

4.2.2 高斯滤波

比均值滤波聪明一点。它给中心像素更高的权重,越远的像素权重越低。这样去噪的同时,能保留更多细节。

gaussian = cv2.GaussianBlur(img, (5, 5), sigmaX=1.5)
关键参数:sigmaX 控制「扩散范围」。sigma 越大,图像越模糊。做人像预处理时,我习惯用 sigma=1.0~1.5,既能去掉传感器噪声,又不会让皮肤纹理完全消失。

4.2.3 中值滤波

这个我特别喜欢。它不是算平均,而是取核内所有像素的中位数。效果呢?对椒盐噪声(那种黑白点)简直是绝杀。

median = cv2.medianBlur(img, 5)  # 核大小必须是奇数

我记得有一次做老照片修复,照片上全是扫描产生的椒盐噪声。均值滤波搞不定,高斯滤波也一般,换成中值滤波后,效果立竿见影。

但要注意:中值滤波的核不能太大。超过 7×7 后,图像会变得像水彩画,细节全没了。

4.3 本章知识体系

下面这张图,把本章的核心操作和它们之间的关系串起来了。你可以把它当作预处理阶段的「决策地图」。

图像预处理技术知识体系 图像预处理技术体系 原始图像 几何变换 图像滤波 缩放 旋转/裁剪 仿射变换 透视变换 均值滤波 高斯滤波 中值滤波 应用场景速查 • 人脸对齐 → 仿射变换(三点对应) • 证件照校正 → 透视变换(四点对应) • 传感器去噪 → 高斯滤波(sigma=1.0~1.5) • 椒盐噪声去除 → 中值滤波(核大小≤7)

4.4 预处理流程实战建议

在实际项目中,我不会一股脑把所有操作都做一遍。而是根据最终目标来选:

  1. 先看输入质量:有噪声?先滤波。角度歪?先旋转。
  2. 再定输出规格:模型要求 512×512?那就缩放到目标尺寸。
  3. 最后做对齐:人脸检测后,用仿射变换把关键点对齐到标准位置。
我的流水线示例:高斯滤波(去噪) → 人脸检测 → 仿射变换(对齐) → 裁剪(人脸区域) → 缩放(模型输入尺寸)。这套流程在我做过的三个项目里都跑得很稳。

嗯,预处理这块内容就这些。你把这些操作练熟了,后面做风格迁移、人像美化时,会发现「数据干净了,算法自然就听话了」。