Spaces:
Sleeping
Sleeping
File size: 2,212 Bytes
2eba0cc | 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 | # Fuses calibrated gaze position with eye openness (EAR) for focus detection.
# Takes L2CS gaze angles + MediaPipe landmarks, outputs screen coords + focus decision.
import math
import numpy as np
from .gaze_calibration import GazeCalibration
from .eye_scorer import compute_avg_ear
_EAR_BLINK = 0.18
_ON_SCREEN_MARGIN = 0.08
class GazeEyeFusion:
def __init__(self, calibration, ear_weight=0.3, gaze_weight=0.7, focus_threshold=0.52):
if not calibration.is_fitted:
raise ValueError("Calibration must be fitted first")
self._cal = calibration
self._ear_w = ear_weight
self._gaze_w = gaze_weight
self._threshold = focus_threshold
self._smooth_x = 0.5
self._smooth_y = 0.5
self._alpha = 0.5
def update(self, yaw_rad, pitch_rad, landmarks):
gx, gy = self._cal.predict(yaw_rad, pitch_rad)
# EMA smooth the gaze position
self._smooth_x += self._alpha * (gx - self._smooth_x)
self._smooth_y += self._alpha * (gy - self._smooth_y)
gx, gy = self._smooth_x, self._smooth_y
on_screen = (
-_ON_SCREEN_MARGIN <= gx <= 1.0 + _ON_SCREEN_MARGIN and
-_ON_SCREEN_MARGIN <= gy <= 1.0 + _ON_SCREEN_MARGIN
)
ear = None
ear_score = 1.0
if landmarks is not None:
ear = compute_avg_ear(landmarks)
ear_score = 0.0 if ear < _EAR_BLINK else min(ear / 0.30, 1.0)
# penalise gaze near screen edges
gaze_score = 1.0 if on_screen else 0.0
if on_screen:
dx = max(0.0, abs(gx - 0.5) - 0.3)
dy = max(0.0, abs(gy - 0.5) - 0.3)
gaze_score = max(0.0, 1.0 - math.sqrt(dx**2 + dy**2) * 5.0)
score = float(np.clip(self._gaze_w * gaze_score + self._ear_w * ear_score, 0, 1))
return {
"gaze_x": round(float(gx), 4),
"gaze_y": round(float(gy), 4),
"on_screen": on_screen,
"ear": round(ear, 4) if ear is not None else None,
"focus_score": round(score, 4),
"focused": score >= self._threshold,
}
def reset(self):
self._smooth_x = 0.5
self._smooth_y = 0.5
|