from __future__ import annotations from dataclasses import dataclass import numpy as np WRIST = 0 THUMB_CMC, THUMB_MCP, THUMB_IP, THUMB_TIP = 1, 2, 3, 4 INDEX_MCP, INDEX_PIP, INDEX_DIP, INDEX_TIP = 5, 6, 7, 8 MIDDLE_MCP, MIDDLE_PIP, MIDDLE_DIP, MIDDLE_TIP = 9, 10, 11, 12 RING_MCP, RING_PIP, RING_DIP, RING_TIP = 13, 14, 15, 16 PINKY_MCP, PINKY_PIP, PINKY_DIP, PINKY_TIP = 17, 18, 19, 20 HAND_CONNECTIONS: tuple[tuple[int, int], ...] = ( (0, 1), (1, 2), (2, 3), (3, 4), (0, 5), (5, 6), (6, 7), (7, 8), (5, 9), (9, 10), (10, 11), (11, 12), (9, 13), (13, 14), (14, 15), (15, 16), (13, 17), (17, 18), (18, 19), (19, 20), (0, 17), ) @dataclass class Hand: """One detected hand.""" keypoints: np.ndarray scores: np.ndarray box: np.ndarray conf: float @property def scale(self) -> float: """Characteristic hand size.""" palm = np.linalg.norm(self.keypoints[MIDDLE_MCP] - self.keypoints[WRIST]) if palm < 1e-3: palm = max(self.box[2] - self.box[0], self.box[3] - self.box[1]) * 0.5 return float(max(palm, 1e-3)) @property def center(self) -> np.ndarray: """Palm center point.""" return self.keypoints[[WRIST, INDEX_MCP, PINKY_MCP]].mean(axis=0) def point(self, idx: int) -> np.ndarray: """Keypoint by index.""" return self.keypoints[idx]