Files
Table-Python/source/Tbd/helper.py
T
2026-07-31 09:02:21 +02:00

148 lines
4.7 KiB
Python

import numpy as np
import json
import cv2
import threading
from dataclasses import dataclass, field, asdict
from typing import List, Optional, Tuple, Dict
from pygrabber.dshow_graph import FilterGraph
class DShowMJPGCapture:
"""
cv2.VideoCapture-like wrapper (read/release/isOpened) that selects an exact
DirectShow media type (e.g. MJPG at a given resolution) before capturing.
This exists because cv2.VideoCapture(..., cv2.CAP_DSHOW).set(CAP_PROP_FOURCC, ...)
is unreliable on some webcams: OpenCV opens its own separate DirectShow filter
graph, which renegotiates its own format independently of anything set through
pygrabber beforehand. Doing format selection AND frame grabbing in the same
graph (via pygrabber's sample grabber) avoids that.
"""
def __init__(self, index: int, width: int, height: int,
fourcc_substr: str = "MJPG", timeout: float = 2.0):
self.index = index
self.width = width
self.height = height
self.selected_format = None
self._opened = False
self._latest_frame = None
self._frame_event = threading.Event()
self._timeout = timeout
self._graph = FilterGraph()
self._graph.add_video_input_device(index)
device = self._graph.get_input_device()
formats = device.get_formats()
match = next(
(f for f in formats
if f["width"] == width and f["height"] == height
and fourcc_substr.upper() in f["media_type_str"].upper()),
None,
)
if match is None:
raise RuntimeError(
f"No {fourcc_substr} format at {width}x{height} available on camera {index}. "
f"Available formats: {formats}"
)
device.set_format(match["index"])
self.selected_format = match
self._graph.add_sample_grabber(self._on_frame)
self._graph.add_null_render()
self._graph.prepare_preview_graph()
self._graph.run()
self._opened = True
def _on_frame(self, frame):
self._latest_frame = frame
self._frame_event.set()
def isOpened(self) -> bool:
return self._opened
def read(self):
if not self._opened:
return False, None
self._frame_event.clear()
self._graph.grab_frame()
if not self._frame_event.wait(self._timeout):
return False, None
return True, self._latest_frame
def release(self):
if self._opened:
self._graph.stop()
self._graph.remove_filters()
self._opened = False
# No-ops for compatibility with code paths that still call .set()/.get()
# on the capture object (real config happens via device.set_format above).
def set(self, prop_id, value):
return False
def get(self, prop_id):
return 0.0
# ----- Helper Functions -----
def compute_distance(p1, p2):
return np.hypot(p2[0] - p1[0], p2[1] - p1[1])
# 4) compute straightness per finger
def finger_straight(i_tip, i_pip, i_mcp):
d1 = np.hypot(*(np.array(i_tip)-np.array(i_pip)))
d2 = np.hypot(*(np.array(i_pip)-np.array(i_mcp)))
return float(np.clip(d1/(d2+1e-3), 0, 1))
def default_profile():
return []
@dataclass
class CameraObject():
capture: Optional[cv2.VideoCapture]
index: int
pxHeight: int = 1536
pxWidth: int = 2048
# Calibration
camera_matrix: Optional[np.ndarray] = None # 3x3
distortion_coefficients: Optional[np.ndarray] = None # 1x5 / 1x8
rotation_matrix_world_to_camera: Optional[np.ndarray] = None # 3x3 (world->cam)
translation_vector_world_to_camera: Optional[np.ndarray] = None # 3x1 (world->cam)
camera_projection_matrix: Optional[np.ndarray] = None
def load_video_capture(self):
#self.capture = cv2.VideoCapture(self.index, cv2.CAP_MSMF)
self.capture = cv2.VideoCapture(self.index, cv2.CAP_ANY)
#self.capture = cv2.VideoCapture(self.index, cv2.CAP_DSHOW)
self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, 2048.0)
self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 1536.0)
@dataclass
class Hand2D():
camera_id: str # e.g. "overhead_left"
handedness: str # "Left" / "Right"
score: float # detection/tracking confidence
landmarks_px: np.ndarray # shape (21,2) in pixels
@dataclass
class CharucoDetection:
camera_id: int
# Output of Charuco detection
charuco_corners: np.ndarray # (N, 1, 2) float32
charuco_ids: np.ndarray # (N, 1) int32
gray_frame: Optional[np.ndarray] = None
@dataclass
class CalibrationSample:
detections: dict[int, CharucoDetection] # camera_id → detection