From 41c4a3de3b62aa423d9ddce06bf3ea6c26a951f7 Mon Sep 17 00:00:00 2001 From: sladro Date: Tue, 6 Jan 2026 11:53:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=80=E5=8F=91=E5=90=8E=E5=A4=84=E7=90=86?= =?UTF-8?q?=E6=8F=92=E4=BB=B62?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Readme.md | 97 ++++++++++++++++++++++++++++-- plugins/det_post/det_post_node.cpp | 7 +++ 2 files changed, 98 insertions(+), 6 deletions(-) diff --git a/Readme.md b/Readme.md index bbb9590..e0e4e93 100644 --- a/Readme.md +++ b/Readme.md @@ -80,11 +80,13 @@ 示意: ```text -[in] → [pre] → [ai] ─────→ [alarm] - └──→ [osd] → [storage] - └──→ [publish] +[in] → [pre] → [ai_yolo] → [det_post] ───→ [alarm] + └──────→ [osd] → [storage] + └──→ [publish] ``` +> 说明:`det_post` 是检测结果后处理节点(可选)。它会对 `frame->det` 做过滤/修正/二次验证,使 OSD 与告警读取到一致的“后处理结果”。 + --- ## 三、数据模型(Frame & 元数据) @@ -322,6 +324,23 @@ GraphMgr 负责: "nms": 0.5, "class_filter": [0, 1] }, + { + "id": "pp_cam1", + "type": "det_post", + "role": "filter", + "enable": true, + "default": { "enable": false }, + "per_class": [ + { + "class_id": 0, + "enable": true, + "processors": [ + { "type": "conf_gate", "conf_min": 0.65 }, + { "type": "bbox_gate", "min_w": 10, "min_h": 10 } + ] + } + ] + }, { "id": "osd_cam1", "type": "osd", @@ -373,8 +392,9 @@ GraphMgr 负责: "edges": [ ["in_cam1", "pre_cam1"], ["pre_cam1", "ai_cam1"], - ["ai_cam1", "alarm_cam1"], - ["ai_cam1", "osd_cam1"], + ["ai_cam1", "pp_cam1"], + ["pp_cam1", "alarm_cam1"], + ["pp_cam1", "osd_cam1"], ["osd_cam1", "storage_cam1"], ["osd_cam1", "publish_cam1"] ] @@ -383,6 +403,10 @@ GraphMgr 负责: } ``` +> 说明:如果不需要后处理: +> - **推荐**:保留 `pp_cam1` 节点(`enable:true`),并确保其不启用任何处理链(例如:`default.enable=false` 且 `per_class` 为空),此时 `det_post` 会作为轻量透传。 +> - 若确实要移除该节点:需要**同时删除并重连拓扑**(例如改回 `ai_cam1 -> osd_cam1` 与 `ai_cam1 -> alarm_cam1`),仅设置 `enable=false` 会导致下游节点无输入而构图失败。 + 通过简单改动即可支持不同组合: - 不报警:`alarm_cam1.enable = false` 或移除该节点/边。 @@ -400,13 +424,15 @@ GraphMgr 负责: { "id": "in", "type": "input_rtsp", "role": "source", "url": "${url}" }, { "id": "pre", "type": "preprocess", "role": "filter", "dst_w": 640, "dst_h": 640 }, { "id": "ai", "type": "ai_yolo", "role": "filter", "model_path": "/models/yolov8n.rknn", "conf": 0.5 }, + { "id": "pp", "type": "det_post", "role": "filter", "enable": true, "default": { "enable": false } }, { "id": "osd", "type": "osd", "role": "filter" }, { "id": "pub", "type": "publish", "role": "sink", "path": "/live/${name}" } ], "edges": [ ["in", "pre"], ["pre", "ai"], - ["ai", "osd"], + ["ai", "pp"], + ["pp", "osd"], ["osd", "pub"] ] } @@ -485,6 +511,65 @@ GraphMgr 负责: | `ai_pose` | 关键点检测(可选扩展) | `model_path` | NPU | | `ai_custom` | 自定义 AI 算法 | `model_path`, `custom_param` 等 | NPU | +#### 6.2.1 det_post(检测结果后处理,按类别策略链) + +`det_post` 是一个 **Filter** 节点,位于 `ai_yolo` 之后,负责对 `frame->det->items` 做**按类别可配置**的后处理。 + +**设计目标:** +- 不同类别可以挂不同的处理器链(Processor/Strategy Registry),避免“一个类别一个插件”。 +- 默认改写 `frame->det`,让 `osd` / `alarm` 读到一致结果。 + +**配置示例:** + +```json +{ + "id": "pp_cam1", + "type": "det_post", + "role": "filter", + "enable": true, + + "default": { "enable": false }, + "per_class": [ + { + "class_id": 3, + "enable": true, + "processors": [ + { "type": "conf_gate", "conf_min": 0.65 }, + { "type": "bbox_gate", "min_w": 10, "min_h": 10 }, + { "type": "roi_gate", "roi": { "x": 0.1, "y": 0.1, "w": 0.8, "h": 0.8 }, "mode": "center" } + ] + }, + { + "class_id": 5, + "enable": true, + "processors": [ + { "type": "hsv_ratio", "h_min": 0, "h_max": 20, "s_min": 20, "v_min": 70, "ratio_min": 0.7, + "max_samples": 2048, "step": 0, "min_samples": 32, + "crop": { "x": 0.0, "y": 0.0, "w": 1.0, "h": 1.0 } } + ] + }, + { + "class_id": 7, + "enable": true, + "processors": [ + { "type": "lab_kmeans", "k": 3, "iters": 8, "l_max": 50, "ratio_min": 0.4, + "max_samples": 2048, "step": 0, "min_samples": 64, + "crop": { "x": 0.0, "y": 0.0, "w": 1.0, "h": 1.0 } } + ] + } + ] +} +``` + +**内置 processors(当前实现):** +- `conf_gate`:按置信度过滤(`conf_min`) +- `bbox_gate`:按框尺寸过滤(`min_w`, `min_h`) +- `roi_gate`:按 ROI 过滤(`roi:{x,y,w,h}` 归一化;`mode:center` 使用 bbox 中心点判断) +- `hsv_ratio`:框内采样像素转 HSV,统计阈值命中占比(`h_min/h_max/s_min/s_max/v_min/v_max/ratio_min`,支持 `crop/max_samples/step`) +- `lab_kmeans`:框内采样像素转 Lab,KMeans 聚类后取最暗簇做判定(`k/iters/l_max/ratio_min`,支持 `crop/max_samples/step`) + +> 注意:`hsv_ratio/lab_kmeans` 需要 `Frame` 提供可读的像素数据(`frame->data` 可访问)。若上游是纯 DMA-BUF 且未映射到 CPU,这两类处理器会自然退化为“不过滤”。 + **ai_yolo 参数说明:** - `model_version`: `"v5"`, `"v8"` 或 `"auto"`(自动检测) - `num_classes`: 模型类别数(默认 80) diff --git a/plugins/det_post/det_post_node.cpp b/plugins/det_post/det_post_node.cpp index 83f77c7..517ebcf 100644 --- a/plugins/det_post/det_post_node.cpp +++ b/plugins/det_post/det_post_node.cpp @@ -772,6 +772,13 @@ public: return NodeStatus::OK; } + // Fast path: no enabled pipelines configured. + if (!cfg->def.enable && cfg->per_class.empty()) { + PushToDownstream(frame); + processed_.fetch_add(1); + return NodeStatus::OK; + } + if (!frame->det || frame->det->items.empty()) { PushToDownstream(frame); processed_.fetch_add(1);