connect live API to 6-4 stream algorithm

This commit is contained in:
op
2026-06-04 20:32:13 +08:00
parent 4b824474cf
commit 968fb476f9
26 changed files with 711 additions and 70 deletions

View File

@@ -25,14 +25,14 @@ operation-room-monitor/
- Docker Compose V2、NVIDIA 驱动、NVIDIA Container Toolkit
- 复制 `backend/.env.example``backend/.env` 并填写
- 算法子进程包:`backend/algorithm_subprocesses/5.15/`(含 `main.py``weights/`;镜像构建时会 `COPY` 进容器,勿在 `.dockerignore` 中整目录排除)
- 实时算法子进程包:`backend/algorithm_subprocesses/6-4/`(含 `main_basket_stream.py``configs/``weights/`;镜像构建时会 `COPY` 进容器,勿在 `.dockerignore` 中整目录排除)
- 标注视频中文字体:镜像内已安装 `fonts-noto-cjk``fonts-wqy-microhei`(供 `visualize_result_video.py` 绘制耗材标签)
- 医生识别(MediaPipe Pose:镜像内已安装 `libgles2``libegl1``libegl-mesa0``libglx-mesa0``libgl1-mesa-dri` 等 Mesa/GLVND 库;构建阶段会 `import mediapipe` 校验 `libGLESv2.so.2` 可用。子进程强制 CPU delegate。若仍见该错误,请 **`docker compose build --no-cache api`** 后重启(勿沿用旧 tarball 镜像)
- MediaPipe 手检/医生识别:镜像内已安装 `libgles2``libegl1``libegl-mesa0``libglx-mesa0``libgl1-mesa-dri` 等 Mesa/GLVND 库;构建阶段会 `import mediapipe` 校验 `libGLESv2.so.2` 可用。若仍见该错误,请 **`docker compose build --no-cache api`** 后重启(勿沿用旧 tarball 镜像)
- **构建时预下载的预训练资源**`scripts/bake_pretrained_weights.py`,本地有则跳过,见 `backend/weights/.gitkeep`
- `swin3d_t-7615ae03.pth`VideoSwin / batch Phase1
- `resnet50-0676ba61.pth`(医生 ReID 骨干)
- `pose_landmarker_lite.task`MediaPipe Pose
- 业务权重随 `algorithm_subprocesses/5.15/weights/` 打进镜像YOLO / ActionFormer 等,非 hub 下载)
- `hand_landmarker.task`MediaPipe Hand Landmarker构建阶段写入 `algorithm_subprocesses/6-4/weights/`
- 业务权重随 `algorithm_subprocesses/6-4/weights/` 打进镜像(手检备用 YOLO / 好坏帧 / 耗材分类等,非 hub 下载)
- 可选备用:`backend/app/resources/actionformer_epoch_045.pth.tar`
---

View File

