From 61637442c61a8c82e2b38c491d3a409080ccf2a9 Mon Sep 17 00:00:00 2001 From: Sebastian Fitt Date: Sat, 29 Apr 2023 15:48:29 +0200 Subject: [PATCH] feat: show bitrate, fps, latency in tracking mode --- EyeTrackApp/camera.py | 19 +++++---- EyeTrackApp/camera_widget.py | 81 +++++++++++++++++++++++------------- 2 files changed, 63 insertions(+), 37 deletions(-) diff --git a/EyeTrackApp/camera.py b/EyeTrackApp/camera.py index 8f179b8..f6e9e78 100644 --- a/EyeTrackApp/camera.py +++ b/EyeTrackApp/camera.py @@ -53,6 +53,7 @@ class Camera: self.last_frame_time = time.time() self.frame_number = 0 self.fps = 0 + self.bps = 0 self.start = True self.buffer = b'' @@ -128,6 +129,7 @@ class Camera: raise RuntimeError("Problem while getting frame") frame_number = self.wired_camera.get(cv2.CAP_PROP_POS_FRAMES) self.fps = self.wired_camera.get(cv2.CAP_PROP_FPS) + self.bps = image.nbytes if should_push: self.push_image_to_queue(image, frame_number, self.fps) except: @@ -156,10 +158,11 @@ class Camera: return jpeg def get_serial_camera_picture(self, should_push): - if self.serial_connection is None: + conn = self.serial_connection + if conn is None: return try: - if self.serial_connection.in_waiting: + if conn.in_waiting: jpeg = self.get_next_jpeg_frame() if jpeg: # Create jpeg frame from byte string @@ -169,9 +172,9 @@ class Camera: return # Discard the serial buffer. This is due to the fact that it # may build up some outdated frames. A bit of a workaround here tbh. - if self.serial_connection.in_waiting > 32768: - print(f"{Fore.CYAN}[INFO] Discarding the serial buffer ({self.serial_connection.in_waiting} bytes{Fore.RESET}") - self.serial_connection.reset_input_buffer() + if conn.in_waiting >= 32768: + print(f"{Fore.CYAN}[INFO] Discarding the serial buffer ({conn.in_waiting} bytes){Fore.RESET}") + conn.reset_input_buffer() self.buffer = b'' # Calculate the fps. current_frame_time = time.time() @@ -179,13 +182,13 @@ class Camera: self.last_frame_time = current_frame_time if delta_time > 0: self.fps = 1 / delta_time - # print(f'FPS: {int(self.fps)}') + self.bps = len(jpeg) / delta_time self.frame_number = self.frame_number + 1 if should_push: self.push_image_to_queue(image, self.frame_number, self.fps) except Exception: print(f"{Fore.YELLOW}[WARN] Serial capture source problem, assuming camera disconnected, waiting for reconnect.{Fore.RESET}") - self.serial_connection.close() + conn.close() self.camera_status = CameraState.DISCONNECTED pass @@ -208,7 +211,7 @@ class Camera: dsrdtr=False, rtscts=False) # Set explicit buffer size for serial. - conn.set_buffer_size(rx_size = 65536, tx_size = 65536) + conn.set_buffer_size(rx_size = 32768, tx_size = 32768) print(f"{Fore.CYAN}[INFO] ETVR Serial Tracker device connected on {port}{Fore.RESET}") self.serial_connection = conn diff --git a/EyeTrackApp/camera_widget.py b/EyeTrackApp/camera_widget.py index 361757d..d61b23f 100644 --- a/EyeTrackApp/camera_widget.py +++ b/EyeTrackApp/camera_widget.py @@ -1,6 +1,7 @@ import PySimpleGUI as sg from config import EyeTrackConfig from config import EyeTrackSettingsConfig +from collections import deque from threading import Event, Thread from eye_processor import EyeProcessor, EyeInfoOrigin from enum import Enum @@ -24,6 +25,8 @@ class CameraWidget: self.gui_save_tracking_button = f"-SAVETRACKINGBUTTON{widget_id}-" self.gui_tracking_layout = f"-TRACKINGLAYOUT{widget_id}-" self.gui_tracking_image = f"-IMAGE{widget_id}-" + self.gui_tracking_fps = f"-TRACKINGFPS{widget_id}-" + self.gui_tracking_bps = f"-TRACKINGBPS{widget_id}-" self.gui_output_graph = f"-OUTPUTGRAPH{widget_id}-" self.gui_restart_calibration = f"-RESTARTCALIBRATION{widget_id}-" self.gui_stop_calibration = f"-STOPCALIBRATION{widget_id}-" @@ -46,6 +49,35 @@ class CameraWidget: else: raise RuntimeError("\033[91m[WARN] Cannot have a camera widget represent both eyes!\033[0m") + self.cancellation_event = Event() + # Set the event until start is called, otherwise we can block if shutdown is called. + self.cancellation_event.set() + self.capture_event = Event() + self.capture_queue = Queue() + self.roi_queue = Queue() + + self.image_queue = Queue() + + self.ransac = EyeProcessor( + self.config, + self.settings_config, + self.cancellation_event, + self.capture_event, + self.capture_queue, + self.image_queue, + self.eye_id, + ) + + self.camera_status_queue = Queue() + self.camera = Camera( + self.config, + 0, + self.cancellation_event, + self.capture_event, + self.camera_status_queue, + self.capture_queue, + ) + self.roi_layout = [ [ sg.Graph( @@ -82,6 +114,8 @@ class CameraWidget: [ sg.Text("Mode:", background_color='#424042'), sg.Text("Calibrating", key=self.gui_mode_readout, background_color='#424042'), + sg.Text("", key=self.gui_tracking_fps, background_color='#424042'), + sg.Text("", key=self.gui_tracking_bps, background_color='#424042'), # sg.Checkbox( # "Circle crop:", # default=self.config.gui_circular_crop, @@ -123,40 +157,23 @@ class CameraWidget: ], ] - self.cancellation_event = Event() - # Set the event until start is called, otherwise we can block if shutdown is called. - self.cancellation_event.set() - self.capture_event = Event() - self.capture_queue = Queue() - self.roi_queue = Queue() - - self.image_queue = Queue() - - self.ransac = EyeProcessor( - self.config, - self.settings_config, - self.cancellation_event, - self.capture_event, - self.capture_queue, - self.image_queue, - self.eye_id, - ) - - self.camera_status_queue = Queue() - self.camera = Camera( - self.config, - 0, - self.cancellation_event, - self.capture_event, - self.camera_status_queue, - self.capture_queue, - ) - self.x0, self.y0 = None, None self.x1, self.y1 = None, None self.figure = None self.is_mouse_up = True self.in_roi_mode = False + self.movavg_fps_queue = deque(maxlen=120) + self.movavg_bps_queue = deque(maxlen=120) + + def _movavg_fps(self, next_fps): + self.movavg_fps_queue.append(next_fps) + fps = round(sum(self.movavg_fps_queue) / len(self.movavg_fps_queue)) + millisec = round((1 / fps if fps else 0) * 1000) + return f"{fps} Fps {millisec} ms" + + def _movavg_bps(self, next_bps): + self.movavg_bps_queue.append(next_bps) + return f"{sum(self.movavg_bps_queue) / len(self.movavg_bps_queue) * 0.001 * 0.001 * 8:.3f} Mbps" def started(self): return not self.cancellation_event.is_set() @@ -261,6 +278,10 @@ class CameraWidget: needs_roi_set = self.config.roi_window_h <= 0 or self.config.roi_window_w <= 0 + # TODO: Refactor if statements below... + window[self.gui_tracking_fps].update('') + window[self.gui_tracking_bps].update('') + if self.config.capture_source is None or self.config.capture_source == "": window[self.gui_mode_readout].update("Waiting for camera address") window[self.gui_roi_message].update(visible=False) @@ -275,6 +296,8 @@ class CameraWidget: window[self.gui_mode_readout].update("Calibration") else: window[self.gui_mode_readout].update("Tracking") + window[self.gui_tracking_fps].update(self._movavg_fps(self.camera.fps)) + window[self.gui_tracking_bps].update(self._movavg_bps(self.camera.bps)) if self.in_roi_mode: try: