Files
operating-room-monitor-server/app/services/tear_gated_segment_consumption/geometry.py

93 lines
2.5 KiB
Python
Raw Normal View History

"""手部框、撕动作几何与概率(从离线 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