From 31885fca17b16d847b861e23683a68bf0be0ca7d Mon Sep 17 00:00:00 2001 From: Prohurtz <48768484+RedHawk989@users.noreply.github.com> Date: Sun, 18 Sep 2022 10:58:15 -0700 Subject: [PATCH] fix settings page and toggles --- EyeTrackApp/camera_widget.py | 16 +- EyeTrackApp/config.py | 1 + EyeTrackApp/eyetrackapp.py | 33 ++- EyeTrackApp/osc.py | 3 +- EyeTrackApp/settings_widget.py | 365 ++++++++++++++++----------------- 5 files changed, 206 insertions(+), 212 deletions(-) diff --git a/EyeTrackApp/camera_widget.py b/EyeTrackApp/camera_widget.py index ff77b8a..039bed3 100644 --- a/EyeTrackApp/camera_widget.py +++ b/EyeTrackApp/camera_widget.py @@ -35,6 +35,8 @@ class CameraWidget: self.config = main_config.right_eye elif self.eye_id == EyeId.LEFT: self.config = main_config.left_eye + elif self.eye_id == EyeId.SETTINGS: + self.config = main_config.settings else: raise RuntimeError("Cannot have a camera widget represent both eyes!") @@ -297,15 +299,15 @@ class CameraWidget: ): graph.update(background_color="white") - # if eye_info.y < 0: # flip visualzation to be correct - # visy = abs(eye_info.y) - # elif eye_info.y >= 0: - # visy = -abs(eye_info.y) + if eye_info.y < 0: # flip visualzation to be correct + visy = abs(eye_info.y) + elif eye_info.y >= 0: + visy = -abs(eye_info.y) try: graph.draw_circle( - (eye_info.x * -100, eye_info.y * -100), - 23, + (eye_info.x * -100, visy * -100), + 25, fill_color="black", line_color="white", ) @@ -315,9 +317,9 @@ class CameraWidget: graph.update(background_color="#6f4ca1") elif eye_info.info_type == InformationOrigin.FAILURE: graph.update(background_color="red") - # Relay information to OSC if eye_info.info_type != InformationOrigin.FAILURE: + self.osc_queue.put((self.eye_id, eye_info)) except Empty: return diff --git a/EyeTrackApp/config.py b/EyeTrackApp/config.py index 584ce85..52c426b 100644 --- a/EyeTrackApp/config.py +++ b/EyeTrackApp/config.py @@ -31,6 +31,7 @@ class EyeTrackConfig: version: "int" = 1 right_eye: EyeTrackCameraConfig = EyeTrackCameraConfig() left_eye: EyeTrackCameraConfig = EyeTrackCameraConfig() + settings: EyeTrackCameraConfig = EyeTrackCameraConfig() eye_display_id: "EyeId" = EyeId.RIGHT @staticmethod diff --git a/EyeTrackApp/eyetrackapp.py b/EyeTrackApp/eyetrackapp.py index c0eac57..77c3575 100644 --- a/EyeTrackApp/eyetrackapp.py +++ b/EyeTrackApp/eyetrackapp.py @@ -48,8 +48,10 @@ def main(): eyes = [ CameraWidget(EyeId.RIGHT, config, osc_queue), CameraWidget(EyeId.LEFT, config, osc_queue), + # CameraWidget(EyeId.SETTINGS, config, osc_queue), + ] - Settings = [ + settings = [ SettingsWidget(EyeId.SETTINGS, config, osc_queue), ] @@ -78,7 +80,7 @@ def main(): ), sg.Radio( "Settings", - "SETTINGSRADIO", + "EYESELECTRADIO", background_color='#292929', default=(config.eye_display_id == EyeId.SETTINGS), key=SETTINGS_RADIO_NAME, @@ -100,10 +102,10 @@ def main(): background_color='#424042', ), sg.Column( - Settings[0].widget_layout, + settings[0].widget_layout, vertical_alignment="top", key=SETTINGS_NAME, - visible=(config.eye_display_id in [EyeId.SETTINGS]), + visible=(config.eye_display_id in [EyeId.SETTINGS, EyeId.BOTH]), background_color='#424042', ), ], @@ -113,11 +115,11 @@ def main(): eyes[0].start() if config.eye_display_id in [EyeId.LEFT, EyeId.BOTH]: eyes[1].start() - if config.eye_display_id in [EyeId.SETTINGS]: - Settings[0].start() + if config.eye_display_id in [EyeId.SETTINGS, EyeId.BOTH]: + settings[0].start() # Create the window - window = sg.Window("EyeTrackVR v0.1.3 nightly", layout, icon='Images/logo.ico', background_color='#292929') + window = sg.Window("EyeTrackVR v0.1.4 nightly", layout, icon='Images/logo.ico', background_color='#292929') # GUI Render loop while True: @@ -126,7 +128,7 @@ def main(): # If we're in either mode and someone hits q, quit immediately if event == "Exit" or event == sg.WIN_CLOSED: - Settings[0].stop() + # eyes[2].stop() for eye in eyes: eye.stop() cancellation_event.set() @@ -136,19 +138,18 @@ def main(): # t2s_thread.join() print("Exiting EyeTrackApp") return - print(config.eye_display_id) + if values[RIGHT_EYE_RADIO_NAME] and config.eye_display_id != EyeId.RIGHT: - Settings[0].stop() eyes[0].start() eyes[1].stop() - Settings[0].stop() + settings[0].stop() window[RIGHT_EYE_NAME].update(visible=True) window[LEFT_EYE_NAME].update(visible=False) window[SETTINGS_NAME].update(visible=False) config.eye_display_id = EyeId.RIGHT config.save() elif values[LEFT_EYE_RADIO_NAME] and config.eye_display_id != EyeId.LEFT: - Settings[0].stop() + settings[0].stop() eyes[0].stop() eyes[1].start() window[RIGHT_EYE_NAME].update(visible=False) @@ -157,24 +158,22 @@ def main(): config.eye_display_id = EyeId.LEFT config.save() elif values[BOTH_EYE_RADIO_NAME] and config.eye_display_id != EyeId.BOTH: - Settings[0].stop() + settings[0].stop() eyes[0].start() eyes[1].start() window[RIGHT_EYE_NAME].update(visible=True) window[LEFT_EYE_NAME].update(visible=True) - window[SETTINGS_NAME].update(visible=False) + window[SETTINGS_NAME].update(visible=True) config.eye_display_id = EyeId.BOTH config.save() elif values[SETTINGS_RADIO_NAME] and config.eye_display_id != EyeId.SETTINGS: + settings[0].start() eyes[0].stop() eyes[1].stop() - Settings[0].start() window[RIGHT_EYE_NAME].update(visible=False) window[LEFT_EYE_NAME].update(visible=False) - window.Element(LEFT_EYE_NAME).Update(value=False) window[SETTINGS_NAME].update(visible=True) - config.eye_display_id = EyeId.SETTINGS config.save() diff --git a/EyeTrackApp/osc.py b/EyeTrackApp/osc.py index 8ea8b3a..df90e72 100644 --- a/EyeTrackApp/osc.py +++ b/EyeTrackApp/osc.py @@ -44,7 +44,7 @@ class VRChatOSC: (eye_id, eye_info) = self.msg_queue.get(block=True, timeout=0.1) except queue.Empty: continue - + if not eye_info.blink: @@ -99,4 +99,5 @@ class VRChatOSC: self.client.send_message("/avatar/parameters/RightEyeLidExpandedSqueeze", float(0)) # close eye self.client.send_message("/avatar/parameters/LeftEyeLidExpandedSqueeze", float(0)) + diff --git a/EyeTrackApp/settings_widget.py b/EyeTrackApp/settings_widget.py index 35178e6..5fca68b 100644 --- a/EyeTrackApp/settings_widget.py +++ b/EyeTrackApp/settings_widget.py @@ -14,106 +14,155 @@ class SettingsWidget: self.gui_show_color_image = f"-SETTING1{widget_id}-" self.gui_camera_addr = f"-CAMERAADDR{widget_id}-" self.gui_threshold_slider = f"-THREADHOLDSLIDER{widget_id}-" - self.gui_rotation_slider = f"-ROTATIONSLIDER{widget_id}-" + self.gui_roi_button = f"-ROIMODE{widget_id}-" self.gui_roi_layout = f"-ROILAYOUT{widget_id}-" self.gui_roi_selection = f"-GRAPH{widget_id}-" self.gui_tracking_button = f"-TRACKINGMODE{widget_id}-" 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_output_graph = f"-OUTPUTGRAPH{widget_id}-" self.gui_restart_calibration = f"-RESTARTCALIBRATION{widget_id}-" self.gui_recenter_eye = f"-RECENTEREYE{widget_id}-" self.gui_mode_readout = f"-APPMODE{widget_id}-" self.gui_show_color_image = f"-SHOWCOLORIMAGE{widget_id}-" + self.gui_flip_x_axis = f"-FLIPXAXIS{widget_id}-" + self.gui_flip_y_axis = f"-FLIPYAXIS{widget_id}-" self.gui_roi_message = f"-ROIMESSAGE{widget_id}-" + self.gui_save_button = f"-SAVE{widget_id}-" - self.main_config = main_config - self.config = main_config.left_eye + self.gui_general_settings_layout = f"-GENERALSETTINGSLAYOUT{widget_id}-" + # self.gui_algo_settings_layout = f"-ALGOSETTINGSLAYOUT{widget_id}-" + + self.gui_blob_fallback = f"-BLOBFALLBACK{widget_id}-" + self.gui_speed_coefficient_slider = f"-SPEEDCOEFFICIENTSLIDER{widget_id}-" + self.gui_min_cutoff_slider = f"-MINCUTOFFSLIDER{widget_id}-" + # self.main_config = main_config + #self.config = main_config.left_eye - + self.osc_queue = osc_queue # Define the window's contents - self.setting_layout = [ + self.general_settings_layout = [ + + # [ + # sg.Button("Recenter Eye", key=self.gui_recenter_eye, button_color = '#6f4ca1'), + + # ], + [sg.Checkbox( + "Flip X axis", + default=False, + key=self.gui_flip_x_axis, + background_color='#424042', + ), + ], + [sg.Checkbox( + "Flip Y axis", + default=False, + key=self.gui_flip_y_axis, + background_color='#424042', + ), + ], + [ - sg.Text("Rotation", background_color='#424042'), + sg.Text("Tracking algorithim toggles:", background_color='#424042'), + # sg.InputText(self.config.capture_source, key=self.gui_camera_addr), + ], + + [sg.Checkbox( + "Blob Fallback", + default=True, + key=self.gui_blob_fallback, + background_color='#424042', + ), + ], + [ + sg.Text("Filter paramaters:", background_color='#242224'), + + # sg.InputText(self.config.capture_source, key=self.gui_camera_addr), + ], + [ + sg.Text("Min Frequency Cutoff", background_color='#424042'), sg.Slider( - range=(0, 360), - default_value=5, + range=(0, 10), + default_value=4, orientation="h", - key=self.gui_rotation_slider, + key=self.gui_min_cutoff_slider, background_color='#424042' ), ], [ - sg.Button("Recenter Eye", key=self.gui_recenter_eye, button_color = '#6f4ca1'), - sg.Checkbox( - "Show Color Image:", - default=self.config.show_color_image, - key=self.gui_show_color_image, + sg.Text("Speed Coefficient", background_color='#424042'), + sg.Slider( + range=(0, 20), + default_value=9, + orientation="h", + key=self.gui_speed_coefficient_slider, + background_color='#424042' ), ], - [sg.Image(filename="", key=self.gui_tracking_image)], - [ - sg.Graph( - (200, 200), - (-100, 100), - (100, -100), - background_color="white", - key=self.gui_output_graph, - drag_submits=True, - enable_events=True, - ), - sg.Text("Please set an Eye Cropping.", key=self.gui_roi_message, background_color='#424042', visible=False), - ], + + + #[sg.Image(filename="", key=self.gui_tracking_image)], + # [ + # sg.Graph( + # (200, 200), + # (-100, 100), + # (100, -100), + # background_color="white", + # key=self.gui_output_graph, + # drag_submits=True, + # enable_events=True, + # ), + # sg.Text("Please set an Eye Cropping.", key=self.gui_roi_message, background_color='#424042', visible=False), + # ], ] + self.widget_layout = [ [ - sg.Text("Camera Address ass", background_color='#424042'), - sg.InputText(self.config.capture_source, key=self.gui_camera_addr), + sg.Text("Settings", background_color='#242224'), + # sg.InputText(self.config.capture_source, key=self.gui_camera_addr), ], + [ + sg.Column(self.general_settings_layout, key=self.gui_general_settings_layout, background_color='#424042' ), + #sg.Column(self.algo_settings_layout, key=self.gui_algo_settings_layout, background_color='#424042' ), + # sg.Column(self.roi_layout, key=self.gui_roi_layout, background_color='#424042', visible=False), + ], + [ sg.Button( - "Save and Restart Tracking", key=self.gui_save_tracking_button, button_color = '#6f4ca1' + "Save Settings", key=self.gui_save_button, button_color = '#6f4ca1' ), ], - [ - sg.Button("Tracking Mode", key=self.gui_tracking_button, button_color = '#6f4ca1'), - sg.Button("Cropping Mode", key=self.gui_roi_button, button_color = '#6f4ca1'), - ], - # [ - # sg.Column(self.tracking_layout, key=self.gui_tracking_layout, background_color='#424042' ), - # sg.Column(self.roi_layout, key=self.gui_roi_layout, background_color='#424042', visible=False), - # ], ] 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.capture_event = Event() + # self.capture_queue = Queue() + # self.roi_queue = Queue() self.image_queue = Queue() - self.ransac = EyeProcessor( - self.config, - self.cancellation_event, - self.capture_event, - self.capture_queue, - self.image_queue, - ) + # self.ransac = EyeProcessor( + # self.config, + # self.cancellation_event, + # self.capture_event, + # self.capture_queue, + # self.image_queue, + # ) - 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.camera_status_queue = Queue() + #self.camera = Camera( + # self.config, + # 0, + # self.cancellation_event, + #self.capture_event, + # self.camera_status_queue, + #self.capture_queue, + #) def started(self): @@ -124,44 +173,44 @@ class SettingsWidget: if not self.cancellation_event.is_set(): return self.cancellation_event.clear() - self.ransac_thread = Thread(target=self.ransac.run) - self.ransac_thread.start() - self.camera_thread = Thread(target=self.camera.run) - self.camera_thread.start() + #self.ransac_thread = Thread(target=self.ransac.run) + # self.ransac_thread.start() + # self.camera_thread = Thread(target=self.camera.run) + # self.camera_thread.start() def stop(self): # If we're not running yet, bail if self.cancellation_event.is_set(): return self.cancellation_event.set() - self.ransac_thread.join() - self.camera_thread.join() + # self.ransac_thread.join() + #self.camera_thread.join() def render(self, window, event, values): changed = False # If anything has changed in our configuration settings, change/update those. - if ( - event == self.gui_save_tracking_button - and values[self.gui_camera_addr] != self.config.capture_source - ): - print("New value: {}".format(values[self.gui_camera_addr])) - try: + # if ( + # event == self.gui_save_tracking_button + # and values[self.gui_camera_addr] != self.config.capture_source + #): + # print("New value: {}".format(values[self.gui_camera_addr])) + # try: # Try storing ints as ints, for those using wired cameras. - self.config.capture_source = int(values[self.gui_camera_addr]) - except ValueError: - if values[self.gui_camera_addr] == "": - self.config.capture_source = None - else: - self.config.capture_source = values[self.gui_camera_addr] - changed = True + # # self.config.capture_source = int(values[self.gui_camera_addr]) + # except ValueError: + # if values[self.gui_camera_addr] == "": + # self.config.capture_source = None + # else: + # self.config.capture_source = values[self.gui_camera_addr] + # changed = True - if self.config.threshold != values[self.gui_threshold_slider]: - self.config.threshold = int(values[self.gui_threshold_slider]) - changed = True + #if self.config.threshold != values[self.gui_threshold_slider]: + # self.config.threshold = int(values[self.gui_threshold_slider]) + # changed = True - if self.config.rotation_angle != values[self.gui_rotation_slider]: - self.config.rotation_angle = int(values[self.gui_rotation_slider]) - changed = True + #if self.config.rotation_angle != values[self.gui_rotation_slider]: + # self.config.rotation_angle = int(values[self.gui_rotation_slider]) + # changed = True if self.config.show_color_image != values[self.gui_show_color_image]: self.config.show_color_image = values[self.gui_show_color_image] @@ -170,115 +219,57 @@ class SettingsWidget: if changed: self.main_config.save() - if event == self.gui_tracking_button: - print("Moving to tracking mode") + + # elif self.camera.camera_status == CameraState.CONNECTING: + # window[self.gui_mode_readout].update("Camera Connecting") + # elif self.camera.camera_status == CameraState.DISCONNECTED: + # window[self.gui_mode_readout].update("CAMERA DISCONNECTED") + #elif needs_roi_set: + # window[self.gui_mode_readout].update("Awaiting Eye Cropping Setting") + #elif self.ransac.calibration_frame_counter != None: + # window[self.gui_mode_readout].update("Calibration") + # else: + # window[self.gui_mode_readout].update("Tracking") - elif event == self.gui_roi_button: - print("Move to roi mode") - self.in_roi_mode = True - self.camera.set_output_queue(self.roi_queue) - window[self.gui_roi_layout].update(visible=True) - window[self.gui_tracking_layout].update(visible=False) - elif event == "{}+UP".format(self.gui_roi_selection): - # Event for mouse button up in ROI mode - self.is_mouse_up = True - if abs(self.x0 - self.x1) != 0 and abs(self.y0 - self.y1) != 0: - self.config.roi_window_x = min([self.x0, self.x1]) - self.config.roi_window_y = min([self.y0, self.y1]) - self.config.roi_window_w = abs(self.x0 - self.x1) - self.config.roi_window_h = abs(self.y0 - self.y1) - self.main_config.save() - elif event == self.gui_roi_selection: - # Event for mouse button down or mouse drag in ROI mode - if self.is_mouse_up: - self.is_mouse_up = False - self.x0, self.y0 = values[self.gui_roi_selection] - self.x1, self.y1 = values[self.gui_roi_selection] - elif event == self.gui_restart_calibration: - self.ransac.calibration_frame_counter = 300 - elif event == self.gui_recenter_eye: - self.ransac.recenter_eye = True - - needs_roi_set = self.config.roi_window_h <= 0 or self.config.roi_window_w <= 0 - - 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) - window[self.gui_output_graph].update(visible=False) - elif self.camera.camera_status == CameraState.CONNECTING: - window[self.gui_mode_readout].update("Camera Connecting") - elif self.camera.camera_status == CameraState.DISCONNECTED: - window[self.gui_mode_readout].update("CAMERA DISCONNECTED") - elif needs_roi_set: - window[self.gui_mode_readout].update("Awaiting Eye Cropping Setting") - elif self.ransac.calibration_frame_counter != None: - window[self.gui_mode_readout].update("Calibration") - else: - window[self.gui_mode_readout].update("Tracking") - - if self.in_roi_mode: - try: - if self.roi_queue.empty(): - self.capture_event.set() - maybe_image = self.roi_queue.get(block=False) - imgbytes = cv2.imencode(".ppm", maybe_image[0])[1].tobytes() - graph = window[self.gui_roi_selection] - if self.figure: - graph.delete_figure(self.figure) + # if self.in_roi_mode: + # try: + # if self.roi_queue.empty(): + # self.capture_event.set() + # maybe_image = self.roi_queue.get(block=False) + # imgbytes = cv2.imencode(".ppm", maybe_image[0])[1].tobytes() + # graph = window[self.gui_roi_selection] + # if self.figure: + # graph.delete_figure(self.figure) # INCREDIBLY IMPORTANT ERASE. Drawing images does NOT overwrite the buffer, the fucking # graph keeps every image fed in until you call this. Therefore we have to make sure we # erase before we redraw, otherwise we'll leak memory *very* quickly. - graph.erase() - graph.draw_image(data=imgbytes, location=(0, 0)) - if None not in (self.x0, self.y0, self.x1, self.y1): - self.figure = graph.draw_rectangle( - (self.x0, self.y0), (self.x1, self.y1), line_color="#6f4ca1" - ) - except Empty: - pass - else: - if needs_roi_set: - window[self.gui_roi_message].update(visible=True) - window[self.gui_output_graph].update(visible=False) - return - try: - window[self.gui_roi_message].update(visible=False) - window[self.gui_output_graph].update(visible=True) - (maybe_image, eye_info) = self.image_queue.get(block=False) - imgbytes = cv2.imencode(".ppm", maybe_image)[1].tobytes() - window[self.gui_tracking_image].update(data=imgbytes) + # graph.erase() + # graph.draw_image(data=imgbytes, location=(0, 0)) + # if None not in (self.x0, self.y0, self.x1, self.y1): + # self.figure = graph.draw_rectangle( + # (self.x0, self.y0), (self.x1, self.y1), line_color="#6f4ca1" + # ) + # except Empty: + # pass + # else: + # if needs_roi_set: + # window[self.gui_roi_message].update(visible=True) + # window[self.gui_output_graph].update(visible=False) + # return + # try: + # window[self.gui_roi_message].update(visible=False) + # window[self.gui_output_graph].update(visible=True) + (maybe_image, eye_info) = self.image_queue.get(block=False) + # imgbytes = cv2.imencode(".ppm", maybe_image)[1].tobytes() + # window[self.gui_tracking_image].update(data=imgbytes) # Update the GUI - graph = window[self.gui_output_graph] - graph.erase() - - if ( - eye_info.info_type != InformationOrigin.FAILURE - and not eye_info.blink - ): - graph.update(background_color="white") - - # if eye_info.y < 0: # flip visualzation to be correct - # visy = abs(eye_info.y) - # elif eye_info.y >= 0: - # visy = -abs(eye_info.y) - try: - - graph.draw_circle( - (eye_info.x * -100, eye_info.y * -100), - 23, - fill_color="black", - line_color="white", - ) - except: - pass - elif eye_info.blink: - graph.update(background_color="#6f4ca1") - elif eye_info.info_type == InformationOrigin.FAILURE: - graph.update(background_color="red") + # graph = window[self.gui_output_graph] + # graph.erase() # Relay information to OSC - if eye_info.info_type != InformationOrigin.FAILURE: - self.osc_queue.put((EyeId.SETTINGS, eye_info)) - except Empty: - return + # if eye_info.info_type != InformationOrigin.FAILURE: + self.osc_queue.put((self.eye_id, eye_info)) + #self.osc_queue.put((EyeId.SETTINGS)) + # except Empty: + # return