Restore production defaults in config template

This commit is contained in:
tian 2026-04-18 12:39:18 +08:00
parent cfe42bdad3
commit 64af954fe4
6 changed files with 205 additions and 33 deletions

View File

@ -0,0 +1,48 @@
{
"description": "Use sensitive face alarm thresholds for short validation videos. Keep production templates conservative.",
"instance_overrides": {
"*": {
"override": {
"nodes": {
"alarm": {
"face_track_aggregation": {
"known": {
"min_hits": 1,
"hit_window_ms": 3000,
"reentry_cooldown_ms": 8000
},
"unknown": {
"min_track_age_ms": 1500,
"min_quality_hits": 4
}
},
"face_rules": [
{
"name": "unknown_face",
"type": "unknown",
"cooldown_ms": 7000,
"max_known_sim": 0.35,
"min_hits": 1,
"hit_window_ms": 1500,
"min_face_area_ratio": 0.001,
"min_face_aspect": 0.6,
"max_face_aspect": 1.6
},
{
"name": "known_person",
"type": "person",
"cooldown_ms": 7000,
"min_sim": 0.45,
"min_hits": 1,
"hit_window_ms": 1500,
"min_face_area_ratio": 0.0002,
"min_face_aspect": 0.55,
"max_face_aspect": 1.6
}
]
}
}
}
}
}
}

View File

@ -0,0 +1,28 @@
{
"description": "Use sensitive workshoe alarm thresholds for short validation videos. Keep production templates conservative.",
"instance_overrides": {
"*": {
"override": {
"nodes": {
"alarm": {
"rules": [
{
"name": "non_compliant_workshoe",
"class_ids": [2],
"roi": {"x": 0.0, "y": 0.0, "w": 1.0, "h": 1.0},
"min_score": 0.1,
"min_box_area_ratio": 0.0,
"require_track_id": false,
"min_duration_ms": 0,
"min_hits": 1,
"hit_window_ms": 3000,
"cooldown_ms": 1000,
"per_track_cooldown_ms": 0
}
]
}
}
}
}
}
}

View File

@ -70,7 +70,7 @@
"nms_thresh": 0.4,
"max_faces": 50,
"debug": {
"stats": true,
"stats": false,
"stats_interval": 30
}
},
@ -103,8 +103,8 @@
"dtype": "auto"
},
"debug": {
"enabled": true,
"log_matches": true,
"enabled": false,
"log_matches": false,
"min_log_interval_ms": 0
}
},
@ -142,9 +142,9 @@
"bottom": 0.16
},
"debug": {
"stats": true,
"stats": false,
"stats_interval": 30,
"detections": true
"detections": false
}
},
{
@ -344,29 +344,29 @@
"w": 1.0,
"h": 1.0
},
"min_score": 0.1,
"min_score": 0.3,
"min_box_area_ratio": 0.0,
"require_track_id": false,
"min_duration_ms": 0,
"min_hits": 1,
"hit_window_ms": 3000,
"cooldown_ms": 1000,
"min_duration_ms": 800,
"min_hits": 2,
"hit_window_ms": 2000,
"cooldown_ms": 15000,
"per_track_cooldown_ms": 0
}
],
"face_track_aggregation": {
"known": {
"min_hits": 1,
"min_hits": 3,
"hit_window_ms": 3000,
"reentry_cooldown_ms": 8000
"reentry_cooldown_ms": 300000
},
"unknown": {
"min_track_age_ms": 1500,
"min_track_age_ms": 2000,
"min_quality_hits": 4
}
},
"face_debug": {
"log_unknown_candidates": true,
"log_unknown_candidates": false,
"unknown_candidate_interval_ms": 0
},
"face_rules": [
@ -375,7 +375,7 @@
"type": "unknown",
"cooldown_ms": 7000,
"max_known_sim": 0.35,
"min_hits": 1,
"min_hits": 2,
"hit_window_ms": 1500,
"min_face_area_ratio": 0.001,
"min_face_aspect": 0.6,
@ -385,11 +385,11 @@
"name": "known_person",
"type": "person",
"cooldown_ms": 7000,
"min_sim": 0.45,
"min_hits": 1,
"min_sim": 0.6,
"min_hits": 2,
"hit_window_ms": 1500,
"min_face_area_ratio": 0.0002,
"min_face_aspect": 0.55,
"min_face_area_ratio": 0.001,
"min_face_aspect": 0.6,
"max_face_aspect": 1.6
}
],

View File

