feat(input/gestures): add CameraCapture and public __init__ exports
This commit is contained in:
parent
a31e6099c6
commit
524cc62812
2 changed files with 72 additions and 0 deletions
|
|
@ -0,0 +1,15 @@
|
||||||
|
"""
|
||||||
|
cf_input.gestures — camera capture, hand detection, landmark normalization.
|
||||||
|
|
||||||
|
Public API:
|
||||||
|
CameraCapture — OpenCV frame source
|
||||||
|
HandsDetector — MediaPipe Hands wrapper
|
||||||
|
HandLandmarks — immutable detected hand dataclass
|
||||||
|
normalize_hand() — scale/translation-invariant feature vector
|
||||||
|
"""
|
||||||
|
|
||||||
|
from circuitforge_core.input.gestures.camera import CameraCapture
|
||||||
|
from circuitforge_core.input.gestures.hands import HandLandmarks, HandsDetector
|
||||||
|
from circuitforge_core.input.gestures.normalizer import normalize_hand
|
||||||
|
|
||||||
|
__all__ = ["CameraCapture", "HandLandmarks", "HandsDetector", "normalize_hand"]
|
||||||
57
circuitforge_core/input/gestures/camera.py
Normal file
57
circuitforge_core/input/gestures/camera.py
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
"""
|
||||||
|
OpenCV camera capture — context manager wrapping VideoCapture.
|
||||||
|
|
||||||
|
Yields BGR frames. Callers convert to RGB before passing to HandsDetector:
|
||||||
|
frame_rgb = frame_bgr[:, :, ::-1]
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Iterator
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
class CameraCapture:
|
||||||
|
"""
|
||||||
|
Thin wrapper around cv2.VideoCapture.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
with CameraCapture(device_index=0) as cam:
|
||||||
|
for frame_bgr in cam.frames():
|
||||||
|
process(frame_bgr)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
device_index: int = 0,
|
||||||
|
width: int = 640,
|
||||||
|
height: int = 480,
|
||||||
|
fps: int = 30,
|
||||||
|
) -> None:
|
||||||
|
self._cap = cv2.VideoCapture(device_index)
|
||||||
|
self._cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
|
||||||
|
self._cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
|
||||||
|
self._cap.set(cv2.CAP_PROP_FPS, fps)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_open(self) -> bool:
|
||||||
|
return self._cap.isOpened()
|
||||||
|
|
||||||
|
def frames(self) -> Iterator[np.ndarray]:
|
||||||
|
"""Yield BGR uint8 frames until camera fails or caller breaks."""
|
||||||
|
while self._cap.isOpened():
|
||||||
|
ok, frame = self._cap.read()
|
||||||
|
if not ok:
|
||||||
|
break
|
||||||
|
yield frame
|
||||||
|
|
||||||
|
def release(self) -> None:
|
||||||
|
self._cap.release()
|
||||||
|
|
||||||
|
def __enter__(self) -> CameraCapture:
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *_: object) -> None:
|
||||||
|
self.release()
|
||||||
Loading…
Reference in a new issue