3. 图像处理基础:图像读取与显示、色彩空间转换、图像缩放与裁剪、图像滤波与增强
说实话,很多人一上来就调模型、跑训练,结果连摄像头传回来的图像是 BGR 还是 RGB 都没搞清楚。我见过不少项目,前期跑得挺顺,一到现场就崩,最后发现是色彩通道搞反了。嗯,这章咱们就把这些基本功打扎实。
本章核心脉络: 图像处理是人脸识别系统的"眼睛"。从读取一张图开始,到把它变成模型能吃的标准输入,每一步都藏着坑。
3.1 图像读取与显示
我习惯用 OpenCV 做图像读取。原因很简单——它快,而且直接返回 numpy 数组,方便后续处理。但有个坑:OpenCV 默认读出来的是 BGR 格式,不是我们常见的 RGB。
import cv2
import matplotlib.pyplot as plt
# 读取图像
img = cv2.imread('face.jpg')
print(img.shape) # (高度, 宽度, 通道数)
# 显示图像(OpenCV 方式)
cv2.imshow('Face', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 用 matplotlib 显示(需要转换通道)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)
plt.axis('off')
plt.show()
我曾经踩过的坑: 用 matplotlib 直接显示 OpenCV 读出来的图,结果人脸是蓝色的。后来才反应过来——matplotlib 默认显示 RGB,而 OpenCV 给的是 BGR。通道顺序搞反了,颜色就全乱了。
3.2 色彩空间转换
人脸识别里,色彩空间转换不是花架子。我做过一个项目,在室内跑得好好的,一到室外光线变化大,识别率直接掉了一半。后来把图像转成 YUV 或 HSV 空间,只取亮度通道做检测,问题就解决了。
| 色彩空间 | 特点 | 人脸识别中的应用 |
|---|---|---|
| BGR / RGB | 三通道,颜色信息完整 | 原始输入,模型训练常用 |
| Gray | 单通道,计算量小 | 人脸检测、特征提取(如 LBP、Haar) |
| YUV / YCrCb | 亮度与色度分离 | 光照变化场景下的鲁棒处理 |
| HSV | 色调、饱和度、明度 | 肤色检测、人脸分割 |
# 常用转换
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YUV)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# 只取 Y 通道(亮度)
y_channel = yuv[:, :, 0]
我的建议: 如果做实时人脸检测,优先用灰度图。单通道计算量只有三通道的 1/3,而且很多检测算法(比如 Haar Cascade)本身就是在灰度图上训练的。
3.3 图像缩放与裁剪
模型输入通常固定尺寸,比如 112×112 或 160×160。但摄像头来的图是 1920×1080,怎么办?直接 resize 会变形,直接 crop 可能丢掉人脸。我一般先检测人脸位置,再根据人脸框做裁剪和缩放。
# 缩放
resized = cv2.resize(img, (112, 112)) # 直接指定尺寸
# 保持宽高比的缩放
h, w = img.shape[:2]
target_size = 112
if h > w:
new_h = target_size
new_w = int(w * target_size / h)
else:
new_w = target_size
new_h = int(h * target_size / w)
resized = cv2.resize(img, (new_w, new_h))
# 裁剪
cropped = img[100:300, 200:400] # [y_start:y_end, x_start:x_end]
注意: 缩放时插值方法会影响质量。缩小用 INTER_AREA,放大用 INTER_CUBIC 或 INTER_LINEAR。我见过有人用默认的 INTER_LINEAR 放大图像,结果人脸边缘全是锯齿。
3.4 图像滤波与增强
滤波说白了就是去噪。摄像头传感器、传输过程都会引入噪声。我做过一个门禁系统,晚上红外补光下图像全是噪点,人脸检测基本废了。加了个高斯滤波,效果立竿见影。
| 滤波方法 | 原理 | 适用场景 |
|---|---|---|
| 均值滤波 | 邻域像素取平均 | 简单去噪,但会模糊边缘 |
| 高斯滤波 | 加权平均,中心权重高 | 高斯噪声,保留边缘较好 |
| 中值滤波 | 取邻域中值 | 椒盐噪声,边缘保留最好 |
| 双边滤波 | 空间+像素值双权重 | 去噪同时保留边缘细节 |
# 高斯滤波
blurred = cv2.GaussianBlur(img, (5, 5), 1.5)
# 中值滤波(对椒盐噪声特别有效)
median = cv2.medianBlur(img, 5)
# 双边滤波(保留边缘)
bilateral = cv2.bilateralFilter(img, 9, 75, 75)
图像增强呢,我常用直方图均衡化。特别是光照不均匀的场景,比如半边脸亮半边脸暗。直方图均衡化能把亮度分布拉开,让暗部细节出来。
# 直方图均衡化(灰度图)
equalized = cv2.equalizeHist(gray)
# CLAHE(限制对比度自适应直方图均衡化)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
clahe_result = clahe.apply(gray)
经验之谈: 普通直方图均衡化容易放大噪声。我更喜欢用 CLAHE,它把图像分成小块分别处理,效果更自然。人脸识别预处理里,CLAHE 几乎是标配。
3.5 综合实战:人脸预处理流水线
把上面这些串起来,就是一个标准的人脸预处理流程。我一般在项目里封装成一个函数,输入原始图像,输出标准化的人脸图像。
def preprocess_face(face_img, target_size=(112, 112)):
# 1. 缩放
resized = cv2.resize(face_img, target_size)
# 2. 转灰度(可选)
gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)
# 3. 去噪
denoised = cv2.GaussianBlur(gray, (3, 3), 1.0)
# 4. 增强
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
enhanced = clahe.apply(denoised)
# 5. 归一化到 [0, 1]
normalized = enhanced.astype('float32') / 255.0
return normalized
核心要点回顾:
- OpenCV 读图默认 BGR,显示时记得转 RGB
- 灰度图能减少计算量,适合实时检测
- 缩放保持宽高比,裁剪基于人脸框
- 滤波去噪选对方法,CLAHE 增强是利器
嗯,图像处理这块就这些。别看都是基础操作,但每一步都直接影响后续的人脸检测和识别效果。我见过太多人在这上面翻车了——你想想看,模型再强,输入的是脏数据,输出能好吗?