@@ -11,17 +11,17 @@
1. 配置 `**camera_id` → RTSP URL** 映射:
- `**OR_SITE_CONFIG_JSON_FILE**`推荐UTF-8 JSON 文件,**仅支持**站点对象:`{"video_rtsp_urls":{...},"voice_or_room_bindings":[...]}`。根级只允许这两个键;`voice_or_room_bindings` 可为 `[]`。见 `[app/resources/or_site_config.sample.json](../app/resources/or_site_config.sample.json)`。服务每次解析映射时会重新读文件,便于联调覆盖 `video_rtsp_urls`
- `**VIDEO_RTSP_URL_TEMPLATE**`(可选):单模板字符串,可用 `{camera_id}`;在 `video_rtsp_urls` 未给出某路时使用。
2. 调用 `POST /client/surgeries/start` 时,`camera_ids` 用于语音终端绑定;**默认仅 `RTSP_PRIMARY_CAMERA_ID`or-cam-03** 参与 RTSP 录像与 batch 算法。
2. 调用 `POST /client/surgeries/start` 时,`camera_ids` 用于语音终端绑定`basket_roi_xyxy` 提供主摄篮子 ROI**默认仅 `RTSP_PRIMARY_CAMERA_ID`or-cam-03** 参与 RTSP 录像与 6-4 篮子推流算法。
3. **开录确认**:主摄 RTSP 录像进程在超时内成功连接并写入首段数据后,才认为开录成功。
4. **RTSP 预热(可选)**:设置 `RTSP_PREWARM_ENABLED=true`API 进程启动即对站点 JSON 中 **全部** `video_rtsp_urls` 机位后台 ffmpeg keep-alive 拉流;开录/停录时暂停/恢复对应机位,避免与录像双拉。日志含 `RTSP prewarm connected``connect_ms`)与 `RTSP start_surgery timing``resolve_ms` / `spawn_ms` / `ready_ms` / `prewarm_was_warm`)。
## RTSP 录像切片(链路 1
## RTSP 录像与 6-4 推流算法(链路 1
- FastAPI 进程内 **ffmpeg** 从 RTSP 拉流,默认每 **120 秒**`RTSP_SEGMENT_DURATION_SEC`)落盘一个 MP4 切片到 `logs/rtsp_segments/{surgery_id}/{camera_id}/`
- 每个完整切片通过 [`BatchAlgorithmService`](../backend/app/algo_host/batch_service.py) 子进程调用 `algorithm_subprocesses/5.15/main.py`(与链路 3 相同),解析 `result.tsv` 后写入活跃会话
- 停录时 flush 尾切片并等待 batch 队列 drain`RTSP_SLICE_BATCH_DRAIN_TIMEOUT_SEC`
- 开录后服务端同时为主摄启动 `algorithm_subprocesses/6-4/main_basket_stream.py` 长驻子进程,传入 `basket_roi_xyxy` 生成的 ROI JSON避免无头容器弹窗框选
- 停录时向 6-4 子进程发送中断信号,等待其写出 `logs/sixfour_stream/{surgery_id}/{camera_id}/output/result.tsv`,解析 TSV 后写入活跃会话并持久化最终结果
- 落盘切片默认 **24 小时**后自动删除(`RTSP_SEGMENT_TTL_HOURS`;进程启动与后台定时 sweep
- 设置 `RTSP_RECORD_ALL_CAMERAS=true` 可对请求中所有可解析 RTSP 的机位分别录像+跑 batch多机位代码已预留
- 设置 `RTSP_RECORD_ALL_CAMERAS=true` 可对请求中所有可解析 RTSP 的机位分别录像;当前 6-4 算法只跑服务端选出的主摄
- **同一机位同时只允许一场手术录制**(默认主摄 `RTSP_PRIMARY_CAMERA_ID`):另一场次开录同一 camera 时返回 `409` / `CAMERA_ALREADY_RECORDING`;注册表在拉起 ffmpeg 前即占用机位,避免双路 RTSP 抢流导致录像周期性丢帧。
- **宿主机读切片**Compose 中 `api` 使用 `HOST_UID`/`HOST_GID` 与宿主机对齐落盘权限;详见 [Docker部署.md](Docker部署.md)「RTSP 切片在宿主机无法用 VLC 打开」。
@@ -48,11 +48,11 @@ SDK **不作为构建期依赖**:将厂商提供的 Linux x86_64 动态库挂
## 推理与结果查询
### 链路 1RTSP 切片 + batch`5.15/main.py`
### 链路 1RTSP + 6-4 推流算法(`main_basket_stream.py`
- 开录后 FastAPI 用 ffmpeg 录像并按 `RTSP_SEGMENT_DURATION_SEC` 切片;每段 MP4 经 batch 子进程推理,结果按 TSV 置信度写入会话
- 开录后 FastAPI 用 ffmpeg 保留录像,同时启动 6-4 篮子推流子进程;客户端必须传主摄 `basket_roi_xyxy`
- **候选耗材清单**`candidate_consumables`):非空时**仅**清单内名称参与自动记账与待确认;**缺省或 `[]`** 时,用 `consumable_classifier_labels.yaml` 的 **全部类名**作为候选。
- 当 Top1 置信度 **≥** `VIDEO_AUTO_CONFIRM_CONFIDENCE`**默认 0.9**)且标签在候选清单内时,自动写入 `source=video_batch` 明细;中间区间入待确认队列,由语音终端 TTS/确认。
- 当 Top1 置信度 **≥** `VIDEO_AUTO_CONFIRM_CONFIDENCE`**默认 0.9**)且标签在候选清单内时,自动写入 `source=sixfour_stream` 明细;中间区间入待确认队列,由语音终端 TTS/确认。
- 客户端 `GET /client/surgeries/{surgery_id}/pending-confirmation`,确认后 `POST .../pending-confirmation/{id}/resolve` 等。
### 链路 3离线 batch`POST /internal/demo/offline-batch`

View File

