Restore production defaults in config template
This commit is contained in:
parent
cfe42bdad3
commit
64af954fe4
48
configs/overlays/face_test_sensitive.json
Normal file
48
configs/overlays/face_test_sensitive.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
configs/overlays/shoe_test_sensitive.json
Normal file
28
configs/overlays/shoe_test_sensitive.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
],
|
||||
|
||||
@ -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. 调参顺序
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user