import numpy as np from merlin.features.gaze import GazeEstimator, GazeDirection _LEFT_IRIS = 468 _RIGHT_IRIS = 473 _LEFT_INNER = 133 _RIGHT_OUTER = 263 def _face(n: int = 478) -> np.ndarray: return np.zeros((n, 3), dtype=np.float32) def _set_gaze_center(face: np.ndarray) -> None: """Iris centers at midpoint of eye span → center gaze.""" face[_LEFT_INNER] = [0.3, 0.5, 0.0] face[_RIGHT_OUTER] = [0.7, 0.5, 0.0] mid = (face[_LEFT_INNER] + face[_RIGHT_OUTER]) / 2.0 face[_LEFT_IRIS] = mid.copy() face[_RIGHT_IRIS] = mid.copy() def _set_gaze_left(face: np.ndarray) -> None: """Iris centers shifted left relative to eye span.""" face[_LEFT_INNER] = [0.3, 0.5, 0.0] face[_RIGHT_OUTER] = [0.7, 0.5, 0.0] face[_LEFT_IRIS] = [0.35, 0.5, 0.0] face[_RIGHT_IRIS] = [0.35, 0.5, 0.0] def test_center_gaze_label(): face = _face() _set_gaze_center(face) g = GazeEstimator().estimate(face) assert g.label == "center" def test_left_gaze_label(): face = _face() _set_gaze_left(face) g = GazeEstimator().estimate(face) assert g.label == "left" def test_zero_eye_width_returns_center(): """Degenerate case: all landmarks at same point → center.""" face = _face() g = GazeEstimator().estimate(face) assert g.dx == 0.0 and g.dy == 0.0 def test_gazeresult_is_frozen(): g = GazeDirection(dx=0.1, dy=0.2) import pytest with pytest.raises((AttributeError, TypeError)): g.dx = 0.5