Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ JS_SRC = web/src/eyegestures.js
MIN = $(JS_SRC:.js=.min.js)

format:
$(FORMAT_TOOLS) python -m isort eyeGestures
$(FORMAT_TOOLS) python -m black eyeGestures
$(FORMAT_TOOLS) python -m isort eye_gestures eyeGestures
$(FORMAT_TOOLS) python -m black eye_gestures eyeGestures

format_check:
$(CHECK_TOOLS) python -m isort eyeGestures --check
$(CHECK_TOOLS) python -m black eyeGestures --check
$(CHECK_TOOLS) python -m isort eye_gestures eyeGestures --check
$(CHECK_TOOLS) python -m black eye_gestures eyeGestures --check

check: format_check
$(CHECK_TOOLS) python -m pylint eyeGestures
$(CHECK_TOOLS) python -m flake8 eyeGestures
$(CHECK_TOOLS) python -m mypy eyeGestures
$(CHECK_TOOLS) python -m pylint eye_gestures eyeGestures
$(CHECK_TOOLS) python -m flake8 eye_gestures eyeGestures
$(CHECK_TOOLS) python -m mypy eye_gestures eyeGestures

# Web build
min:
Expand Down Expand Up @@ -61,4 +61,4 @@ test:
${PYTHON} -m unittest discover -s tests -p 'test_*.py'; \
else \
echo "No test files found in tests/; skipping test execution."; \
fi
fi
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ gestures.start();

