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