aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/argaze/RegionOfInterest/ROI2DScene.py48
-rw-r--r--src/argaze/RegionOfInterest/ROI3DScene.py89
-rw-r--r--src/argaze/utils/export_tobii_segment_aruco_rois.py32
3 files changed, 96 insertions, 73 deletions
diff --git a/src/argaze/RegionOfInterest/ROI2DScene.py b/src/argaze/RegionOfInterest/ROI2DScene.py
index acf8e0a..066d8f1 100644
--- a/src/argaze/RegionOfInterest/ROI2DScene.py
+++ b/src/argaze/RegionOfInterest/ROI2DScene.py
@@ -9,25 +9,22 @@ class ROI2D(DataStructures.DictObject):
"""Define Region Of Interest 2D
```
{
- 'name': str,
- 'vertices': array of (x, y) tuples
+ 'vertices': array of (x, y) tuples,
'pointer': (x, y) tuple or None
}
```
"""
- def __init__(self, name, vertices, pointer = None):
+ def __init__(self, vertices, pointer = None):
- super().__init__(type(self).__name__, **{'name': name, 'vertices': vertices, 'pointer': pointer})
+ super().__init__(type(self).__name__, **{'vertices': vertices, 'pointer': pointer})
-class ROI2DScene(list):
- """List of ROI2D."""
+class ROI2DScene(DataStructures.DictObject):
+ """Define ROI 2D scene as dictionnary of named ROI2Ds."""
- def __new__(cls):
- return super(ROI2DScene, cls).__new__(cls)
+ def __init__(self, **rois_2d):
- def __init__(self):
- pass
+ super().__init__(type(self).__name__, **rois_2d)
def __del__(self):
pass
@@ -35,28 +32,41 @@ class ROI2DScene(list):
def inside(self, pointer):
"""Store pointer position if it is inside ROIs."""
- for roi in self:
+ for name in self.keys():
+
+ roi2D = self[name]
- if mpath.Path(roi.vertices).contains_points([pointer])[0]:
+ if mpath.Path(roi2D.vertices).contains_points([pointer])[0]:
- roi.pointer = pointer
+ roi2D.pointer = pointer
else:
- roi.pointer = None
+ roi2D.pointer = None
def draw(self, frame):
"""Draw ROI polygons on frame."""
- for roi in self:
+ for name in self.keys():
- inside = roi.pointer != None
+ roi2D = self[name]
+ inside = roi2D.pointer != None
color = (0, 255, 0) if inside else (0, 0, 255)
if inside:
- cv.putText(frame, roi.name, (roi.vertices[3][0], roi.vertices[3][1]), cv.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1, cv.LINE_AA)
+ cv.putText(frame, name, (roi2D.vertices[3][0], roi2D.vertices[3][1]), cv.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1, cv.LINE_AA)
- cv.line(frame, roi.vertices[-1], roi.vertices[0], color, 1)
- for A, B in zip(roi.vertices, roi.vertices[1:]):
+ cv.line(frame, roi2D.vertices[-1], roi2D.vertices[0], color, 1)
+ for A, B in zip(roi2D.vertices, roi2D.vertices[1:]):
cv.line(frame, A, B, color, 1)
+
+class TimeStampedROI2DScenes(DataStructures.TimeStampedBuffer):
+ """Define timestamped buffer to store ROI2D scenes"""
+
+ def __setitem__(self, key, value: ROI2DScene):
+ """Force value to be a ROI2DScene"""
+ if type(value) != ROI2DScene:
+ raise ValueError('value must be a ROI2DScene')
+
+ super().__setitem__(key, value)
diff --git a/src/argaze/RegionOfInterest/ROI3DScene.py b/src/argaze/RegionOfInterest/ROI3DScene.py
index 683110c..9b753a3 100644
--- a/src/argaze/RegionOfInterest/ROI3DScene.py
+++ b/src/argaze/RegionOfInterest/ROI3DScene.py
@@ -13,34 +13,35 @@ class ROI3D(DataStructures.DictObject):
"""Define Region Of Interest 3D
```
{
- 'name': str,
'vertices': array of (x, y, z) tuples
}
```
"""
- def __init__(self, name, vertices):
+ def __init__(self, vertices):
- super().__init__(type(self).__name__, **{'name': name, 'vertices': vertices})
+ super().__init__(type(self).__name__, **{'vertices': vertices})
-class ROI3DScene(list):
- """List of ROI3D dictionary.
- ```
- {
- 'NAME': str,
- 'VERTICES': array of (x, y, z) tuples
- }
- ```
+class ROI3DScene(DataStructures.DictObject):
+ """Define ROI 3D scene as dictionnary of named ROI3Ds.
+ ```
+ {
+ 'rotation': (x, y, z) tuples,
+ 'translation': (x, y, z) tuples,
+ 'ROI name 1': ROI3D,
+ 'ROI name 2': ROI3D,
+ ...
+ }
+ ```
"""
- def __new__(cls):
- return super(ROI3DScene, cls).__new__(cls)
+ def __init__(self, **rois_3d):
- def __init__(self):
+ # append rotation and translation matrix
+ rois_3d['rotation'] = [0, 0, 0]
+ rois_3d['translation'] = [0, 0, 0]
- # define rotation and translation matrix
- self.__rotation = [0, 0, 0]
- self.__translation = [0, 0, 0]
+ super().__init__(type(self).__name__, **rois_3d)
def __del__(self):
pass
@@ -70,10 +71,9 @@ class ROI3DScene(list):
# start parsing
try:
- roi3D_list = []
- roi3D = {}
+ name = None
vertices = []
- faces = []
+ faces = {}
# open the file and read through it line by line
with open(obj_filepath, 'r') as file:
@@ -92,7 +92,7 @@ class ROI3DScene(list):
# extract roi3D name
elif key == 'name':
- roi3D['name'] = str(match.group(1))
+ name = str(match.group(1))
# fill vertices array
elif key == 'vertice':
@@ -102,13 +102,7 @@ class ROI3DScene(list):
# extract roi3D vertice id
elif key == 'face':
- roi3D['face'] = [int(i) for i in match.group(1).split()]
-
- # store roi3D dict into scene array
- roi3D_list.append(roi3D)
-
- # clear roi3D dict
- roi3D = {}
+ faces[name] = [int(i) for i in match.group(1).split()]
# go to next line
line = file.readline()
@@ -116,41 +110,32 @@ class ROI3DScene(list):
file.close()
# retreive all roi3D vertices
- for roi3D in roi3D_list:
- roi3D['vertices'] = [ vertices[i-1] for i in roi3D['face'] ]
- roi3D.pop('face', None)
- self.append(ROI3D(**roi3D))
+ for name, face in faces.items():
+ self.append(name, ROI3D(**{'vertices': [ vertices[i-1] for i in face ]}))
except IOError:
raise IOError(f'File not found: {obj_filepath}')
- def set_rotation(self, rvec: list):
- """Set scene rotation vector."""
- self.__rotation = rvec
-
- def set_translation(self, tvec: list):
- """Set scene translation vector."""
- self.__translation = tvec
-
def project(self, K, D):
"""Project 3D scene onto 2D scene according optical parameters.
- **Returns:** AOI2DScene"""
+ **Returns:** ROI2DScene"""
+
+ roi2D_scene = {}
+
+ for name in self.keys():
- roi2D_scene = ROI2DScene.ROI2DScene()
+ if name == 'rotation' or name == 'translation':
+ continue
- for roi3D in self:
+ roi3D = self[name]
vertices_3D = numpy.array(roi3D.vertices).astype('float32')
- vertices_2D, J = cv.projectPoints(vertices_3D, self.__rotation, self.__translation, K, D)
- vertices_2D = vertices_2D.astype('int').reshape((len(vertices_2D), 2))
+ vertices_2D, J = cv.projectPoints(vertices_3D, self.rotation, self.translation, K, D)
+ vertices_2D = vertices_2D.astype('int').reshape((len(vertices_2D), 2)).tolist()
- roi2D = {
- 'name': roi3D.name,
- 'vertices': vertices_2D,
- 'pointer': None
- }
+ roi2D = ROI2DScene.ROI2D(vertices_2D)
- roi2D_scene.append(ROI2DScene.ROI2D(**roi2D))
+ roi2D_scene[name] = roi2D
- return roi2D_scene
+ return ROI2DScene.ROI2DScene(**roi2D_scene)
diff --git a/src/argaze/utils/export_tobii_segment_aruco_rois.py b/src/argaze/utils/export_tobii_segment_aruco_rois.py
index f9c4cb2..8919249 100644
--- a/src/argaze/utils/export_tobii_segment_aruco_rois.py
+++ b/src/argaze/utils/export_tobii_segment_aruco_rois.py
@@ -3,6 +3,7 @@
import argparse
import bisect
+from argaze import DataStructures
from argaze import GazeFeatures
from argaze.TobiiGlassesPro2 import TobiiEntities, TobiiVideo
from argaze.ArUcoMarkers import *
@@ -24,10 +25,25 @@ def main():
parser.add_argument('-r', '--roi_scene', metavar='ROI_SCENE', type=str, default='roi3D_scene.obj', help='obj roi scene filepath')
parser.add_argument('-d', '--dictionary', metavar='DICT', type=str, default='DICT_ARUCO_ORIGINAL', help='aruco marker dictionnary')
parser.add_argument('-m', '--marker_size', metavar='MKR', type=float, default=6, help='aruco marker size (cm)')
+ parser.add_argument('-o', '--output', metavar='OUT', type=str, default=None, help='destination path (segment folder by default)')
args = parser.parse_args()
if args.segment_path != None:
+ # Manage destination path
+ if args.output != None:
+
+ if not os.path.exists(os.path.dirname(args.output)):
+
+ os.makedirs(os.path.dirname(args.output))
+ print(f'{os.path.dirname(args.output)} folder created')
+
+ rois_filepath = args.output
+
+ else:
+
+ rois_filepath = f'{args.segment_path}/rois.json'
+
# Load a tobii segment
tobii_segment = TobiiEntities.TobiiSegment(args.segment_path)
@@ -53,6 +69,10 @@ def main():
# Create ROIs 3D scene
roi3D_scene = ROI3DScene.ROI3DScene()
roi3D_scene.load(args.roi_scene)
+ print(f'ROIs names: {roi3D_scene.keys()[2::]}')
+
+ # Create Timestamped buffer to store 2D ROIs
+ roi2D_timestamped_buffer = ROI2DScene.TimeStampedROI2DScenes()
# Video and data replay loop
try:
@@ -88,8 +108,8 @@ def main():
marker_rotation = aruco_tracker.get_marker_rotation(i)
marker_translation = aruco_tracker.get_marker_translation(i)
- roi3D_scene.set_rotation(marker_rotation)
- roi3D_scene.set_translation(marker_translation)
+ roi3D_scene.rotation = marker_rotation
+ roi3D_scene.translation = marker_translation
# Edit Zero distorsion matrix
D0 = numpy.asarray([0.0, 0.0, 0.0, 0.0, 0.0])
@@ -105,6 +125,9 @@ def main():
# Draw 2D rois
roi2D_scene.draw(video_frame.matrix)
+ # Store 2D rois
+ roi2D_timestamped_buffer[video_ts] = roi2D_scene
+
# Close window using 'Esc' key
if cv.waitKey(1) == 27:
break
@@ -118,6 +141,11 @@ def main():
# Stop frame display
cv.destroyAllWindows()
+ # Export fixations analysis results
+ roi2D_timestamped_buffer.export_as_json(rois_filepath)
+
+ print(f'Timestamped ROIs positions saved into {rois_filepath}')
+
if __name__ == '__main__':
main() \ No newline at end of file