##### Python
```python
from eyeGestures.utils import VideoCapture
from eyeGestures import EyeGestures_v4
from eye_gestures.utils import VideoCapture
from eye_gestures import EyeGestures_v4

# Initialize gesture engine and video capture
gestures = EyeGestures_v3()
Expand Down
4 changes: 2 additions & 2 deletions examples/legacy/PupilLabDry.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from eyeGestures.utils import VideoCapture
from eyeGestures.eyegestures import EyeGestures
from eye_gestures.utils import VideoCapture
from eye_gestures.eyegestures import EyeGestures
from screeninfo import get_monitors

if __name__ == '__main__':
Expand Down
4 changes: 2 additions & 2 deletions examples/legacy/display_calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
dir_path = os.path.dirname(os.path.realpath(__file__))
sys.path.append(f'{dir_path}/..')

from eyeGestures.utils import VideoCapture
from eyeGestures.eyegestures import EyeGestures_v2
from eye_gestures.utils import VideoCapture
from eye_gestures.eyegestures import EyeGestures_v2

gestures = EyeGestures_v2()
gestures.uploadCalibrationMap([[0,0],[0,1],[1,0],[1,1]])
Expand Down
4 changes: 2 additions & 2 deletions examples/legacy/simple_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
dir_path = os.path.dirname(os.path.realpath(__file__))
sys.path.append(f'{dir_path}/..')

from eyeGestures.utils import VideoCapture
from eyeGestures.eyegestures import EyeGestures_v1
from eye_gestures.utils import VideoCapture
from eye_gestures.eyegestures import EyeGestures_v1

gestures = EyeGestures_v1(285,115,80,15)

Expand Down
4 changes: 2 additions & 2 deletions examples/simple_example_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
dir_path = os.path.dirname(os.path.realpath(__file__))
sys.path.append(f'{dir_path}/..')

from eyeGestures.utils import VideoCapture
from eyeGestures import EyeGestures_v2
from eye_gestures.utils import VideoCapture
from eye_gestures import EyeGestures_v2

gestures = EyeGestures_v2()
cap = VideoCapture(0)
Expand Down
4 changes: 2 additions & 2 deletions examples/simple_example_v2_fast.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
dir_path = os.path.dirname(os.path.realpath(__file__))
sys.path.append(f'{dir_path}/..')

from eyeGestures.utils import VideoCapture
from eyeGestures import EyeGestures_v2
from eye_gestures.utils import VideoCapture
from eye_gestures import EyeGestures_v2

gestures = EyeGestures_v2()
cap = VideoCapture(0)
Expand Down
4 changes: 2 additions & 2 deletions examples/simple_example_v3.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
dir_path = os.path.dirname(os.path.realpath(__file__))
sys.path.append(f'{dir_path}/..')

from eyeGestures.utils import VideoCapture
from eyeGestures import EyeGestures_v3
from eye_gestures.utils import VideoCapture
from eye_gestures import EyeGestures_v3

gestures = EyeGestures_v3()
cap = VideoCapture(0)
Expand Down
4 changes: 2 additions & 2 deletions examples/simple_example_v4.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
dir_path = os.path.dirname(os.path.realpath(__file__))
sys.path.append(f'{dir_path}/..')

from eyeGestures.utils import VideoCapture
from eyeGestures import EyeGestures_v4
from eye_gestures.utils import VideoCapture
from eye_gestures import EyeGestures_v4

gestures = EyeGestures_v4()
cap = VideoCapture(0)
Expand Down
2 changes: 1 addition & 1 deletion examples/troubleshooting_camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("EyeGestures v3 example")

from eyeGestures.utils import VideoCapture
from eye_gestures.utils import VideoCapture

cap = VideoCapture(0)

Expand Down
170 changes: 34 additions & 136 deletions eyeGestures/__init__.py
Original file line number Diff line number Diff line change
@@ -1,138 +1,36 @@
from typing import Any, Dict, List, Optional, Tuple

import cv2
import numpy as np
import numpy.typing as npt

from eyeGestures.face import Face, FaceFinder
from eyeGestures.Fixation import Fixation
from eyeGestures.utils import recoverable

try:
from .EyegesturesEngine import EyeGesturesEnginePython as RustEyeGesturesEngine # type: ignore[import-untyped]
except ImportError:
"""Deprecated alias for :mod:`eye_gestures`.

The module was renamed from ``eyeGestures`` to ``eye_gestures``. Importing it
under the old name still works but emits a :class:`DeprecationWarning`.
"""

import importlib
import pkgutil
import sys
import warnings

warnings.warn(
"the 'eyeGestures' package has been renamed to 'eye_gestures'; "
"importing 'eyeGestures' is deprecated and will be removed in the future. "
"Please update your imports to use `eye_gestures`.",
DeprecationWarning,
stacklevel=2,
)

import eye_gestures # noqa: E402 # pylint: disable=wrong-import-position

# Alias `eyeGestures` to `eye_gestures`
sys.modules[__name__] = eye_gestures

# Recursively alias `eyeGestures.*` to `eye_gestures.*` (incl. subsubmodules)
for _submodule in pkgutil.walk_packages(
eye_gestures.__path__,
prefix="eye_gestures.",
onerror=lambda _: None, # otherwise swallows ImportError throws
):
try:
from EyegesturesEngine import (
EyeGesturesEnginePython as RustEyeGesturesEngine, # type: ignore[import-not-found, import-untyped]
)
_module = importlib.import_module(_submodule.name)
except ImportError:
RustEyeGesturesEngine = None

VERSION = "4.0.0"


class EyeGestures_v4:
"""Main class for EyeGesture tracker. It configures and manages entire algorithm"""

def __init__(self, calibration_radius: int = 1000) -> None:
self.calibration_radius = calibration_radius

self.average_points: npt.NDArray[np.float64] = np.zeros((1, 2))
self.calibration: bool = False
self.filled_points: int = 0

self.finder = FaceFinder()
self.face = Face()
self.engine: Optional[Any] = None

self.fix: Optional[float] = None
self.fixationTracker: Dict[str, Fixation] = dict()
self.key_points_buffer: Dict[str, List[npt.NDArray[np.float64]]] = dict()

self.starting_head_position = np.zeros((1, 2))
self.starting_size = np.zeros((1, 2))

def getLandmarks(self, frame: cv2.typing.MatLike) -> Tuple[npt.NDArray[np.float64], bool, cv2.typing.MatLike]:

frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = cv2.flip(frame, 1)

# try:
self.face.process(frame, self.finder.find(frame))
face_landmarks = self.face.get_landmarks()
assert face_landmarks is not None
l_eye = self.face.get_left_eye()
r_eye = self.face.get_right_eye()
l_eye_landmarks = l_eye.getLandmarks()
r_eye_landmarks = r_eye.getLandmarks()
blink = l_eye.getBlink() and r_eye.getBlink()

# get x,y offset
x_offset = np.min(face_landmarks[:, 0])
y_offset = np.min(face_landmarks[:, 1])
x_width = np.max(face_landmarks[:, 0]) - x_offset
y_width = np.max(face_landmarks[:, 1]) - y_offset

# get head position
head_offset = np.zeros((1, 2))
scale_x = 1
scale_y = 1
if np.array_equal(self.starting_head_position, np.zeros((1, 2))):
self.starting_head_position = np.array([[x_offset, y_offset]])
self.starting_size = np.array([[x_width, y_width]])
else:
head_offset = np.array([[x_offset, y_offset]]) - self.starting_head_position
scale_x = self.starting_size[0, 0] / x_width
scale_y = self.starting_size[0, 1] / y_width

# eye_events = np.array([event.blink,event.fixation]).reshape(1, 2)
assert l_eye_landmarks is not None
assert r_eye_landmarks is not None
key_points: npt.NDArray[np.float64] = np.concatenate(
(
l_eye_landmarks,
r_eye_landmarks,
np.array([[scale_x, scale_y]]),
head_offset,
)
)
key_points[:, 0] = key_points[:, 0] - head_offset[:, 0]
key_points[:, 1] = key_points[:, 1] - head_offset[:, 1]

key_points[:, 0] = key_points[:, 0] * scale_x
key_points[:, 1] = key_points[:, 1] * scale_y

key_points[-1, 0] = head_offset[0, 0]
key_points[-1, 1] = head_offset[0, 1]
# print(self.starting_size,x_width,y_width)
subframe = frame[
int(y_offset) : int(y_offset + y_width),
int(x_offset) : int(x_offset + x_width),
]
return key_points, blink, subframe

def whichAlgorithm(self) -> str:
return "rust engine"

def reset(self) -> None:
self.filled_points = 0

def setFixation(self, fix: float) -> None:
self.fix = fix

@recoverable(ret_error_params=(None, None, None))
def step(
self,
frame: cv2.typing.MatLike,
width: int,
height: int,
):

if self.engine is None:
self.engine = RustEyeGesturesEngine(width, height)

find_result = self.finder.find(frame)
if find_result is None or not find_result.face_landmarks:
raise ValueError("No face landmarks detected")

landmarks = find_result.face_landmarks[0]
scaled_landmarks = np.array([(landmark.x, landmark.y) for landmark in landmarks], dtype=np.float64)
assert self.engine is not None
result = self.engine.process(scaled_landmarks.flatten().tolist())

x, y = result[0], result[1]
is_calibrating = result[2] == 1.0
calib_x, calib_y = result[3], result[4]
calib_x = min(max(calib_x, 0), width)
calib_y = min(max(calib_y, 0), height)
return [x, y], is_calibrating, [calib_x, calib_y]
continue
_alias = __name__ + _submodule.name.removeprefix("eye_gestures")
sys.modules[_alias] = _module
File renamed without changes.
Loading