3. 图像处理基础:图像读取与显示、色彩空间转换、图像缩放与裁剪、图像滤波与增强

说实话,很多人一上来就调模型、跑训练,结果连摄像头传回来的图像是 BGR 还是 RGB 都没搞清楚。我见过不少项目,前期跑得挺顺,一到现场就崩,最后发现是色彩通道搞反了。嗯,这章咱们就把这些基本功打扎实。

本章核心脉络: 图像处理是人脸识别系统的"眼睛"。从读取一张图开始,到把它变成模型能吃的标准输入,每一步都藏着坑。
图像处理基础流程 图像读取 色彩空间转换 缩放与裁剪 滤波与增强 关键说明: • 读取时注意通道顺序:OpenCV 默认 BGR,PIL 默认 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 增强是利器

嗯,图像处理这块就这些。别看都是基础操作,但每一步都直接影响后续的人脸检测和识别效果。我见过太多人在这上面翻车了——你想想看,模型再强,输入的是脏数据,输出能好吗?