| """Hand seeds for the canonical T-Rex 250-point CoTracker layout. |
| |
| No appearance auto-repair. Tune prompts in ``sam2_prompt_hands.py``. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import numpy as np |
|
|
| from trex_track.layout import NUM_HEAD_PER_HAND |
|
|
| NUM_HAND_POINTS = NUM_HEAD_PER_HAND |
|
|
| __all__ = [ |
| "NUM_HAND_POINTS", |
| "wrist_hand_50", |
| "head_hands_50", |
| |
| "wrist_hand_25", |
| "head_hands_25", |
| ] |
|
|
|
|
| def _stable_spatial_order(points_xy: np.ndarray) -> np.ndarray: |
| """Give sampled slots deterministic top-to-bottom, then left-to-right IDs.""" |
|
|
| points = np.asarray(points_xy, dtype=np.float32).reshape(-1, 2) |
| order = np.lexsort((points[:, 0], points[:, 1])) |
| return points[order] |
|
|
|
|
| def wrist_hand_50( |
| predictor, |
| rgb_hwc: np.ndarray, |
| view: str, |
| *, |
| n_points: int = NUM_HAND_POINTS, |
| seed: int | None = 0, |
| ) -> tuple[np.ndarray, np.ndarray, str]: |
| """One SAM2 pass → 50 stably ordered queries on a wrist hand mask. |
| |
| Background 5×5 grid is NOT produced here — callers prepend ``make_image_grid``. |
| """ |
| from trex_track.sam2_prompt_hands import sample_points_on_mask, sam2_wrist_mask |
|
|
| mask, tag, _meta = sam2_wrist_mask(predictor, rgb_hwc, view) |
| rng = np.random.default_rng(seed) |
| pts = sample_points_on_mask(mask, n_points, rng=rng, distal_bias=True) |
| h, w = rgb_hwc.shape[:2] |
| pts[:, 0] = np.clip(pts[:, 0], 0, w - 1) |
| pts[:, 1] = np.clip(pts[:, 1], 0, h - 1) |
| return _stable_spatial_order(pts), mask.astype(bool), tag |
|
|
|
|
| def head_hands_50( |
| predictor, |
| rgb_hwc: np.ndarray, |
| *, |
| n_points: int = NUM_HAND_POINTS, |
| seed: int | None = 0, |
| ) -> tuple[np.ndarray, np.ndarray, np.ndarray, str]: |
| """One SAM2 pass per hand → 100 queries (left 50, then right 50).""" |
| from trex_track.sam2_prompt_hands import sample_points_on_mask, sam2_head_hand_mask |
|
|
| rng = np.random.default_rng(seed) |
| left, tag_l, _ = sam2_head_hand_mask(predictor, rgb_hwc, "left") |
| right, tag_r, _ = sam2_head_hand_mask(predictor, rgb_hwc, "right") |
| pts_l = _stable_spatial_order( |
| sample_points_on_mask(left, n_points, rng=rng, distal_bias=True) |
| ) |
| pts_r = _stable_spatial_order( |
| sample_points_on_mask(right, n_points, rng=rng, distal_bias=True) |
| ) |
| h, w = rgb_hwc.shape[:2] |
| queries = np.concatenate([pts_l, pts_r], axis=0).astype(np.float32) |
| queries[:, 0] = np.clip(queries[:, 0], 0, w - 1) |
| queries[:, 1] = np.clip(queries[:, 1], 0, h - 1) |
| tag = f"{tag_l}|{tag_r}" |
| return queries, left.astype(bool), right.astype(bool), tag |
|
|
|
|
| |
| wrist_hand_25 = wrist_hand_50 |
| head_hands_25 = head_hands_50 |
|
|