-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patheye_tracker.py
More file actions
161 lines (130 loc) · 6.22 KB
/
Copy patheye_tracker.py
File metadata and controls
161 lines (130 loc) · 6.22 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
152
153
154
155
156
157
158
159
160
161
import cv2
import mediapipe as mp
import numpy as np
import threading
import time
import logging
# Configure logging to suppress some MediaPipe warnings
logging.getLogger('mediapipe').setLevel(logging.ERROR)
class EyeTracker:
def __init__(self):
# Initialize eye tracking variables
self.mp_face_mesh = mp.solutions.face_mesh
self.face_mesh = self.mp_face_mesh.FaceMesh(
min_detection_confidence=0.5,
min_tracking_confidence=0.5,
refine_landmarks=True, # Enable for more accurate eye tracking
max_num_faces=1 # Track only one face
)
self.cap = None
self.eye_data = []
self.is_tracking = False
self.tracking_thread = None
self.window_name = "Eye Tracking"
def start_tracking(self):
"""Start tracking eye movements"""
if self.is_tracking:
print("Eye tracking is already running.")
return
self.is_tracking = True
self.tracking_thread = threading.Thread(target=self._track_eyes)
self.tracking_thread.daemon = True
self.tracking_thread.start()
print("Eye tracking started. Press 'ESC' in the window to stop.")
def stop_tracking(self):
"""Stop tracking eye movements"""
self.is_tracking = False
# Wait for the tracking thread to finish
if self.tracking_thread and self.tracking_thread.is_alive():
self.tracking_thread.join(timeout=1.0)
# Release resources
if self.cap is not None:
self.cap.release()
cv2.destroyAllWindows()
print("Eye tracking stopped.")
def _track_eyes(self):
"""Track eye movements using webcam"""
self.cap = cv2.VideoCapture(0)
while self.is_tracking and self.cap.isOpened():
success, image = self.cap.read()
if not success:
break
# Get image dimensions
h, w, _ = image.shape
# Convert the BGR image to RGB
image = cv2.cvtColor(cv2.flip(image, 1), cv2.COLOR_BGR2RGB)
image.flags.writeable = False
# Process the image and detect face landmarks
results = self.face_mesh.process(image)
# Draw face landmarks and track eye movement
image.flags.writeable = True
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
# Track eye focus
timestamp = time.time()
eye_focus = self._calculate_eye_focus(results, w, h)
# Add eye focus data to the list
if eye_focus is not None:
self.eye_data.append({
'timestamp': timestamp,
'eye_focus': eye_focus
})
# Display focus level on the image
cv2.putText(image, f"Focus: {eye_focus:.2f}", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
# Display the image with face mesh
cv2.imshow(self.window_name, image)
# Check if ESC key is pressed
if cv2.waitKey(5) & 0xFF == 27:
self.is_tracking = False
break
def _calculate_eye_focus(self, results, image_width, image_height):
"""Calculate eye focus level based on eye openness"""
if not results.multi_face_landmarks:
return None
face_landmarks = results.multi_face_landmarks[0]
# Use specific MediaPipe indices for measuring eye openness
# These are the key points that define how open/closed the eyes are
# Right eye - upper lid: 159, lower lid: 145
# Left eye - upper lid: 386, lower lid: 374
right_upper = face_landmarks.landmark[159]
right_lower = face_landmarks.landmark[145]
left_upper = face_landmarks.landmark[386]
left_lower = face_landmarks.landmark[374]
# Calculate vertical distance for each eye (in pixels)
right_eye_height = abs((right_upper.y - right_lower.y) * image_height)
left_eye_height = abs((left_upper.y - left_lower.y) * image_height)
# For horizontal reference (to normalize for distance from camera)
# Right eye corners: 33 (right), 133 (left)
# Left eye corners: 362 (right), 263 (left)
right_corner_right = face_landmarks.landmark[33]
right_corner_left = face_landmarks.landmark[133]
left_corner_right = face_landmarks.landmark[362]
left_corner_left = face_landmarks.landmark[263]
# Calculate horizontal eye width
right_eye_width = abs((right_corner_right.x - right_corner_left.x) * image_width)
left_eye_width = abs((left_corner_right.x - left_corner_left.x) * image_width)
# Calculate normalized eye openness (height/width ratio)
# This helps account for distance from camera and face size
right_eye_openness = min(right_eye_height / right_eye_width if right_eye_width > 0 else 0, 1.0)
left_eye_openness = min(left_eye_height / left_eye_width if left_eye_width > 0 else 0, 1.0)
# Average the openness of both eyes
avg_openness = (right_eye_openness + left_eye_openness) / 2
# Define focus level based on eye openness
# More open eyes generally indicate better focus
if avg_openness > 0.25:
focus_level = 1.0 # Very focused
elif avg_openness > 0.20:
focus_level = 0.8
elif avg_openness > 0.15:
focus_level = 0.6
elif avg_openness > 0.10:
focus_level = 0.4
else:
focus_level = 0.0 # Not focused (eyes mostly closed)
return focus_level
def get_eye_data(self):
"""Get all collected eye tracking data"""
return self.eye_data
def clear_data(self):
"""Clear all collected eye tracking data"""
self.eye_data = []