输入一张 RGB 图片,单图活体算法判断它更像真人采集,还是照片、屏幕等呈现攻击。它不需要先看到转头或眨眼。

单张图里没有时间变化,也没有实测深度。算法只能利用这张图中留下的成像线索,因此可能误判清晰的屏幕翻拍,也可能误判光照很差的真人。

1. 图里有哪些线索

线索可能观察到的差异容易失效的条件
局部纹理印刷网点、显示屏像素、重拍造成的细节变化低分辨率、压缩、失焦会抹掉差异
频率分布重采样或显示屏产生的周期纹理、摩尔纹高质量屏幕不一定出现明显纹理
颜色与反光纸张、屏幕与皮肤的成像响应不同光源、相机和肤色也会改变响应
周围区域纸边、屏幕边框、脸周围的材质过紧裁剪会丢失,模型也可能只学背景

这些是可能有用的统计线索,没有哪条单独构成通用规则。真人皮肤同样可能有反光,照片也可以很清晰。不能用“模糊就假”“有高光就真”代替防伪模型。

2. 传统纹理方法

一种可解释的基线是 LBP → 直方图 → SVM。

对灰度图中的一个像素,取周围 8 个邻居。邻居大于等于中心像素记为 1,否则记为 0,按顺序组成一个 8 位数:

LBP=∑k=07I(gk≥gc)2kLBP=\sum_{k=0}^{7}I(g_k\ge g_c)2^k

把脸图分成小块,每块统计 LBP 值的频数,再拼成向量。用带“真人/攻击”标签的图片训练 SVM,推理时对同样的纹理向量分类。

它提取的是局部明暗模式,便于理解,也不需要大网络;相机、光照或材质变化后,特征分布可能变化很大。FFT 频谱、颜色统计也能作为手工特征,但都需要分类训练与独立测试。

3. 网络怎样提取防伪特征

原图 → 人脸框 → 带周围区域的裁剪 → 缩放与通道处理
→ 卷积特征图 → 汇聚/展平 → 分类层 → logits → 分数

卷积的一个输出可以写成:

hk,u,v=σ(bk+∑c,i,jWk,c,i,jxc,u+i,v+j)h_{k,u,v}=\sigma\left(b_k+\sum_{c,i,j}W_{k,c,i,j}x_{c,u+i,v+j}\right)

W 是训练出来的滤波器,σ 是非线性函数。浅层响应局部边缘和纹理,后续层组合更大范围的信息。分类层将特征加权,输出每类的原始分数 logits。

训练数据决定哪些差异被当作证据。若真人图都来自相机 A,攻击图都来自相机 B,网络可能学会识别相机,换设备就失效。自动学特征不代表一定学到了希望它使用的特征。

MiniFASNet 使用轻量卷积结构减少计算,属于这类单图防伪网络。本文使用 Silent-Face 的一个预训练权重;只有网络结构而没有训练好的参数,还不能进行有意义的真假判别。上游实现

4. 怎样训练

先采集带标签的真人图片和攻击图片,按固定规则检测、裁剪与预处理。设网络输出 z,真实类别为 y,一个基本分类目标是:

pk=ezk∑jezj,L=−log⁡pyp_k=\frac{e^{z_k}}{\sum_j e^{z_j}},\qquad L=-\log p_y

反向传播计算损失对参数的梯度,优化器更新参数。重复多批次后,网络逐渐提高正确类别的分数。这个交叉熵流程解释的是分类训练的基本方式,具体模型可能还加入额外监督,不能据此宣称复现了完整训练。

训练和测试应按人物、采集片段、设备等分组拆分。把同一段视频的相邻帧随机分到两边,会使测试图与训练图过于相似,高估效果。

有的方法用辅助深度图监督,鼓励真人脸预测出脸部起伏。单图网络输出的深度是估计值,不等于深度相机的测量,也不能据此保证识别所有立体面具。

5. 分数怎样变成判定

假设二分类模型输出 [z_attack, z_live]=[1,3]:

import numpy as np
z = np.array([1.0, 3.0])
exp_z = np.exp(z - z.max()) # 数值稳定的 softmax
p = exp_z / exp_z.sum()
live_score = float(p[1]) # 约 0.881

argmax(p) 选择分数最大的类别;live_score ≥ τ 则给真人类单独设阈值。二分类中最大类选择通常对应 0.5 分界,三分类不能这样等同。

例如三类分数为 [0.31, 0.38, 0.31],索引 1 虽然最高,却不到 0.5。采用哪种规则必须明确。softmax 分数也不是经过校准的真实概率,更不是模型准确率。

阈值调高通常减少攻击被接受,也增加真人被拒绝。分别统计:

APCER = 被判为真人的攻击图数 / 攻击图总数
BPCER = 被判为攻击的真人图数 / 真人图总数

这里按图片统计,并应分攻击类型查看 APCER,避免均值掩盖某种攻击上的失败。总体准确率还会受到真假样本比例影响。

6. 一个具体的输入约定

以 2.7_80x80_MiniFASNetV2.pth 为例,本文按 Silent-Face 的固定版本处理:

项目本例做法
裁剪人脸框按 2.7 倍扩展,边界处理复用上游 CropImage
输入大小80 × 80
通道与数值OpenCV 的 BGR,转 float32,保持 0–255,不额外除以 255
张量布局HWC → NCHW,即 [1, 3, 80, 80]
输出三类 logits,softmax 的索引 1 作为真人类分数

裁剪保留部分脸周围的成像信息,所以不直接复用身份网络的对齐小脸图。靠近图像边缘时,上游裁剪器会限制尺度并调整位置,“2.7 倍”不保证每张图都能完整取到。

依据是上游的推理代码、实际张量转换、裁剪代码及类别判断。换权重必须重新核对,不能推广到所有活体模型。

本文示例使用一个模型,并要求显式提供阈值,没有照搬上游的双模型融合与最大类判定。检测器也换成了 YuNet,因此输出不保证与上游演示完全一致。

7. 单图与多帧

多帧方法还能观察运动、眨眼变化或时序颜色信号;深度和红外方法则需要额外传感器数据。它们能利用的证据不同。

只给一张图,就做这一张图的防伪分类。一张侧脸只能支持“当前头部偏转”的判断,无法说明它刚刚完成了一次转头。姿态计算见下一篇,单图推理代码见算法示例。