-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhand_tracking.py
More file actions
84 lines (67 loc) · 3.04 KB
/
hand_tracking.py
File metadata and controls
84 lines (67 loc) · 3.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
"""
Hand tracking module using MediaPipe Hands.
Provides a simple interface to detect hand landmarks from camera frames.
"""
import cv2
import mediapipe as mp
from typing import Optional, Tuple, List
import config
class HandTracker:
"""Wrapper for MediaPipe Hands that processes frames and returns landmarks."""
def __init__(self):
"""Initialize MediaPipe Hands detector."""
self.mp_hands = mp.solutions.hands
self.mp_drawing = mp.solutions.drawing_utils
self.hands = self.mp_hands.Hands(
static_image_mode=False, # Real-time video mode (optimized for video streams)
max_num_hands=config.MAX_NUM_HANDS,
min_detection_confidence=config.MIN_DETECTION_CONFIDENCE,
min_tracking_confidence=config.MIN_TRACKING_CONFIDENCE,
model_complexity=config.MODEL_COMPLEXITY, # Balanced performance/accuracy
)
def process_frame(
self, frame
) -> Tuple[Optional[List[Tuple[float, float]]], any]:
"""
Process a BGR frame and return landmarks if hand is detected.
Args:
frame: BGR frame from OpenCV (numpy array)
Returns:
Tuple of (landmarks, annotated_frame):
- landmarks: List of (x, y) normalized coordinates, or None if no hand
- annotated_frame: BGR frame with hand skeleton drawn
"""
# Convert BGR to RGB for MediaPipe
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
results = self.hands.process(rgb_frame)
# Draw hand skeleton on frame
annotated_frame = frame.copy()
if results.multi_hand_landmarks:
# Smart hand presence detection (inspired by handTrack)
# Only track one hand at a time - prevents accidental control from nearby people
num_hands = len(results.multi_hand_landmarks)
if num_hands > 1:
# Multiple hands detected - use the most confident one
# MediaPipe doesn't provide confidence per hand, so use first one
# In practice, this prevents accidental control from second hand
hand_landmarks = results.multi_hand_landmarks[0]
else:
# Single hand - use it
hand_landmarks = results.multi_hand_landmarks[0]
# Draw skeleton
self.mp_drawing.draw_landmarks(
annotated_frame,
hand_landmarks,
self.mp_hands.HAND_CONNECTIONS,
)
# Extract normalized (x, y) coordinates (ignore z)
landmarks = [
(landmark.x, landmark.y) for landmark in hand_landmarks.landmark
]
return landmarks, annotated_frame
else:
# No hand detected - return None (handTrack-inspired: auto-disable when no hand)
return None, annotated_frame
def close(self):
"""Clean up MediaPipe resources."""
self.hands.close()