一种通过人脸关键点在连续帧中的运动模式区分真实人脸和照片攻击的算法及其纯JS实现

16 阅读3分钟

摘要

本文提出了一种基于人脸关键点在连续视频帧中运动模式差异的活体检测算法,用于区分真实人脸与照片攻击。该算法通过分析人脸关键点在时间序列上的几何变化,利用真实人脸三维结构与二维照片在运动时的透视差异,实现了有效的活体检测。算法采用纯JavaScript实现,无需后端支持,可在前端环境中实时运行。

在线演示

实时体验演示 | 用手机扫码快速测试

人脸活体检测演示二维码

算法原理

核心思想

算法基于以下观察:

真实人脸具有三维结构,在头部运动时,不同位置的关键点会因透视效应产生不同的位移量 二维照片在移动时,所有像素点遵循相同的仿射变换,各关键点的相对位置关系不变 通过对比连续帧中关键点的运动模式,可以区分真实人脸和照片攻击

人脸关键点获取

算法使用Human.js提取人脸的468个关键点,这些关键点覆盖了面部的主要结构特征,包括眼部、鼻部、嘴部、脸颊、耳部等区域。

前置处理

通过代码强制保障有且仅有一张人脸出现在摄像头内, 并确保人脸区域占比在50%-80%之间

设计与实现

人脸运动检测阶段

这一步的核心目的是通过算法强迫人脸动起来,从而捕获其运动特征。

关键点中心化处理

为消除人脸整体平移对检测结果的影响,算法以鼻尖(索引为1)为原点对所有关键点进行中心化处理:

p' = p - p_nose

其中,p'为中心化后的坐标,p为原始坐标,p_nose为鼻尖坐标。

帧间位移计算

算法通过计算连续帧间对应关键点的欧氏距离来衡量运动强度:

displacement.x = currentMesh[idx][0] - previousMesh[idx][0]
displacement.y = currentMesh[idx][1] - previousMesh[idx][1]
displacement.magnitude = sqrt(displacement.x² + displacement.y²)

然后取所有关键点位移的平均值作为整体运动强度。

运动状态判定

设定运动阈值movementThreshold,当计算得到的运动强度超过该阈值时,判定为发生有效运动。为降低噪声干扰,设置最小连续运动帧数要求,只有连续达到指定帧数的运动才被认定为有效运动。

照片攻击检测阶段

核心原理

该算法基于以下关键观察:

  1. 真实人脸运动:由于透视效应,近处点(如鼻尖)的移动幅度大于远处点(如耳朵)
  2. 照片攻击:所有点按照同一仿射变换移动,各点运动向量高度一致

算法通过比较鼻尖、脸颊、耳朵等在多帧中的 2D 位移比例来判断是否为照片攻击。

四维指标综合判断

算法从四个维度进行综合判断:

  • 运动位移方差:真实人脸各点运动差异大,方差较高;照片各点运动一致,方差较低
  • 透视比率:计算近处点平均位移与远处点平均位移的比值,真实人脸该比率通常大于1,而照片近似1
  • 运动方向一致性:照片攻击时所有点的运动方向高度一致,一致性指标较高
  • 仿射变换匹配度:照片的各点运动符合单一仿射变换,匹配度较高

综合评分计算

四项指标有一项超过阈值,即可判定为照片攻击,否则加权平均作为最终判断评分。

容错机制

单次判定可能会被某个角度运动的照片骗过,为了提高算法的鲁棒性,需连续多次判定(例如15帧),必须全部通过才取信为真实人脸。

注:本文所描述的算法已在开源项目face-liveness-detector中实现,可供参考和使用。