From e5894c22067abf5654ba88f4aa05bc4f0f1630a9 Mon Sep 17 00:00:00 2001 From: sladro Date: Thu, 8 Jan 2026 16:26:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BA=86=E6=96=87=E6=A1=A3?= =?UTF-8?q?=EF=BC=8C=E5=8F=AF=E6=81=A2=E5=A4=8D=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FaceRecognition_SchemeB.md | 281 ------------------------------------- Readme.md | 204 +++++++++++++++++++++++++-- 2 files changed, 190 insertions(+), 295 deletions(-) delete mode 100644 FaceRecognition_SchemeB.md diff --git a/FaceRecognition_SchemeB.md b/FaceRecognition_SchemeB.md deleted file mode 100644 index 87ce971..0000000 --- a/FaceRecognition_SchemeB.md +++ /dev/null @@ -1,281 +0,0 @@ -# 人脸识别方案 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** 插件: - -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 face_det;` -- `std::shared_ptr face_recog;` - -优点:不与 `user_meta` 冲突,类型清晰,插件协作简单。 - -**选项 2(兼容性强,侵入更小)**:引入“MetaBundle”统一承载多类 meta,并把 `Frame::user_meta` 变成 bundle。 - -- `user_meta` 保存 `std::shared_ptr`,由 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 系列轻量 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 映射) -- **持久化存储**:用于重启恢复/管理 - -持久化方案: - -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 暴力点积足够。 -- 大库(≥几万人):再考虑 ANN(HNSW/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` -- 对齐/裁剪/推理/归一化 -- 实现 Gallery:SQLite 或文件(先做最简单可用版本) -- 输出 `FaceRecogResult` - -### Step 4:OSD/Alarm 接入(最小可用) - -- OSD:显示 `name`/`unknown` -- Alarm:支持“陌生人出现/指定人员出现”触发 - -### Step 5:配置与热更新 - -- `UpdateConfig` 支持阈值、gallery reload 等 - -### Step 6:验证 - -- 使用样例视频/RTSP 流验证:识别准确率、误认率、性能、内存 diff --git a/Readme.md b/Readme.md index 4250177..2b88de0 100644 --- a/Readme.md +++ b/Readme.md @@ -1,11 +1,5 @@ - -## 回答 - -# RK3588 智能视频分析系统 PRD(完整方案 v1.1) - -> 在你 v1.0 的基础上,已经补上你点名要的这几块: -> **DAG 多分支示例 + 节点通用字段 + 生命周期 Drain + Frame 结构 + 性能 / 线程 / 监控章节**,现在这份可以直接当总 PRD 用。 +# RK3588 智能视频分析系统 PRD(v1.2) --- @@ -145,7 +139,9 @@ struct Frame { uint64_t pts; // 时间戳(微秒) uint64_t frame_id; // 单调递增ID - std::shared_ptr det; // AI 检测结果 + std::shared_ptr det; // AI 检测结果(通用目标检测) + std::shared_ptr face_det; // 人脸检测结果 + std::shared_ptr face_recog;// 人脸识别结果 std::shared_ptr user_meta; // 扩展元数据(具体类型由插件间约定) }; ``` @@ -153,9 +149,54 @@ struct Frame { **约定:** - **零拷贝优先**:解码/前处理尽量通过 `dma_fd` 传递,必要时才用 data。 -- 节点默认只读图像数据,**可以更新 `det` / `user_meta`**。 +- 节点默认只读图像数据,**可以更新 `det` / `face_det` / `face_recog` / `user_meta`**。 - 所有队列中传递的都是 `std::shared_ptr`,实现多下游共享。 +### 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 landmarks; // 5点关键点:左眼/右眼/鼻尖/左嘴角/右嘴角 +}; + +struct FaceDetResult { + std::vector 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 embedding; // 特征向量(可选,调试/注册用) +}; + +struct FaceRecogResult { + std::vector items; + int img_w; + int img_h; + std::string model_name; // 模型名(如 "arcface") +}; +``` + --- ## 四、插件模型与生命周期 @@ -520,11 +561,13 @@ GraphMgr 负责: ### 6.2 AI 分析 -| 节点类型 | 功能描述 | 关键参数 | 硬件依赖 | -| :----------- | :------------------------- | :----------------------------------- | :------- | -| `ai_yolo` | 通用目标检测(YOLOv5/v8) | `model_path`, `model_version`, `num_classes`, `conf`, `nms`, `class_filter` | NPU(RKNN) | -| `ai_pose` | 关键点检测(可选扩展) | `model_path` | NPU | -| `ai_custom` | 自定义 AI 算法 | `model_path`, `custom_param` 等 | NPU | +| 节点类型 | 功能描述 | 关键参数 | 硬件依赖 | +| :-------------- | :------------------------- | :----------------------------------- | :------- | +| `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 | #### 6.2.1 det_post(检测结果后处理,按类别策略链) @@ -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 业务逻辑与输出 | 节点类型 | 功能描述 | 关键参数 | 硬件依赖 |