eye-annotation-tool 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- eye_annotation_tool-1.0.0/.github/workflows/publish.yml +33 -0
- eye_annotation_tool-1.0.0/.gitignore +170 -0
- eye_annotation_tool-1.0.0/LICENSE +21 -0
- eye_annotation_tool-1.0.0/PKG-INFO +117 -0
- eye_annotation_tool-1.0.0/README.md +90 -0
- eye_annotation_tool-1.0.0/ai/README.md +54 -0
- eye_annotation_tool-1.0.0/ai/__init__.py +6 -0
- eye_annotation_tool-1.0.0/ai/plugin_interface.py +28 -0
- eye_annotation_tool-1.0.0/ai/plugin_manager.py +119 -0
- eye_annotation_tool-1.0.0/ai/plugins/__init__.py +4 -0
- eye_annotation_tool-1.0.0/ai/plugins/eyelid_detectors/__init__.py +1 -0
- eye_annotation_tool-1.0.0/ai/plugins/eyelid_detectors/placeholder_eyelid_detector.py +42 -0
- eye_annotation_tool-1.0.0/ai/plugins/glint_detectors/__init__.py +0 -0
- eye_annotation_tool-1.0.0/ai/plugins/glint_detectors/threshold_glint_detector.py +80 -0
- eye_annotation_tool-1.0.0/ai/plugins/iris_detectors/__init__.py +1 -0
- eye_annotation_tool-1.0.0/ai/plugins/iris_detectors/placeholder_iris_detector.py +45 -0
- eye_annotation_tool-1.0.0/ai/plugins/pupil_detectors/__init__.py +1 -0
- eye_annotation_tool-1.0.0/ai/plugins/pupil_detectors/pupil_core_detector.py +50 -0
- eye_annotation_tool-1.0.0/ai/plugins/pupil_detectors/threshold_pupil_detector.py +92 -0
- eye_annotation_tool-1.0.0/annotation_app/__init__.py +9 -0
- eye_annotation_tool-1.0.0/annotation_app/_version.py +34 -0
- eye_annotation_tool-1.0.0/annotation_app/controllers/__init__.py +6 -0
- eye_annotation_tool-1.0.0/annotation_app/controllers/annotation_controller.py +88 -0
- eye_annotation_tool-1.0.0/annotation_app/controllers/navigation_controller.py +84 -0
- eye_annotation_tool-1.0.0/annotation_app/gui/__init__.py +19 -0
- eye_annotation_tool-1.0.0/annotation_app/gui/ai_assist_handler.py +157 -0
- eye_annotation_tool-1.0.0/annotation_app/gui/annotation_controls.py +136 -0
- eye_annotation_tool-1.0.0/annotation_app/gui/custom_widgets.py +279 -0
- eye_annotation_tool-1.0.0/annotation_app/gui/image_viewer.py +847 -0
- eye_annotation_tool-1.0.0/annotation_app/gui/main_window.py +323 -0
- eye_annotation_tool-1.0.0/annotation_app/gui/menu_handler.py +110 -0
- eye_annotation_tool-1.0.0/annotation_app/gui/shortcut_handler.py +53 -0
- eye_annotation_tool-1.0.0/annotation_app/main.py +26 -0
- eye_annotation_tool-1.0.0/annotation_app/resources/Funded_by_EU_Eyes4ICU.png +0 -0
- eye_annotation_tool-1.0.0/annotation_app/resources/app_icon.ico +0 -0
- eye_annotation_tool-1.0.0/annotation_app/resources/main_page.png +0 -0
- eye_annotation_tool-1.0.0/annotation_app/utils/__init__.py +14 -0
- eye_annotation_tool-1.0.0/annotation_app/utils/annotation_io.py +153 -0
- eye_annotation_tool-1.0.0/annotation_app/utils/image_processing.py +70 -0
- eye_annotation_tool-1.0.0/annotation_app/utils/settings_handler.py +59 -0
- eye_annotation_tool-1.0.0/pyproject.toml +160 -0
- eye_annotation_tool-1.0.0/run.py +6 -0
- eye_annotation_tool-1.0.0/uv.lock +790 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
deploy:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
with:
|
|
14
|
+
fetch-depth: 0
|
|
15
|
+
|
|
16
|
+
- name: Set up Python
|
|
17
|
+
uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: '3.11'
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install --upgrade pip
|
|
24
|
+
pip install build twine
|
|
25
|
+
|
|
26
|
+
- name: Build package
|
|
27
|
+
run: python -m build
|
|
28
|
+
|
|
29
|
+
- name: Publish to PyPI
|
|
30
|
+
env:
|
|
31
|
+
TWINE_USERNAME: __token__
|
|
32
|
+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
|
33
|
+
run: twine upload dist/*
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
.vscode
|
|
2
|
+
ai/settings.json
|
|
3
|
+
.DS_Store
|
|
4
|
+
annotation_app/_version.py
|
|
5
|
+
|
|
6
|
+
# Byte-compiled / optimized / DLL files
|
|
7
|
+
__pycache__/
|
|
8
|
+
*.py[cod]
|
|
9
|
+
*$py.class
|
|
10
|
+
|
|
11
|
+
# C extensions
|
|
12
|
+
*.so
|
|
13
|
+
|
|
14
|
+
# Distribution / packaging
|
|
15
|
+
.Python
|
|
16
|
+
build/
|
|
17
|
+
develop-eggs/
|
|
18
|
+
dist/
|
|
19
|
+
downloads/
|
|
20
|
+
eggs/
|
|
21
|
+
.eggs/
|
|
22
|
+
lib/
|
|
23
|
+
lib64/
|
|
24
|
+
parts/
|
|
25
|
+
sdist/
|
|
26
|
+
var/
|
|
27
|
+
wheels/
|
|
28
|
+
share/python-wheels/
|
|
29
|
+
*.egg-info/
|
|
30
|
+
.installed.cfg
|
|
31
|
+
*.egg
|
|
32
|
+
MANIFEST
|
|
33
|
+
|
|
34
|
+
# PyInstaller
|
|
35
|
+
# Usually these files are written by a python script from a template
|
|
36
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
37
|
+
*.manifest
|
|
38
|
+
*.spec
|
|
39
|
+
|
|
40
|
+
# Installer logs
|
|
41
|
+
pip-log.txt
|
|
42
|
+
pip-delete-this-directory.txt
|
|
43
|
+
|
|
44
|
+
# Unit test / coverage reports
|
|
45
|
+
htmlcov/
|
|
46
|
+
.tox/
|
|
47
|
+
.nox/
|
|
48
|
+
.coverage
|
|
49
|
+
.coverage.*
|
|
50
|
+
.cache
|
|
51
|
+
nosetests.xml
|
|
52
|
+
coverage.xml
|
|
53
|
+
*.cover
|
|
54
|
+
*.py,cover
|
|
55
|
+
.hypothesis/
|
|
56
|
+
.pytest_cache/
|
|
57
|
+
cover/
|
|
58
|
+
|
|
59
|
+
# Translations
|
|
60
|
+
*.mo
|
|
61
|
+
*.pot
|
|
62
|
+
|
|
63
|
+
# Django stuff:
|
|
64
|
+
*.log
|
|
65
|
+
local_settings.py
|
|
66
|
+
db.sqlite3
|
|
67
|
+
db.sqlite3-journal
|
|
68
|
+
|
|
69
|
+
# Flask stuff:
|
|
70
|
+
instance/
|
|
71
|
+
.webassets-cache
|
|
72
|
+
|
|
73
|
+
# Scrapy stuff:
|
|
74
|
+
.scrapy
|
|
75
|
+
|
|
76
|
+
# Sphinx documentation
|
|
77
|
+
docs/_build/
|
|
78
|
+
|
|
79
|
+
# PyBuilder
|
|
80
|
+
.pybuilder/
|
|
81
|
+
target/
|
|
82
|
+
|
|
83
|
+
# Jupyter Notebook
|
|
84
|
+
.ipynb_checkpoints
|
|
85
|
+
|
|
86
|
+
# IPython
|
|
87
|
+
profile_default/
|
|
88
|
+
ipython_config.py
|
|
89
|
+
|
|
90
|
+
# pyenv
|
|
91
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
92
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
93
|
+
# .python-version
|
|
94
|
+
|
|
95
|
+
# uv
|
|
96
|
+
.python-version
|
|
97
|
+
|
|
98
|
+
# pipenv
|
|
99
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
100
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
101
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
102
|
+
# install all needed dependencies.
|
|
103
|
+
#Pipfile.lock
|
|
104
|
+
|
|
105
|
+
# poetry
|
|
106
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
107
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
108
|
+
# commonly ignored for libraries.
|
|
109
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
110
|
+
#poetry.lock
|
|
111
|
+
|
|
112
|
+
# pdm
|
|
113
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
114
|
+
#pdm.lock
|
|
115
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
116
|
+
# in version control.
|
|
117
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
118
|
+
.pdm.toml
|
|
119
|
+
.pdm-python
|
|
120
|
+
.pdm-build/
|
|
121
|
+
|
|
122
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
123
|
+
__pypackages__/
|
|
124
|
+
|
|
125
|
+
# Celery stuff
|
|
126
|
+
celerybeat-schedule
|
|
127
|
+
celerybeat.pid
|
|
128
|
+
|
|
129
|
+
# SageMath parsed files
|
|
130
|
+
*.sage.py
|
|
131
|
+
|
|
132
|
+
# Environments
|
|
133
|
+
.env
|
|
134
|
+
.venv
|
|
135
|
+
env/
|
|
136
|
+
venv/
|
|
137
|
+
ENV/
|
|
138
|
+
env.bak/
|
|
139
|
+
venv.bak/
|
|
140
|
+
|
|
141
|
+
# Spyder project settings
|
|
142
|
+
.spyderproject
|
|
143
|
+
.spyproject
|
|
144
|
+
|
|
145
|
+
# Rope project settings
|
|
146
|
+
.ropeproject
|
|
147
|
+
|
|
148
|
+
# mkdocs documentation
|
|
149
|
+
/site
|
|
150
|
+
|
|
151
|
+
# mypy
|
|
152
|
+
.mypy_cache/
|
|
153
|
+
.dmypy.json
|
|
154
|
+
dmypy.json
|
|
155
|
+
|
|
156
|
+
# Pyre type checker
|
|
157
|
+
.pyre/
|
|
158
|
+
|
|
159
|
+
# pytype static type analyzer
|
|
160
|
+
.pytype/
|
|
161
|
+
|
|
162
|
+
# Cython debug symbols
|
|
163
|
+
cython_debug/
|
|
164
|
+
|
|
165
|
+
# PyCharm
|
|
166
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
167
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
168
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
169
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
170
|
+
#.idea/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Mohammadhossein Salari
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: eye_annotation_tool
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A tool for annotating pupil and iris in eye images
|
|
5
|
+
Project-URL: Homepage, https://github.com/mh-salari/eye_annotation_tool
|
|
6
|
+
Project-URL: Repository, https://github.com/mh-salari/eye_annotation_tool
|
|
7
|
+
Project-URL: Issues, https://github.com/mh-salari/eye_annotation_tool/issues
|
|
8
|
+
Author-email: Mohammadhossein Salari <mohammadhossein.salari@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: annotation,computer vision,eye,eyelid,iris,pupil
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Requires-Dist: numpy>=1.20.0
|
|
22
|
+
Requires-Dist: opencv-python>=4.5.0
|
|
23
|
+
Requires-Dist: pupil-detectors>=2.0.0
|
|
24
|
+
Requires-Dist: pyqt5>=5.15.0
|
|
25
|
+
Requires-Dist: scipy>=1.7.0
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# EyE Annotation Tool
|
|
29
|
+
|
|
30
|
+
EyE Annotation Tool is a tool for annotating pupil, iris and eyelid in eye images. It provides a user-friendly interface for manual annotation and supports AI-assisted detection.
|
|
31
|
+
|
|
32
|
+
<p align="center">
|
|
33
|
+
<img src="annotation_app/resources/main_page.png" alt="EyE Annotation Tool Main Page" width="800">
|
|
34
|
+
</p>
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
- Load and navigate through multiple eye images
|
|
39
|
+
- Manual annotation of pupil, iris, eyelid, and glints
|
|
40
|
+
- AI-assisted detection of pupil, iris, eyelid, and glints
|
|
41
|
+
- Undo functionality for annotations
|
|
42
|
+
- Save and load annotations
|
|
43
|
+
- Extensible plugin system for custom detectors
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install eye_annotation_tool
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
For the latest development version:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install git+https://github.com/mh-salari/eye_annotation_tool.git
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Using uv
|
|
58
|
+
|
|
59
|
+
If you prefer [uv](https://docs.astral.sh/uv/):
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
git clone https://github.com/mh-salari/eye_annotation_tool.git
|
|
63
|
+
cd eye_annotation_tool
|
|
64
|
+
uv sync
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
> **Apple Silicon (M1/M2/M3) Note:** The `pupil-detectors` dependency only provides pre-built wheels for x86_64. On Apple Silicon Macs, you need to use an x86_64 Python via Rosetta 2:
|
|
68
|
+
>
|
|
69
|
+
> ```bash
|
|
70
|
+
> uv python install cpython-3.11-macos-x86_64
|
|
71
|
+
> uv python pin cpython-3.11-macos-x86_64
|
|
72
|
+
> uv sync
|
|
73
|
+
> ```
|
|
74
|
+
|
|
75
|
+
## Usage
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
eye_annotation_tool
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Or with uv:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
uv run eye_annotation_tool
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Or run it as a module:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
python -m eye_annotation_tool
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Adding Custom Plugins
|
|
94
|
+
|
|
95
|
+
EyE Annotation Tool supports custom plugins for pupil, iris and eyelid detection. To add a new plugin:
|
|
96
|
+
|
|
97
|
+
1. Create a new Python file in the appropriate plugin directory.
|
|
98
|
+
2. Define your detector class in this file.
|
|
99
|
+
3. Ensure your detector follows the required interface.
|
|
100
|
+
|
|
101
|
+
For a detailed guide on creating plugins, see the [Plugin Development Guide](ai/README.md) in the `ai` directory.
|
|
102
|
+
|
|
103
|
+
## Contributing
|
|
104
|
+
|
|
105
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
|
|
109
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
110
|
+
|
|
111
|
+
## Acknowledgments
|
|
112
|
+
|
|
113
|
+
This project has received funding from the European Union's Horizon Europe research and innovation funding program under grant agreement No 101072410, Eyes4ICU project.
|
|
114
|
+
|
|
115
|
+
<p align="center">
|
|
116
|
+
<img src="annotation_app/resources/Funded_by_EU_Eyes4ICU.png" alt="Funded by EU Eyes4ICU" width="500">
|
|
117
|
+
</p>
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# EyE Annotation Tool
|
|
2
|
+
|
|
3
|
+
EyE Annotation Tool is a tool for annotating pupil, iris and eyelid in eye images. It provides a user-friendly interface for manual annotation and supports AI-assisted detection.
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img src="annotation_app/resources/main_page.png" alt="EyE Annotation Tool Main Page" width="800">
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Load and navigate through multiple eye images
|
|
12
|
+
- Manual annotation of pupil, iris, eyelid, and glints
|
|
13
|
+
- AI-assisted detection of pupil, iris, eyelid, and glints
|
|
14
|
+
- Undo functionality for annotations
|
|
15
|
+
- Save and load annotations
|
|
16
|
+
- Extensible plugin system for custom detectors
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install eye_annotation_tool
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
For the latest development version:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install git+https://github.com/mh-salari/eye_annotation_tool.git
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Using uv
|
|
31
|
+
|
|
32
|
+
If you prefer [uv](https://docs.astral.sh/uv/):
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
git clone https://github.com/mh-salari/eye_annotation_tool.git
|
|
36
|
+
cd eye_annotation_tool
|
|
37
|
+
uv sync
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
> **Apple Silicon (M1/M2/M3) Note:** The `pupil-detectors` dependency only provides pre-built wheels for x86_64. On Apple Silicon Macs, you need to use an x86_64 Python via Rosetta 2:
|
|
41
|
+
>
|
|
42
|
+
> ```bash
|
|
43
|
+
> uv python install cpython-3.11-macos-x86_64
|
|
44
|
+
> uv python pin cpython-3.11-macos-x86_64
|
|
45
|
+
> uv sync
|
|
46
|
+
> ```
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
eye_annotation_tool
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Or with uv:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
uv run eye_annotation_tool
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Or run it as a module:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
python -m eye_annotation_tool
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Adding Custom Plugins
|
|
67
|
+
|
|
68
|
+
EyE Annotation Tool supports custom plugins for pupil, iris and eyelid detection. To add a new plugin:
|
|
69
|
+
|
|
70
|
+
1. Create a new Python file in the appropriate plugin directory.
|
|
71
|
+
2. Define your detector class in this file.
|
|
72
|
+
3. Ensure your detector follows the required interface.
|
|
73
|
+
|
|
74
|
+
For a detailed guide on creating plugins, see the [Plugin Development Guide](ai/README.md) in the `ai` directory.
|
|
75
|
+
|
|
76
|
+
## Contributing
|
|
77
|
+
|
|
78
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
79
|
+
|
|
80
|
+
## License
|
|
81
|
+
|
|
82
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
83
|
+
|
|
84
|
+
## Acknowledgments
|
|
85
|
+
|
|
86
|
+
This project has received funding from the European Union's Horizon Europe research and innovation funding program under grant agreement No 101072410, Eyes4ICU project.
|
|
87
|
+
|
|
88
|
+
<p align="center">
|
|
89
|
+
<img src="annotation_app/resources/Funded_by_EU_Eyes4ICU.png" alt="Funded by EU Eyes4ICU" width="500">
|
|
90
|
+
</p>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Creating a New Plugin for EyE Annotation Tool
|
|
2
|
+
|
|
3
|
+
This guide explains how to create a new pupil, iris or eyelid detector plugin for the EyE Annotation Tool application.
|
|
4
|
+
|
|
5
|
+
## Steps to Create a New Plugin
|
|
6
|
+
|
|
7
|
+
1. Create a new Python file in the appropriate directory:
|
|
8
|
+
- For pupil detectors: `ai/plugins/pupil_detectors/`
|
|
9
|
+
- For iris detectors: `ai/plugins/iris_detectors/`
|
|
10
|
+
- For eyelid detectors: `ai/plugins/eyelid_detectors/`
|
|
11
|
+
|
|
12
|
+
2. Import the necessary modules:
|
|
13
|
+
```python
|
|
14
|
+
from ai.plugin_interface import DetectorPlugin
|
|
15
|
+
import numpy as np
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
3. Create a new class that inherits from `DetectorPlugin`:
|
|
19
|
+
```python
|
|
20
|
+
class MyNewDetector(DetectorPlugin):
|
|
21
|
+
def __init__(self):
|
|
22
|
+
# Initialize your detector here
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
def detect(self, image_path):
|
|
26
|
+
# Implement your detection algorithm here
|
|
27
|
+
# Return the ellipse parameters and points
|
|
28
|
+
return ellipse, points
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def name(self):
|
|
32
|
+
# Return a unique name for your detector
|
|
33
|
+
return "my_new_detector"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
4. Implement the `detect` method:
|
|
37
|
+
- Input: `image_path` path of the eye image
|
|
38
|
+
- Output: `ellipse` (dict with keys: 'center', 'axes', 'angle') and `points` (list of point coordinates)
|
|
39
|
+
|
|
40
|
+
5. Set a unique `name` for your detector in the `name` property.
|
|
41
|
+
|
|
42
|
+
6. Save your file with a descriptive name (e.g., `my_new_detector.py`).
|
|
43
|
+
|
|
44
|
+
The plugin manager will automatically discover and load your new plugin when the application starts.
|
|
45
|
+
|
|
46
|
+
## Example
|
|
47
|
+
|
|
48
|
+
See `placeholder_iris_detector.py` for a simple example of a detector plugin.
|
|
49
|
+
|
|
50
|
+
## Notes
|
|
51
|
+
|
|
52
|
+
- Ensure your detector class inherits from `DetectorPlugin` and implements all required methods.
|
|
53
|
+
- The `name` property should return a unique string to identify your detector.
|
|
54
|
+
- Your `detect` method should handle various image sizes and conditions robustly.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Abstract base class for detector plugins."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DetectorPlugin(ABC):
|
|
7
|
+
"""Base class for all detector plugins (pupil, iris, eyelid)."""
|
|
8
|
+
|
|
9
|
+
@abstractmethod
|
|
10
|
+
def __init__(self) -> None:
|
|
11
|
+
"""Initialize the detector plugin."""
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def detect(self, image: str) -> tuple | list:
|
|
15
|
+
"""Detect features in the given image.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
image: Path to the image file.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Detection results as tuple or list depending on detector type.
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def name(self) -> str:
|
|
28
|
+
"""Get the name of the detector plugin."""
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Plugin manager for loading and managing detector plugins."""
|
|
2
|
+
|
|
3
|
+
import importlib.util
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from .plugin_interface import DetectorPlugin
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PluginManager:
|
|
10
|
+
"""Manages loading and accessing detector plugins for pupil, iris, and eyelid detection."""
|
|
11
|
+
|
|
12
|
+
def __init__(self) -> None:
|
|
13
|
+
"""Initialize the PluginManager."""
|
|
14
|
+
self.pupil_detectors = {}
|
|
15
|
+
self.iris_detectors = {}
|
|
16
|
+
self.eyelid_detectors = {}
|
|
17
|
+
self.load_plugins()
|
|
18
|
+
|
|
19
|
+
def load_plugins(self) -> None:
|
|
20
|
+
"""Load all detector plugins from the plugins directory."""
|
|
21
|
+
plugin_dirs = [
|
|
22
|
+
Path(__file__).parent / "plugins" / "pupil_detectors",
|
|
23
|
+
Path(__file__).parent / "plugins" / "iris_detectors",
|
|
24
|
+
Path(__file__).parent / "plugins" / "eyelid_detectors",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
for plugin_dir in plugin_dirs:
|
|
28
|
+
self.load_plugins_from_directory(plugin_dir)
|
|
29
|
+
|
|
30
|
+
def load_plugins_from_directory(self, directory: Path) -> None:
|
|
31
|
+
"""Load plugins from a specific directory and register them by type.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
directory: Path to the directory containing plugin files.
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
plugin_type = Path(directory).name
|
|
38
|
+
for file_path in Path(directory).iterdir():
|
|
39
|
+
if file_path.suffix == ".py" and not file_path.name.startswith("__"):
|
|
40
|
+
module_name = file_path.stem
|
|
41
|
+
module_path = str(file_path)
|
|
42
|
+
spec = importlib.util.spec_from_file_location(module_name, module_path)
|
|
43
|
+
module = importlib.util.module_from_spec(spec)
|
|
44
|
+
spec.loader.exec_module(module)
|
|
45
|
+
for item_name in dir(module):
|
|
46
|
+
item = getattr(module, item_name)
|
|
47
|
+
if isinstance(item, type) and issubclass(item, DetectorPlugin) and item is not DetectorPlugin:
|
|
48
|
+
plugin_instance = item()
|
|
49
|
+
if plugin_type == "pupil_detectors":
|
|
50
|
+
self.pupil_detectors[plugin_instance.name] = plugin_instance
|
|
51
|
+
elif plugin_type == "iris_detectors":
|
|
52
|
+
self.iris_detectors[plugin_instance.name] = plugin_instance
|
|
53
|
+
elif plugin_type == "eyelid_detectors":
|
|
54
|
+
self.eyelid_detectors[plugin_instance.name] = plugin_instance
|
|
55
|
+
else:
|
|
56
|
+
print(f"Unknown plugin type: {plugin_type}")
|
|
57
|
+
|
|
58
|
+
def get_pupil_detector(self, name: str) -> DetectorPlugin | None:
|
|
59
|
+
"""Get a pupil detector plugin by name.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
name: Name of the pupil detector.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
The detector plugin instance or None if not found.
|
|
66
|
+
|
|
67
|
+
"""
|
|
68
|
+
return self.pupil_detectors.get(name)
|
|
69
|
+
|
|
70
|
+
def get_iris_detector(self, name: str) -> DetectorPlugin | None:
|
|
71
|
+
"""Get an iris detector plugin by name.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
name: Name of the iris detector.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
The detector plugin instance or None if not found.
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
return self.iris_detectors.get(name)
|
|
81
|
+
|
|
82
|
+
def get_pupil_detector_names(self) -> list[str]:
|
|
83
|
+
"""Get list of available pupil detector names.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
List of pupil detector names.
|
|
87
|
+
|
|
88
|
+
"""
|
|
89
|
+
return list(self.pupil_detectors.keys())
|
|
90
|
+
|
|
91
|
+
def get_iris_detector_names(self) -> list[str]:
|
|
92
|
+
"""Get list of available iris detector names.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
List of iris detector names.
|
|
96
|
+
|
|
97
|
+
"""
|
|
98
|
+
return list(self.iris_detectors.keys())
|
|
99
|
+
|
|
100
|
+
def get_eyelid_detector(self, name: str) -> DetectorPlugin | None:
|
|
101
|
+
"""Get an eyelid detector plugin by name.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
name: Name of the eyelid detector.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
The detector plugin instance or None if not found.
|
|
108
|
+
|
|
109
|
+
"""
|
|
110
|
+
return self.eyelid_detectors.get(name)
|
|
111
|
+
|
|
112
|
+
def get_eyelid_detector_names(self) -> list[str]:
|
|
113
|
+
"""Get list of available eyelid detector names.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
List of eyelid detector names.
|
|
117
|
+
|
|
118
|
+
"""
|
|
119
|
+
return list(self.eyelid_detectors.keys())
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Eyelid detector plugins."""
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Placeholder eyelid detector for testing purposes."""
|
|
2
|
+
|
|
3
|
+
from ai.plugin_interface import DetectorPlugin
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PlaceholderEyelidDetector(DetectorPlugin):
|
|
7
|
+
"""Placeholder eyelid detector that returns dummy contour points."""
|
|
8
|
+
|
|
9
|
+
def __init__(self) -> None:
|
|
10
|
+
"""Initialize the PlaceholderEyelidDetector."""
|
|
11
|
+
|
|
12
|
+
def detect(self, image_path: str) -> list[tuple[float, float]]: # noqa: PLR6301 ARG002
|
|
13
|
+
"""Detect eyelid contour in the given image (returns placeholder data).
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
image_path: Path to the image file.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
List of points representing the eyelid contour.
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
# For this example, we'll ignore the actual image content
|
|
23
|
+
# and just return 4 fixed points representing an eyelid contour
|
|
24
|
+
|
|
25
|
+
# Assuming image size of 192x192 (same as in the iris example)
|
|
26
|
+
height, width = 192, 192
|
|
27
|
+
|
|
28
|
+
# Define 4 points for the eyelid contour
|
|
29
|
+
# These points form a simple curve across the top of the "eye"
|
|
30
|
+
points = [
|
|
31
|
+
(width * 0.2, height * 0.3), # Left point
|
|
32
|
+
(width * 0.4, height * 0.2), # Left-middle point
|
|
33
|
+
(width * 0.6, height * 0.2), # Right-middle point
|
|
34
|
+
(width * 0.8, height * 0.3), # Right point
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
return points
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def name(self) -> str:
|
|
41
|
+
"""Get the name of the detector plugin."""
|
|
42
|
+
return "test"
|
|
File without changes
|