8.5 KiB
8.5 KiB
人脸识别方案 B(ai_face_det + ai_face_recog)设计文档
目标:在现有 Graph(DAG)+Plugin Node 框架中,实现“有人脸库,可识别具体姓名/陌生人”。
约束:
- 插件化接入:新能力以 Node 插件形式加入 pipeline;不改动主业务流程即可启用/禁用。
- 与现有
publish对Frame::user_meta的使用不冲突。- 运行平台:RK3588(RKNN/NPU 优先),CPU 负责检索与业务逻辑。
1. 总体架构
1.1 现有框架要点(与本方案的衔接)
- Node 插件接口:
INode(Init/Start/Process/Stop/UpdateConfig),通过REGISTER_NODE导出符号加载。 - 数据承载:
Frame内含图像数据(DMA/CPU)以及元数据:frame->det:当前用于通用检测(DetectionResult)frame->user_meta:扩展元数据(当前publish会写入编码包 meta)
- 推理:
AiScheduler支持 RKNN 模型加载与推理(可复用)。
1.2 方案 B 的节点拆分
新增两个 Filter 插件:
ai_face_det:人脸检测(可选输出关键点)
- 输入:RGB/BGR/NV12(根据你的 preprocess 输出)
- 输出:人脸框列表(可含 5 点关键点)、检测置信度
ai_face_recog:人脸对齐/裁剪 + 特征提取 + 人脸库检索 + 陌生人判定
- 输入:原始 Frame +
ai_face_det的检测结果 - 输出:识别结果(person_id/name/similarity/unknown)、可选把“姓名”写回可视化/告警侧
推荐拓扑:
[input_*] → [preprocess] → [ai_face_det] → [ai_face_recog] → [osd/alarm/publish/storage]
2. 数据与元数据协议(关键设计点)
2.1 为什么不直接复用 Frame::user_meta
当前 publish 节点会写 frame->user_meta(编码包 meta)。如果 ai_face_* 也直接写同一字段,会出现覆盖/冲突。
2.2 推荐的元数据承载方式(二选一)
选项 1(推荐,长期干净):扩展 Frame 结构新增专用字段,例如:
std::shared_ptr<FaceDetResult> face_det;std::shared_ptr<FaceRecogResult> face_recog;
优点:不与 user_meta 冲突,类型清晰,插件协作简单。
选项 2(兼容性强,侵入更小):引入“MetaBundle”统一承载多类 meta,并把 Frame::user_meta 变成 bundle。
user_meta保存std::shared_ptr<MetaBundle>,由 bundle 内部维护多个 typed slot(例如 publish_meta、face_meta…)。
优点:不新增 Frame 字段;缺点:需要先统一改造现有写 user_meta 的插件(至少 publish/alarm)。
本文后续描述以 选项 1 为主(更直观)。
2.3 FaceDetResult 数据结构(建议)
faces[]:每个元素包含bbox(x,y,w,h,图像坐标)scorelandmarks[5](可选:左眼/右眼/鼻尖/左嘴角/右嘴角)track_id(可选,后续可加跟踪)
2.4 FaceRecogResult 数据结构(建议)
items[]:每个元素与一个人脸对应bboxbest_person_id/best_name/best_simunknown(bool)second_sim(可选,用于 margin 判定/调试)embedding(可选:用于注册/调试;生产可关闭以省内存)
3. 算法与模型选型
3.1 检测模型(ai_face_det)
推荐轻量:SCRFD/RetinaFace 类人脸检测(可带 5 点)。
- 输入分辨率:320/640 任选(权衡精度与速度)
- 输出:bbox + score +(可选)5 点
3.2 特征模型(ai_face_recog)
推荐:ArcFace 系列轻量 backbone(MobileFaceNet 等)。
- 输入:112x112 aligned face
- 输出:128D 或 512D embedding
- 后处理:L2 normalize
3.3 相似度与陌生人判定
- 相似度:cosine(归一化后点积)
- 判定规则建议:
top1_sim >= T_accept且(可选)top1_sim - top2_sim >= T_margin→ 识别为该人- 否则 unknown
阈值需要按你的数据集标定(不同模型差别大),但架构层支持动态配置。
4. 人脸库(Gallery)设计
4.1 存储形态
建议分两层:
- 内存索引:运行时检索用(向量矩阵 + person 映射)
- 持久化存储:用于重启恢复/管理
持久化方案:
-
SQLite(推荐):
person(id, name, created_at, updated_at, extra_json)embedding(person_id, emb BLOB, normed, created_at)
-
文件(简化版):
gallery.bin(向量)+gallery.json(person 映射)
4.2 多样本策略
- 每人保存 N 个样本 embedding(建议 3~10)。
- 运行时维护
centroid(平均向量再归一化)提高稳定性;检索可先比 centroid,再必要时比多样本。
4.3 检索实现
- 小库(≤几千人):CPU 暴力点积足够。
- 大库(≥几万人):再考虑 ANN(HNSW/Faiss),属于后续优化,不影响方案 B 的接口。
5. 两个节点的职责、接口与配置
5.1 ai_face_det(插件)
职责:
- 从
Frame获取图像(必要时做色彩/resize) - 调用
AiScheduler推理检测模型 - 生成
FaceDetResult写入frame->face_det
配置建议(示例)
{
"id": "face_det",
"type": "ai_face_det",
"role": "filter",
"enable": true,
"model_path": "/models/scrfd_640.rknn",
"conf": 0.6,
"nms": 0.4,
"max_faces": 10,
"output_landmarks": true,
"input_format": "rgb"
}
5.2 ai_face_recog(插件)
职责:
- 读取
frame->face_det - 对每个 face:
- (可选)按 5 点对齐(affine)
- crop/resize 到识别模型输入
AiScheduler推理得到 embedding,并 L2 normalize- 在人脸库检索,输出 best match 或 unknown
- 写入
frame->face_recog
配置建议(示例)
{
"id": "face_recog",
"type": "ai_face_recog",
"role": "filter",
"enable": true,
"model_path": "/models/arcface_112.rknn",
"gallery": {
"backend": "sqlite",
"path": "/data/face_gallery.db",
"load_on_start": true,
"cache_in_memory": true
},
"threshold": {
"accept": 0.45,
"margin": 0.05
},
"max_faces": 10,
"align": true,
"emit_embedding": false
}
6. 与 OSD / Alarm / Publish 的集成策略
6.1 OSD 显示姓名
现状:osd 只读 frame->det 并通过 cls_id → labels 显示类别名,不支持动态姓名。
推荐方案:
- 扩展
osd:若frame->face_recog存在,则在 bbox 处绘制name(sim);否则保持原逻辑。 - 或新增
osd_face插件专门画人脸识别结果(与原osd并存)。
6.2 Alarm 规则与上报
推荐:在 alarm 中新增一种事件源:
- 读取
frame->face_recog,按规则触发(例如:出现黑名单人员、陌生人出现、某人出现次数/停留时间)。
6.3 Publish/Storage
- 与人脸识别无强耦合:只要不覆盖
user_meta,推流/录像可照常工作。
7. 性能与线程模型建议
ai_face_det和ai_face_recog都是 filter 节点,由 GraphMgr 驱动Process(frame)。- 推理层复用
AiScheduler(其内部对每个 model context 有互斥保护),可减少重复加载。 - 关键优化点:
- 尽量在 preprocess 输出 RGB/BGR,避免在两个节点重复色彩转换。
- 对齐与裁剪尽量复用 CPU SIMD 或 RGA(若你已有 RGA 路径)。
- 检索使用批量点积(向量矩阵乘),人数不大时可用简单循环即可。
8. 一步步实现计划(后续按此执行)
你确认开始开发后,我们将严格按以下步骤逐步实现与验证。
Step 0:确定元数据承载方式(必须先定)
- 选择“Frame 新字段(face_det/face_recog)”或“MetaBundle”。
- 若选择新字段:需要同步更新相关 include、序列化/metrics(如有)以及 OSD/Alarm 读取逻辑。
Step 1:新增公共头文件与数据结构
- 定义
FaceDetResult/FaceRecogResult(include 目录下)。
Step 2:实现插件 ai_face_det
- CMake 加入新插件目标
- 使用
AiScheduler加载/推理检测模型 - 输出
FaceDetResult
Step 3:实现插件 ai_face_recog
- 读取
FaceDetResult - 对齐/裁剪/推理/归一化
- 实现 Gallery:SQLite 或文件(先做最简单可用版本)
- 输出
FaceRecogResult
Step 4:OSD/Alarm 接入(最小可用)
- OSD:显示
name/unknown - Alarm:支持“陌生人出现/指定人员出现”触发
Step 5:配置与热更新
UpdateConfig支持阈值、gallery reload 等
Step 6:验证
- 使用样例视频/RTSP 流验证:识别准确率、误认率、性能、内存