4. 图像处理基础(下):图像滤波、边缘检测、二值化与形态学操作、直方图均衡化
好,咱们接着往下聊。上一节我们把图像处理的基础概念和像素操作过了一遍,这一节要上点硬货了。说白了,这些技术就是人脸识别系统的「预处理流水线」——图像进模型之前,得先经过这几道工序。
我个人习惯把这一节的内容分成四大块:滤波去噪、边缘检测、二值化与形态学、直方图均衡化。每一块在实际项目中都踩过坑,咱们一个一个说。
4.1 图像滤波:让图像更干净
滤波是干嘛的?说白了就是去噪。摄像头采集的图像,受光照、传感器影响,总会有噪声。噪声不除掉,后续的边缘检测、人脸定位都会受影响。
4.1.1 均值滤波
均值滤波是最简单的。它用一个窗口(比如3x3),把窗口内所有像素的平均值赋给中心点。嗯,这里要注意:它会把边缘也模糊掉。
我在项目中遇到过一个问题:用均值滤波去噪后,人脸轮廓变模糊了,导致后续的 landmark 检测精度下降。所以均值滤波适合噪声均匀的场景,比如高斯噪声,但对椒盐噪声效果一般。
import cv2
import numpy as np
img = cv2.imread('face.jpg')
# 均值滤波,核大小为5x5
blurred = cv2.blur(img, (5, 5))
4.1.2 高斯滤波
高斯滤波比均值滤波更「聪明」。它根据高斯分布给窗口内的像素分配权重——离中心越近,权重越大。这样能更好地保留边缘信息。
我建议在人脸识别预处理中优先用高斯滤波。它去噪的同时,对边缘的破坏比均值滤波小得多。你想想看,人脸的眼睛、鼻子这些关键特征,边缘信息多宝贵啊。
# 高斯滤波,核大小5x5,标准差1.5
gaussian = cv2.GaussianBlur(img, (5, 5), 1.5)
4.1.3 中值滤波
中值滤波就更有意思了。它取窗口内所有像素的中位数作为输出值。为什么说它特别?它对椒盐噪声(就是图像上那些黑白点)有奇效。
我曾经在一个安防项目中,摄像头因为传输线路干扰,图像上全是椒盐噪声。均值滤波和高斯滤波都试了,效果不理想。换成中值滤波后,噪声几乎全消了。这就是实战中积累的经验。
# 中值滤波,核大小为5
median = cv2.medianBlur(img, 5)
4.2 边缘检测:Canny 算法
边缘检测是人脸识别中非常关键的一步。人脸的五官轮廓、脸部边界,本质上都是边缘信息。Canny 算法是业界最常用的边缘检测方法,没有之一。
Canny 的流程其实不复杂:先高斯滤波去噪,然后计算梯度幅值和方向,接着做非极大值抑制(把不是边缘的像素去掉),最后用双阈值检测连接边缘。
这里有个坑:双阈值的设置。阈值设高了,边缘断断续续;设低了,噪声也被当成边缘。我一般用 cv2.Canny(img, 50, 150) 作为起点,然后根据实际效果微调。
# Canny 边缘检测
edges = cv2.Canny(gaussian, 50, 150)
4.3 图像二值化与形态学操作
4.3.1 图像二值化
二值化就是把图像变成只有黑和白。说白了,就是设定一个阈值,像素值大于阈值的变成255(白),小于的变成0(黑)。
最简单的就是全局阈值:
# 全局阈值二值化
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
但实际项目中,光照不均匀是常态。全局阈值在光照暗的地方会把细节全丢掉。这时候就要用自适应阈值:
# 自适应阈值,根据局部区域计算阈值
adaptive = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
我建议在人脸相关的二值化任务中,优先尝试自适应阈值。它比全局阈值鲁棒得多。
4.3.2 形态学操作:腐蚀与膨胀
二值化之后,图像上经常会有一些小白点(噪声)或者断裂的线条。形态学操作就是用来处理这些问题的。
腐蚀:让白色区域「瘦」一圈。可以去掉小的白点噪声。
膨胀:让白色区域「胖」一圈。可以连接断裂的线条。
我经常把它们组合使用:先腐蚀去噪,再膨胀恢复。这叫「开运算」。反过来先膨胀再腐蚀叫「闭运算」,用来填充小孔。
kernel = np.ones((3,3), np.uint8)
# 腐蚀
eroded = cv2.erode(binary, kernel, iterations=1)
# 膨胀
dilated = cv2.dilate(binary, kernel, iterations=1)
# 开运算:先腐蚀后膨胀
opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
# 闭运算:先膨胀后腐蚀
closing = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
4.4 直方图均衡化
直方图均衡化是提升图像对比度的利器。你想想看,在光线不足或者逆光环境下,人脸图像往往偏暗,细节看不清。直方图均衡化可以把像素值的分布「拉伸」开,让暗的地方变亮,亮的地方更均匀。
它的原理不复杂:统计每个灰度级的像素数量,然后重新映射,让像素分布更均匀。OpenCV 里一行代码就能搞定:
# 直方图均衡化(输入必须是灰度图)
equalized = cv2.equalizeHist(gray)
但这里有个问题:全局直方图均衡化会放大噪声。尤其是在人脸图像中,皮肤区域的噪声会被放大,看起来不自然。
我推荐用 CLAHE(限制对比度自适应直方图均衡化)。它把图像分成小块,每个小块单独做均衡化,并且限制对比度的放大程度。效果比全局均衡化好得多。
# CLAHE 直方图均衡化
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
clahe_img = clahe.apply(gray)
4.5 综合实战:人脸预处理流水线
最后,我把这些技术串起来,给一个实际的人脸预处理流程。这个流程我在多个项目中验证过,效果稳定。
def preprocess_face(img):
# 1. 转灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 2. CLAHE 增强对比度
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
enhanced = clahe.apply(gray)
# 3. 高斯滤波去噪
blurred = cv2.GaussianBlur(enhanced, (3, 3), 1.0)
# 4. 可选:边缘检测(用于特征提取)
edges = cv2.Canny(blurred, 50, 150)
return blurred, edges
嗯,这一节的内容就到这里。图像处理是计算机视觉的基石,这些操作虽然基础,但用好了能大幅提升人脸识别系统的鲁棒性。下一节我们会进入更核心的内容——特征提取与人脸检测算法。