File size: 6,700 Bytes
b8b61aa |
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 |
import sys
import os
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
import cv2
import time
import numpy as np
from scripts.inference import GazePredictor
from utils.ear_utils import BlinkDetector
from pygame import mixer
def smooth_values(history, current_value, window_size=5):
if current_value is not None:
history.append(current_value)
if len(history) > window_size:
history.pop(0)
return np.mean(history, axis=0) if isinstance(current_value, np.ndarray) and history else current_value if current_value is not None else 0
def track_gaze(model_path):
gaze_predictor = GazePredictor(model_path)
blink_detector = BlinkDetector()
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Error: Could not open webcam.")
return
GAZE_STABILITY_THRESHOLD = 0.5
TIME_THRESHOLD = 15
BLINK_RATE_THRESHOLD = 1
EYE_CLOSURE_THRESHOLD = 10
HEAD_STABILITY_THRESHOLD = 0.05
gaze_history = []
head_history = []
ear_history = []
stable_gaze_time = 0
stable_head_time = 0
eye_closed_time = 0
blink_count = 0
start_time = time.time()
is_unconscious = False
# Initialize pygame mixer
mixer.init()
ALARM_PATH = os.path.normpath(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "sounds", "alarm.wav")))
if not os.path.exists(ALARM_PATH):
print(f"Warning: Alarm sound file not found at {ALARM_PATH}. No sound will play.")
while True:
ret, frame = cap.read()
if not ret:
print("Failed to capture frame")
break
head_pose_gaze, gaze_h, gaze_v = gaze_predictor.predict_gaze(frame)
current_gaze = np.array([gaze_h, gaze_v])
smoothed_gaze = smooth_values(gaze_history, current_gaze)
ear, left_eye, right_eye, head_pose, left_iris, right_iris = blink_detector.detect_blinks(frame)
if ear is None:
cv2.putText(frame, "No face detected", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
print("No face detected")
smoothed_head = smooth_values(head_history, None)
smoothed_ear = smooth_values(ear_history, None)
else:
print(f"EAR: {ear:.2f}, Head Pose: {head_pose:.2f}, Gaze: [{smoothed_gaze[0]:.2f}, {smoothed_gaze[1]:.2f}]")
smoothed_head = smooth_values(head_history, head_pose)
smoothed_ear = smooth_values(ear_history, ear)
if smoothed_ear >= blink_detector.EAR_THRESHOLD:
cv2.drawMarker(frame, left_iris, (0, 255, 0), markerType=cv2.MARKER_CROSS, markerSize=10, thickness=2)
cv2.drawMarker(frame, right_iris, (0, 255, 0), markerType=cv2.MARKER_CROSS, markerSize=10, thickness=2)
cv2.putText(frame, f"Gaze H: {smoothed_gaze[0]:.2f}", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.putText(frame, f"Gaze V: {smoothed_gaze[1]:.2f}", (10, 90), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.putText(frame, f"Head Pose: {smoothed_head:.2f}", (10, 120), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.putText(frame, f"EAR: {smoothed_ear:.2f}", (10, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
if len(gaze_history) > 1:
gaze_diff = np.sqrt(np.sum((smoothed_gaze - gaze_history[-2])**2))
print(f"Gaze Diff: {gaze_diff:.2f}")
if gaze_diff < GAZE_STABILITY_THRESHOLD:
if stable_gaze_time == 0:
stable_gaze_time = time.time()
elif time.time() - stable_gaze_time > TIME_THRESHOLD:
cv2.putText(frame, "Gaze Fixed", (10, 180), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
else:
stable_gaze_time = 0
if len(head_history) > 1 and head_pose is not None:
head_diff = abs(smoothed_head - head_history[-2])
print(f"Head Diff: {head_diff:.2f}")
if head_diff < HEAD_STABILITY_THRESHOLD:
if stable_head_time == 0:
stable_head_time = time.time()
else:
stable_head_time = 0
if ear is not None and smoothed_ear < blink_detector.EAR_THRESHOLD:
if eye_closed_time == 0:
eye_closed_time = time.time()
elif time.time() - eye_closed_time > EYE_CLOSURE_THRESHOLD:
cv2.putText(frame, "Eyes Closed", (10, 210), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
else:
if eye_closed_time > 0 and time.time() - eye_closed_time < 0.5:
blink_count += 1
eye_closed_time = 0
elapsed_minutes = (time.time() - start_time) / 60
blink_rate = blink_count / elapsed_minutes if elapsed_minutes > 0 else 0
cv2.putText(frame, f"Blink Rate: {blink_rate:.1f}/min", (10, 240), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
unconscious_conditions = [
stable_gaze_time > 0 and time.time() - stable_gaze_time > TIME_THRESHOLD,
blink_rate < BLINK_RATE_THRESHOLD and elapsed_minutes > 1,
eye_closed_time > 0 and time.time() - eye_closed_time > EYE_CLOSURE_THRESHOLD,
stable_head_time > 0 and time.time() - stable_head_time > TIME_THRESHOLD
]
print(f"Conditions: {unconscious_conditions}")
if sum(unconscious_conditions) >= 2:
if not is_unconscious and os.path.exists(ALARM_PATH):
print(f"Attempting to play alarm at {ALARM_PATH}")
try:
mixer.music.load(ALARM_PATH)
mixer.music.play()
except Exception as e:
print(f"Error playing alarm sound: {e}")
print("Unconscious detected!")
cv2.putText(frame, "Unconscious Detected", (10, 270), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
is_unconscious = True
else:
is_unconscious = False
cv2.imshow("Gaze Tracking", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
mixer.quit()
if __name__ == "__main__":
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
MODEL_PATH = os.path.join(SCRIPT_DIR, "..", "models", "gaze_estimation_model.pth")
if not os.path.exists(MODEL_PATH):
print(f"Error: Missing model file at {MODEL_PATH}")
sys.exit(1)
track_gaze(MODEL_PATH) |