@ -546,24 +546,31 @@ python tools/analyze_face_recog_log.py .\logs\media-server_latest.log
}
```
当前告警策略:
生产默认告警策略:
- 只对 `cls=2 non_black_shoe` 告警
- 蓝框稳定出现 2 次以上,且持续约 `800ms`,才触发
- 触发后进入 `15s` 冷却,避免反复刷屏
测试短视频如果需要快速验证链路,可以使用 `configs/overlays/shoe_test_sensitive.json` 临时恢复更灵敏的阈值:
- `min_score=0.1`
- `min_duration_ms=0`
- `min_hits=1`
- `cooldown_ms=1000`
完整流程中还可以配置人脸告警:
```json
{
"face_track_aggregation": {
"known": {
"min_hits": 1,
"min_hits": 3,
"hit_window_ms": 3000,
"reentry_cooldown_ms": 8000
"reentry_cooldown_ms": 300000
},
"unknown": {
"min_track_age_ms": 1500,
"min_track_age_ms": 2000,
"min_quality_hits": 4
}
},
@ -573,7 +580,7 @@ python tools/analyze_face_recog_log.py .\logs\media-server_latest.log
"type": "unknown",
"cooldown_ms": 7000,
"max_known_sim": 0.35,
"min_hits": 1,
"min_hits": 2,
"hit_window_ms": 1500,
"min_face_area_ratio": 0.001,
"min_face_aspect": 0.6,
@ -583,11 +590,11 @@ python tools/analyze_face_recog_log.py .\logs\media-server_latest.log
"name": "known_person",
"type": "person",
"cooldown_ms": 7000,
"min_sim": 0.45,
"min_hits": 1,
"min_sim": 0.6,
"min_hits": 2,
"hit_window_ms": 1500,
"min_face_area_ratio": 0.0002,
"min_face_aspect": 0.55,
"min_face_area_ratio": 0.001,
"min_face_aspect": 0.6,
"max_face_aspect": 1.6
}
]
@ -598,12 +605,12 @@ python tools/analyze_face_recog_log.py .\logs\media-server_latest.log
| 参数 | 作用 | 设置建议 |
|------|------|----------|
| `face_track_aggregation.known.min_hits` | 同一人体 track 需要多少次 `known` 才触发已知人告警 | 验证链路用 `1`正式运行建议 `2``3` |
| `face_track_aggregation.known.min_hits` | 同一人体 track 需要多少次 `known` 才触发已知人告警 | 正式运行建议 `2``3`;验证链路可用 `face_test_sensitive` 降为 `1` |
| `face_track_aggregation.known.hit_window_ms` | `known` 证据累计窗口 | `3000`,人脸出现时间短可适当加大 |
| `face_track_aggregation.known.reentry_cooldown_ms` | 同一已知人短时间离开再进入时的抑制时间 | 打卡场景建议开启,例如 `8000` 以上 |
| `face_track_aggregation.unknown.min_track_age_ms` | 陌生人候选 track 至少持续多久 | 当前测试为 `1500`;正式场景可提高到 `2000` 以减少短暂误报 |
| `face_track_aggregation.known.reentry_cooldown_ms` | 同一已知人短时间离开再进入时的抑制时间 | 打卡场景建议使用分钟级,例如模板默认 `300000` |
| `face_track_aggregation.unknown.min_track_age_ms` | 陌生人候选 track 至少持续多久 | 正式默认 `2000`;测试短视频可用 `face_test_sensitive` 降为 `1500` |
| `face_track_aggregation.unknown.min_quality_hits` | 陌生人候选需要多少次有效质量帧 | 建议 `4` 起,保证陌生人告警更准 |
| `known_person.min_sim` | 确认已知人的最低相似度条件 | 当前测试为 `0.45`,越高越保守 |
| `known_person.min_sim` | 确认已知人的最低相似度条件 | 模板默认 `0.6`;测试短视频可用 `face_test_sensitive` 降为 `0.45` |
| `unknown_face.max_known_sim` | 陌生人候选允许的最高“已知人相似度” | 当前测试为 `0.35`;超过该值说明仍像库中某人,不直接报陌生人 |
| `face_rules[].min_face_area_ratio` | 过滤小脸框 | 1080p 下 `0.0002` 约等于 `415px²``0.001` 约等于 `2074px²` |
| `face_rules[].cooldown_ms` | 同一规则冷却 | 测试可 `7000`,正式按后台接收频率调整 |
@ -668,6 +675,7 @@ python tools/render_config.py \
--template configs/templates/workshop_face_shoe_alarm.json \
--profile configs/profiles/local_3588_test.json \
--overlay configs/overlays/face_debug.json \
--overlay configs/overlays/face_test_sensitive.json \
--out configs/generated/local_3588_face_debug.json
```
@ -686,6 +694,13 @@ python tools/render_config.py \
| `configs/overlays/` | 测试或运行场景覆盖,例如 debug、阈值、频率 |
| `configs/generated/` | 渲染产物,不手工维护,不提交生成的 JSON |
`--overlay` 可以指定多次,后面的 overlay 会覆盖前面的同名字段。建议:
- 生产:不加测试敏感 overlay或只加 `production_quiet.json`
- 人脸测试:`face_debug.json` + `face_test_sensitive.json`
- 鞋子测试:`shoe_debug.json` + `shoe_test_sensitive.json`
- 人脸和鞋子同时测试:四个 overlay 可按 debug、test sensitive 的顺序叠加。
---
## 7. 调参顺序

View File

@ -32,7 +32,7 @@ configs/
- HLS/RTSP 发布
- 告警、截图、录像、MinIO 上传、External API 上传
模板中只保留 DAG 和插件结构,设备差异通过占位符表达,例如 `${rtsp_url}`、`${face_gallery_path}`、`${minio_endpoint}`、`${external_get_token_url}`。
模板中保留 DAG、插件结构和生产默认阈值。设备差异通过占位符表达,例如 `${rtsp_url}`、`${face_gallery_path}`、`${minio_endpoint}`、`${external_get_token_url}`。短视频验证、临时放宽阈值或打开高频日志,应通过 overlay 完成,不应直接改模板。
## Profile
@ -44,9 +44,12 @@ Overlay 用于测试或运行场景覆盖:
- `configs/overlays/face_debug.json`:打开人脸识别和陌生人候选诊断日志。
- `configs/overlays/shoe_debug.json`:打开鞋子关联和颜色判断 debug。
- `configs/overlays/face_test_sensitive.json`:恢复人脸短视频测试用的灵敏触发阈值。
- `configs/overlays/shoe_test_sensitive.json`:恢复鞋子短视频测试用的灵敏触发阈值。
- `configs/overlays/production_quiet.json`:关闭高频 debug 日志,适合正式运行。
Overlay 支持 `instance_overrides."*"`,可以一次覆盖所有 instance也可以使用具体 instance 名只覆盖单路相机。
`--overlay` 可以指定多次,后指定的 overlay 覆盖同名字段。建议将 debug overlay 和 test-sensitive overlay 分开组合,避免把测试阈值误带入生产。
## 渲染命令
@ -55,9 +58,12 @@ python tools/render_config.py \
--template configs/templates/workshop_face_shoe_alarm.json \
--profile configs/profiles/local_3588_test.json \
--overlay configs/overlays/face_debug.json \
--overlay configs/overlays/face_test_sensitive.json \
--out configs/generated/local_3588_face_debug.json
```
生产运行通常不加 test-sensitive overlay需要安静日志时可叠加 `configs/overlays/production_quiet.json`
设备运行:
```bash

View File

@ -1,6 +1,7 @@
import importlib.util
import pathlib
import sys
import tempfile
import unittest
@ -65,6 +66,80 @@ class RenderConfigTest(unittest.TestCase):
self.assertTrue(node_override["debug"]["enabled"])
self.assertTrue(node_override["debug"]["log_matches"])
def test_render_applies_multiple_overlays_in_order(self):
module = load_module()
with tempfile.TemporaryDirectory() as tmp_dir:
tmp = pathlib.Path(tmp_dir)
template_path = tmp / "template.json"
profile_path = tmp / "profile.json"
overlay1_path = tmp / "overlay1.json"
overlay2_path = tmp / "overlay2.json"
template_path.write_text(
"""
{
"name": "pipeline",
"template": {
"nodes": [{"id": "alarm", "type": "alarm"}],
"edges": []
}
}
""",
encoding="utf-8",
)
profile_path.write_text(
"""
{
"instances": [{"name": "cam1", "params": {}}]
}
""",
encoding="utf-8",
)
overlay1_path.write_text(
"""
{
"instance_overrides": {
"*": {
"override": {
"nodes": {
"alarm": {
"face_debug": {"log_unknown_candidates": true},
"rules": [{"name": "shoe", "cooldown_ms": 1000}]
}
}
}
}
}
}
""",
encoding="utf-8",
)
overlay2_path.write_text(
"""
{
"instance_overrides": {
"*": {
"override": {
"nodes": {
"alarm": {
"face_debug": {"unknown_candidate_interval_ms": 0},
"rules": [{"name": "shoe", "cooldown_ms": 15000}]
}
}
}
}
}
}
""",
encoding="utf-8",
)
rendered = module.render(template_path, profile_path, [overlay1_path, overlay2_path])
alarm = rendered["instances"][0]["override"]["nodes"]["alarm"]
self.assertTrue(alarm["face_debug"]["log_unknown_candidates"])
self.assertEqual(alarm["face_debug"]["unknown_candidate_interval_ms"], 0)
self.assertEqual(alarm["rules"][0]["cooldown_ms"], 15000)
if __name__ == "__main__":
unittest.main()