aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/argaze/ArFeatures.py83
-rw-r--r--src/argaze/ArUcoMarkers/ArUcoCamera.py10
-rw-r--r--src/argaze/DataStructures.py22
3 files changed, 61 insertions, 54 deletions
diff --git a/src/argaze/ArFeatures.py b/src/argaze/ArFeatures.py
index 4e05764..f725380 100644
--- a/src/argaze/ArFeatures.py
+++ b/src/argaze/ArFeatures.py
@@ -94,10 +94,13 @@ DEFAULT_ARLAYER_DRAW_PARAMETERS = {
}
@dataclass
-class ArLayer():
+class ArLayer(DataStructures.SharedObject):
"""
Defines a space where to make matching of gaze movements and AOI and inside which those matchings need to be analyzed.
+ !!! note
+ Inherits from DataStructures.SharedObject class to be shared by multiple threads
+
Parameters:
name: name of the layer
aoi_scene: AOI scene description
@@ -118,15 +121,15 @@ class ArLayer():
def __post_init__(self):
+ # Init sharedObject
+ super().__init__()
+
# Define parent attribute: it will be setup by parent later
self.__parent = None
# Init current gaze movement
self.__gaze_movement = GazeFeatures.UnvalidGazeMovement()
- # Init lock to share looking data with multiples threads
- self.__look_lock = threading.Lock()
-
# Cast aoi scene to its effective dimension
if self.aoi_scene.dimension == 2:
@@ -372,7 +375,7 @@ class ArLayer():
"""
# Lock layer exploitation
- self.__look_lock.acquire()
+ self.acquire()
# Store look execution start date
look_start = time.perf_counter()
@@ -460,7 +463,7 @@ class ArLayer():
execution_times['total'] = (time.perf_counter() - look_start) * 1e3
# Unlock layer exploitation
- self.__look_lock.release()
+ self.release()
# Return look data
return looked_aoi, aoi_scan_path_analysis, execution_times, exception
@@ -479,8 +482,8 @@ class ArLayer():
return self.draw(image, **self.draw_parameters)
- # Lock frame exploitation
- self.__look_lock.acquire()
+ # Lock layer exploitation
+ self.acquire()
# Draw aoi if required
if draw_aoi_scene is not None:
@@ -492,8 +495,8 @@ class ArLayer():
self.aoi_matcher.draw(image, self.aoi_scene, **draw_aoi_matching)
- # Unlock frame exploitation
- self.__look_lock.release()
+ # Unlock layer exploitation
+ self.release()
# Define default ArFrame image parameters
DEFAULT_ARFRAME_IMAGE_PARAMETERS = {
@@ -517,10 +520,13 @@ DEFAULT_ARFRAME_IMAGE_PARAMETERS = {
}
@dataclass
-class ArFrame():
+class ArFrame(DataStructures.SharedObject):
"""
Defines a rectangular area where to project in timestamped gaze positions and inside which they need to be analyzed.
+ !!! note
+ Inherits from DataStructures.SharedObject class to be shared by multiple threads
+
Parameters:
name: name of the frame
size: defines the dimension of the rectangular area where gaze positions are projected
@@ -551,6 +557,9 @@ class ArFrame():
def __post_init__(self):
+ # Init sharedObject
+ super().__init__()
+
# Define parent attribute: it will be setup by parent later
self.__parent = None
@@ -562,9 +571,6 @@ class ArFrame():
# Init current gaze position
self.__gaze_position = GazeFeatures.UnvalidGazePosition()
- # Init lock to share looked data with multiples threads
- self.__look_lock = threading.Lock()
-
# Prepare logging if needed
self.__ts_logs = {}
@@ -852,7 +858,7 @@ class ArFrame():
"""
# Lock frame exploitation
- self.__look_lock.acquire()
+ self.acquire()
# Store look execution start date
look_start = time.perf_counter()
@@ -988,7 +994,7 @@ class ArFrame():
execution_times['total'] = (time.perf_counter() - look_start) * 1e3
# Unlock frame exploitation
- self.__look_lock.release()
+ self.release()
# Return look data
return self.__gaze_position, identified_gaze_movement, scan_step_analysis, layer_analysis, execution_times, exception
@@ -1009,7 +1015,7 @@ class ArFrame():
"""
# Lock frame exploitation
- self.__look_lock.acquire()
+ self.acquire()
# Draw background only
if background_weight is not None and (heatmap_weight is None or self.heatmap is None):
@@ -1066,7 +1072,7 @@ class ArFrame():
self.__gaze_position.draw(image, **draw_gaze_positions)
# Unlock frame exploitation
- self.__look_lock.release()
+ self.release()
return image
@@ -1393,9 +1399,6 @@ class ArCamera(ArFrame):
layer.aoi_scan_path.expected_aoi = expected_aoi_list
layer.aoi_matcher.exclude = exclude_aoi_list
-
- # Init a lock to share scene projections into camera frame between multiple threads
- self._frame_lock = threading.Lock()
def __str__(self) -> str:
"""
@@ -1470,7 +1473,7 @@ class ArCamera(ArFrame):
wait_start = time.perf_counter()
waiting_time = 0
- while self._frame_lock.locked():
+ while super().locked():
time.sleep(1e-6)
waiting_time = (time.perf_counter() - wait_start) * 1e3
@@ -1485,12 +1488,12 @@ class ArCamera(ArFrame):
#if waiting_time > 0:
# print(f'ArCamera: waiting {waiting_time:.3f} ms before to process gaze position at {timestamp} time.')
- # Lock camera frame exploitation
- self._frame_lock.acquire()
-
# Project gaze position into camera frame
yield self, super().look(timestamp, gaze_position)
+ # Lock camera frame exploitation
+ super().acquire()
+
# Project gaze position into each scene frames if possible
for scene_frame in self.scene_frames:
@@ -1517,7 +1520,7 @@ class ArCamera(ArFrame):
pass
# Unlock camera frame exploitation
- self._frame_lock.release()
+ super().release()
def map(self):
"""Project camera frame background into scene frames background.
@@ -1527,11 +1530,11 @@ class ArCamera(ArFrame):
"""
# Can't use camera frame when it is locked
- if self._frame_lock.locked():
+ if super().locked():
return
# Lock camera frame exploitation
- self._frame_lock.acquire()
+ super().acquire()
# Project camera frame background into each scene frame if possible
for frame in self.scene_frames:
@@ -1555,29 +1558,7 @@ class ArCamera(ArFrame):
pass
# Unlock camera frame exploitation
- self._frame_lock.release()
-
- def image(self, **kwargs: dict) -> numpy.array:
- """
- Get frame image.
-
- Parameters:
- kwargs: ArFrame.image parameters
- """
-
- # Can't use camera frame when it is locked
- if self._frame_lock.locked():
- return
-
- # Lock camera frame exploitation
- self._frame_lock.acquire()
-
- _image = super().image(**kwargs)
-
- # Unlock camera frame exploitation
- self._frame_lock.release()
-
- return _image
+ super().release()
def to_json(self, json_filepath):
"""Save camera to .json file."""
diff --git a/src/argaze/ArUcoMarkers/ArUcoCamera.py b/src/argaze/ArUcoMarkers/ArUcoCamera.py
index 75c8bfa..c7df7e0 100644
--- a/src/argaze/ArUcoMarkers/ArUcoCamera.py
+++ b/src/argaze/ArUcoMarkers/ArUcoCamera.py
@@ -155,7 +155,7 @@ class ArUcoCamera(ArFeatures.ArCamera):
detection_time = self.aruco_detector.detect_markers(image)
# Lock camera frame exploitation
- self._frame_lock.acquire()
+ super().acquire()
# Store projection execution start date
projection_start = time.perf_counter()
@@ -212,7 +212,7 @@ class ArUcoCamera(ArFeatures.ArCamera):
projection_time = (time.perf_counter() - projection_start) * 1e3
# Unlock camera frame exploitation
- self._frame_lock.release()
+ super().release()
# Return detection time, projection time and exceptions
return detection_time, projection_time, exceptions
@@ -228,9 +228,13 @@ class ArUcoCamera(ArFeatures.ArCamera):
"""
# Get camera frame image
- # Note: don't use self._frame_lock here as super().image manage it.
+ # Note: don't lock/unlock camera frame here as super().image manage it.
image = super().image(**kwargs)
+ # Check image
+ if image is None:
+ return
+
# Draw optic parameters grid if required
if draw_optic_parameters_grid is not None:
diff --git a/src/argaze/DataStructures.py b/src/argaze/DataStructures.py
index fc5072b..d3b6544 100644
--- a/src/argaze/DataStructures.py
+++ b/src/argaze/DataStructures.py
@@ -13,6 +13,7 @@ import collections
import json
import ast
import bisect
+import threading, traceback, sys
import pandas
import numpy
@@ -99,6 +100,27 @@ class JsonEncoder(json.JSONEncoder):
return public_dict
+class SharedObject():
+ """Enable multiple threads sharing."""
+
+ def __init__(self):
+ self._lock = threading.Lock()
+
+ def acquire(self):
+ self._lock.acquire()
+
+ def release(self):
+ self._lock.release()
+
+ def locked(self) -> bool:
+ return self._lock.locked()
+
+ def __enter__(self):
+ self.acquire()
+
+ def __exit__(self, type, value, traceback):
+ self.release()
+
class TimeStampedBuffer(collections.OrderedDict):
"""Ordered dictionary to handle timestamped data.
```