Files
silver_pose/docs/api.md
T

155 lines
9.1 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`,可经设置调整并追溯。
- 缺少环境变量、模型不存在或哈希不符时,应用显示配置错误,不启动监控。
### V2 Go 运行配置
`v2/config.example.json` 是 V2 的公开配置形状。它保留 V1 的来源、模型、事件和工件语义,但将 V2 交付依赖显式写入 `model.onnx`、`model.ort_dll` 和 `tools.ffmpeg`/`tools.ffprobe`:
- `source` 只接受 `id`、`rtsp_url_env`、`transport`、`timeout_seconds` 和 `low_latency`。任何 `url`、`rtsp_url`、`host`、`username` 或 `password` 字段都会被拒绝,即使它们为空;实际流地址只从指定环境变量进入进程内存。
- `model.sha256` 必须是锁定 ONNX 的 64 位十六进制 SHA-256;`confidence_threshold` 是 Pose 框过滤阈值。配置相对路径相对于配置文件解析,但只在启动预检时要求实际文件存在。
- `event` 字段与 V1 的默认事件语义相同。V2 使用 `source.id`、模型 SHA、模型置信度和这些事件字段生成 `cfg-<sha256>`;URL、工具路径和工件路径均不进入该版本值。
- `artifacts.event_dir` 只保存本地 PNG/JSONL 事件证据。V2 发布脚本生成的 `runtime-manifest.json` 记录 ONNX Runtime、FFmpeg、FFprobe 和 ONNX 的来源文件哈希;示例 `runtime-manifest.example.json` 不是交付证据。
## 核心数据
```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 不同内容的记录。
### V2 本地事件证据
V2 的 `internal/render` 把 BGR 视频帧与已经完成的 `FrameResult` 合成为 RGBA 图像。它画 person box、17 点骨架、ID、状态和 UTC 时间;`NORMAL` 为绿色、`SUSPECT`/`RECOVERING` 为琥珀色,只有任一人员为 `CONFIRMED` 时才绘制红色骨架和六像素红色画面边框。
`internal/alert.Dispatcher` 只接收 `CONFIRMED` 的 `FallEvent` 和已叠加的图像。它在写文件前用 `event_id` 去重,写入 `artifacts.event_dir/YYYYMMDD/FALL-*.png` 与同目录 `events.jsonl`;失败会释放该 ID 使后续帧可以重试。JSONL 的字段与下文 `AlertRecord` 一致,且模块从不接收、保存或输出 RTSP URL。
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` 字段和事件幂等语义。