Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[flake8]
max-line-length = 120
extend-ignore = E203, E266, E712, W503
5 changes: 3 additions & 2 deletions .github/workflows/code_quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ jobs:
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
run: pip install uv
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements_dev.txt
uv sync
- name: Analysing code with checkers
run: |
make check
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ dist/*
*.csv
data/*
.mypy_cache
.venvs
.venvs
*.task
/engine/target
64 changes: 50 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,24 +1,60 @@
.PHONY: format format_check test
.PHONY: format format_check test min wasm python python-help

# Detect OS and set PYTHON command accordingly
ifeq ($(OS),Windows_NT)
PYTHON := $(shell where python3)
else
PYTHON := $(shell which python3)
endif
# Python build
FORMAT_TOOLS = uv run --with isort --with black
CHECK_TOOLS = uv run --with pylint --with flake8 --with mypy --with isort --with black

MINIFY = npx terser
MINIFY_FLAGS = --compress --mangle
JS_SRC = web/src/eyegestures.js
MIN = $(JS_SRC:.js=.min.js)

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

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

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

# Web build
min:
$(MINIFY) $(JS_SRC) $(MINIFY_FLAGS) -o engine/pkg/eyegestures.min.js

wasm:
cd ./engine && wasm-pack build --target web
$(MINIFY) $(JS_SRC) $(MINIFY_FLAGS) -o engine/pkg/eyegestures.min.js

# Python build
PYTHON = uv run
MATURIN_PYTHON_FLAGS = --release --no-default-features --features python

# Set to 1 to allow builds for newer CPython versions than PyO3 officially supports.
PYO3_USE_ABI3_FORWARD_COMPATIBILITY ?= 1
PYTHON_INTERPRETERS=python3.12 python3.13

ifeq ($(OS),Windows_NT)
PYO3_FORWARD_ENV = set PYO3_USE_ABI3_FORWARD_COMPATIBILITY=$(PYO3_USE_ABI3_FORWARD_COMPATIBILITY) &&
else
PYO3_FORWARD_ENV = PYO3_USE_ABI3_FORWARD_COMPATIBILITY=$(PYO3_USE_ABI3_FORWARD_COMPATIBILITY)
endif

ifeq ($(OS),Windows_NT)
python:
$(PYO3_FORWARD_ENV) maturin build $(MATURIN_PYTHON_FLAGS) --target i686-pc-windows-msvc --interpreter $(PYTHON_INTERPRETERS)
$(PYO3_FORWARD_ENV) maturin build $(MATURIN_PYTHON_FLAGS) --target x86_64-pc-windows-msvc --interpreter $(PYTHON_INTERPRETERS)
else
python:
$(PYO3_FORWARD_ENV) maturin build $(MATURIN_PYTHON_FLAGS) --interpreter $(PYTHON_INTERPRETERS)
endif

clean:
rm -f src/eyegestures.min.js

test:
${PYTHON} -m unittest tests/test_*
124 changes: 9 additions & 115 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,15 @@ Our [Mission](https://github.com/NativeSensors/EyeGestures/blob/main/MISSION.md)
> [!Warning]
> **Before contributing read the `contributing.md` guide!**

> [!NOTE]
> Check JS port: [EyeGesturesLite](https://github.com/NativeSensors/EyeGesturesLite)!

### 💜 Sponsors:


### 💜 Sponsors:
> [!IMPORTANT]
> EyeGestures is a fully volunteer-based project and exists thanks to your donations and support.
>
> <a href="https://buy.polar.sh/polar_cl_H2FqQHunwgXtU7i9mP7VJ95OZ10XVO3vm6MpSimoPPg">Donation</a>
>
> 📢📢 **If you are bussiness and would like to collaborate, reach us: contact@eyegestures.com** 📢📢
>
> We can provide custom comercial license and invoices if needed.
>
> <!-- POLAR type=ads id=203bbe subscription_benefit_id=bb272b6d-f698-44e3-a417-36a6fa203bbe width=240 height=100 -->
>
> <!-- POLAR-END id=eizdelw -->
> We are happy to be involved in your project!
>
---------
### 🔨 Projects build with EyeGestures:
Expand All @@ -52,23 +43,11 @@ Our [Mission](https://github.com/NativeSensors/EyeGestures/blob/main/MISSION.md)
<img src="https://github.com/user-attachments/assets/33e7782b-7977-4f88-ad54-c43d44c6dced" width="300" height="150">
</p>

- [EyePilot](https://polar.sh/NativeSensors/products/5fce104c-46ec-4203-892b-a26e0e0ead18)
- [EyePilot](https://polar.sh/NativeSensors/products/5fce104c-46ec-4203-892b-a26e0e0ead18) - waiting to be restored
- [EyePather](https://polar.sh/NativeSensors/posts/eyepather-new-tool-in-eyegestures-ecosystem) - discontinued
- [EyeFocus](https://polar.sh/NativeSensors/products/3756d557-134e-4e7e-ac50-d850126aa325?ref=producthunt) <- 4th best product on PH!
- [EyeFocus](https://polar.sh/NativeSensors/products/3756d557-134e-4e7e-ac50-d850126aa325?ref=producthunt)
- Add your project! contact@eyegestures.com or PR

Subscribe and get access to our software:
<p align="left">
<a href="https://polar.sh/NativeSensors"><picture><source media="(prefers-color-scheme: dark)" srcset="https://polar.sh/embed/subscribe.svg?org=NativeSensors&label=Subscribe&darkmode"><img alt="Subscribe on Polar" src="https://polar.sh/embed/subscribe.svg?org=NativeSensors&label=Subscribe"></picture></a>

--------
<p align="center">
<img src="https://github.com/user-attachments/assets/0a5c5363-f389-4b99-a75a-8ebe9a8bd7f5" width="500" height="250">
</p>

### ⚓ Ports:
- JavaScript version: [EyeGesturesLite](https://github.com/NativeSensors/EyeGesturesLite)!

### 💻 Install
```
python3 -m pip install eyeGestures
Expand All @@ -82,20 +61,16 @@ python3 -m pip install eyeGestures
Tracker works best when your camera or laptop is at arm's length, similar to how you would typically use it. If you are further away, it may be less responsive for now - currently working on solving this issue.

```
python3 examples/simple_example_v2.py
```

```
python3 examples/simple_example.py [legacy tracker, will become obsolete]
python3 examples/simple_example_v4.py
```

### 🔧 Build your own:

#### Using EyeGesture Engine V3 - Faster smaller better:
#### Engine V4 - Rust based enginee:

```python
from eyeGestures.utils import VideoCapture
from eyeGestures import EyeGestures_v3
from eyeGestures import EyeGestures_v4

# Initialize gesture engine and video capture
gestures = EyeGestures_v3()
Expand Down Expand Up @@ -123,90 +98,9 @@ while True:
<!-- POLAR type=ads id=eizdelwu subscription_benefit_id=bb272b6d-f698-44e3-a417-36a6fa203bbe width=240 height=100 -->
<!-- POLAR-END id=eizdelwu -->

#### Using EyeGesture Engine V2 - [Legacy]:

```python
from eyeGestures.utils import VideoCapture
from eyeGestures import EyeGestures_v2

# Initialize gesture engine and video capture
gestures = EyeGestures_v2()
cap = VideoCapture(0)
calibrate = True
screen_width = 500
screen_height= 500

# Process each frame
while True:
ret, frame = cap.read()
event, cevent = gestures.step(frame,
calibrate,
screen_width,
screen_height,
context="my_context")

if event:
cursor_x, cursor_y = event.point[0], event.point[1]
fixation = event.fixation
# calibration_radius: radius for data collection during calibration
```

<!-- POLAR type=ads id=eizdelwu subscription_benefit_id=bb272b6d-f698-44e3-a417-36a6fa203bbe width=240 height=100 -->
<!-- POLAR-END id=eizdelwu -->

#### Customize [only V1/V2]:

You can customize your calibration points/map to fit your solutions. Simple copy snippet below, and place your calibration poitns on x,y planes from 0.0 to 1.0. It will be then automatically scaled to your display.

```python
gestures = EyeGestures_v2()
gestures.uploadCalibrationMap([[0,0],[0,1],[1,0],[1,1]])
```

V2 is two stage tracker. It runs V1 under the hood but then uses it as feature extractor for V2 machine learning component, and combines both outputs to generate new gaze point. It is possible to control how much V1 affects V2 by:

```python
gestures.setClassicImpact(N) # setting N = 2 is working best for my testing
```
This makes that sample obtained from V2 is averaged with N times sample from V1 (same sample copied that many times). In outcome having V2 impacting output in `1/N+1` and V1 `N/N+1`.

It is also worth to know that you can enable hidden calibration for V1 (same calibration when using only V1, but now it is invisible to user):
```python
gestures.enableCNCalib()
```

#### Using EyeGesture Engine V1 - Model-Based Approach [not recommended]:

```python
from eyeGestures.utils import VideoCapture
from eyeGestures import EyeGestures_v1

# Initialize gesture engine with RoI parameters
gestures = EyeGestures_v1()

cap = VideoCapture(0)
ret, frame = cap.read()
calibrate = True
screen_width = 500
screen_height= 500

# Obtain estimations from camera frames
event, cevent = gestures.estimate(
frame,
"main",
calibrate, # set calibration - switch to False to stop calibration
screen_width,
screen_height,
0, 0, 0.8, 10
)

if event:
cursor_x, cursor_y = event.point[0], event.point[1]
fixation = event.fixation
# calibration_radius: radius for data collection during calibration
```
#### Legacy Enginees:

Feel free to copy and paste the relevant code snippets for your project.
With release of V4 rust based engine shared betweeen all eyegestures versions (web and desktop), fully pythonic enginees become deprecated. This decision was driven by trying to achieve single algorithm powering eyegestures application. Keeping multiple different enginees was beyond ability to maintain it. Source code can still be found in `src/Legacy`.

### 🔥 Web Demos:

Expand Down
Loading
Loading