diff --git a/EyeTrackApp/config.py b/EyeTrackApp/config.py index 15ee660..54a1b0f 100644 --- a/EyeTrackApp/config.py +++ b/EyeTrackApp/config.py @@ -221,6 +221,7 @@ class EyeTrackSettingsConfig(BaseModel): gui_smartinversion_enabled: bool = False gui_smartinversion_select_right: bool = True gui_smartinversion_thresh: float = 0.25 + gui_smartinversion_frame_count: int = 10 class EyeTrackConfig(BaseModel): diff --git a/EyeTrackApp/settings/modules/CommonFieldValidators.py b/EyeTrackApp/settings/modules/CommonFieldValidators.py index b42dab5..c15bdaf 100644 --- a/EyeTrackApp/settings/modules/CommonFieldValidators.py +++ b/EyeTrackApp/settings/modules/CommonFieldValidators.py @@ -32,3 +32,12 @@ def check_is_ip_address(v: str): return v except ValueError: raise ValueError("Please provide a valid IP Address") + +def try_convert_to_int(v: str): + """" + Checks if value provided can be converted to an integer and returns the converted result + """ + try: + return int(v) + except ValueError: + raise ValueError("Please provide a number with no decimal points") \ No newline at end of file diff --git a/EyeTrackApp/settings/modules/SmartInversionSettingsModule.py b/EyeTrackApp/settings/modules/SmartInversionSettingsModule.py index c263091..1ea8c18 100644 --- a/EyeTrackApp/settings/modules/SmartInversionSettingsModule.py +++ b/EyeTrackApp/settings/modules/SmartInversionSettingsModule.py @@ -6,13 +6,15 @@ from settings.constants import BACKGROUND_COLOR import PySimpleGUI as sg from settings.modules.CommonFieldValidators import try_convert_to_float +from settings.modules.CommonFieldValidators import try_convert_to_int + class SmartInversionValidationModule(BaseValidationModel): gui_smartinversion_enabled: bool gui_smartinversion_select_right: bool gui_smartinversion_thresh: Annotated[float, AfterValidator(try_convert_to_float)] - gui_smartinversion_recessive_difference: Annotated[float, AfterValidator(try_convert_to_float)] + gui_smartinversion_frame_count: Annotated[int, AfterValidator(try_convert_to_int)] class SmartInversionSettingsModule(BaseSettingsModule): def __init__(self, config, widget_id, **kwargs): @@ -21,6 +23,7 @@ class SmartInversionSettingsModule(BaseSettingsModule): self.gui_smartinversion_enabled = f"-gui_smartinversion_enabled{widget_id}-" self.gui_smartinversion_select_right = f"-gui_smartinversion_select_right{widget_id}-" self.gui_smartinversion_thresh = f"-gui_smartinversion_thresh{widget_id}-" + self.gui_smartinversion_frame_count =f"-gui_smartinversion_frame_count{widget_id}" def get_layout(self): @@ -36,17 +39,7 @@ class SmartInversionSettingsModule(BaseSettingsModule): background_color="#424042", tooltip="Enables Smart Inversion Tracking System", ), - ], - [ - sg.Text("Max. X-Axis Difference", background_color=BACKGROUND_COLOR), - sg.InputText( - self.config.gui_smartinversion_thresh, - key=self.gui_smartinversion_thresh, - size=(0, 10), - tooltip="Sets the maximum allowed difference in eye position (x-axis) to determine if the eyes are inverted or not." - ), - ], - [ + sg.Radio( "Use Left Eye", "smartinversion_selectedeye", @@ -62,5 +55,23 @@ class SmartInversionSettingsModule(BaseSettingsModule): background_color="#424042", tooltip="Uses the right eye as the tracked eye.", ) - ] + ], + [ + sg.Text("Max. X-Axis Difference", background_color=BACKGROUND_COLOR), + sg.InputText( + self.config.gui_smartinversion_thresh, + key=self.gui_smartinversion_thresh, + size=(0, 10), + tooltip="Sets the maximum allowed difference in eye position (x-axis) to determine if the eyes are inverted or not." + ), + ], + [ + sg.Text("Inversion Trigger Frame Count", background_color=BACKGROUND_COLOR), + sg.InputText( + self.config.gui_smartinversion_frame_count, + key=self.gui_smartinversion_frame_count, + size=(0, 10), + tooltip="How many frames the inversion conditions must be true (or no longer true) before the inversion state is toggled on or back off." + ), + ], ] \ No newline at end of file diff --git a/EyeTrackApp/utils/smart_inversion.py b/EyeTrackApp/utils/smart_inversion.py index 235c932..1f343c6 100644 --- a/EyeTrackApp/utils/smart_inversion.py +++ b/EyeTrackApp/utils/smart_inversion.py @@ -1,11 +1,18 @@ from eye import EyeId from utils.misc_utils import clamp -inverted_frames = int -cleared_frames = int - def smart_inversion(self, var, out_x, out_y): + #Checks to see if the class already has frame counts or inversion attributes + if not hasattr(self, "inverted_frame_count"): + self.inverted_frame_count = 0 + + if not hasattr(self, "normal_frame_count"): + self.normal_frame_count = 0 + + if not hasattr(self, "is_inverted"): + self.is_inverted = False + #Updates eye positions with latest if self.eye_id == EyeId.LEFT: var.l_eye_x = out_x @@ -15,11 +22,32 @@ def smart_inversion(self, var, out_x, out_y): var.r_eye_x = out_x var.right_y = out_y - #Checks if eyes are inverted + #Checks if eyes are inverted, and then activates inversion if the conditions have been true for a specified number of frames. if (var.l_eye_x > 0 and var.r_eye_x < 0) and (abs(var.l_eye_x - var.r_eye_x) > self.settings.gui_smartinversion_thresh): - is_inverted = True - else: - is_inverted = False + self.inverted_frame_count = min(self.inverted_frame_count + 1, self.settings.gui_smartinversion_frame_count) + print(f"Inverted frame count: {self.inverted_frame_count}") + + + if self.inverted_frame_count == self.settings.gui_smartinversion_frame_count: + self.is_inverted = True + self.normal_frame_count = 0 + print(f"Inversion Active") + + #Checks if the eyes are no longer inverted, and then clears inversion if the conditions haven't been true for a specified number of frames. + elif self.is_inverted and ( + not (var.l_eye_x > 0 and var.r_eye_x < 0) or + abs(var.l_eye_x - var.r_eye_x) <= self.settings.gui_smartinversion_thresh + ): + + self.normal_frame_count = min(self.normal_frame_count + 1, self.settings.gui_smartinversion_frame_count) + print(f"Normal frame count: {self.normal_frame_count}) + + + if self.normal_frame_count == self.settings.gui_smartinversion_frame_count: + self.is_inverted = False + self.inverted_frame_count = 0 + print(f"Inversion Cleared") + #Determines which eye is being tracked based off selection and sets values accordingly if self.settings.gui_smartinversion_select_right: @@ -36,7 +64,7 @@ def smart_inversion(self, var, out_x, out_y): #If eyes are inverted, and eye being processed is recessive, invert x value. if self.eye_id == recessive_eye: - if is_inverted: + if self.is_inverted: out_x = -tracked_eye_x else: out_x = tracked_eye_x diff --git a/conftest.py b/conftest.py index 224e9f5..6fe7217 100644 --- a/conftest.py +++ b/conftest.py @@ -79,6 +79,7 @@ def eyetrack_settings_config(): gui_smartinversion_enabled=False, gui_smartinversion_select_right=True, gui_smartinversion_thresh=0.5, + gui_smartinversion_frame_count=10, )