aboutsummaryrefslogtreecommitdiff
path: root/src/argaze/utils/contexts/OpenCV.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/argaze/utils/contexts/OpenCV.py')
-rw-r--r--src/argaze/utils/contexts/OpenCV.py172
1 files changed, 169 insertions, 3 deletions
diff --git a/src/argaze/utils/contexts/OpenCV.py b/src/argaze/utils/contexts/OpenCV.py
index 20b01bc..c2361a8 100644
--- a/src/argaze/utils/contexts/OpenCV.py
+++ b/src/argaze/utils/contexts/OpenCV.py
@@ -18,6 +18,7 @@ __copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)"
__license__ = "GPLv3"
import logging
+import threading
import time
import cv2
@@ -30,13 +31,13 @@ class Window(ArFeatures.LiveProcessingContext):
@DataFeatures.PipelineStepInit
def __init__(self, **kwargs):
- # Init ArContext class
+ # Init LiveProcessingContext class
super().__init__()
@DataFeatures.PipelineStepEnter
def __enter__(self):
- logging.info('OpenCV context starts...')
+ logging.info('OpenCV window context starts...')
# Create a window to display context
cv2.namedWindow(self.name, cv2.WINDOW_AUTOSIZE)
@@ -52,7 +53,7 @@ class Window(ArFeatures.LiveProcessingContext):
@DataFeatures.PipelineStepExit
def __exit__(self, exception_type, exception_value, exception_traceback):
- logging.info('OpenCV context stops...')
+ logging.info('OpenCV window context stops...')
# Delete window
cv2.destroyAllWindows()
@@ -66,3 +67,168 @@ class Window(ArFeatures.LiveProcessingContext):
# Process timestamped gaze position
self._process_gaze_position(timestamp = int((time.time() - self.__start_time) * 1e3), x = x, y = y)
+
+
+class Movie(ArFeatures.PostProcessingContext):
+
+ @DataFeatures.PipelineStepInit
+ def __init__(self, **kwargs):
+
+ # Init PostProcessingContext class
+ super().__init__()
+
+ # Init private attributes
+ self.__path = None
+ self.__movie = None
+ self.__movie_fps = None
+ self.__movie_width = None
+ self.__movie_height = None
+ self.__movie_length = None
+
+ self.__current_image_index = None
+ self.__next_image_index = None
+ self.__refresh = False
+
+ @property
+ def path(self) -> str:
+ """Movie file path."""
+ return self.__path
+
+ @path.setter
+ def path(self, path: str):
+
+ self.__path = path
+
+ # Load movie
+ self.__movie = cv2.VideoCapture(self.__path)
+ self.__movie_fps = self.__movie.get(cv2.CAP_PROP_FPS)
+ self.__movie_width = int(self.__movie.get(cv2.CAP_PROP_FRAME_WIDTH))
+ self.__movie_height = int(self.__movie.get(cv2.CAP_PROP_FRAME_HEIGHT))
+ self.__movie_length = self.__movie.get(cv2.CAP_PROP_FRAME_COUNT)
+
+ @DataFeatures.PipelineStepEnter
+ def __enter__(self):
+
+ logging.info('OpenCV movie context starts...')
+
+ # Create a window to display context
+ cv2.namedWindow(self.name, cv2.WINDOW_AUTOSIZE)
+
+ # Init timestamp
+ self.__start_time = time.time()
+
+ # Attach mouse event callback to window
+ cv2.setMouseCallback(self.name, self.__on_mouse_event)
+
+ # Open reading thread
+ self.__reading_thread = threading.Thread(target=self.__read)
+
+ logging.debug('> starting reading thread...')
+ self.__reading_thread.start()
+
+ return self
+
+ def __read(self):
+ """Iterate on movie images."""
+
+ # Init image selection
+ _, current_image = self.__movie.read()
+ current_image_time = self.__movie.get(cv2.CAP_PROP_POS_MSEC)
+ self.__next_image_index = 0 #int(self.__start * self.__movie_fps)
+
+ while not self._stop_event.is_set():
+
+ # Check pause event (and stop event)
+ while self._pause_event.is_set() and not self._stop_event.is_set():
+
+ logging.debug('> reading is paused at %i', current_image_time)
+
+ time.sleep(1)
+
+ # Select a new image and detect markers once
+ if self.__next_image_index != self.__current_image_index or self.__refresh:
+
+ self.__movie.set(cv2.CAP_PROP_POS_FRAMES, self.__next_image_index)
+
+ success, image = self.__movie.read()
+
+ video_height, video_width, _ = image.shape
+
+ if success:
+
+ # Refresh once
+ self.__refresh = False
+
+ self.__current_image_index = self.__movie.get(cv2.CAP_PROP_POS_FRAMES) - 1
+ current_image_time = self.__movie.get(cv2.CAP_PROP_POS_MSEC)
+
+ # Timestamp image
+ image = DataFeatures.TimestampedImage(image, timestamp=current_image_time)
+
+ # Process movie image
+ self._process_camera_image(timestamp=current_image_time, image=image)
+
+ # Wait
+ time.sleep(1 / self.__movie_fps)
+
+ @DataFeatures.PipelineStepExit
+ def __exit__(self, exception_type, exception_value, exception_traceback):
+
+ logging.info('OpenCV movie context stops...')
+
+ # Close data stream
+ self._stop_event.set()
+
+ # Stop reading thread
+ threading.Thread.join(self.__reading_thread)
+
+ # Delete window
+ cv2.destroyAllWindows()
+
+ def __on_mouse_event(self, event, x, y, flags, param):
+ """Process pointer position."""
+
+ logging.debug('Window.on_mouse_event %i %i', x, y)
+
+ if not self.is_paused():
+
+ # Process timestamped gaze position
+ self._process_gaze_position(timestamp = int((time.time() - self.__start_time) * 1e3), x = x, y = y)
+
+ def refresh(self):
+ """Refresh current frame."""
+ self.__refresh = True
+
+ def previous(self):
+
+ self.__next_image_index -= 1
+
+ # Clip image index
+ if self.__next_image_index < 0:
+ self.__next_image_index = 0
+
+ def next(self):
+
+ self.__next_image_index += 1
+
+ # Clip image index
+ if self.__next_image_index < 0:
+ self.__next_image_index = 0
+
+ @property
+ def duration(self) -> int|float:
+ """Get data duration."""
+
+ return self.__movie_length / self.__movie_fps
+
+ @property
+ def progression(self) -> float:
+ """Get data processing progression between 0 and 1."""
+
+ if self.__current_image_index is not None:
+
+ return self.__current_image_index / self.__movie_length
+
+ else:
+
+ return 0. \ No newline at end of file