Repository navigation
Expand file tree
/
Copy pathHandTracking.py
More file actions
151 lines (115 loc) · 5.74 KB
/
Copy pathHandTracking.py
File metadata and controls
151 lines (115 loc) · 5.74 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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import math
import cv2
import mediapipe as mp
import time
BaseOptions = mp.tasks.BaseOptions
HandLandmarker = mp.tasks.vision.HandLandmarker
HandLandmarkerOptions = mp.tasks.vision.HandLandmarkerOptions
HandLandmarkerResult = mp.tasks.vision.HandLandmarkerResult
VisionRunningMode = mp.tasks.vision.RunningMode
class HandTrackingManager:
def __init__(self, callback_function=None):
"""VARIABLES"""
self.is_pinching = False
self.latest_result = None
self.frame_distances = []
self.N = 8
self.PINCH_THRESHOLD = 50
self.SEPARATION_THRESHOLD = 100
self.model_path = 'custom_models/hand_landmarker.task'
self.callback_for_gesture = callback_function
self.mouse_position = (0,0)
# Set up the hand landmark detector
options = HandLandmarkerOptions(
base_options=BaseOptions(model_asset_path=self.model_path),
running_mode=VisionRunningMode.LIVE_STREAM,
num_hands=1,
result_callback=self.print_result
)
self.landmarker = HandLandmarker.create_from_options(options)
def analyze_frame(self, frame, frame_timestamp_ms):
# Convert frame to RGB format
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=frame_rgb)
# Detect hand landmarks asynchronously
self.landmarker.detect_async(mp_image, frame_timestamp_ms)
# Draw landmarks if detection has results
if self.latest_result is not None:
if self.identify_pinch_gesture(self.latest_result, frame):
print("Pinch Detected!")
cv2.putText(frame, "Pinch Detected!", (10, 100),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 3)
frame = self.draw_landmarks(frame, self.latest_result)
# Display the frame
cv2.imshow("Hand Tracking", frame)
def identify_pinch_gesture(self, latest_result, frame): #
"""Identifies a pinch gesture when the distance shrinks from >100 to <30 within N frames."""
if not latest_result.hand_landmarks:
return False
height, width, _ = frame.shape
hand = latest_result.hand_landmarks[0]
start_point = (int(hand[4].x * width), int(hand[4].y * height))
end_point = (int(hand[8].x * width), int(hand[8].y * height))
mid_point = (int((hand[4].x + hand[8].x) * width / 2), int((hand[4].y + hand[8].y) * height / 2))
last_distance = math.sqrt((end_point[0] - start_point[0]) ** 2 + (end_point[1] - start_point[1]) ** 2)
self.mouse_position = mid_point
self.frame_distances.append(last_distance)
if len(self.frame_distances) > self.N:
self.frame_distances.pop(0) # Keep only last N frames
if last_distance <= self.PINCH_THRESHOLD:
if self.is_pinching:
print("Hand still pinching", self.mouse_position)
else:
None
#print("Hand closed")
for old_distance in self.frame_distances:
if old_distance >= self.SEPARATION_THRESHOLD:
self.is_pinching = True
self.callback_for_gesture(self.mouse_position)
self.frame_distances = []
return True
self.is_pinching = True
return False
self.is_pinching = False
return False
def draw_landmarks(self, frame, result):
"""Draw hand landmarks on the frame and overlay the Z-coordinates of the index finger."""
if not result.hand_landmarks:
return frame
height, width, _ = frame.shape
for hand in result.hand_landmarks:
for i, landmark in enumerate(hand):
x, y = int(landmark.x * width), int(landmark.y * height)
# Draw landmark points
cv2.circle(frame, (x, y), 5, (0, 255, 0), -1) # Green dot
# Display Z-coordinates only for index finger joints
if i in [5, 6, 7, 8]: # Index finger landmarks
z_text = f"Z: {landmark.z:.4f}"
cv2.putText(frame, z_text, (x + 10, y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 0, 0), 1, cv2.LINE_AA)
# half_point = end_point[0] - int((end_point[0] - start_point[0]) / 2), end_point[1] - int((end_point[1] - start_point[1]) / 2)
start_point = (int(hand[4].x * width), int(hand[4].y * height))
end_point = (int(hand[8].x * width), int(hand[8].y * height))
distance = math.sqrt((end_point[0] - start_point[0]) ** 2 + (end_point[1] - start_point[1]) ** 2)
length = "Distance between index and thumb " + str(distance)
self.frame_distances.append(distance)
if len(self.frame_distances) > self.N:
self.frame_distances.pop(0) # Keep only last N frames
cv2.line(frame, start_point, end_point, (0, 255, 255), 2)
cv2.putText(frame, length, (0, 50),
cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 1, cv2.LINE_AA)
return frame
def print_result(self, result: HandLandmarkerResult, output_image, timestamp_ms: int):
"""Callback function to process results."""
self.latest_result = result # Store latest result for visualization
if __name__ == "__main__":
hand_tracking_manager = HandTrackingManager()
webcam = cv2.VideoCapture(0)
while webcam.isOpened():
frame_timestamp_ms = int(time.time() * 1000)
ret, frame = webcam.read()
hand_tracking_manager.analyze_frame(frame, frame_timestamp_ms)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
webcam.release()
cv2.destroyAllWindows()