From 6a4b47183147baa29dcf34a2602591c598c3cab2 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Tue, 14 Apr 2026 12:06:40 +0800 Subject: [PATCH] Refine shoe box gating responsibilities --- configs/full_pipeline_1080p_test_alarm.json | 20 +-- .../shoe_face_test_record_2026-04-13.md | 88 +++++++--- plugins/logic_gate/logic_gate_node.cpp | 164 +++++++++++------- 3 files changed, 179 insertions(+), 93 deletions(-) diff --git a/configs/full_pipeline_1080p_test_alarm.json b/configs/full_pipeline_1080p_test_alarm.json index 2510d15..2c42e5e 100644 --- a/configs/full_pipeline_1080p_test_alarm.json +++ b/configs/full_pipeline_1080p_test_alarm.json @@ -228,11 +228,15 @@ "height_scale": 0.58 }, "enable_size_preferred": true, - "min_shoe_height_ratio": 0.06, - "min_shoe_area_ratio": 0.01, - "max_shoe_height_ratio": 0.28, + "min_shoe_height_ratio": 0.08, + "min_shoe_area_ratio": 0.012, + "max_shoe_height_ratio": 0.24, "max_shoe_width_ratio": 0.6, - "max_shoe_area_ratio": 0.15 + "max_shoe_area_ratio": 0.15, + "max_shoe_roi_width_ratio": 0.65, + "max_shoe_roi_height_ratio": 0.6, + "max_shoe_roi_area_ratio": 0.25, + "max_shoe_aspect_ratio": 2.0 } }, { @@ -253,12 +257,8 @@ "method": "brightness", "dark_threshold": 90, "roi_expand": 1.0, - "enable_size_filter": true, - "min_shoe_height_ratio": 0.06, - "min_shoe_area_ratio": 0.01, - "max_shoe_height_ratio": 0.28, - "max_shoe_width_ratio": 0.6, - "max_shoe_area_ratio": 0.15 + "center_w_scale": 0.6, + "center_h_scale": 0.6 } }, { diff --git a/docs/workshop_ai/shoe_face_test_record_2026-04-13.md b/docs/workshop_ai/shoe_face_test_record_2026-04-13.md index 855c816..88fe296 100644 --- a/docs/workshop_ai/shoe_face_test_record_2026-04-13.md +++ b/docs/workshop_ai/shoe_face_test_record_2026-04-13.md @@ -55,10 +55,26 @@ - `roi_expand = 1.0` - `center_w_scale = 0.6` - `center_h_scale = 0.6` -- `enable_size_filter = true` -- `max_shoe_height_ratio = 0.28` + +说明: + +- `shoe_color` 现在只负责颜色分析 +- 几何阈值不再放在 `shoe_color` 内判断 +- 鞋框几何筛选统一前置到 `shoe_assoc.person_shoe_check` + +### 3.3 当前鞋框几何筛选参数 + +当前测试版 `shoe_assoc.person_shoe_check` 参数为: + +- `min_shoe_height_ratio = 0.08` +- `min_shoe_area_ratio = 0.012` +- `max_shoe_height_ratio = 0.24` - `max_shoe_width_ratio = 0.60` - `max_shoe_area_ratio = 0.15` +- `max_shoe_roi_width_ratio = 0.65` +- `max_shoe_roi_height_ratio = 0.60` +- `max_shoe_roi_area_ratio = 0.25` +- `max_shoe_aspect_ratio = 2.0` ## 4. 当前设备侧代码调试状态 @@ -67,14 +83,19 @@ 1. 颜色分析不再直接看整块鞋框,而是看鞋框中心 `60% x 60%` 2. `shoe_color` 不再处理未成功关联到人的鞋框 - 即:未关联鞋框会直接跳过 -3. 在 `shoe_color` 前增加了尺寸过滤 - - 过滤条件支持写进配置 - - 当前启用高度、宽度、面积三项约束 +3. 鞋框几何筛选统一前置到 `shoe_assoc` + - 当前启用三层几何约束: + - `shoe / person` + - `shoe / roi` + - `shoe` 自身形状 4. `logic_gate` debug 日志已补充: - `person bbox` - `foot_region` - `skip unmatched shoe` - - `size filter skip` + - `person_ratios` + - `roi_ratios` + - `aspect` + - `gates(person/roi/shape)` ## 5. 人脸识别结果 @@ -106,22 +127,20 @@ ### 6.2 现阶段黑鞋结论 -在当前测试参数下,黑鞋误报已经明显下降。 +在当前测试参数下,黑鞋误报已经明显下降,但多人场景仍存在少量误报。 设备侧日志表现为: - 未关联鞋框会被 `skip unmatched shoe` -- 大坏框会被 `size filter skip` +- 很多明显过大或过高的候选,在 `shoe_assoc` 阶段已经不再成为 `size_preferred=true` - 主小人正常鞋框进入颜色分析后,多数结果为: - `is_dark = true` -- 最近一轮黑鞋日志中,没有再看到新的: - - `color violation` - - `RULE MATCHED` - - `ALARM` +- 多人全黑鞋日志里,误报已从“大混合框”为主,收敛到“细长高框”和“接近 ROI 的偏大框” 阶段性判断: -- 当前这组过滤参数对黑鞋误报收敛有效 +- 当前这组前置几何筛选,对黑鞋误报收敛有效 +- 但仍需继续收紧 `shoe / roi` 与 `aspect` 相关约束 ### 6.3 现阶段白鞋结论 @@ -136,7 +155,7 @@ - `RULE MATCHED` - `ALARM` -同时,过大的白鞋框仍会被尺寸过滤挡掉。 +同时,明显不合理的大框更多会在 `shoe_assoc` 阶段被压出优先候选。 阶段性判断: @@ -161,13 +180,36 @@ - 调到 `0.15` 后,白鞋有效框放回了一部分 - 同时黑鞋明显误报暂未重新抬头 -### 7.2 现阶段推荐测试值 +### 7.2 当前三层几何约束设计 + +为避免不同参照系混在一起判断,本轮将鞋框几何约束拆成三层: + +1. `shoe / roi` + 目的:过滤掉几乎接近脚部 ROI 大小的大混合框 + +2. `shoe / person` + 目的:过滤掉相对人体过高、过宽、过大或过小的鞋框 + +3. `shoe` 自身形状 + 目的:过滤掉高度明显大于宽度的细长竖框 + +每层独立打日志,避免后续再出现“看起来像通过了,但不知道是哪一层漏过去”的情况。 + +几何筛选当前只属于 `shoe_assoc.person_shoe_check`,不再由 `shoe_color` 重复判断。 + +### 7.3 现阶段推荐测试值 如果继续沿当前方向验证,建议先保持: -- `max_shoe_height_ratio = 0.28` +- `min_shoe_height_ratio = 0.08` +- `min_shoe_area_ratio = 0.012` +- `max_shoe_height_ratio = 0.24` - `max_shoe_width_ratio = 0.60` - `max_shoe_area_ratio = 0.15` +- `max_shoe_roi_width_ratio = 0.65` +- `max_shoe_roi_height_ratio = 0.60` +- `max_shoe_roi_area_ratio = 0.25` +- `max_shoe_aspect_ratio = 2.0` ## 8. 下一步建议 @@ -180,10 +222,12 @@ - 白鞋是否稳定告警 - 多人时是否出现串人、串鞋 4. 如果后续再调参数,优先围绕: - - `max_shoe_area_ratio` - - `max_shoe_width_ratio` + - `max_shoe_roi_height_ratio` + - `max_shoe_roi_area_ratio` + - `max_shoe_aspect_ratio` + - `max_shoe_height_ratio` -不建议下一步立即再收紧 `max_shoe_height_ratio` +不建议在没有看清 `shoe / roi` 与 `aspect` 日志前,再继续单独调颜色阈值 ### 8.1 建议补充的多人测试视频 @@ -214,4 +258,8 @@ - 当前测试参数下: - 黑鞋误报明显下降 - 白鞋仍可触发告警 -- `size filter + 只处理已关联鞋框` 是当前阶段最有效的修正方向 +- `前置几何筛选 + 只处理已关联鞋框` 是当前阶段最有效的修正方向 +- 当前剩余误报主要集中在: + - 细长高框 + - 接近 ROI 的大混合框 + - 少量远处小局部框 diff --git a/plugins/logic_gate/logic_gate_node.cpp b/plugins/logic_gate/logic_gate_node.cpp index 2e1f17c..f85d283 100644 --- a/plugins/logic_gate/logic_gate_node.cpp +++ b/plugins/logic_gate/logic_gate_node.cpp @@ -24,11 +24,15 @@ struct PersonShoeCheckConfig { bool attach_person_track_to_shoe = true; bool emit_missing_violation = true; bool enable_size_preferred = false; - float min_shoe_height_ratio = 0.06f; - float min_shoe_area_ratio = 0.01f; - float max_shoe_height_ratio = 0.28f; + float min_shoe_height_ratio = 0.08f; + float min_shoe_area_ratio = 0.012f; + float max_shoe_height_ratio = 0.24f; float max_shoe_width_ratio = 0.60f; float max_shoe_area_ratio = 0.15f; + float max_shoe_roi_width_ratio = 0.65f; + float max_shoe_roi_height_ratio = 0.60f; + float max_shoe_roi_area_ratio = 0.25f; + float max_shoe_aspect_ratio = 2.0f; float match_iou = 0.02f; float x_offset = -0.20f; float y_offset = 0.64f; @@ -42,12 +46,6 @@ struct LogicGateConfig { int boots_class = 3; int violation_class = 10; bool enable_color_check = true; - bool enable_size_filter = false; - float min_shoe_height_ratio = 0.06f; - float min_shoe_area_ratio = 0.01f; - float max_shoe_height_ratio = 1.0f / 6.0f; - float max_shoe_width_ratio = 1.0f; - float max_shoe_area_ratio = 0.15f; bool pass_through = true; bool debug = false; std::string violation_key = "ppe_violation"; @@ -55,6 +53,19 @@ struct LogicGateConfig { PersonShoeCheckConfig person_shoe; }; +struct ShoeGeomMetrics { + float person_width_ratio = 0.0f; + float person_height_ratio = 0.0f; + float person_area_ratio = 0.0f; + float roi_width_ratio = 0.0f; + float roi_height_ratio = 0.0f; + float roi_area_ratio = 0.0f; + float aspect_ratio = 0.0f; + bool person_gate = false; + bool roi_gate = false; + bool shape_gate = false; +}; + class LogicGateNode : public INode { public: std::string Id() const override { return id_; } @@ -128,6 +139,51 @@ private: cy >= outer.y && cy <= (outer.y + outer.h); } + static ShoeGeomMetrics ComputeShoeGeomMetrics(const Rect& shoe_bbox, + const Rect& person_bbox, + const Rect& foot_region, + float min_shoe_height_ratio, + float min_shoe_area_ratio, + float max_shoe_width_ratio, + float max_shoe_height_ratio, + float max_shoe_area_ratio, + float max_shoe_roi_width_ratio, + float max_shoe_roi_height_ratio, + float max_shoe_roi_area_ratio, + float max_shoe_aspect_ratio) { + ShoeGeomMetrics m; + const float person_w = std::max(1.0f, person_bbox.w); + const float person_h = std::max(1.0f, person_bbox.h); + const float person_area = std::max(1.0f, person_w * person_h); + const float roi_w = std::max(1.0f, foot_region.w); + const float roi_h = std::max(1.0f, foot_region.h); + const float roi_area = std::max(1.0f, roi_w * roi_h); + const float shoe_w = std::max(1.0f, shoe_bbox.w); + const float shoe_h = std::max(1.0f, shoe_bbox.h); + const float shoe_area = std::max(1.0f, shoe_w * shoe_h); + + m.person_width_ratio = shoe_w / person_w; + m.person_height_ratio = shoe_h / person_h; + m.person_area_ratio = shoe_area / person_area; + m.roi_width_ratio = shoe_w / roi_w; + m.roi_height_ratio = shoe_h / roi_h; + m.roi_area_ratio = shoe_area / roi_area; + m.aspect_ratio = shoe_h / shoe_w; + + m.person_gate = + m.person_height_ratio >= min_shoe_height_ratio && + m.person_area_ratio >= min_shoe_area_ratio && + m.person_width_ratio <= max_shoe_width_ratio && + m.person_height_ratio <= max_shoe_height_ratio && + m.person_area_ratio <= max_shoe_area_ratio; + m.roi_gate = + m.roi_width_ratio <= max_shoe_roi_width_ratio && + m.roi_height_ratio <= max_shoe_roi_height_ratio && + m.roi_area_ratio <= max_shoe_roi_area_ratio; + m.shape_gate = m.aspect_ratio <= max_shoe_aspect_ratio; + return m; + } + LogicGateConfig ParseConfig(const SimpleJson& cfg) { LogicGateConfig config; config.mode = cfg.ValueOr("mode", config.mode); @@ -148,12 +204,6 @@ private: config.color.roi_expand = color->ValueOr("roi_expand", 1.0f); config.color.center_w_scale = color->ValueOr("center_w_scale", 0.6f); config.color.center_h_scale = color->ValueOr("center_h_scale", 0.6f); - config.enable_size_filter = color->ValueOr("enable_size_filter", false); - config.min_shoe_height_ratio = color->ValueOr("min_shoe_height_ratio", config.min_shoe_height_ratio); - config.min_shoe_area_ratio = color->ValueOr("min_shoe_area_ratio", config.min_shoe_area_ratio); - config.max_shoe_height_ratio = color->ValueOr("max_shoe_height_ratio", config.max_shoe_height_ratio); - config.max_shoe_width_ratio = color->ValueOr("max_shoe_width_ratio", config.max_shoe_width_ratio); - config.max_shoe_area_ratio = color->ValueOr("max_shoe_area_ratio", config.max_shoe_area_ratio); config.color.debug_output = config.debug; } @@ -183,6 +233,14 @@ private: "max_shoe_width_ratio", config.person_shoe.max_shoe_width_ratio); config.person_shoe.max_shoe_area_ratio = ps->ValueOr( "max_shoe_area_ratio", config.person_shoe.max_shoe_area_ratio); + config.person_shoe.max_shoe_roi_width_ratio = ps->ValueOr( + "max_shoe_roi_width_ratio", config.person_shoe.max_shoe_roi_width_ratio); + config.person_shoe.max_shoe_roi_height_ratio = ps->ValueOr( + "max_shoe_roi_height_ratio", config.person_shoe.max_shoe_roi_height_ratio); + config.person_shoe.max_shoe_roi_area_ratio = ps->ValueOr( + "max_shoe_roi_area_ratio", config.person_shoe.max_shoe_roi_area_ratio); + config.person_shoe.max_shoe_aspect_ratio = ps->ValueOr( + "max_shoe_aspect_ratio", config.person_shoe.max_shoe_aspect_ratio); if (const SimpleJson* fr = ps->Find("foot_region"); fr && fr->IsObject()) { config.person_shoe.x_offset = fr->ValueOr("x_offset", config.person_shoe.x_offset); config.person_shoe.y_offset = fr->ValueOr("y_offset", config.person_shoe.y_offset); @@ -283,16 +341,21 @@ private: const float iou = IoU(foot_region, shoe.bbox); const bool center_inside = CenterInside(foot_region, shoe.bbox); const bool pass_gate = center_inside || iou >= config_.person_shoe.match_iou; - const float person_area = std::max(1.0f, person.bbox.w * person.bbox.h); - const float width_ratio = shoe.bbox.w / std::max(1.0f, person.bbox.w); - const float height_ratio = shoe.bbox.h / std::max(1.0f, person.bbox.h); - const float area_ratio = (shoe.bbox.w * shoe.bbox.h) / person_area; + const ShoeGeomMetrics metrics = ComputeShoeGeomMetrics( + shoe.bbox, + person.bbox, + foot_region, + config_.person_shoe.min_shoe_height_ratio, + config_.person_shoe.min_shoe_area_ratio, + config_.person_shoe.max_shoe_width_ratio, + config_.person_shoe.max_shoe_height_ratio, + config_.person_shoe.max_shoe_area_ratio, + config_.person_shoe.max_shoe_roi_width_ratio, + config_.person_shoe.max_shoe_roi_height_ratio, + config_.person_shoe.max_shoe_roi_area_ratio, + config_.person_shoe.max_shoe_aspect_ratio); const bool size_preferred = !config_.person_shoe.enable_size_preferred || - (height_ratio >= config_.person_shoe.min_shoe_height_ratio && - area_ratio >= config_.person_shoe.min_shoe_area_ratio && - width_ratio <= config_.person_shoe.max_shoe_width_ratio && - height_ratio <= config_.person_shoe.max_shoe_height_ratio && - area_ratio <= config_.person_shoe.max_shoe_area_ratio); + (metrics.person_gate && metrics.roi_gate && metrics.shape_gate); const float match_score = shoe.score * 10.0f + (center_inside ? 1.0f : 0.0f) + iou; if (config_.debug) { LogInfo("[logic_gate] shoe candidate person_track=" + std::to_string(person.track_id) + @@ -308,9 +371,16 @@ private: " center_inside=" + std::string(center_inside ? "true" : "false") + " pass_gate=" + std::string(pass_gate ? "true" : "false") + " size_preferred=" + std::string(size_preferred ? "true" : "false") + - " ratios=(w:" + std::to_string(width_ratio) + - ",h:" + std::to_string(height_ratio) + - ",a:" + std::to_string(area_ratio) + ")" + + " person_ratios=(w:" + std::to_string(metrics.person_width_ratio) + + ",h:" + std::to_string(metrics.person_height_ratio) + + ",a:" + std::to_string(metrics.person_area_ratio) + ")" + + " roi_ratios=(w:" + std::to_string(metrics.roi_width_ratio) + + ",h:" + std::to_string(metrics.roi_height_ratio) + + ",a:" + std::to_string(metrics.roi_area_ratio) + ")" + + " aspect=" + std::to_string(metrics.aspect_ratio) + + " gates=(person:" + std::string(metrics.person_gate ? "true" : "false") + + ",roi:" + std::string(metrics.roi_gate ? "true" : "false") + + ",shape:" + std::string(metrics.shape_gate ? "true" : "false") + ")" + " match_score=" + std::to_string(match_score)); } if (!pass_gate) continue; @@ -323,9 +393,9 @@ private: if (should_replace) { best_shoe_local = static_cast(si); best_match_score = match_score; - best_width_ratio = width_ratio; - best_height_ratio = height_ratio; - best_area_ratio = area_ratio; + best_width_ratio = metrics.person_width_ratio; + best_height_ratio = metrics.person_height_ratio; + best_area_ratio = metrics.person_area_ratio; best_size_preferred = size_preferred; best_center_inside = center_inside; } @@ -346,7 +416,7 @@ private: " score=" + std::to_string(matched_shoe.score) + " center_inside=" + std::string(best_center_inside ? "true" : "false") + " size_preferred=" + std::string(best_size_preferred ? "true" : "false") + - " ratios=(w:" + std::to_string(best_width_ratio) + + " person_ratios=(w:" + std::to_string(best_width_ratio) + ",h:" + std::to_string(best_height_ratio) + ",a:" + std::to_string(best_area_ratio) + ")" + " match_score=" + std::to_string(best_match_score)); @@ -390,12 +460,8 @@ private: auto& detections = frame->det->items; std::vector boots; boots.reserve(detections.size()); - std::unordered_map person_boxes_by_track; for (const auto& det : detections) { if (det.cls_id == config_.boots_class) boots.push_back(det); - if (det.cls_id == config_.person_shoe.person_class && det.track_id >= 0) { - person_boxes_by_track[det.track_id] = det.bbox; - } } for (const auto& boot : boots) { @@ -411,34 +477,6 @@ private: continue; } - if (config_.enable_size_filter) { - auto it = person_boxes_by_track.find(boot.track_id); - if (it != person_boxes_by_track.end()) { - const Rect& person_bbox = it->second; - const float person_area = std::max(1.0f, person_bbox.w * person_bbox.h); - const float width_ratio = boot.bbox.w / std::max(1.0f, person_bbox.w); - const float height_ratio = boot.bbox.h / std::max(1.0f, person_bbox.h); - const float area_ratio = (boot.bbox.w * boot.bbox.h) / person_area; - if (height_ratio < config_.min_shoe_height_ratio || - area_ratio < config_.min_shoe_area_ratio || - width_ratio > config_.max_shoe_width_ratio || - height_ratio > config_.max_shoe_height_ratio || - area_ratio > config_.max_shoe_area_ratio) { - if (config_.debug) { - LogInfo("[logic_gate] size filter skip track_id=" + std::to_string(boot.track_id) + - " shoe=(" + std::to_string(static_cast(boot.bbox.w)) + "x" + - std::to_string(static_cast(boot.bbox.h)) + ")" + - " person=(" + std::to_string(static_cast(person_bbox.w)) + "x" + - std::to_string(static_cast(person_bbox.h)) + ")" + - " ratios=(w:" + std::to_string(width_ratio) + - ",h:" + std::to_string(height_ratio) + - ",a:" + std::to_string(area_ratio) + ")"); - } - continue; - } - } - } - const Rect mapped_bbox = MapDetCoordToFrame(boot.bbox, frame); const auto color_result = color_analyzer_->Analyze(*frame, mapped_bbox); if (color_result.is_dark) continue;