本项目是一个基于 face-api.js 实现的纯前端人脸识别演示系统。它能够在浏览器中实时进行人脸检测、识别、年龄/性别预测以及表情分析,无需任何后端服务器支持。
人脸识别通常分为三个关键步骤,本项目严格遵循这一工作流:
- 任务:在图像中找到人脸的位置。
- 模型:
SsdMobilenetv1或TinyFaceDetector。 - 输出:一个矩形框(Bounding Box),标记人脸所在的坐标。
- 任务:识别面部关键点(如眼睛、鼻子、嘴巴、下巴轮廓)。
- 模型:
faceLandmark68Net(68点特征识别)。 - 意义:通过这些点,系统可以矫正侧脸或歪头的情况,确保后续提取的特征更加准确。
- 任务:将人脸图像转换为一组数字向量(由 128 个浮点数组成,称为 Face Descriptor/Embedding)。
- 模型:
faceRecognitionNet。 - 比对:
- 1:1 比对:计算两个向量之间的“欧氏距离”。距离越小,代表越像是同一个人。
- 1:N 搜索:将当前检测到的人脸特征与已注册的人脸库逐一比对,找到最匹配的一个。
在 loadModels() 函数中,我们加载了以下模型:
| 模型名称 | 作用 |
|---|---|
ssdMobilenetv1 |
高精度人脸检测,识别率高但计算量稍大。 |
tinyFaceDetector |
高性能人脸检测,速度极快,适合移动端,但对侧脸支持稍弱。 |
faceLandmark68Net |
识别人脸上的 68 个关键坐标点。 |
faceRecognitionNet |
将人脸转化为唯一的 128 位特征向量,用于比对身份。 |
ageGenderNet |
预测年龄(Age)和性别(Gender)。 |
faceExpressionNet |
识别表情(开心、伤心、愤怒、平静等)。 |
这是最核心的入口。它会扫描 input(可以是图片、视频流或 Canvas),并返回检测到的所有人脸对象。
.withFaceLandmarks(): 链式调用,告诉 API 同时识别 68 个特征点。.withFaceDescriptors(): 链式调用,告诉 API 同时计算人脸特征向量(用于识别身份)。.withAgeAndGender(): 链式调用,预测年龄和性别。.withFaceExpressions(): 链式调用,识别面部表情。
- 作用:坐标转换。
- 原因:AI 识别出的坐标是基于原始图片分辨率的,但网页上显示的图片尺寸(CSS 尺寸)可能不同。该函数将识别框的坐标映射到网页实际显示的尺寸上,确保识别框能准确对齐人脸。
- 作用:匹配器。
- 原理:它接收一组“已标记身份的人脸特征”(人脸库),并设置一个“相似度阈值”。
match.distance:这是欧氏距离。distance越小,匹配度越高。confidence(置信度):代码中使用1 - match.distance计算得出,范围 0-1。0.88 意味着相似度极高。
- 作用:封装已知的身份。
- 用法:将一个人的名字(Label)和他的特征向量数组(Descriptors)绑定在一起。
- 初始化 (
window.onload):- 调用
loadLibrary()从本地存储(localStorage)恢复之前保存的人脸数据。 - 后台静默调用
loadModels()预热 AI 模型。
- 调用
- 启动摄像头 (
startCamera):- 使用浏览器
getUserMedia获取视频流。 - 使用
updateCanvasSize确保绘图层(Canvas)和视频层完全重合且尺寸一致。
- 使用浏览器
- 实时检测 (
detectFromVideo):- 设置一个
setInterval每秒执行一次检测。 - 检测到人脸后,遍历每一张脸:
- 从
FaceMatcher中寻找最匹配的人名。 - 使用
faceapi.draw.DrawBox在 Canvas 上画出带有序号、姓名和置信度的框。 - 在下方生成 HTML 结果卡片,展示具体的年龄、性别和表情。
- 从
- 设置一个
- 人脸录入 (
addPersonToLibrary):- 用户上传图片 -> 提取该图片的单张人脸特征 -> 绑定用户输入的姓名 -> 存入内存数组。
- 调用
saveLibrary()将数组序列化为 JSON 字符串,持久化存储到浏览器的localStorage中。