Files
operating-room-monitor-server/app/services/tear_gated_segment_consumption/geometry.py
Kevin 8a4bad99d3 feat: 配置写死与 baked 模块,Alembic 建表,百度仅 BAIDU_*
- 新增 app/baked/algorithm|pipeline,非部署参数不再走 env;Settings 保留 DB/HTTP/RTSP/海康/百度/MinIO/Demo
- 移除 init_db_schema 与 reload 配置;main 仅 check_database;start*.sh 在 uvicorn 前执行 alembic upgrade head
- 依赖 psycopg[binary] 供 Alembic 同步 URL;alembic/env 注释与预发清单更新
- 撕段门控消费管线、各视频/语音/归档调用改为 baked
- 百度环境变量仅 BAIDU_APP_ID、BAIDU_API_KEY、BAIDU_SECRET_KEY 与 BAIDU_* 超时/ASR;人脸脚本与 baidu_speech 文案同步
- 全量单测与 .env.example 更新;.gitignore 忽略 refs/(本地权重/视频不入库)

Made-with: Cursor
2026-04-24 15:33:22 +08:00

93 lines
2.5 KiB
Python

"""手部框、撕动作几何与概率(从离线 tear 脚本抽离,不依赖 OpenCV 绘制)。"""
from __future__ import annotations
from itertools import combinations
from typing import Any
import numpy as np
from ultralytics import YOLO
def union_boxes(boxes: list[list[float]]) -> list[float]:
x1 = min(b[0] for b in boxes)
y1 = min(b[1] for b in boxes)
x2 = max(b[2] for b in boxes)
y2 = max(b[3] for b in boxes)
return [x1, y1, x2, y2]
def pad_box(
xyxy: list[float],
img_w: int,
img_h: int,
pad_ratio: float = 0.30,
) -> tuple[int, int, int, int]:
x1, y1, x2, y2 = xyxy
bw, bh = x2 - x1, y2 - y1
px, py = bw * pad_ratio, bh * pad_ratio
return (
max(0, int(x1 - px)),
max(0, int(y1 - py)),
min(img_w, int(x2 + px)),
min(img_h, int(y2 + py)),
)
def collect_hand_boxes(det_model: YOLO, boxes) -> list[list[float]]:
names = det_model.names
out: list[list[float]] = []
for box in boxes:
cid = int(box.cls[0])
label = names.get(cid, "")
if label == "hand":
out.append(box.xyxy[0].tolist())
return out
def box_edge_distance(a: list[float], b: list[float]) -> float:
dx = max(0, max(a[0], b[0]) - min(a[2], b[2]))
dy = max(0, max(a[1], b[1]) - min(a[3], b[3]))
return float((dx**2 + dy**2) ** 0.5)
def box_avg_width(boxes: list[list[float]]) -> float:
if not boxes:
return 0.0
return sum(b[2] - b[0] for b in boxes) / len(boxes)
def find_tearing_pair(
hand_boxes: list[list[float]],
gap_ratio: float = 1.5,
) -> tuple[list[float], list[float]] | None:
if len(hand_boxes) < 2:
return None
avg_w = box_avg_width(hand_boxes)
threshold = avg_w * gap_ratio
best_dist = float("inf")
best_pair: tuple[list[float], list[float]] | None = None
for a, b in combinations(hand_boxes, 2):
d = box_edge_distance(a, b)
if d < best_dist:
best_dist = d
best_pair = (a, b)
if best_pair is not None and best_dist <= threshold:
return best_pair
return None
def prob_tearing(tprobs, tear_names: dict[Any, str]) -> float:
if tprobs is None:
return 0.0
data = tprobs.data
if data is None:
return 0.0
d = data.detach().float().cpu().numpy().ravel()
for i, name in tear_names.items():
if name == "tearing":
ii = int(i)
if 0 <= ii < len(d):
return float(d[ii])
return 0.0