4、图像基础回顾:色彩空间、图像梯度、边缘检测、特征点提取在活体检测中的应用
做活体检测这几年,我最大的感触是:很多攻击手段,其实在图像底层就露出了马脚。你想想看,一张打印照片和一张真实人脸,它们在像素层面的统计特性是完全不同的。这一章,我们就从图像基础入手,看看色彩空间、梯度、边缘和特征点这些老面孔,怎么在活体检测里发挥新作用。
4.1 色彩空间:活体检测的第一道防线
RGB 是我们最熟悉的色彩空间,但说实话,它在活体检测里并不好用。为什么?因为 RGB 的三个通道高度相关,光照一变,三个通道一起变,很难分离出有效信息。
4.1.1 YCbCr 色彩空间
我个人习惯用 YCbCr 做活体检测的预处理。Y 是亮度分量,Cb 和 Cr 是蓝色和红色色度分量。真实人脸和照片人脸在色度分量上的分布有明显差异。
关键发现:照片攻击在 Cb、Cr 通道上的纹理细节远少于真实人脸。因为打印过程会损失高频色度信息。
转换公式很简单,OpenCV 一行搞定:
import cv2
import numpy as np
# 读取图像
img = cv2.imread('face.jpg')
# BGR 转 YCbCr
ycbcr = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
# 分离通道
y, cb, cr = cv2.split(ycbcr)
4.1.2 HSV 色彩空间
HSV(色调、饱和度、明度)在应对光照变化时表现更好。真实人脸在 H(色调)通道上分布集中,而照片因为打印材质和反光,H 通道往往出现异常波动。
我曾经在做一个门禁项目时,发现某批照片攻击在 S(饱和度)通道上的方差特别小。后来一查,原来是打印纸的吸墨特性导致色彩饱和度偏低。这个发现帮我们堵住了一类攻击。
| 色彩空间 | 活体检测优势 | 常见应用场景 |
|---|---|---|
| RGB | 直观,但光照敏感 | 基础显示、数据标注 |
| YCbCr | 分离亮度和色度,区分真假人脸纹理 | 纹理分析、LBP 特征提取 |
| HSV | 抗光照变化,捕捉材质差异 | 反光检测、屏幕攻击识别 |
| Lab | 感知均匀,适合颜色分布统计 | 肤色建模、光照归一化 |
我的建议:不要只用单一色彩空间。把 YCbCr 的 Cb/Cr 通道和 HSV 的 S 通道拼接起来,往往能获得更好的区分度。
4.2 图像梯度:捕捉真假人脸的纹理差异
梯度说白了就是像素值的变化率。真实人脸有丰富的微纹理——毛孔、汗毛、皮肤褶皱,这些在梯度图上表现为高频响应。而照片人脸呢?打印网点、墨点扩散,梯度响应平滑得多。
4.2.1 Sobel 算子
Sobel 是最常用的梯度算子。它用两个 3x3 的核分别计算水平和垂直方向的梯度:
# 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)
# 计算梯度幅值
magnitude = np.sqrt(sobel_x**2 + sobel_y**2)
嗯,这里要注意:ksize 参数别设太大。我见过有人用 ksize=7,结果把真实人脸的毛孔细节全平滑掉了,活体检测直接失效。
4.2.2 梯度直方图特征
光算梯度还不够,我们需要统计特征。把梯度幅值按方向分 bin,就得到了梯度直方图。真实人脸的梯度直方图通常呈现多峰分布,而照片攻击往往是单峰或双峰。
避坑指南:我曾经在梯度直方图里踩过一个坑——忘记做归一化。不同光照下梯度幅值差异很大,不做归一化的话,模型会学到光照信息而不是纹理信息。
4.3 边缘检测:找到照片攻击的破绽
边缘检测和梯度计算是近亲,但目的不同。梯度告诉我们变化有多剧烈,边缘告诉我们变化在哪里发生。
4.3.1 Canny 边缘检测
Canny 是工业界最成熟的边缘检测算法。它包含四个步骤:高斯滤波、计算梯度、非极大值抑制、双阈值检测。
# Canny 边缘检测
edges = cv2.Canny(gray, threshold1=50, threshold2=150)
# 计算边缘密度
edge_density = np.sum(edges > 0) / (edges.shape[0] * edges.shape[1])
print(f"边缘密度: {edge_density:.4f}")
真实人脸的边缘密度通常在 0.05~0.15 之间,而照片攻击往往低于 0.03。为什么?因为打印过程模糊了边缘,尤其是眼睛、眉毛、嘴唇这些关键部位。
4.3.2 边缘方向直方图
除了边缘密度,边缘方向也很有价值。真实人脸因为立体结构,边缘方向分布比较均匀;而平面照片的边缘方向往往集中在少数几个角度上。
实战技巧:把 Canny 边缘和 Sobel 梯度结合起来。先用 Canny 找到边缘位置,再用 Sobel 计算这些位置的梯度方向。这样既减少了计算量,又保留了方向信息。
4.4 特征点提取:从局部到全局的活体线索
特征点提取在活体检测里有两个用途:一是定位关键区域(眼睛、鼻子、嘴巴),二是提取局部纹理特征。
4.4.1 LBP(局部二值模式)
LBP 是我在活体检测项目里用得最多的特征。它把每个像素和周围像素比较,生成一个二进制编码。真实人脸和照片人脸的 LBP 直方图差异非常明显。
from skimage.feature import local_binary_pattern
# 计算 LBP 特征
radius = 1
n_points = 8 * radius
lbp = local_binary_pattern(gray, n_points, radius, method='uniform')
# 计算 LBP 直方图
hist, _ = np.histogram(lbp.ravel(), bins=np.arange(0, n_points + 3),
range=(0, n_points + 2))
# 归一化
hist = hist.astype("float")
hist /= (hist.sum() + 1e-6)
参数选择:radius=1, n_points=8 是最常用的组合。radius 太大(比如 3 以上)会丢失局部细节,反而降低区分度。
4.4.2 HOG(方向梯度直方图)
HOG 本质上是梯度特征的升级版。它把图像分成小块(cell),在每个块内统计梯度方向直方图,然后做块间归一化。
我记得有一次做屏幕翻拍攻击检测,HOG 特征的表现远超预期。屏幕翻拍会产生摩尔纹,这些纹路在 HOG 的某些方向 bin 上会形成异常峰值。
4.4.3 关键点检测(SIFT/ORB)
SIFT 和 ORB 这类特征点检测器,在活体检测里主要用于对齐和配准。比如,先用 ORB 检测人脸关键点,然后做仿射变换对齐,再提取 LBP 或 HOG 特征。
注意:SIFT 有专利限制,商业项目建议用 ORB 或 AKAZE 替代。ORB 速度比 SIFT 快一个数量级,精度在活体检测场景下完全够用。
4.5 本章知识体系
下面这张图把本章的核心知识点串起来了。你可以看到,从原始图像到活体判断,每一步都在提取不同层次的线索。
4.6 实战要点总结
说了这么多,我帮你把核心要点提炼一下:
- 色彩空间选型:YCbCr 看纹理,HSV 看材质,Lab 看肤色分布。别只用 RGB。
- 梯度参数调优:Sobel 的 ksize 用 3,Canny 的双阈值用 (50, 150) 做起点,根据实际数据微调。
- 边缘密度阈值:0.03 是个经验分界线。低于这个值,大概率是照片攻击。
- LBP 参数:radius=1, n_points=8, method='uniform',这是经过大量项目验证的黄金组合。
- 特征融合:别把鸡蛋放在一个篮子里。色彩特征 + 纹理特征 + 边缘特征,三者结合才能应对各种攻击手段。
最后说一句:图像基础不是过时的老技术,而是活体检测的基石。很多深度学习模型之所以翻车,就是因为忽略了这些底层特征。打好基础,后面的路才走得稳。