Add reverse workspace filter option

This commit is contained in:
gqc
2026-07-08 19:16:02 +08:00
parent 041c830bfb
commit cb078492bc
6 changed files with 215 additions and 44 deletions
+13 -4
View File
@@ -14,7 +14,7 @@ time_period_P_r30_linaro
- 目标设备运行库较旧,需要使用 Linaro GCC 7.4.1 交叉编译,避免引入高版本 `glibc/libstdc++` 依赖。 - 目标设备运行库较旧,需要使用 Linaro GCC 7.4.1 交叉编译,避免引入高版本 `glibc/libstdc++` 依赖。
- 支持按时间段控制是否执行推理。 - 支持按时间段控制是否执行推理。
- 支持 workspace 区域过滤,只上传区域内目标。 - 支持 workspace 区域过滤,默认只上传区域内目标,也可通过 `-R` 反向为只上传区域外目标
- 优化识别结果上报逻辑,降低多进程运行时漏帧/低置信波动造成的漏报。 - 优化识别结果上报逻辑,降低多进程运行时漏帧/低置信波动造成的漏报。
- 增加低置信度、过滤原因、上传决策等日志,便于现场复盘。 - 增加低置信度、过滤原因、上传决策等日志,便于现场复盘。
@@ -100,6 +100,7 @@ file /home/smos/yolov5-linaro-build/yolov5
-w, --workspace 检测区域,多边形点坐标,逗号分隔 -w, --workspace 检测区域,多边形点坐标,逗号分隔
-r, --threshold 置信度阈值,整数 30 到 90 -r, --threshold 置信度阈值,整数 30 到 90
-P, --time-period 推理生效时间段 -P, --time-period 推理生效时间段
-R, --reverse-workspace 反向 workspace 过滤,上传 -w 区域外目标
``` ```
示例: 示例:
@@ -120,6 +121,14 @@ workspace 示例:
./yolov5 -s input.h264 -m abc -c camera001 -t 19 -p 8080 -w 100,100,500,100,500,500,100,500 ./yolov5 -s input.h264 -m abc -c camera001 -t 19 -p 8080 -w 100,100,500,100,500,500,100,500
``` ```
反向 workspace 示例:
```bash
./yolov5 -s input.h264 -m abc -c camera001 -t 19 -p 8080 -w 100,100,500,100,500,500,100,500 -R
```
开启 `-R` 后,`-w` 画出的区域会作为排除区域:区域内目标不上报,只上报区域外目标。`-R` 必须配合 `-w` 使用。
如果不传 `-w`,默认全画面有效。 如果不传 `-w`,默认全画面有效。
time-period 示例: time-period 示例:
@@ -166,10 +175,10 @@ h264 分段文件会写到:
常见日志事件: 常见日志事件:
- `frame_seen`:本次推理帧序号和原始检测数量。 - `frame_seen`:本次推理帧序号和原始检测数量。
- `strong_present`:超过阈值且 workspace - `strong_present`:超过阈值且通过 workspace 过滤
- `weak_present`:高于 `0.30`、低于阈值且 workspace - `weak_present`:高于 `0.30`、低于阈值且通过 workspace 过滤
- `filtered_low_conf`:低于 `0.30` - `filtered_low_conf`:低于 `0.30`
- `filtered_workspace`不在 workspace 内 - `filtered_workspace`未通过 workspace 过滤。`workspace_mode=normal` 表示普通模式,`workspace_mode=reverse` 表示 `-R` 反向模式
- `no_target`:本帧没有可上传目标。 - `no_target`:本帧没有可上传目标。
- `upload_positive`:本帧触发正例上传。 - `upload_positive`:本帧触发正例上传。
- `upload_skip_throttle`:同一 `class_idx` 500ms 节流跳过。 - `upload_skip_throttle`:同一 `class_idx` 500ms 节流跳过。
+119
View File
@@ -0,0 +1,119 @@
# `-R` 反向 workspace 过滤设计记录
## 背景
当前推理程序通过 `-w/--workspace` 配置一个多边形工作区域。检测结果在进入上报候选前,会用目标框中心点调用 `is_point_in_workspace(left, top, right, bottom)` 判断是否位于该区域内:
- 在 workspace 内:继续进入 `current_objects`,后续按类别编码合并并上报。
- 在 workspace 外:记录 `filtered_workspace`,不参与上报。
- 未传 `-w``init_polygon(NULL, 0)`,默认全画面有效。
本次需求是新增 `-R` 参数,让 `-w` 画出来的区域变成排除区域:开启 `-R` 后,区域内的识别结果不上报,只上报区域外的内容。
## 准备实现方式
1. 在命令行参数解析里新增无参数开关:
- 短参数:`-R`
- 长参数建议:`--reverse-workspace` 或沿用旧目录线索里的 `--reverse-detect`
- 默认关闭,保持现有行为完全不变。
2. 增加一个全局配置变量,例如:
- `bool ReverseWorkspace = false;`
- 放在 `vertix.cpp/vertix.h` 或更靠近推理过滤逻辑的公共头文件中,供 `main.cpp` 设置、`rknn.cpp` 读取。
3. 不改 YOLO 后处理、NMS、阈值、类别编码和上传 JSON,只改 workspace 过滤判定。
4.`rknn.cpp::print_object_detect_result` 中,把直接判断:
```cpp
if (!is_point_in_workspace(left, top, right, bottom)) {
...
continue;
}
```
调整成先计算原始位置关系,再根据 `-R` 反转:
```cpp
bool in_workspace = is_point_in_workspace(left, top, right, bottom);
bool pass_workspace = ReverseWorkspace ? !in_workspace : in_workspace;
if (!pass_workspace) {
...
continue;
}
```
这样 `-R` 只改变“是否允许上报”,不改变多边形算法本身。
5. 低置信度日志里的 workspace 判断也需要同步使用同一个通过规则。否则开启 `-R` 后,低置信度日志仍会按原来的“区域内”记录 `weak_present`,和上报语义不一致。
6. 日志名称建议保留现有兼容字段,同时增加模式信息:
- 正常模式下继续用 `strong_present`、`weak_present`、`filtered_workspace`。
- `-R` 模式下被过滤的目标本质是“命中排除区域”,建议日志里增加 `workspace_mode=reverse` 或新增 `filtered_reverse_workspace`,便于现场排查。
- 命令行输出的 `in result` / `not in result` 也建议区分“在多边形内”和“通过过滤后可上报”,避免 `-R` 模式下读日志误判。
## 边界约定
- `-R` 单独使用、未传 `-w` 时,当前默认 workspace 是全画面。如果直接反转,会变成全画面都被排除,导致无目标上报。建议实现时明确处理:
- 推荐方案:`-R` 必须配合 `-w` 使用;如果未传 `-w`,启动时报错并退出。
- 备选方案:未传 `-w` 时忽略 `-R` 并打印警告。这个更宽松,但容易让现场误以为反向过滤已生效。
- 多边形仍使用目标框中心点判断,不改成框与区域相交判断。这样和现有 `-w` 行为保持一致。
- `-P/--time-period`、阈值 `-r`、心跳、G1000 截图通知、同 `class_idx` 合并逻辑都不需要变化。
## 需要改动的文件
- `main.cpp`
- `long_options` 增加 `-R` 对应长参数。
- `getopt_long_only` 的短参数串加入 `R`。
- `case 'R'` 中设置全局反向开关。
- `print_usage` 的帮助信息补充 `-R` 说明。
- `vertix.h` / `vertix.cpp` 或其他公共配置位置
- 声明和定义反向 workspace 开关。
- 可选:封装一个 `is_box_allowed_by_workspace(...)`,把反转逻辑集中起来,避免 `rknn.cpp` 多处重复判断。
- `rknn.cpp`
- 上报候选过滤处使用统一的 `pass_workspace`。
- 低置信度 `weak_present` / `filtered_low_conf` 日志处同步使用统一规则。
- 日志追加 `workspace_mode=normal|reverse` 或新增反向过滤事件名。
- `README.md` 或运行说明文档
- 增加 `-R` 用法和示例。
## 验证计划
1. 编译验证:
- 按 `docs/cross_compile_wsl_linaro.md` 的 Linaro 路线构建。
- 确认 aarch64 产物正常生成,且不引入高版本 `glibc/libstdc++` 依赖。
2. 行为验证:
- 不带 `-R``-w` 内目标上报,外部目标过滤,行为和当前版本一致。
- 带 `-R -w ...``-w` 内目标过滤,外部目标上报。
- 带 `-R` 但不带 `-w`:按最终选定边界策略报错或警告。
3. 日志验证:
- 反向模式下能看出过滤原因是命中排除区域。
- `upload_positive` 和 `upload_positive_payload` 只出现于通过反向过滤后的区域外目标。
## 历史线索
`updata/main.cpp` 里曾出现过 `--reverse-detect` 和 `ReverseDetectBox = 1`,但当前主线 `main.cpp/rknn.cpp/vertix.*` 没有完整接入该变量。后续实现可以参考命名,但不要直接认为旧目录已经完成当前需求。
## 实现状态
已按本文方案实现:
- `main.cpp`:新增 `-R/--reverse-workspace`,并要求必须配合 `-w/--workspace` 使用。
- `vertix.cpp` / `vertix.h`:新增 `ReverseWorkspace` 开关和 workspace 模式说明函数。
- `rknn.cpp`:强置信上报候选和低置信日志都按 `ReverseWorkspace ? !in_workspace : in_workspace` 判定。
- `README.md`:补充 `-R` 参数、示例和日志说明。
构建验证已通过:
```text
wsl -d Ubuntu bash -lc "cd /mnt/c/Users/Smos.DESKTOP-6MT98U8/Desktop/yolov5-inference/build-aarch64 && cmake /mnt/c/Users/Smos.DESKTOP-6MT98U8/Desktop/yolov5-inference -DCMAKE_TOOLCHAIN_FILE=/mnt/c/Users/Smos.DESKTOP-6MT98U8/Desktop/yolov5-inference/toolchain-aarch64.cmake && make -j16 && file yolov5"
```
输出产物为 `ELF 64-bit LSB executable, ARM aarch64`。构建过程中出现 `Clock skew detected` 提示,属于 Windows/WSL 时间戳差异提示,不是本次代码编译错误。
+15 -3
View File
@@ -96,8 +96,10 @@ static const char *help_info[100] = {
"camera id", "camera id",
"type id", "type id",
"upload port", "upload port",
"workspace, use comma spearated coordinate, like 0,1,2,3" "workspace, use comma spearated coordinate, like 0,1,2,3",
"threshold, like 0.7, default is 0.6"}; "threshold, like 0.7, default is 0.6",
"time period, like 08:00:00-18:00:00",
"reverse workspace filter, upload targets outside -w"};
static struct option long_options[] = { static struct option long_options[] = {
{"server", required_argument, NULL, 's'}, {"server", required_argument, NULL, 's'},
@@ -109,6 +111,7 @@ static struct option long_options[] = {
{"workspace", required_argument, NULL, 'w'}, {"workspace", required_argument, NULL, 'w'},
{"threshold", required_argument, NULL, 'r'}, {"threshold", required_argument, NULL, 'r'},
{"time-period", required_argument, NULL, 'P'}, {"time-period", required_argument, NULL, 'P'},
{"reverse-workspace", no_argument, NULL, 'R'},
{0, 0, 0}}; {0, 0, 0}};
void print_usage(char *cmd) void print_usage(char *cmd)
@@ -154,12 +157,13 @@ void parse_options(int argc, char **argv)
float *points; float *points;
int thresh; int thresh;
conf_threshold = DEFAULT_CONF_THRESHOLD; conf_threshold = DEFAULT_CONF_THRESHOLD;
ReverseWorkspace = false;
char *cmd = argv[0]; char *cmd = argv[0];
int ok; int ok;
while (1) while (1)
{ {
int option_index = 0; int option_index = 0;
c = getopt_long_only(argc, argv, "es:m:w:c:t:p:r:P:", long_options, &option_index); c = getopt_long_only(argc, argv, "es:m:w:c:t:p:r:P:R", long_options, &option_index);
if (c == -1) if (c == -1)
{ {
break; break;
@@ -209,12 +213,20 @@ void parse_options(int argc, char **argv)
exit(-1); exit(-1);
} }
break; break;
case 'R':
ReverseWorkspace = true;
break;
default: default:
print_usage(cmd); print_usage(cmd);
exit(-1); exit(-1);
} }
} }
if (ReverseWorkspace && !polygon_inited) {
printf("-R/--reverse-workspace must be used with -w/--workspace\n");
exit(-1);
}
if (!polygon_inited) { if (!polygon_inited) {
init_polygon(NULL, 0); init_polygon(NULL, 0);
} }
+28 -12
View File
@@ -658,19 +658,31 @@ int print_object_detect_result(rknn_app_context_t *rknn, object_detect_result_li
int low_top = group->results[i].box.top; int low_top = group->results[i].box.top;
int low_right = group->results[i].box.right; int low_right = group->results[i].box.right;
int low_bottom = group->results[i].box.bottom; int low_bottom = group->results[i].box.bottom;
if (group->results[i].prop >= LOW_CONF_SNAPSHOT_MIN && is_point_in_workspace(low_left, low_top, low_right, low_bottom)) bool low_in_workspace = is_point_in_workspace(low_left, low_top, low_right, low_bottom);
bool low_pass_workspace = ReverseWorkspace ? !low_in_workspace : low_in_workspace;
if (group->results[i].prop >= LOW_CONF_SNAPSHOT_MIN && low_pass_workspace)
{ {
detect_log_line("%s ts=%ld camera=%s weak_present frame=%lld result=%2d code=%d box=(%3d %3d %3d %3d) prop=%f thresh=%f label=%s", detect_log_line("%s ts=%ld camera=%s weak_present frame=%lld result=%2d code=%d box=(%3d %3d %3d %3d) prop=%f thresh=%f label=%s workspace_mode=%s in_workspace=%d",
now_text, now_ts, CameraID ? CameraID : "", frame_seq, i, now_text, now_ts, CameraID ? CameraID : "", frame_seq, i,
codes[group->results[i].cls_id], low_left, low_top, low_right, low_bottom, codes[group->results[i].cls_id], low_left, low_top, low_right, low_bottom,
group->results[i].prop, conf_threshold, label); group->results[i].prop, conf_threshold, label,
workspace_filter_mode(), low_in_workspace ? 1 : 0);
}
else if (group->results[i].prop >= LOW_CONF_SNAPSHOT_MIN)
{
detect_log_line("%s ts=%ld camera=%s filtered_workspace frame=%lld result=%2d code=%d box=(%3d %3d %3d %3d) prop=%f label=%s workspace_mode=%s in_workspace=%d",
now_text, now_ts, CameraID ? CameraID : "", frame_seq, i,
codes[group->results[i].cls_id], low_left, low_top, low_right, low_bottom,
group->results[i].prop, label,
workspace_filter_mode(), low_in_workspace ? 1 : 0);
} }
else else
{ {
detect_log_line("%s ts=%ld camera=%s filtered_low_conf frame=%lld result=%2d code=%d box=(%3d %3d %3d %3d) prop=%f min=%f label=%s", detect_log_line("%s ts=%ld camera=%s filtered_low_conf frame=%lld result=%2d code=%d box=(%3d %3d %3d %3d) prop=%f min=%f label=%s workspace_mode=%s in_workspace=%d",
now_text, now_ts, CameraID ? CameraID : "", frame_seq, i, now_text, now_ts, CameraID ? CameraID : "", frame_seq, i,
codes[group->results[i].cls_id], low_left, low_top, low_right, low_bottom, codes[group->results[i].cls_id], low_left, low_top, low_right, low_bottom,
group->results[i].prop, LOW_CONF_SNAPSHOT_MIN, label); group->results[i].prop, LOW_CONF_SNAPSHOT_MIN, label,
workspace_filter_mode(), low_in_workspace ? 1 : 0);
} }
} }
continue; continue;
@@ -679,16 +691,19 @@ int print_object_detect_result(rknn_app_context_t *rknn, object_detect_result_li
int top = group->results[i].box.top; int top = group->results[i].box.top;
int right = group->results[i].box.right; int right = group->results[i].box.right;
int bottom = group->results[i].box.bottom; int bottom = group->results[i].box.bottom;
bool in_workspace = is_point_in_workspace(left, top, right, bottom);
bool pass_workspace = ReverseWorkspace ? !in_workspace : in_workspace;
if (!is_point_in_workspace(left, top, right, bottom)) if (!pass_workspace)
{ {
printf("not in "); printf(ReverseWorkspace ? "excluded " : "not in ");
if (should_log_detail) if (should_log_detail)
{ {
detect_log_line("%s ts=%ld camera=%s filtered_workspace frame=%lld result=%2d code=%d box=(%3d %3d %3d %3d) prop=%f label=%s", detect_log_line("%s ts=%ld camera=%s filtered_workspace frame=%lld result=%2d code=%d box=(%3d %3d %3d %3d) prop=%f label=%s workspace_mode=%s in_workspace=%d",
now_text, now_ts, CameraID ? CameraID : "", frame_seq, i, now_text, now_ts, CameraID ? CameraID : "", frame_seq, i,
codes[group->results[i].cls_id], left, top, right, bottom, codes[group->results[i].cls_id], left, top, right, bottom,
group->results[i].prop, label); group->results[i].prop, label,
workspace_filter_mode(), in_workspace ? 1 : 0);
} }
printf("result %2d: (%3d %3d %3d %3d) %f, %s\n", i, left, top, right, bottom, group->results[i].prop, label); printf("result %2d: (%3d %3d %3d %3d) %f, %s\n", i, left, top, right, bottom, group->results[i].prop, label);
continue; continue;
@@ -696,13 +711,14 @@ int print_object_detect_result(rknn_app_context_t *rknn, object_detect_result_li
if (group->results[i].prop >= conf_threshold) if (group->results[i].prop >= conf_threshold)
{ {
printf("in "); printf(ReverseWorkspace ? "outside " : "in ");
if (should_log_detail) if (should_log_detail)
{ {
detect_log_line("%s ts=%ld camera=%s strong_present frame=%lld result=%2d code=%d box=(%3d %3d %3d %3d) prop=%f thresh=%f label=%s", detect_log_line("%s ts=%ld camera=%s strong_present frame=%lld result=%2d code=%d box=(%3d %3d %3d %3d) prop=%f thresh=%f label=%s workspace_mode=%s in_workspace=%d",
now_text, now_ts, CameraID ? CameraID : "", frame_seq, i, now_text, now_ts, CameraID ? CameraID : "", frame_seq, i,
codes[group->results[i].cls_id], left, top, right, bottom, codes[group->results[i].cls_id], left, top, right, bottom,
group->results[i].prop, conf_threshold, label); group->results[i].prop, conf_threshold, label,
workspace_filter_mode(), in_workspace ? 1 : 0);
} }
append_upload_box(&current_objects[group->results[i].cls_id], left, top, right, bottom, group->results[i].prop, label); append_upload_box(&current_objects[group->results[i].cls_id], left, top, right, bottom, group->results[i].prop, label);
current_objects[group->results[i].cls_id].number += 1; current_objects[group->results[i].cls_id].number += 1;
+12
View File
@@ -5,6 +5,7 @@
#include "pose_body.h" #include "pose_body.h"
Polygon workspace; Polygon workspace;
bool ReverseWorkspace = false;
time_gap_t global_time_gap = { time_gap_t global_time_gap = {
.in = 0, .in = 0,
.size = 0, .size = 0,
@@ -143,6 +144,17 @@ bool is_point_in_workspace(int left, int top, int right, int bottom)
return inside; return inside;
} }
bool is_box_allowed_by_workspace(int left, int top, int right, int bottom)
{
bool inside = is_point_in_workspace(left, top, right, bottom);
return ReverseWorkspace ? !inside : inside;
}
const char *workspace_filter_mode()
{
return ReverseWorkspace ? "reverse" : "normal";
}
// 获取今天的秒数 // 获取今天的秒数
int get_today_seconds() { int get_today_seconds() {
time_t current_time; time_t current_time;
+3
View File
@@ -38,11 +38,14 @@ typedef struct
} Polygon; } Polygon;
extern Polygon workspace; extern Polygon workspace;
extern bool ReverseWorkspace;
#define DUMP_POLYGON_INFO #define DUMP_POLYGON_INFO
void init_polygon(float *datas, int size); void init_polygon(float *datas, int size);
bool is_point_in_workspace(int left, int top, int right, int bottom); bool is_point_in_workspace(int left, int top, int right, int bottom);
bool is_box_allowed_by_workspace(int left, int top, int right, int bottom);
const char *workspace_filter_mode();
#endif #endif