@@ -66,7 +66,7 @@
## 2. 摄像头 ID 与 RTSP
RTSP 地址、账号、口令等由客户端对接工程师提供给服务端运维,运维再写入服务端环境。客户端只在 **链路 1**`POST /client/surgeries/start` 中传 `camera_ids`。**链路 3 上传视频不需要 `camera_ids`。**
RTSP 地址、账号、口令等由客户端对接工程师提供给服务端运维,运维再写入服务端环境。客户端只在 **链路 1**`POST /client/surgeries/start` 中传 `camera_ids` 与主摄 `basket_roi_xyxy`。**链路 3 上传视频不需要 `camera_ids`。**
| **camera_id** | **RTSP** | **备注** |
| ------------- | -------- | -------- |
@@ -114,7 +114,7 @@ sequenceDiagram
participant Server as 服务端
Client->>Server: POST /client/surgeries/start
Note over Client,Server: body: surgery_id, camera_ids, candidate_consumables
Note over Client,Server: body: surgery_id, camera_ids, basket_roi_xyxy, candidate_consumables
Server-->>Client: 200 accepted开录已确认
par 术中
@@ -248,12 +248,13 @@ sequenceDiagram
**业务说明**
- 服务端会为 `camera_ids` 中的每个摄像头建立拉流与推理任务只有在确认开录成功(如首帧就绪)后才返回 HTTP `200`
- 服务端会为主摄启动 6-4 篮子推流算法,并保留 RTSP 录像任务只有在确认开录成功(如首帧就绪)后才返回 HTTP `200`
- 本接口**同步阻塞**直至开录确认;默认 RTSP 等待约 **95 秒/次**、最多 **3 次重试**。客户端 HTTP 超时建议 **≥ 300 秒**Apipost 等工具默认 3060 秒易触发 `ESOCKETTIMEDOUT`)。服务端可通过 `VIDEO_OPEN_TIMEOUT_SEC` 调大(默认 90
- `candidate_consumables` 缺省或 `[]` 时,服务端会展开为 `consumable_classifier_labels.yaml` 中的全部类名(无有效 yaml 时开录失败)。
- 非空时,每项可为**耗材名称**或**产品编码**`label_id`);编码通过 yaml 解析为类名后再参与推理与白名单。亦支持医院导出对象(见下表)。
- **41 类完整类名与产品编码对照**:见 [`docs/耗材产品编码与类名对照表.md`](耗材产品编码与类名对照表.md)(须用完整类名或编码,口语简称如「纱布」不会自动映射)。
- `basket_roi_xyxy` 为主摄篮子 ROI 的原始像素坐标,必须基于服务端实际算法主摄(默认 `RTSP_PRIMARY_CAMERA_ID`,常为 `or-cam-03`)的预览画面标注。
**请求体JSON**
@@ -261,6 +262,7 @@ sequenceDiagram
| ----------------------- | ---------- | ------ | ----------------------------------- |
| `surgery_id` | `string` | 是 | 6 位数字 |
| `camera_ids` | `string[]` | 是 | 至少 1 个;必须与运维配置的摄像头 ID 完全一致,见第 2 节 |
| `basket_roi_xyxy` | `number[4]` | 是 | 主摄篮子 ROI格式 `[x1,y1,x2,y2]`,原始像素坐标,要求 `x2>x1``y2>y1` |
| `candidate_consumables` | `string[]` 或对象数组 | 否 | 非空时仅这些名称/编码参与自动记账与待确认;缺省或 `[]` 时使用全部候选。字符串可为类名或 `label_id`;对象见下 |
**`candidate_consumables` 数组元素**
@@ -285,7 +287,7 @@ sequenceDiagram
| **HTTP** | **说明** |
| -------- | -------------------------------------------------------- |
| `200` | 开录已确认 |
| `422` | 参数校验失败,例如 `surgery_id` 非 6 位或 `camera_ids` 为空数组 |
| `422` | 参数校验失败,例如 `surgery_id` 非 6 位`camera_ids` 为空数组或缺少/非法 `basket_roi_xyxy` |
| `503` | 开录未确认或录制子系统故障;`detail.code` 常见为 `RECORDING_CANNOT_START` |
**请求示例**
@@ -294,6 +296,7 @@ sequenceDiagram
{
"surgery_id": "123456",
"camera_ids": ["or-cam-01", "or-cam-02", "or-cam-03", "or-cam-04"],
"basket_roi_xyxy": [260, 180, 1120, 860],
"candidate_consumables": ["纱布", "缝线", "止血钳"]
}
```
@@ -304,6 +307,7 @@ sequenceDiagram
{
"surgery_id": "123456",
"camera_ids": ["or-cam-01"],
"basket_roi_xyxy": [260, 180, 1120, 860],
"candidate_consumables": ["14764-2-4", "8036-5-22"]
}
```