aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/img/aruco_marker_pipeline.pngbin102120 -> 104416 bytes
-rw-r--r--docs/img/pipeline_input_context.pngbin0 -> 49064 bytes
-rw-r--r--docs/user_guide/pipeline_input_context/configuration_and_connection.md35
-rw-r--r--docs/user_guide/pipeline_input_context/context_definition.md58
-rw-r--r--docs/user_guide/pipeline_input_context/introduction.md15
-rw-r--r--docs/user_guide/utils/demonstrations_scripts.md9
-rw-r--r--mkdocs.yml4
-rw-r--r--src/argaze/ArFeatures.py2
-rw-r--r--src/argaze/DataFeatures.py4
-rw-r--r--src/argaze/utils/contexts/Random.py93
-rw-r--r--src/argaze/utils/demo/random_context.json12
11 files changed, 229 insertions, 3 deletions
diff --git a/docs/img/aruco_marker_pipeline.png b/docs/img/aruco_marker_pipeline.png
index 178da7f..c7c7b87 100644
--- a/docs/img/aruco_marker_pipeline.png
+++ b/docs/img/aruco_marker_pipeline.png
Binary files differ
diff --git a/docs/img/pipeline_input_context.png b/docs/img/pipeline_input_context.png
new file mode 100644
index 0000000..8c195ea
--- /dev/null
+++ b/docs/img/pipeline_input_context.png
Binary files differ
diff --git a/docs/user_guide/pipeline_input_context/configuration_and_connection.md b/docs/user_guide/pipeline_input_context/configuration_and_connection.md
new file mode 100644
index 0000000..392860f
--- /dev/null
+++ b/docs/user_guide/pipeline_input_context/configuration_and_connection.md
@@ -0,0 +1,35 @@
+Load and connect a context
+==========================
+
+Once an [ArContext is defined](context_definition.md), it have to be connected to a pipeline.
+
+# Load JSON configuration file
+
+An [ArContext](../../argaze.md/#argaze.ArFeatures.ArContext) can be loaded from a JSON configuration file thanks to the argaze.load package method.
+
+Here is a JSON configuration file related to the [previously defined Example context](context_definition.md):
+
+```json
+{
+ "my_context.Example": {
+ "name": "My example context",
+ "parameter": ...,
+ "pipeline": "pipeline.json"
+ }
+}
+```
+
+Then, here is how to load the JSON file:
+
+```python
+import argaze
+
+# Load ArContext
+with argaze.load('./configuration.json') as ar_context:
+
+ # Do something with ArContext
+ ...
+```
+
+!!! note
+ There is nothing to do to execute a loaded context as it is handled inside its own **__enter__** method.
diff --git a/docs/user_guide/pipeline_input_context/context_definition.md b/docs/user_guide/pipeline_input_context/context_definition.md
new file mode 100644
index 0000000..9f4981c
--- /dev/null
+++ b/docs/user_guide/pipeline_input_context/context_definition.md
@@ -0,0 +1,58 @@
+Define a context class
+======================
+
+The [ArContext](../../argaze.md/#argaze.ArFeatures.ArContext) class defines a generic class interface to handle pipeline inputs according to [Python context manager feature](https://docs.python.org/3/reference/datamodel.html#context-managers).
+
+# Write Python context file
+
+A specific [ArContext](../../argaze.md/#argaze.ArFeatures.ArContext) can be defined into a Python file.
+
+Here is an example context defined into *my_context.py* file:
+
+```python
+from argaze import ArFeatures, DataFeatures
+
+class Example(ArFeatures.ArContext):
+
+ @DataFeatures.PipelineStepInit
+ def __init__(self, **kwargs):
+
+ # Init ArContext class
+ super().__init__()
+
+ # Init private attribute
+ self.__parameter = ...
+
+ @property
+ def parameter(self):
+ """Any required parameter."""
+ return self.__parameter
+
+ @parameter.setter
+ def parameter(self, parameter):
+ self.__parameter = parameter
+
+ @DataFeatures.PipelineStepEnter
+ def __enter__(self):
+
+ # Start context according any required parameter
+ ... self.parameter
+
+ # Assuming that timestamp, x and y values are available
+ ...
+
+ # Process timestamped gaze position
+ self._process_gaze_position(timestamp = timestamp, x = x, y = y)
+
+ return self
+
+ @DataFeatures.PipelineStepExit
+ def __exit__(self, exception_type, exception_value, exception_traceback):
+
+ # End context
+ ...
+```
+
+!!! note ""
+
+ The next chapter explains how to [load a context to connect it with a pipeline](configuration_and_connection.md). \ No newline at end of file
diff --git a/docs/user_guide/pipeline_input_context/introduction.md b/docs/user_guide/pipeline_input_context/introduction.md
new file mode 100644
index 0000000..002f1e2
--- /dev/null
+++ b/docs/user_guide/pipeline_input_context/introduction.md
@@ -0,0 +1,15 @@
+Overview
+========
+
+This section explains how to wrap any pipeline detailled in previous sections into various context.
+
+First, let's look at the schema below: it gives an overview of the main notions involved in the following chapters.
+
+![Pipeline input context](../../img/pipeline_input_context.png)
+
+To build your own input context, you need to know:
+
+* [How to define a context class](context_definition.md),
+* [How to load and connect a context](configuration_and_connection.md),
+* [How to visualize a context](visualization.md),
+* [How to pause and resume a context](pause_and_resume.md)
diff --git a/docs/user_guide/utils/demonstrations_scripts.md b/docs/user_guide/utils/demonstrations_scripts.md
index f3f2b9e..ed2f8d9 100644
--- a/docs/user_guide/utils/demonstrations_scripts.md
+++ b/docs/user_guide/utils/demonstrations_scripts.md
@@ -9,6 +9,14 @@ Collection of command-line scripts for demonstration purpose.
!!! note
*Use -h option to get command arguments documentation.*
+## Random context
+
+Load **random_context.json** file to analyze random gaze positions.
+
+```shell
+python -m argaze ./src/argaze/utils/demo/random_context.json
+```
+
## OpenCV window context
Load **opencv_window_context.json** file to analyze mouse pointer positions over OpenCV window.
@@ -16,6 +24,7 @@ Load **opencv_window_context.json** file to analyze mouse pointer positions over
```shell
python -m argaze ./src/argaze/utils/demo/opencv_window_context.json
```
+
## Tobii Pro Glasses 2
### Tobii live stream context
diff --git a/mkdocs.yml b/mkdocs.yml
index 50417b1..bdb8603 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -36,6 +36,10 @@ nav:
- user_guide/aruco_marker_pipeline/advanced_topics/scripting.md
- user_guide/aruco_marker_pipeline/advanced_topics/optic_parameters_calibration.md
- user_guide/aruco_marker_pipeline/advanced_topics/aruco_detector_configuration.md
+ - Pipeline Input Context:
+ - user_guide/pipeline_input_context/introduction.md
+ - user_guide/pipeline_input_context/context_definition.md
+ - user_guide/pipeline_input_context/configuration_and_connection.md
- utils:
- user_guide/utils/ready-made_scripts.md
- user_guide/utils/demonstrations_scripts.md
diff --git a/src/argaze/ArFeatures.py b/src/argaze/ArFeatures.py
index a3ce1e3..58b23a0 100644
--- a/src/argaze/ArFeatures.py
+++ b/src/argaze/ArFeatures.py
@@ -1442,7 +1442,7 @@ DEFAULT_ARCONTEXT_IMAGE_PARAMETERS = {
class ArContext(DataFeatures.PipelineStepObject):
"""
- Define class to ...
+ Defines abstract Python context manager to handle pipeline inputs.
"""
# noinspection PyMissingConstructor
diff --git a/src/argaze/DataFeatures.py b/src/argaze/DataFeatures.py
index 08e0ef1..1bc43a8 100644
--- a/src/argaze/DataFeatures.py
+++ b/src/argaze/DataFeatures.py
@@ -678,9 +678,9 @@ def PipelineStepEnter(method):
logging.debug('%s.__enter__', get_class_path(self))
- method(self)
+ PipelineStepObject.__enter__(self)
- return PipelineStepObject.__enter__(self)
+ return method(self)
return wrapper
diff --git a/src/argaze/utils/contexts/Random.py b/src/argaze/utils/contexts/Random.py
new file mode 100644
index 0000000..7fc4d91
--- /dev/null
+++ b/src/argaze/utils/contexts/Random.py
@@ -0,0 +1,93 @@
+"""Define eye tracking data file context
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <https://www.gnu.org/licenses/>.
+"""
+
+__author__ = "Théo de la Hogue"
+__credits__ = []
+__copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)"
+__license__ = "GPLv3"
+
+import logging
+import time
+import threading
+import random
+
+from argaze import ArFeatures, DataFeatures, GazeFeatures
+
+class GazePositionGenerator(ArFeatures.ArContext):
+
+ @DataFeatures.PipelineStepInit
+ def __init__(self, **kwargs):
+
+ # Init ArContext class
+ super().__init__()
+
+ # Init private attribute
+ self.__range = (0, 0)
+ self.__x = 0
+ self.__y = 0
+
+ @property
+ def range(self) -> tuple[int, int]:
+ """Maximal ranges for X and Y axis"""
+ return self.__range
+
+ @range.setter
+ def range(self, range: tuple[int, int]):
+ self.__range = range
+
+ @DataFeatures.PipelineStepEnter
+ def __enter__(self):
+
+ logging.info('GazePositionGenerator context starts...')
+
+ # Create stop event
+ self.__stop_event = threading.Event()
+
+ # Start gaze position generator thread
+ self.__gaze_thread = threading.Thread(target=self.__generate_gaze_position)
+ self.__gaze_thread.start()
+
+ return self
+
+ def __generate_gaze_position(self):
+ """Generate gaze position."""
+
+ start_time = time.time()
+ self.__x = int(self.range[0] / 2)
+ self.__y = int(self.range[1] / 2)
+
+ while not self.__stop_event.is_set():
+
+ # Edit millisecond timestamp
+ timestamp = int((time.time() - start_time) * 1e3)
+
+ self.__x += random.randint(-10, 10)
+ self.__y += random.randint(-10, 10)
+
+ logging.debug('> timestamp=%i, x=%i, y=%i', timestamp, self.__x, self.__y)
+
+ # Process timestamped random gaze position
+ self._process_gaze_position(timestamp = timestamp, x = self.__x, y = self.__y)
+
+ # wait 10ms
+ time.sleep(0.01)
+
+ @DataFeatures.PipelineStepExit
+ def __exit__(self, exception_type, exception_value, exception_traceback):
+
+ logging.info('GazePositionGenerator context ends...')
+
+ # Stop gaze position generator thread
+ self.__stop_event.set()
+ threading.Thread.join(self.__gaze_thread)
+ \ No newline at end of file
diff --git a/src/argaze/utils/demo/random_context.json b/src/argaze/utils/demo/random_context.json
new file mode 100644
index 0000000..bb18a41
--- /dev/null
+++ b/src/argaze/utils/demo/random_context.json
@@ -0,0 +1,12 @@
+{
+ "argaze.utils.contexts.Random.GazePositionGenerator" : {
+ "name": "Random gaze position generator",
+ "range": [1920, 1149],
+ "pipeline": "gaze_analysis_pipeline.json",
+ "catch_exceptions": true,
+ "image_parameters": {
+ "draw_times": true,
+ "draw_exceptions": true
+ }
+ }
+} \ No newline at end of file