Refine shoe box gating responsibilities

This commit is contained in:
tian 2026-04-14 12:06:40 +08:00
parent ebbe70e076
commit 6a4b471831
3 changed files with 179 additions and 93 deletions

View File

@ -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
}
},
{

View File

@ -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 的大混合框
- 少量远处小局部框

View File

@ -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<std::string>("mode", config.mode);
@ -148,12 +204,6 @@ private:
config.color.roi_expand = color->ValueOr<float>("roi_expand", 1.0f);
config.color.center_w_scale = color->ValueOr<float>("center_w_scale", 0.6f);
config.color.center_h_scale = color->ValueOr<float>("center_h_scale", 0.6f);
config.enable_size_filter = color->ValueOr<bool>("enable_size_filter", false);
config.min_shoe_height_ratio = color->ValueOr<float>("min_shoe_height_ratio", config.min_shoe_height_ratio);
config.min_shoe_area_ratio = color->ValueOr<float>("min_shoe_area_ratio", config.min_shoe_area_ratio);
config.max_shoe_height_ratio = color->ValueOr<float>("max_shoe_height_ratio", config.max_shoe_height_ratio);
config.max_shoe_width_ratio = color->ValueOr<float>("max_shoe_width_ratio", config.max_shoe_width_ratio);
config.max_shoe_area_ratio = color->ValueOr<float>("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<float>(
"max_shoe_area_ratio", config.person_shoe.max_shoe_area_ratio);
config.person_shoe.max_shoe_roi_width_ratio = ps->ValueOr<float>(
"max_shoe_roi_width_ratio", config.person_shoe.max_shoe_roi_width_ratio);
config.person_shoe.max_shoe_roi_height_ratio = ps->ValueOr<float>(
"max_shoe_roi_height_ratio", config.person_shoe.max_shoe_roi_height_ratio);
config.person_shoe.max_shoe_roi_area_ratio = ps->ValueOr<float>(
"max_shoe_roi_area_ratio", config.person_shoe.max_shoe_roi_area_ratio);
config.person_shoe.max_shoe_aspect_ratio = ps->ValueOr<float>(
"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<float>("x_offset", config.person_shoe.x_offset);
config.person_shoe.y_offset = fr->ValueOr<float>("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<int>(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<Detection> boots;
boots.reserve(detections.size());
std::unordered_map<int, Rect> 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<int>(boot.bbox.w)) + "x" +
std::to_string(static_cast<int>(boot.bbox.h)) + ")" +
" person=(" + std::to_string(static_cast<int>(person_bbox.w)) + "x" +
std::to_string(static_cast<int>(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;