Files
silver_pose/docs/api.md
T
ilaandClaude Opus 4.8 b1f0e8aeb4 feat(v1): relax fall sensitivity for low overhead cameras
Adds configurable event.require_rapid_drop (default off), require_lower_body
(default off) and horizontal_angle_threshold_degrees (default 45). With the
lenient defaults a sustained horizontal pose alone enters SUSPECT and confirms
after the confirm window, which is now the main false-positive guard; missing
knees/ankles no longer reject the pose. Thresholds flow into config_version.
End-to-end smoke confirms a lying pose; 70 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 23:29:16 +08:00

140 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 本地模块与事件合约
> Silver Pose 没有后端 API;本文定义 V1/V2 必须保持一致的本地配置、数据和事件合约。
## 配置合约
`v1/config.example.json` 的最小形状:
```json
{
"source": {
"id": "lobby-camera-01",
"rtsp_url_env": "SILVER_POSE_RTSP_URL",
"mode": "stream",
"roi_normalized": [0.0, 0.0, 1.0, 1.0]
},
"model": {
"path": "models/best.pt",
"sha256": "0000000000000000000000000000000000000000000000000000000000000000",
"confidence_threshold": 0.25
},
"event": {
"keypoint_confidence_threshold": 0.4,
"suspect_window_seconds": 0.5,
"confirm_window_seconds": 1.0,
"recovery_window_seconds": 2.0,
"cooldown_seconds": 10.0
},
"artifacts": {
"event_dir": "../artifacts/events"
}
}
```
- 来源二选一:`rtsp_url_env`(从同名环境变量读取真实 URL)**或**结构化字段 `host`/`port`(默认 554)/`channel`(默认 `101`,`102` 为子码流)/`username`/`password`;两者都会在内存中解析出 RTSP URL。结构化凭证只允许出现在**未跟踪的** `config.local.json`;公开示例 `config.example.json` 只能用 `rtsp_url_env`,禁止任何 `host`/`username`/`password`/完整 URL(有测试守卫)。
- 结构化模式下应用用 `build_rtsp_url` 拼接,并对用户名/密码做 RFC 3986 百分号编码,因此密码含 `@`、`:`、`/` 也不会破坏 URL 解析。
- `source.mode` 显式声明来源类型,取值 `stream`(默认,实时 RTSP,允许有界重连)或 `replay`(本地录像,EOF 不重放);由配置决定,不再按 URL 前缀猜测。
- 抓流低延迟调优(非凭证,可进公开示例):`source.transport`(`tcp` 默认 / `udp`)、`source.timeout_seconds`(默认 5)、`source.low_latency`(默认 true)。应用用 `build_ffmpeg_options` 拼成 `OPENCV_FFMPEG_CAPTURE_OPTIONS`,并在打开流之前写入 `os.environ`(OpenCV 在打开时读取该环境变量);因此无需再靠外部脚本设置该变量。
- 凭证(`host`/`username`/`password`/解析出的 URL)绝不进入 `runtime_config_version`、事件 JSONL、日志或截图文件名。
- `model.confidence_threshold` 是模型检测置信度,可在设置草稿中调整,并在下次开始监控时经 `PoseAdapter.set_confidence_threshold` 真正生效。
- 数值是待现场录像校准的默认值;每个值必须真正进入事件逻辑:`keypoint_confidence_threshold` 决定姿态质量门槛;`suspect_window_seconds` 限制快速下移到水平姿态的最大间隔;`confirm_window_seconds` 是水平倒地候选需持续的确认时间;`recovery_window_seconds` 是恢复姿态需持续的时间;`cooldown_seconds` 是确认事件后允许开始恢复判断前的最短等待时间。示例中的全零 SHA-256 只占位配置形状,T-103 必须以受控模型的真实哈希替换并验证后才能启动推理。
- 灵敏度参数(默认放宽,适配低位俯视):`require_rapid_drop`(默认 false,持续水平即可进入疑似,不再强制先检测到快速下移);`require_lower_body`(默认 false,质量门控只强制肩+髋,膝踝可缺);`horizontal_angle_threshold_degrees`(默认 45,躯干与水平夹角≤此值算水平)。放宽后由 `confirm_window_seconds` 作为主要防误报闸——弯腰、捡物不会持续水平到确认窗满。三者进入 `config_version`,可经设置调整并追溯。
- 缺少环境变量、模型不存在或哈希不符时,应用显示配置错误,不启动监控。
## 核心数据
```text
Keypoint = { x: float, y: float, confidence: float }
PersonPose = {
box_xyxy: [float, float, float, float],
box_confidence: float,
keypoints: Keypoint[17]
}
TrackedPersonPose = {
track_id: string,
detected_at_monotonic: float,
pose: PersonPose
}
PoseQuality = {
accepted: bool,
reason: string,
visible_joint_count: int
}
PoseEvidence = {
accepted: bool,
horizontal_pose: bool,
rapid_vertical_change: bool,
horizontal_angle_degrees: float | null,
hip_center_y: float | null,
torso_length: float | null,
reason: string
}
FallEvent = {
event_id: string,
track_id: string,
config_version: string,
suspected_at_monotonic: float,
confirmed_at_monotonic: float,
latency_seconds: float,
state: "CONFIRMED"
}
```
`FallEvent` 是状态机的纯内存确认事件,只在状态首次进入 `CONFIRMED` 时创建一次。连续帧更新 UI 状态,但不重复创建事件。`config_version` 是由运行配置快照计算的非敏感版本标识;T-202 的 `alerts` 会在不改变事件幂等语义的前提下,为截图/JSONL 记录补充来源、UTC 时间和证据。
`event_id` 带每次监控运行的会话前缀(`FALL-<session>-NNNNNN`),跨监控重启全局唯一;因此同一天目录内的截图不会被覆盖,`events.jsonl` 也不会出现同 id 不同内容的记录。
T-202 的 `alerts` 按 `event_id` 去重,对首次 CONFIRMED 只触发一次副作用(声音、弹窗、截图、JSONL 一行)。JSONL 记录如下,文件名与字段不含 RTSP 地址、凭证或客户姓名:
```text
AlertRecord(JSONL) = {
event_id: string,
track_id: string,
config_version: string,
source_id: string,
state: "CONFIRMED",
confirmed_at_utc: string, # ISO-8601 UTC
suspected_at_monotonic: float,
confirmed_at_monotonic: float,
latency_seconds: float,
screenshot: string # 相对 event_dir 的 "YYYYMMDD/FALL-xxxxxx.png"
}
```
`PersonPose` 是 T-103 的纯模型输出,不带人员 ID;T-104 的跟踪模块产生 `TrackedPersonPose` 后,才允许事件证据按人员连续积累。
## 视频来源帧合约
```text
FramePacket = {
image: ndarray | null,
timestamp_monotonic: float,
status: "connected" | "retrying" | "error" | "eof" | "closed",
error: string | null
}
```
- `SourceMode.REPLAY` 的录像优先使用容器时间戳;首帧时间戳无效或倒退时,回退为帧序号/FPS,保证回放时间单调;到达 EOF 后保持 EOF,不重放。
- `SourceMode.STREAM` 的实时流以成功读帧时的单调时钟计时;读取失败进入有界指数退避重连,不使用 `CAP_PROP_POS_MSEC` 作为事件时间。
- `retrying`、`error`、`eof` 和 `closed` 都没有图像,且绝不伪造人员、姿态或摔倒事件。
- 可重连来源以有界指数退避重新打开;断流不推进状态机的证据时间。
## 事件合约
| 事件 | 触发者 | 负载 | 结果 |
| --- | --- | --- | --- |
| `source.connected` | 视频源 | `source_id`、时间 | UI 显示在线。 |
| `source.error` | 视频源 | `source_id`、错误码、可重试标记 | UI 显示异常;不报警。 |
| `person.updated` | 跟踪与 Pose | `TrackedPersonPose`、`PoseQuality`、`PoseEvidence` | UI 绘制骨架与 ID。 |
| `fall.suspected` | 状态机 | `track_id`、开始时间 | UI 显示黄色疑似状态。 |
| `fall.confirmed` | 状态机 | `FallEvent` | 红色叠加、声音、弹窗、截图、JSONL。 |
| `fall.recovered` | 状态机 | `track_id`、时间 | UI 恢复绿色正常状态。 |
## 副作用边界
- 仅 `alerts` 模块可写事件截图、播放声音、弹窗和追加 JSONL。
- 仅 `video_source` 模块读 RTSP 或本地视频。
- 仅 `pose` 模块加载模型;UI 不得直接调用模型。
- V2 可以改变实现语言,但不得改变配置含义、`FallEvent` 字段和事件幂等语义。