From 166e8de8c6d71e29f65c67ad93395ecd8f2ea6df Mon Sep 17 00:00:00 2001 From: Théo de la Hogue Date: Mon, 22 Apr 2024 12:01:24 +0200 Subject: Using pipeline step object enter method before private enter method. --- src/argaze/DataFeatures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 -- cgit v1.1 From c9f2536946d3fbef34cf4289db50c1dc5d260f18 Mon Sep 17 00:00:00 2001 From: Théo de la Hogue Date: Mon, 22 Apr 2024 12:01:55 +0200 Subject: Improving ArContext class documentation. --- src/argaze/ArFeatures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 -- cgit v1.1 From 818156366467a8a48f419edb15eebeeef143535b Mon Sep 17 00:00:00 2001 From: Théo de la Hogue Date: Mon, 22 Apr 2024 12:02:46 +0200 Subject: Adding a new Random context demonstration. --- src/argaze/utils/contexts/Random.py | 77 +++++++++++++++++++++++++++++++ src/argaze/utils/demo/random_context.json | 12 +++++ 2 files changed, 89 insertions(+) create mode 100644 src/argaze/utils/contexts/Random.py create mode 100644 src/argaze/utils/demo/random_context.json diff --git a/src/argaze/utils/contexts/Random.py b/src/argaze/utils/contexts/Random.py new file mode 100644 index 0000000..192e477 --- /dev/null +++ b/src/argaze/utils/contexts/Random.py @@ -0,0 +1,77 @@ +"""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 . +""" + +__author__ = "Théo de la Hogue" +__credits__ = [] +__copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)" +__license__ = "GPLv3" + +import logging +import time +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...') + + start_time = time.time() + self.__x = int(self.range[0] / 2) + self.__y = int(self.range[1] / 2) + + while True: + + # Edit millisecond timestamp + timestamp = int((time.time() - start_time) * 1e3) + + self.__x += random.randint(-10, 10) + self.__y += random.randint(-10, 10) + + logging.info('> 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) + + return self + + @DataFeatures.PipelineStepExit + def __exit__(self, exception_type, exception_value, exception_traceback): + + logging.info('GazePositionGenerator context ends...') \ 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 -- cgit v1.1 From 4f17f53b4d1bc539d6a7538ce057412268ee529f Mon Sep 17 00:00:00 2001 From: Théo de la Hogue Date: Mon, 22 Apr 2024 12:03:25 +0200 Subject: Adding command for Random context demonstration. --- docs/user_guide/utils/demonstrations_scripts.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/user_guide/utils/demonstrations_scripts.md b/docs/user_guide/utils/demonstrations_scripts.md index f3f2b9e..82018e3 100644 --- a/docs/user_guide/utils/demonstrations_scripts.md +++ b/docs/user_guide/utils/demonstrations_scripts.md @@ -16,6 +16,15 @@ Load **opencv_window_context.json** file to analyze mouse pointer positions over ```shell python -m argaze ./src/argaze/utils/demo/opencv_window_context.json ``` + +## Random context + +Load **random_context.json** file to analyze random gaze positions. + +```shell +python -m argaze ./src/argaze/utils/demo/random_context.json +``` + ## Tobii Pro Glasses 2 ### Tobii live stream context -- cgit v1.1 From 191da6950e7d4e9a7822b779ffb41dec3a1b7cb5 Mon Sep 17 00:00:00 2001 From: Théo de la Hogue Date: Mon, 22 Apr 2024 14:29:55 +0200 Subject: Moving position generation into a separated thread. --- src/argaze/utils/contexts/Random.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/argaze/utils/contexts/Random.py b/src/argaze/utils/contexts/Random.py index 192e477..7fc4d91 100644 --- a/src/argaze/utils/contexts/Random.py +++ b/src/argaze/utils/contexts/Random.py @@ -18,6 +18,7 @@ __license__ = "GPLv3" import logging import time +import threading import random from argaze import ArFeatures, DataFeatures, GazeFeatures @@ -49,11 +50,23 @@ class GazePositionGenerator(ArFeatures.ArContext): 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 True: + while not self.__stop_event.is_set(): # Edit millisecond timestamp timestamp = int((time.time() - start_time) * 1e3) @@ -61,17 +74,20 @@ class GazePositionGenerator(ArFeatures.ArContext): self.__x += random.randint(-10, 10) self.__y += random.randint(-10, 10) - logging.info('> timestamp=%i, x=%i, y=%i', timestamp, self.__x, self.__y) + 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) - - return self @DataFeatures.PipelineStepExit def __exit__(self, exception_type, exception_value, exception_traceback): - logging.info('GazePositionGenerator context ends...') \ No newline at end of file + 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 -- cgit v1.1 From e7344955a1368c24c55fc12b2cb9ff2582278785 Mon Sep 17 00:00:00 2001 From: Théo de la Hogue Date: Mon, 22 Apr 2024 14:31:24 +0200 Subject: First work around ArContext documentation. --- docs/img/aruco_marker_pipeline.png | Bin 102120 -> 104416 bytes docs/img/pipeline_input_context.png | Bin 0 -> 49064 bytes .../configuration_and_connection.md | 35 +++++++++++++ .../pipeline_input_context/context_definition.md | 58 +++++++++++++++++++++ .../pipeline_input_context/introduction.md | 15 ++++++ docs/user_guide/utils/demonstrations_scripts.md | 12 ++--- mkdocs.yml | 4 ++ 7 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 docs/img/pipeline_input_context.png create mode 100644 docs/user_guide/pipeline_input_context/configuration_and_connection.md create mode 100644 docs/user_guide/pipeline_input_context/context_definition.md create mode 100644 docs/user_guide/pipeline_input_context/introduction.md diff --git a/docs/img/aruco_marker_pipeline.png b/docs/img/aruco_marker_pipeline.png index 178da7f..c7c7b87 100644 Binary files a/docs/img/aruco_marker_pipeline.png and b/docs/img/aruco_marker_pipeline.png differ diff --git a/docs/img/pipeline_input_context.png b/docs/img/pipeline_input_context.png new file mode 100644 index 0000000..8c195ea Binary files /dev/null and b/docs/img/pipeline_input_context.png 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 82018e3..ed2f8d9 100644 --- a/docs/user_guide/utils/demonstrations_scripts.md +++ b/docs/user_guide/utils/demonstrations_scripts.md @@ -9,20 +9,20 @@ Collection of command-line scripts for demonstration purpose. !!! note *Use -h option to get command arguments documentation.* -## OpenCV window context +## Random context -Load **opencv_window_context.json** file to analyze mouse pointer positions over OpenCV window. +Load **random_context.json** file to analyze random gaze positions. ```shell -python -m argaze ./src/argaze/utils/demo/opencv_window_context.json +python -m argaze ./src/argaze/utils/demo/random_context.json ``` -## Random context +## OpenCV window context -Load **random_context.json** file to analyze random gaze positions. +Load **opencv_window_context.json** file to analyze mouse pointer positions over OpenCV window. ```shell -python -m argaze ./src/argaze/utils/demo/random_context.json +python -m argaze ./src/argaze/utils/demo/opencv_window_context.json ``` ## Tobii Pro Glasses 2 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 -- cgit v1.1