safesight-edge/FaceRecognition_SchemeB.md
sladro 7f30d70d5a
Some checks are pending
CI / host-build (push) Waiting to run
CI / rk3588-cross-build (push) Waiting to run
测试人脸识别功能,刚刚开发完成
2026-01-07 17:15:07 +08:00

282 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 人脸识别方案 Bai_face_det + ai_face_recog设计文档
> 目标:在现有 **Graph(DAG)+Plugin Node** 框架中,实现“有人脸库,可识别具体姓名/陌生人”。
>
> 约束:
> - 插件化接入:新能力以 Node 插件形式加入 pipeline不改动主业务流程即可启用/禁用。
> - 与现有 `publish` 对 `Frame::user_meta` 的使用不冲突。
> - 运行平台RK3588RKNN/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** 插件:
1) `ai_face_det`:人脸检测(可选输出关键点)
- 输入RGB/BGR/NV12根据你的 preprocess 输出)
- 输出:人脸框列表(可含 5 点关键点)、检测置信度
2) `ai_face_recog`:人脸对齐/裁剪 + 特征提取 + 人脸库检索 + 陌生人判定
- 输入:原始 Frame + `ai_face_det` 的检测结果
- 输出识别结果person_id/name/similarity/unknown、可选把“姓名”写回可视化/告警侧
推荐拓扑:
```text
[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图像坐标
- `score`
- `landmarks[5]`(可选:左眼/右眼/鼻尖/左嘴角/右嘴角)
- `track_id`(可选,后续可加跟踪)
### 2.4 FaceRecogResult 数据结构(建议)
- `items[]`:每个元素与一个人脸对应
- `bbox`
- `best_person_id` / `best_name` / `best_sim`
- `unknown`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 系列轻量 backboneMobileFaceNet 等)。
- 输入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 映射)
- **持久化存储**:用于重启恢复/管理
持久化方案:
1) SQLite推荐
- `person(id, name, created_at, updated_at, extra_json)`
- `embedding(person_id, emb BLOB, normed, created_at)`
2) 文件(简化版):
- `gallery.bin`(向量)+ `gallery.json`person 映射)
### 4.2 多样本策略
- 每人保存 N 个样本 embedding建议 3~10
- 运行时维护 `centroid`(平均向量再归一化)提高稳定性;检索可先比 centroid再必要时比多样本。
### 4.3 检索实现
- 小库≤几千人CPU 暴力点积足够。
- 大库(≥几万人):再考虑 ANNHNSW/Faiss属于后续优化不影响方案 B 的接口。
---
## 5. 两个节点的职责、接口与配置
### 5.1 ai_face_det插件
**职责**
-`Frame` 获取图像(必要时做色彩/resize
- 调用 `AiScheduler` 推理检测模型
- 生成 `FaceDetResult` 写入 `frame->face_det`
**配置建议(示例)**
```json
{
"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`
**配置建议(示例)**
```json
{
"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`
- 对齐/裁剪/推理/归一化
- 实现 GallerySQLite 或文件(先做最简单可用版本)
- 输出 `FaceRecogResult`
### Step 4OSD/Alarm 接入(最小可用)
- OSD显示 `name`/`unknown`
- Alarm支持“陌生人出现/指定人员出现”触发
### Step 5配置与热更新
- `UpdateConfig` 支持阈值、gallery reload 等
### Step 6验证
- 使用样例视频/RTSP 流验证:识别准确率、误认率、性能、内存