更新了文档,可恢复版本
Some checks failed
CI / host-build (push) Has been cancelled
CI / rk3588-cross-build (push) Has been cancelled

This commit is contained in:
sladro 2026-01-08 16:26:36 +08:00
parent c7ec975874
commit e5894c2206
2 changed files with 190 additions and 295 deletions

View File

@ -1,281 +0,0 @@
# 人脸识别方案 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 流验证:识别准确率、误认率、性能、内存

196
Readme.md
View File

@ -1,11 +1,5 @@
## 回答
# RK3588 智能视频分析系统 PRD完整方案 v1.1
> 在你 v1.0 的基础上,已经补上你点名要的这几块:
> **DAG 多分支示例 + 节点通用字段 + 生命周期 Drain + Frame 结构 + 性能 / 线程 / 监控章节**,现在这份可以直接当总 PRD 用。
# RK3588 智能视频分析系统 PRDv1.2
---
@ -145,7 +139,9 @@ struct Frame {
uint64_t pts; // 时间戳(微秒)
uint64_t frame_id; // 单调递增ID
std::shared_ptr<DetectionResult> det; // AI 检测结果
std::shared_ptr<DetectionResult> det; // AI 检测结果(通用目标检测)
std::shared_ptr<FaceDetResult> face_det; // 人脸检测结果
std::shared_ptr<FaceRecogResult> face_recog;// 人脸识别结果
std::shared_ptr<void> user_meta; // 扩展元数据(具体类型由插件间约定)
};
```
@ -153,9 +149,54 @@ struct Frame {
**约定:**
- **零拷贝优先**:解码/前处理尽量通过 `dma_fd` 传递,必要时才用 data。
- 节点默认只读图像数据,**可以更新 `det` / `user_meta`**。
- 节点默认只读图像数据,**可以更新 `det` / `face_det` / `face_recog` / `user_meta`**。
- 所有队列中传递的都是 `std::shared_ptr<Frame>`,实现多下游共享。
### 3.3 人脸检测结果结构FaceDetResult
```cpp
struct Point2f {
float x;
float y;
};
struct FaceDetItem {
Rect bbox; // 人脸框
float score; // 检测置信度
int track_id; // 跟踪ID可选-1表示无
bool has_landmarks; // 是否有关键点
std::array<Point2f, 5> landmarks; // 5点关键点左眼/右眼/鼻尖/左嘴角/右嘴角
};
struct FaceDetResult {
std::vector<FaceDetItem> faces;
int img_w; // 原始图像宽
int img_h; // 原始图像高
std::string model_name; // 模型名(如 "retinaface"
};
```
### 3.4 人脸识别结果结构FaceRecogResult
```cpp
struct FaceRecogItem {
Rect bbox; // 人脸框(与检测结果对应)
int best_person_id; // 匹配的人员ID-1表示陌生人
std::string best_name; // 匹配的人员姓名("unknown"表示陌生人)
float best_sim; // 最高相似度
float second_sim; // 次高相似度用于margin判定
bool unknown; // 是否为陌生人
std::vector<float> embedding; // 特征向量(可选,调试/注册用)
};
struct FaceRecogResult {
std::vector<FaceRecogItem> items;
int img_w;
int img_h;
std::string model_name; // 模型名(如 "arcface"
};
```
---
## 四、插件模型与生命周期
@ -521,8 +562,10 @@ GraphMgr 负责:
### 6.2 AI 分析
| 节点类型 | 功能描述 | 关键参数 | 硬件依赖 |
| :----------- | :------------------------- | :----------------------------------- | :------- |
| :-------------- | :------------------------- | :----------------------------------- | :------- |
| `ai_yolo` | 通用目标检测YOLOv5/v8 | `model_path`, `model_version`, `num_classes`, `conf`, `nms`, `class_filter` | NPU(RKNN) |
| `ai_face_det` | 人脸检测含5点关键点 | `model_path`, `conf`, `nms`, `max_faces`, `output_landmarks` | NPU(RKNN) |
| `ai_face_recog` | 人脸识别(对齐+特征提取+检索) | `model_path`, `gallery`, `threshold`, `align` | NPU(RKNN) |
| `ai_pose` | 关键点检测(可选扩展) | `model_path` | NPU |
| `ai_custom` | 自定义 AI 算法 | `model_path`, `custom_param` 等 | NPU |
@ -592,6 +635,139 @@ GraphMgr 负责:
> 所有 AI 节点通过 **AiScheduler** 使用 NPU不直接持有 NPU 句柄。
#### 6.2.2 ai_face_det人脸检测
人脸检测节点,基于 RetinaFace/SCRFD 等轻量模型,输出人脸框和 5 点关键点。
**配置示例:**
```json
{
"id": "face_det_cam1",
"type": "ai_face_det",
"role": "filter",
"enable": true,
"model_path": "./models/RetinaFace_mobile320.rknn",
"conf": 0.6,
"nms": 0.4,
"max_faces": 10,
"output_landmarks": true,
"input_format": "rgb"
}
```
**参数说明:**
- `model_path`: RetinaFace RKNN 模型路径
- `conf`: 置信度阈值(默认 0.6
- `nms`: NMS 阈值(默认 0.4
- `max_faces`: 最大检测人脸数
- `output_landmarks`: 是否输出 5 点关键点(用于后续对齐)
- `input_format`: 输入格式 `rgb``bgr`
**输出:** 写入 `frame->face_det`,供 `ai_face_recog``osd` 读取。
#### 6.2.3 ai_face_recog人脸识别
人脸识别节点,读取检测结果,对每个人脸进行对齐、特征提取、人脸库检索。
**配置示例:**
```json
{
"id": "face_recog_cam1",
"type": "ai_face_recog",
"role": "filter",
"enable": true,
"model_path": "./models/mobilefacenet_arcface.rknn",
"align": true,
"emit_embedding": false,
"max_faces": 10,
"input_format": "rgb",
"threshold": {
"accept": 0.45,
"margin": 0.05
},
"gallery": {
"backend": "sqlite",
"path": "./models/face_gallery.db",
"load_on_start": true,
"expected_dim": 512
}
}
```
**参数说明:**
- `model_path`: ArcFace/MobileFaceNet RKNN 模型路径(输入 112x112输出 512D embedding
- `align`: 是否使用 5 点关键点做仿射对齐(推荐开启)
- `emit_embedding`: 是否在结果中输出 embedding调试用生产关闭以省内存
- `threshold.accept`: 相似度接受阈值(高于此值认为匹配成功)
- `threshold.margin`: top1 与 top2 相似度差值阈值(可选,用于降低误识率)
- `gallery.backend`: 人脸库后端(当前支持 `sqlite`
- `gallery.path`: SQLite 数据库路径
- `gallery.expected_dim`: embedding 维度(需与模型匹配)
**判定规则:**
- `top1_sim >= accept` 且(可选)`top1_sim - top2_sim >= margin` → 识别为该人
- 否则标记为 `unknown`
**输出:** 写入 `frame->face_recog`,供 `osd``alarm` 读取。
#### 6.2.4 人脸识别 Pipeline 示例
```text
[input_rtsp] → [preprocess] → [ai_face_det] → [ai_face_recog] → [osd] → [publish]
```
**完整配置示例:**
```json
{
"name": "cam1_face_recog",
"nodes": [
{ "id": "in_cam1", "type": "input_rtsp", "url": "rtsp://..." },
{ "id": "pre_cam1", "type": "preprocess", "dst_w": 1280, "dst_h": 720, "dst_format": "rgb" },
{ "id": "face_det_cam1", "type": "ai_face_det", "model_path": "./models/RetinaFace_mobile320.rknn", "conf": 0.6 },
{ "id": "face_recog_cam1", "type": "ai_face_recog", "model_path": "./models/mobilefacenet_arcface.rknn", "gallery": { "path": "./models/face_gallery.db" } },
{ "id": "osd_cam1", "type": "osd", "draw_face_det": true },
{ "id": "pub_cam1", "type": "publish", "outputs": [{ "proto": "rtsp_server", "port": 8555, "path": "/live/face" }] }
],
"edges": [
["in_cam1", "pre_cam1"],
["pre_cam1", "face_det_cam1"],
["face_det_cam1", "face_recog_cam1"],
["face_recog_cam1", "osd_cam1"],
["osd_cam1", "pub_cam1"]
]
}
```
> **注意**:人脸识别应在**原始分辨率**(如 1280x720上进行检测模型内部会自动缩放到 320x320 推理,输出坐标缩放回原始尺寸。这样从高清图裁剪对齐人脸,识别质量更高。
#### 6.2.5 人脸库Gallery说明
人脸库使用 SQLite 存储,表结构:
```sql
CREATE TABLE person (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
created_at TEXT,
extra TEXT -- JSON 扩展字段
);
CREATE TABLE embedding (
id INTEGER PRIMARY KEY,
person_id INTEGER NOT NULL,
embedding BLOB NOT NULL, -- 512D float32 向量
created_at TEXT,
FOREIGN KEY (person_id) REFERENCES person(id)
);
```
**检索实现:**
- 小库≤几千人CPU 暴力点积,无需额外索引
- 大库(≥几万人):可扩展 HNSW/Faiss 等 ANN 索引
### 6.3 业务逻辑与输出
| 节点类型 | 功能描述 | 关键参数 | 硬件依赖 |