def __init__(self, display): """Initiates an eyetracker dummy object, that simulates gaze position using the mouse arguments display -- a pygaze display.Display instance keyword arguments None """ # try to copy docstrings (but ignore it if it fails, as we do # not need it for actual functioning of the code) try: copy_docstr(BaseEyeTracker, Dummy) except: # we're not even going to show a warning, since the copied # docstring is useful for code editors; these load the docs # in a non-verbose manner, so warning messages would be lost pass self.recording = False self.blinking = False self.bbpos = (settings.DISPSIZE[0]/2, settings.DISPSIZE[1]/2) self.resolution = settings.DISPSIZE[:] self.simulator = Mouse(disptype=settings.DISPTYPE, mousebuttonlist=None, timeout=2, visible=False) self.kb = Keyboard(disptype=settings.DISPTYPE, keylist=None, timeout=None) self.angrybeep = Sound(osc='saw',freq=100, length=100, attack=0, decay=0, soundfile=None) self.display = display self.screen = Screen(disptype=settings.DISPTYPE, mousevisible=False)
def CDPInitialisation(self): self.findtracker = tr.find_all_eyetrackers() if self.findtracker == (): print("Veuillez réassayer, aucun EyeTracker détecté") return () self.filename = 0 self.baseFilename = 0 self.blackdisp = libscreen.Display(screennr=int( self.Config.getConfiguration('DISPLAY', 'screen_number'))) self.disp = libscreen.Display(screennr=int( self.Config.getConfiguration('DISPLAY', 'screen_number'))) self.blankscreen = libscreen.Screen() self.tracker = CDPProTracker( self.disp) # création de l'objet eyeTracker self.kb = Keyboard(keylist=['space', 'escape', 'q'], timeout=1) self.Visu = CDPBaseVisualisation(self) self.RecSound = libsound.Sound( soundfile=self.Config.getSoundDirname('2.wav')) self.ErrSound = libsound.Sound( soundfile=self.Config.getSoundDirname('punition.wav')) self.nameInd = 0 self.mydisp = [self.disp] print("Eyetracker connecté avec succès")
def confirm_abort_experiment(self): """ Asks for confirmation before aborting the experiment. Displays a confirmation screen, collects the response, and acts accordingly. Exceptions: Raises a response_error upon confirmation. Returns: False if no confirmation was given. """ # Display the confirmation screen scr = Screen(disptype=DISPTYPE) kb = Keyboard(timeout=5000) yc = DISPSIZE[1] / 2 xc = DISPSIZE[0] / 2 ld = 40 # Line height scr.draw_text(u'Really abort experiment?', pos=(xc, yc - 3 * ld)) scr.draw_text(u'Press \'Y\' to abort', pos=(xc, yc - 0.5 * ld)) scr.draw_text(u'Press any other key or wait 5s to go to setup', \ pos=(xc, yc+0.5*ld)) self.display.fill(scr) self.display.show() # process the response: try: key, time = kb.get_key() except: return False # if confirmation, close experiment if key == u'y': raise Exception(u'The experiment was aborted') self.eyelink_graphics.esc_pressed = False return False
def __init__(self, display, logfile=settings.LOGFILE, \ alea_key=settings.ALEAKEY, \ animated_calibration=settings.ALEAANIMATEDCALIBRATION, \ eventdetection=settings.EVENTDETECTION, \ saccade_velocity_threshold=35, \ saccade_acceleration_threshold=9500, \ blink_threshold=settings.BLINKTHRESH, \ **args): """Initializes the AleaTracker object arguments display -- a pygaze.display.Display instance keyword arguments logfile -- logfile name (string value); note that this is the name for the eye data log file (default = LOGFILE) """ # try to copy docstrings (but ignore it if it fails, as we do # not need it for actual functioning of the code) try: copy_docstr(BaseEyeTracker, AleaTracker) except: # we're not even going to show a warning, since the copied # docstring is useful for code editors; these load the docs # in a non-verbose manner, so warning messages would be lost pass # object properties self.disp = display self.screen = Screen() self.dispsize = self.disp.dispsize # display size in pixels self.screensize = settings.SCREENSIZE # display size in cm self.kb = Keyboard(keylist=['space', 'escape', 'q'], timeout=1) self.errorbeep = Sound(osc='saw', freq=100, length=100) # show a message self.screen.clear() self.screen.draw_text( text="Initialising the eye tracker, please wait...", fontsize=20) self.disp.fill(self.screen) self.disp.show() # output file properties self.outputfile = logfile + '.tsv' # calibration properties self.animated_calibration = animated_calibration == True # eye tracker properties self.connected = False self.recording = False self.errdist = 2 # degrees; maximal error for drift correction self.pxerrdist = 30 # initial error in pixels self.maxtries = 100 # number of samples obtained before giving up (for obtaining accuracy and tracker distance information, as well as starting or stopping recording) self.prevsample = (-1, -1) self.prevps = -1 # event detection properties self.fixtresh = 1.5 # degrees; maximal distance from fixation start (if gaze wanders beyond this, fixation has stopped) self.fixtimetresh = 100 # milliseconds; amount of time gaze has to linger within self.fixtresh to be marked as a fixation self.spdtresh = saccade_velocity_threshold # degrees per second; saccade velocity threshold self.accthresh = saccade_acceleration_threshold # degrees per second**2; saccade acceleration threshold self.blinkthresh = blink_threshold # milliseconds; blink detection threshold used in PyGaze method self.eventdetection = eventdetection self.set_detection_type(self.eventdetection) self.weightdist = 10 # weighted distance, used for determining whether a movement is due to measurement error (1 is ok, higher is more conservative and will result in only larger saccades to be detected) # connect to the tracker self.alea = OGAleaTracker(alea_key, file_path=self.outputfile) # get info on the sample rate # TODO: Compute after streaming some samples? self.samplerate = 60.0 self.sampletime = 1000.0 / self.samplerate # initiation report self.log("pygaze initiation report start") self.log("display resolution: {}x{}".format( \ self.dispsize[0], self.dispsize[1])) self.log("display size in cm: {}x{}".format( \ self.screensize[0], self.screensize[1])) self.log("samplerate: {} Hz".format(self.samplerate)) self.log("sampletime: {} ms".format(self.sampletime)) self.log("fixation threshold: {} degrees".format(self.fixtresh)) self.log("speed threshold: {} degrees/second".format(self.spdtresh)) self.log("acceleration threshold: {} degrees/second**2".format( \ self.accthresh)) self.log("pygaze initiation report end")
def __init__(self, display, ip='127.0.0.1', sendport=4444, receiveport=5555, logfile=settings.LOGFILE, eventdetection=settings.EVENTDETECTION, saccade_velocity_threshold=35, saccade_acceleration_threshold=9500, **args): """Initializes the SMItracker object arguments display -- a pygaze.display.Display instance keyword arguments ip -- internal ip address for iViewX (default = '127.0.0.1') sendport -- port number for iViewX sending (default = 4444) receiveport -- port number for iViewX receiving (default = 5555) logfile -- logfile name (string value); note that this is the name for the SMI logfile, NOT the .idf file (default = LOGFILE) """ # try to copy docstrings (but ignore it if it fails, as we do # not need it for actual functioning of the code) try: copy_docstr(BaseEyeTracker, SMITracker) except: # we're not even going to show a warning, since the copied # docstring is useful for code editors; these load the docs # in a non-verbose manner, so warning messages would be lost pass # object properties self.disp = display self.screen = Screen() self.dispsize = settings.DISPSIZE # display size in pixels self.screensize = settings.SCREENSIZE # display size in cm self.kb = Keyboard(keylist=['space', 'escape', 'q'], timeout=1) self.errorbeep = Sound(osc='saw', freq=100, length=100) # output file properties self.outputfile = logfile self.description = "experiment" # TODO: EXPERIMENT NAME self.participant = "participant" # TODO: PP NAME # eye tracker properties self.connected = False self.recording = False self.eye_used = 0 # 0=left, 1=right, 2=binocular self.left_eye = 0 self.right_eye = 1 self.binocular = 2 self.errdist = 2 # degrees; maximal error for drift correction self.maxtries = 100 # number of samples obtained before giving up (for obtaining accuracy and tracker distance information, as well as starting or stopping recording) self.prevsample = (-1, -1) self.prevps = -1 # event detection properties self.fixtresh = 1.5 # degrees; maximal distance from fixation start (if gaze wanders beyond this, fixation has stopped) self.fixtimetresh = 100 # milliseconds; amount of time gaze has to linger within self.fixtresh to be marked as a fixation self.spdtresh = saccade_velocity_threshold # degrees per second; saccade velocity threshold self.accthresh = saccade_acceleration_threshold # degrees per second**2; saccade acceleration threshold self.eventdetection = eventdetection self.set_detection_type(self.eventdetection) self.weightdist = 10 # weighted distance, used for determining whether a movement is due to measurement error (1 is ok, higher is more conservative and will result in only larger saccades to be detected) # set logger res = iViewXAPI.iV_SetLogger(c_int(1), c_char_p(logfile + '_SMILOG.txt')) if res != 1: err = errorstring(res) raise Exception( "Error in libsmi.SMItracker.__init__: failed to set logger; %s" % err) # first logger argument is for logging type (I'm guessing these are decimal bit codes) # LOG status bitcode # 1 = LOG_LEVEL_BUG 00001 # 2 = LOG_LEVEL_iV_FCT 00010 # 4 = LOG_LEVEL_ETCOM 00100 # 8 = LOG_LEVEL_ALL 01000 # 16 = LOG_LEVEL_IV_COMMAND 10000 # these can be used together, using a bitwise or, e.g.: 1|2|4 (bitcode 00111) # connect to iViewX res = iViewXAPI.iV_Connect(c_char_p(ip), c_int(sendport), c_char_p(ip), c_int(receiveport)) if res == 1: res = iViewXAPI.iV_GetSystemInfo(byref(systemData)) self.samplerate = systemData.samplerate self.sampletime = 1000.0 / self.samplerate if res != 1: err = errorstring(res) raise Exception( "Error in libsmi.SMItracker.__init__: failed to get system information; %s" % err) # handle connection errors else: self.connected = False err = errorstring(res) raise Exception( "Error in libsmi.SMItracker.__init__: establishing connection failed; %s" % err) # initiation report self.log("pygaze initiation report start") self.log("experiment: %s" % self.description) self.log("participant: %s" % self.participant) self.log("display resolution: %sx%s" % (self.dispsize[0], self.dispsize[1])) self.log("display size in cm: %sx%s" % (self.screensize[0], self.screensize[1])) self.log("samplerate: %s Hz" % self.samplerate) self.log("sampletime: %s ms" % self.sampletime) self.log("fixation threshold: %s degrees" % self.fixtresh) self.log("speed threshold: %s degrees/second" % self.spdtresh) self.log("acceleration threshold: %s degrees/second**2" % self.accthresh) self.log("pygaze initiation report end")
imagefile = os.path.join(DIR, 'kitten.png') # # # # # # create instances # initialize the display disp = Display() # initialize a screen scr = Screen() # initialize an EyeTracker tracker = EyeTracker(disp) # initialize a keyboard kb = Keyboard(keylist=['space'], timeout=None) # initialize a sound snd = Sound(soundfile=soundfile) # initialize a Timer timer = Time() # create a new logfile log = Logfile(filename="test") log.write(["test", "time"]) # # # # # # welcome scr.draw_text("Welcome to the PyGaze Supertest!\n\nYou're going to be testing \
import numpy if DISPTYPE == 'psychopy': from psychopy.visual import ImageStim elif DISPTYPE == 'pygame': import pygame # # # # # # INITIALISE # Start a new Display instance to be able to show things on the monitor. # The important parameters will automatically be loaded from the constants. disp = Display() # Initialise a Keyboard instance to detect key presses. Again, important # parameters will be loaded from the constants. kb = Keyboard() # Initialise the EyeTracker and let it know which Display instance to use by # passing it to the EyeTracker. tracker = EyeTracker(disp) # Create a Logfile instance that keeps track of when videos start. log = Logfile() # Write a header to the log file. log.write(['date', 'time', 'trialnr', 'video', 'timestamp']) # # # # # # SCREENS # Create a screen to show instructions on. textscr = Screen()
def __init__(self, libeyelink, tracker): """ Constructor. Arguments: libeyelink -- A libeyelink object. tracker -- An tracker object as returned by pylink.EyeLink(). """ pylink.EyeLinkCustomDisplay.__init__(self) # objects self.libeyelink = libeyelink self.display = libeyelink.display self.screen = Screen(disptype=DISPTYPE, mousevisible=False) self.kb = Keyboard(keylist=None, timeout=0) self.mouse = Mouse(timeout=0) if DISPTYPE == 'pygame': self.kb.set_timeout(timeout=0.001) # If we are using a DISPTYPE that cannot be used directly, we have to # save the camera image to a temporary file on each frame. #if DISPTYPE not in ('pygame', 'psychopy'): import tempfile import os self.tmp_file = os.path.join(tempfile.gettempdir(), '__eyelink__.jpg') # drawing properties self.xc = self.display.dispsize[0]/2 self.yc = self.display.dispsize[1]/2 self.extra_info = True self.ld = 40 # line distance self.fontsize = libeyelink.fontsize self.title = "" self.display_open = True # menu self.menuscreen = Screen(disptype=DISPTYPE, mousevisible=False) self.menuscreen.draw_text(text="Eyelink calibration menu", pos=(self.xc,self.yc-6*self.ld), center=True, font='mono', fontsize=int(2*self.fontsize), antialias=True) self.menuscreen.draw_text(text="%s (pygaze %s, pylink %s)" \ % (libeyelink.eyelink_model, pygaze.version, pylink.__version__), pos=(self.xc,self.yc-5*self.ld), center=True, font='mono', fontsize=int(.8*self.fontsize), antialias=True) self.menuscreen.draw_text(text="Press C to calibrate", pos=(self.xc, self.yc-3*self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text(text="Press V to validate", pos=(self.xc, self.yc-2*self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text(text="Press A to auto-threshold", pos=(self.xc,self.yc-1*self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text(text="Press I to toggle extra info in camera image", pos=(self.xc,self.yc-0*self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text(text="Press Enter to show camera image", pos=(self.xc,self.yc+1*self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text( text="(then change between images using the arrow keys)", pos=(self.xc, self.yc+2*self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text(text="Press Escape to abort experiment", pos=(self.xc, self.yc+4*self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text(text="Press Q to exit menu", pos=(self.xc, self.yc+5*self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) # beeps self.__target_beep__ = Sound(osc='sine', freq=440, length=50, attack=0, decay=0, soundfile=None) self.__target_beep__done__ = Sound(osc='sine', freq=880, length=200, attack=0, decay=0, soundfile=None) self.__target_beep__error__ = Sound(osc='sine', freq=220, length=200, attack=0, decay=0, soundfile=None) # Colors self.color = { pylink.CR_HAIR_COLOR: pygame.Color('white'), pylink.PUPIL_HAIR_COLOR: pygame.Color('white'), pylink.PUPIL_BOX_COLOR: pygame.Color('green'), pylink.SEARCH_LIMIT_BOX_COLOR: pygame.Color('red'), pylink.MOUSE_CURSOR_COLOR: pygame.Color('red'), 'font': pygame.Color('white'), } # Font pygame.font.init() self.font = pygame.font.SysFont('Courier New', 11) # further properties self.state = None self.pal = None self.size = (0,0) self.set_tracker(tracker) self.last_mouse_state = -1 self.bit64 = '64bit' in platform.architecture() self.imagebuffer = self.new_array()
def __init__(self, display, logfile=settings.LOGFILE, \ eventdetection=settings.EVENTDETECTION, \ saccade_velocity_threshold=35, \ saccade_acceleration_threshold=9500, \ blink_threshold=settings.BLINKTHRESH, \ **args): # try to copy docstrings (but ignore it if it fails, as we do # not need it for actual functioning of the code) try: copy_docstr(BaseEyeTracker, EyeLogicTracker) except: # we're not even going to show a warning, since the copied # docstring is useful for code editors; these load the docs # in a non-verbose manner, so warning messages would be lost pass self.disp = display self.screen = Screen() self.dispsize = self.disp.dispsize # display size in pixels self.screensize = settings.SCREENSIZE # display size in cm self.kb = Keyboard(keylist=['space', 'escape', 'q'], timeout=1) self.errorbeep = Sound(osc='saw', freq=100, length=100) # show a message self.screen.clear() self.screen.draw_text( text="Initialising the eye tracker, please wait...", fontsize=20) self.disp.fill(self.screen) self.disp.show() # output file properties self.logfile = logfile # eye tracker properties self._recording = Event() self._recording.clear() self._calibrated = Event() self._calibrated.clear() self.eye_used = 2 # 0=left, 1=right, 2=binocular self.sampleLock = Lock() self.lastSample = None self.maxtries = 100 # number of samples obtained before giving up (for obtaining accuracy and tracker distance information, as well as starting or stopping recording) # event detection properties self.pxfixtresh = 50; self.fixtresh = 1.5 # degrees; maximal distance from fixation start (if gaze wanders beyond this, fixation has stopped) self.fixtimetresh = 100 # milliseconds; amount of time gaze has to linger within self.fixtresh to be marked as a fixation self.spdtresh = saccade_velocity_threshold # degrees per second; saccade velocity threshold self.accthresh = saccade_acceleration_threshold # degrees per second**2; saccade acceleration threshold self.blinkthresh = blink_threshold # milliseconds; blink detection threshold used in PyGaze method self.eventdetection = eventdetection self._log_vars = [ \ "timestampMicroSec", \ "index", \ "porFilteredX", \ "porFilteredY", \ "porLeftX", \ "porLeftY", \ "pupilRadiusLeft", \ "porRightX", \ "porRightY", \ "pupilRadiusRight", \ ] # Open a new log file. dir_name = os.path.dirname(logfile) file_name = os.path.basename(logfile) name, ext = os.path.splitext(file_name) self._data_file_path = os.path.join(dir_name, name+".eyelogic.csv") self._log_file = open(self._data_file_path, "w") # Write a header to the log. header = ["TYPE"] header.extend(self._log_vars) self._sep = ";" self._log_file.write("Sep="+self._sep+"\n") self._log_file.write(self._sep.join(map(str, header))) # Create a lock to prevent simultaneous access to the log file. self._logging_queue = Queue() self._logging_queue_empty = Event() self._logging_queue_empty.set() self._connected = Event() self._connected.set() self._log_counter = 0 self._log_consolidation_freq = 60 self._logging_thread = Thread( target=self.loggingThread, \ name='PyGaze_EyeLogic_Logging', args=[]) global g_api g_api = self # log self.log("pygaze initiation") #self.log("experiment = {}".format(self.description)) #self.log("participant = {}".format(self.participant)) self.log("display resolution = {}x{}".format(self.dispsize[0], \ self.dispsize[1])) self.log("display size in cm = {}x{}".format(self.screensize[0], \ self.screensize[1])) self.log("fixation threshold = {} degrees".format(self.fixtresh)) self.log("speed threshold = {} degrees/second".format(self.spdtresh)) self.log("acceleration threshold = {} degrees/second**2".format( \ self.accthresh)) # connect self.api = ELApi( "PyGaze" ) self.api.registerGazeSampleCallback( gazeSampleCallback ) self.api.registerEventCallback( eventCallback ) resultConnect = self.api.connect() if (resultConnect != ELApi.ReturnConnect.SUCCESS): self._connected.clear() raise Exception("Cannot connect to EyeLogic server = {}".format(errorstringConnect(resultConnect))) self._connected.set() screenConfig = self.api.getScreenConfig() self.log("eye tracker is mounted on screen {}".format(screenConfig.id)) self.rawResolution = (screenConfig.resolutionX, screenConfig.resolutionY) self.log("raw screen resolution = {}x{}".format( self.rawResolution[0], self.rawResolution[1])) self.log("end pygaze initiation") deviceConfig = self.api.getDeviceConfig() if (deviceConfig.deviceSerial == 0): raise Exception("no eye tracking device connected") if (len(deviceConfig.frameRates) == 0): raise Exception("failed to read out device configuration") g_api.sampleRate = deviceConfig.frameRates[0] g_api.sampleTime = 1000.0 / g_api.sampleRate g_api.log("samplerate = {} Hz".format(g_api.sampleRate)) g_api.log("sampletime = {} ms".format(g_api.sampleTime)) self._logging_thread.start() self.screen.clear() self.disp.fill(self.screen) self.disp.show()
def __init__(self, display, logfile=LOGFILE, eventdetection=EVENTDETECTION, \ saccade_velocity_threshold=35, saccade_acceleration_threshold=9500, \ **args): """Initializes the EyeTribeTracker object arguments display -- a pygaze.display.Display instance keyword arguments logfile -- logfile name (string value); note that this is the name for the eye data log file (default = LOGFILE) """ # try to copy docstrings (but ignore it if it fails, as we do # not need it for actual functioning of the code) try: copy_docstr(BaseEyeTracker, EyeTribeTracker) except: # we're not even going to show a warning, since the copied # docstring is useful for code editors; these load the docs # in a non-verbose manner, so warning messages would be lost pass # object properties self.disp = display self.screen = Screen() self.dispsize = DISPSIZE # display size in pixels self.screensize = SCREENSIZE # display size in cm self.kb = Keyboard(keylist=['space', 'escape', 'q'], timeout=1) self.errorbeep = Sound(osc='saw',freq=100, length=100) # output file properties self.outputfile = logfile # eye tracker properties self.connected = False self.recording = False self.errdist = 2 # degrees; maximal error for drift correction self.pxerrdist = 30 # initial error in pixels self.maxtries = 100 # number of samples obtained before giving up (for obtaining accuracy and tracker distance information, as well as starting or stopping recording) self.prevsample = (-1,-1) self.prevps = -1 # event detection properties self.fixtresh = 1.5 # degrees; maximal distance from fixation start (if gaze wanders beyond this, fixation has stopped) self.fixtimetresh = 100 # milliseconds; amount of time gaze has to linger within self.fixtresh to be marked as a fixation self.spdtresh = saccade_velocity_threshold # degrees per second; saccade velocity threshold self.accthresh = saccade_acceleration_threshold # degrees per second**2; saccade acceleration threshold self.eventdetection = eventdetection self.set_detection_type(self.eventdetection) self.weightdist = 10 # weighted distance, used for determining whether a movement is due to measurement error (1 is ok, higher is more conservative and will result in only larger saccades to be detected) # connect to the tracker self.eyetribe = EyeTribe(logfilename=logfile) # get info on the sample rate self.samplerate = self.eyetribe._samplefreq self.sampletime = 1000.0 * self.eyetribe._intsampletime # initiation report self.log("pygaze initiation report start") self.log("display resolution: %sx%s" % (self.dispsize[0],self.dispsize[1])) self.log("display size in cm: %sx%s" % (self.screensize[0],self.screensize[1])) self.log("samplerate: %.2f Hz" % self.samplerate) self.log("sampletime: %.2f ms" % self.sampletime) self.log("fixation threshold: %s degrees" % self.fixtresh) self.log("speed threshold: %s degrees/second" % self.spdtresh) self.log("acceleration threshold: %s degrees/second**2" % self.accthresh) self.log("pygaze initiation report end")
def __init__(self, display, address='192.168.71.50', udpport=49152, logfile=settings.LOGFILE, eventdetection=settings.EVENTDETECTION, saccade_velocity_threshold=35, saccade_acceleration_threshold=9500, blink_threshold=settings.BLINKTHRESH, **args): """Initializes a TobiiProGlassesTracker instance arguments display -- a pygaze.display.Display instance keyword arguments address -- internal ipv4/ipv6 address for Tobii Pro Glasses 2 (default = '192.168.71.50', for IpV6 address use square brackets [fe80::xxxx:xxxx:xxxx:xxxx]) udpport -- UDP port number for Tobii Pro Glasses data streaming (default = 49152) """ # try to copy docstrings (but ignore it if it fails, as we do # not need it for actual functioning of the code) try: copy_docstr(BaseEyeTracker, TobiiProGlassesTracker) except: # we're not even going to show a warning, since the copied # docstring is useful for code editors; these load the docs # in a non-verbose manner, so warning messages would be lost pass # object properties self.disp = display self.screen = Screen() self.dispsize = settings.DISPSIZE # display size in pixels self.screensize = settings.SCREENSIZE # display size in cm self.screendist = settings.SCREENDIST # distance between participant and screen in cm self.pixpercm = (self.dispsize[0] / float(self.screensize[0]) + self.dispsize[1] / float(self.screensize[1])) / 2.0 self.kb = Keyboard(keylist=['space', 'escape', 'q'], timeout=1) self.errorbeep = Sound(osc='saw', freq=100, length=100) # output file properties self.outputfile = logfile self.description = "experiment" # TODO: EXPERIMENT NAME self.participant = "participant" # TODO: PP NAME # eye tracker properties self.eye_used = 0 # 0=left, 1=right, 2=binocular self.left_eye = 0 self.right_eye = 1 self.binocular = 2 self.maxtries = 100 # number of samples obtained before giving up (for obtaining accuracy and tracker distance information, as well as starting or stopping recording) self.prevsample = (-1, -1) # validation properties self.nvalsamples = 1000 # samples for one validation point # event detection properties self.fixtresh = 1.5 # degrees; maximal distance from fixation start (if gaze wanders beyond this, fixation has stopped) self.fixtimetresh = 100 # milliseconds; amount of time gaze has to linger within self.fixtresh to be marked as a fixation self.spdtresh = saccade_velocity_threshold # degrees per second; saccade velocity threshold self.accthresh = saccade_acceleration_threshold # degrees per second**2; saccade acceleration threshold self.blinkthresh = blink_threshold # milliseconds; blink detection threshold used in PyGaze method self.eventdetection = eventdetection self.set_detection_type(self.eventdetection) self.weightdist = 10 # weighted distance, used for determining whether a movement is due to measurement error (1 is ok, higher is more conservative and will result in only larger saccades to be detected) self.tobiiglasses = TobiiGlassesController(udpport, address) self.triggers_values = {} self.logging = False self.current_recording_id = None self.current_participant_id = None self.current_project_id = None
def __init__(self, display, logfile=settings.LOGFILE, eventdetection=settings.EVENTDETECTION, saccade_velocity_threshold=35, saccade_acceleration_threshold=9500, blink_threshold=settings.BLINKTHRESH, **args): """Initializes a TobiiProTracker instance arguments display -- a pygaze.display.Display instance keyword arguments None """ self.gaze = [] self.disp = display # initialize a screen self.screen = Screen() # initialize keyboard self.kb = Keyboard(keylist=['space', 'escape', 'q','enter'], timeout=1) self.recording = False self.screendist = settings.SCREENDIST if hasattr(settings, 'TRACKERSERIALNUMBER'): # Search for a specific eye tracker self.eyetrackers = [t for t in tr.find_all_eyetrackers() if t.serial_number == settings.TRACKERSERIALNUMBER] else: # Search for all eye trackers (The first one found will be selected) self.eyetrackers = tr.find_all_eyetrackers() if self.eyetrackers: self.eyetracker = self.eyetrackers[0] else: print("WARNING! libtobii.TobiiProTracker.__init__: no eye trackers found!") self.LEFT_EYE = 0 self.RIGHT_EYE = 1 self.BINOCULAR = 2 self.eye_used = 0 # 0=left, 1=right, 2=binocular # calibration and validation points lb = 0.1 # left bound xc = 0.5 # horizontal center rb = 0.9 # right bound ub = 0.1 # upper bound yc = 0.5 # vertical center bb = 0.9 # bottom bound self.points_to_calibrate = [self._norm_2_px(p) for p in [(lb, ub), (xc,ub), (rb, ub), (lb,yc), (xc, yc),(rb,yc), (lb, bb),(xc,bb),(rb, bb)]] # event detection properties self.fixtresh = 1.5 # degrees; maximal distance from fixation start (if gaze wanders beyond this, fixation has stopped) self.fixtimetresh = 100 # milliseconds; amount of time gaze has to linger within self.fixtresh to be marked as a fixation self.spdtresh = saccade_velocity_threshold # degrees per second; saccade velocity threshold self.accthresh = saccade_acceleration_threshold # degrees per second**2; saccade acceleration threshold self.blinkthresh = blink_threshold # milliseconds; blink detection threshold used in PyGaze method self.eventdetection = eventdetection self.weightdist = 10 # weighted distance, used for determining whether a movement is due to measurement error (1 is ok, higher is more conservative and will result in only larger saccades to be detected) self.screensize = settings.SCREENSIZE # display size in cm self.pixpercm = (self.disp.dispsize[0] / float(self.screensize[0]) + self.disp.dispsize[1] / float(self.screensize[1])) / 2.0 self.errdist = 2 # degrees; maximal error for drift correction self.pxerrdist = self._deg2pix(self.screendist, self.errdist, self.pixpercm) self.event_data = [] self.t0 = None self._write_enabled = True self.datafile = open("{0}_TOBII_output.tsv".format(logfile), 'w') # initiation report self.datafile.write("pygaze initiation report start\n") self.datafile.write("display resolution: %sx%s\n" % (self.disp.dispsize[0], self.disp.dispsize[1])) self.datafile.write("display size in cm: %sx%s\n" % (self.screensize[0], self.screensize[1])) self.datafile.write("fixation threshold: %s degrees\n" % self.fixtresh) self.datafile.write("speed threshold: %s degrees/second\n" % self.spdtresh) self.datafile.write("acceleration threshold: %s degrees/second**2\n" % self.accthresh) self.datafile.write("pygaze initiation report end\n")
def __init__(self, libeyelink, tracker): """ Constructor. Arguments: libeyelink -- A libeyelink object. tracker -- An tracker object as returned by pylink.EyeLink(). """ pylink.EyeLinkCustomDisplay.__init__(self) # objects self.libeyelink = libeyelink self.display = libeyelink.display self.screen = Screen(disptype=settings.DISPTYPE, mousevisible=False) self.kb = Keyboard(keylist=None, timeout=0) self.mouse = Mouse(timeout=0) if settings.DISPTYPE == 'pygame': self.kb.set_timeout(timeout=0.001) # If we are using a DISPTYPE that cannot be used directly, we have to # save the camera image to a temporary file on each frame. #if DISPTYPE not in ('pygame', 'psychopy'): import tempfile import os self.tmp_file = os.path.join(tempfile.gettempdir(), '__eyelink__.jpg') # drawing properties self.xc = self.display.dispsize[0] / 2 self.yc = self.display.dispsize[1] / 2 self.extra_info = True self.ld = 40 # line distance self.fontsize = libeyelink.fontsize self.title = "" self.display_open = True self.draw_menu_screen() # beeps self.__target_beep__ = Sound(osc='sine', freq=440, length=50, attack=0, decay=0, soundfile=None) self.__target_beep__done__ = Sound(osc='sine', freq=880, length=200, attack=0, decay=0, soundfile=None) self.__target_beep__error__ = Sound(osc='sine', freq=220, length=200, attack=0, decay=0, soundfile=None) # Colors self.color = { pylink.CR_HAIR_COLOR: pygame.Color('white'), pylink.PUPIL_HAIR_COLOR: pygame.Color('white'), pylink.PUPIL_BOX_COLOR: pygame.Color('green'), pylink.SEARCH_LIMIT_BOX_COLOR: pygame.Color('red'), pylink.MOUSE_CURSOR_COLOR: pygame.Color('red'), 'font': pygame.Color('white'), } # Font pygame.font.init() self.font = pygame.font.SysFont('Courier New', 11) # further properties self.state = None self.pal = None self.size = (0, 0) self.set_tracker(tracker) self.last_mouse_state = -1 self.bit64 = '64bit' in platform.architecture() self.imagebuffer = self.new_array()
# monitor) disp = Display() # create a new Screen (to use as a canvas to draw on) scr = Screen() # Create two Sounds, one for nice and one for stern # feedback sine = Sound(osc='sine', freq=4000, length=500) noise = Sound(osc='whitenoise', length=500) # a list of vowels vowels = ['a', 'e', 'i', 'o', 'u', 'y'] # create a new Keyboard instance, to monitor key presses kb = Keyboard(keylist=vowels, timeout=None) # randomly choose one vowel letter = random.choice(vowels) # draw the vowel on a Screen scr.draw_text(text=letter, fontsize=128) # fill the Display with a Screen and update the monitor disp.fill(scr) disp.show() # wait for a response key, presstime = kb.get_key() # check if the pressed key matches the displayed letter
from pygaze.logfile import Logfile import pygaze.libtime as timer from custom import calc_angular_dist, generate_oris, generate_locs, pol2car, StimScreen import numpy # # # # # # PREPARATION # Intialise the Display. disp = Display() # Initialise the basic input devices. kb = Keyboard(keylist=None, timeout=None) mouse = Mouse(mousebuttonlist=None, timeout=None) # Initialise a log. log = Logfile() header = ['trialnr', 'nstim', 'fixonset', 'stimonset', 'maintenanceonset', \ 'probeonset', 'RT', 'response'] for i in range(max(NSTIM)): header.extend(['stimx%d' % (i), 'stimy%d' % (i), 'stimori%d' % (i), \ 'stimerror%d' % (i)]) header.extend(['E', 'X', 'T']) for i in range(max(NSTIM)-1): header.append('NT%d' % i) log.write(header) # Initialise a blank Screen for ad-hoc drawing.
def showCalibrationResults(logfiledir, calibration_result): """shows calibration results on control monitor (screen 0)""" DS = CONTROL_DISPSIZE #(2048, 1152) win = visual.Window(size=DS, fullscr=True, winType='pyglet', screen=1, units='pix', color='black') kb = Keyboard(keylist=['space', 'r'], timeout=0.1) # target points (calibration points) to draw clickable circles targetPoints = [ tobii_norm_2_psy_px(p) for p in [(0.5, 0.5), (0.1, 0.9), (0.1, 0.1), (0.9, 0.9), (0.9, 0.1)] ] clickable_circles = [] for i in range(len(targetPoints)): clickable_circles.append( visual.Circle(win, lineColor='red', pos=targetPoints[i], radius=DS[0] / 120, fillColor='green')) mouse = Mouse(visible=True, win=win) mousePressed = [] for i in range(len(targetPoints)): mousePressed.append(False) # texts on screen infoText = visual.TextStim( win, text= "Press the \'r\' key to recalibrate or \'space\' to continue and start showing stimuli", pos=(0, -0.4 * DS[1]), color="white") infoText.autoDraw = True leftEyeText = visual.TextStim(win, "Left Eye", pos=(0, 0.45 * DS[1]), color="red") leftEyeText.autoDraw = True rightEyeText = visual.TextStim(win, "Right Eye", pos=(0, 0.47 * DS[1]), color="blue") rightEyeText.autoDraw = True recalibration_points = [] # target calibration points and gaze sample points for point in calibration_result.calibration_points: target = visual.Circle(win, lineColor='green', pos=tobii_norm_2_psy_px( point.position_on_display_area), radius=DS[0] / 200, fillColor=None) target.autoDraw = True for sample in point.calibration_samples: if sample.left_eye.validity == tr.VALIDITY_VALID_AND_USED: leftGazePoint = visual.Circle( win, lineColor="red", pos=tobii_norm_2_psy_px( sample.left_eye.position_on_display_area), radius=DS[0] / 450, lineWidth=DS[0] / 450, fillColor="red") leftGazePoint.autoDraw = True if sample.right_eye.validity == tr.VALIDITY_VALID_AND_USED: rightGazePoint = visual.Circle( win, lineColor="blue", pos=tobii_norm_2_psy_px( sample.right_eye.position_on_display_area), radius=DS[0] / 450, lineWidth=DS[0] / 450, fillColor="blue") rightGazePoint.autoDraw = True key = kb.get_key()[0] k = 0 while (not (key == 'space' or key == 'r')): key = kb.get_key()[0] for i in range(len(targetPoints)): if (mouse.isPressedIn(clickable_circles[i], buttons=[0])): if not mousePressed[i] and k == 0: clickable_circles[i].autoDraw = True mousePressed[i] = True recalibration_points.append( calibration_result.calibration_points[i]. position_on_display_area) k = 20 if mousePressed[i] and k == 0: clickable_circles[i].autoDraw = False mousePressed[i] = False recalibration_points.remove( calibration_result.calibration_points[i]. position_on_display_area) k = 20 win.flip() if k > 0: k -= 1 if (key == 'space'): win.getMovieFrame( ) # Defaults to front buffer, I.e. what's on screen now. win.saveMovieFrames("{0}\{1}".format(logfiledir, "calibration_results.png")) win.close() return [] elif (key == 'r'): win.close() if len(recalibration_points) == 0: return [1] else: return recalibration_points
def __init__(self, display, resolution=settings.DISPSIZE, data_file=settings.LOGFILENAME + ".edf", fg_color=settings.FGC, bg_color=settings.BGC, eventdetection=settings.EVENTDETECTION, saccade_velocity_threshold=35, saccade_acceleration_threshold=9500, blink_threshold=settings.BLINKTHRESH, force_drift_correct=True, pupil_size_mode=settings.EYELINKPUPILSIZEMODE, **args): """See pygaze._eyetracker.baseeyetracker.BaseEyeTracker""" # try to import copy docstring (but ignore it if it fails, as we do # not need it for actual functioning of the code) try: copy_docstr(BaseEyeTracker, libeyelink) except: # we're not even going to show a warning, since the copied # docstring is useful for code editors; these load the docs # in a non-verbose manner, so warning messages would be lost pass global _eyelink # Make sure that we have a valid data file. The local_data_file may # contain a folder. The eyelink_data_file is only a basename, i.e. # without folder. The eyelink_data_file must be at most eight characters # and end with a `.edf` extension. self.local_data_file = data_file self.eyelink_data_file = os.path.basename(data_file) stem, ext = os.path.splitext(self.eyelink_data_file) if len(stem) > 8 or ext.lower() != '.edf': raise Exception( "The EyeLink cannot handle filenames longer than eight " "characters (excluding '.edf' extension).") # properties self.display = display self.fontsize = 18 self.scr = Screen(disptype=settings.DISPTYPE, mousevisible=False) self.kb = Keyboard(keylist=["escape", "q"], timeout=1) self.resolution = resolution self.recording = False self.saccade_velocity_treshold = saccade_velocity_threshold self.saccade_acceleration_treshold = saccade_acceleration_threshold self.blink_threshold = blink_threshold self.eye_used = None self.left_eye = 0 self.right_eye = 1 self.binocular = 2 self.pupil_size_mode = pupil_size_mode self.prevsample = (-1, -1) self.prevps = -1 # event detection properties # degrees; maximal distance from fixation start (if gaze wanders beyond # this, fixation has stopped) self.fixtresh = 1.5 # milliseconds; amount of time gaze has to linger within self.fixtresh # to be marked as a fixation self.fixtimetresh = 100 # degrees per second; saccade velocity threshold self.spdtresh = self.saccade_velocity_treshold # degrees per second**2; saccade acceleration threshold self.accthresh = self.saccade_acceleration_treshold self.set_detection_type(eventdetection) # weighted distance, used for determining whether a movement is due to # measurement error (1 is ok, higher is more conservative and will # result in only larger saccades to be detected) self.weightdist = 10 # distance between participant and screen in cm self.screendist = settings.SCREENDIST # distance between participant and screen in cm self.screensize = settings.SCREENSIZE self.pixpercm = (self.resolution[0]/float(self.screensize[0]) + \ self.resolution[1]/float(self.screensize[1])) / 2.0 # only initialize eyelink once if _eyelink == None: try: _eyelink = pylink.EyeLink() except: raise Exception( "Error in libeyelink.libeyelink.__init__(): Failed to " "connect to the tracker!") # determine software version of tracker self.tracker_software_ver = 0 self.eyelink_ver = pylink.getEYELINK().getTrackerVersion() if self.eyelink_ver == 3: tvstr = pylink.getEYELINK().getTrackerVersionString() vindex = tvstr.find("EYELINK CL") self.tracker_software_ver = int(float(tvstr[(vindex + \ len("EYELINK CL")):].strip())) if self.eyelink_ver == 1: self.eyelink_model = 'EyeLink I' elif self.eyelink_ver == 2: self.eyelink_model = 'EyeLink II' elif self.eyelink_ver == 3: self.eyelink_model = 'EyeLink 1000' else: self.eyelink_model = 'EyeLink (model unknown)' # Open graphics self.eyelink_graphics = EyelinkGraphics(self, _eyelink) pylink.openGraphicsEx(self.eyelink_graphics) # Optionally force drift correction. For some reason this must be done # as (one of) the first things, otherwise a segmentation fault occurs. if force_drift_correct: try: self.send_command('driftcorrect_cr_disable = OFF') except: print('Failed to force drift correction (EyeLink 1000 only)') # Set pupil-size mode if self.pupil_size_mode == 'area': pylink.getEYELINK().setPupilSizeDiameter(False) elif self.pupil_size_mode == 'diameter': pylink.getEYELINK().setPupilSizeDiameter(True) else: raise Exception( "pupil_size_mode should be 'area' or 'diameter', not %s" \ % self.pupil_size_mode) pylink.getEYELINK().openDataFile(self.eyelink_data_file) pylink.flushGetkeyQueue() pylink.getEYELINK().setOfflineMode() # notify eyelink of display resolution self.send_command("screen_pixel_coords = 0 0 %d %d" % \ (self.resolution[0], self.resolution[1])) # get some configuration stuff if self.eyelink_ver >= 2: self.send_command("select_parser_configuration 0") if self.eyelink_ver == 2: # turn off scenelink camera stuff self.send_command("scene_camera_gazemap = NO") # set EDF file contents (this specifies which data is written to the EDF # file) self.send_command( "file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON" ) if self.tracker_software_ver >= 4: self.send_command( "file_sample_data = LEFT,RIGHT,GAZE,AREA,GAZERES,STATUS,HTARGET" ) else: self.send_command( "file_sample_data = LEFT,RIGHT,GAZE,AREA,GAZERES,STATUS") # set link data (this specifies which data is sent through the link and # thus can be used in gaze contingent displays) self.send_command( "link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,BUTTON") if self.tracker_software_ver >= 4: self.send_command( "link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,HTARGET" ) else: self.send_command( "link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS") # not quite sure what this means (according to Sebastiaan Mathot, it # might be the button that is used to end drift correction?) self.send_command("button_function 5 'accept_target_fixation'") if not self.connected(): raise Exception( "Error in libeyelink.libeyelink.__init__(): Failed to connect " "to the eyetracker!")
def __init__(self, libeyelink, tracker): """ Constructor. Arguments: libeyelink -- A libeyelink object. tracker -- An tracker object as returned by pylink.EyeLink(). """ pylink.EyeLinkCustomDisplay.__init__(self) # objects self.libeyelink = libeyelink self.display = libeyelink.display self.screen = Screen(disptype=settings.DISPTYPE, mousevisible=False) self.kb = Keyboard(keylist=None, timeout=1) self.mouse = Mouse(timeout=1) # If we are using a DISPTYPE that cannot be used directly, we have to # save the camera image to a temporary file on each frame. #if DISPTYPE not in ('pygame', 'psychopy'): import tempfile import os self.tmp_file = os.path.join(tempfile.gettempdir(), "__eyelink__.jpg") # drawing properties self.xc = self.display.dispsize[0] / 2 self.yc = self.display.dispsize[1] / 2 self.extra_info = True self.ld = 40 # line distance self.fontsize = libeyelink.fontsize self.title = "" self.display_open = True self.draw_menu_screen() # A crosshair is drawn onto the eye image. This should be scaled in # pylink 1.1.0.5 (tested on Python 2.7) but not on pylink 1.11.0.0 # (tested on Python 3.6). I'm not sure when this change happened, so # it's quite likely we'll have to update the minor version used here. pl_version = pylink.__version__.split(".") if int(pl_version[0]) > 1 or int(pl_version[1]) >= 11: self.scale_lines_in_eye_image = False else: self.scale_lines_in_eye_image = True # Beeps self.__target_beep__ = Sound(osc="sine", freq=440, length=50, attack=0, decay=0, soundfile=None) self.__target_beep__done__ = Sound(osc="sine", freq=880, length=200, attack=0, decay=0, soundfile=None) self.__target_beep__error__ = Sound(osc="sine", freq=220, length=200, attack=0, decay=0, soundfile=None) # Colors self.color = { pylink.CR_HAIR_COLOR: pygame.Color("white"), pylink.PUPIL_HAIR_COLOR: pygame.Color("white"), pylink.PUPIL_BOX_COLOR: pygame.Color("green"), pylink.SEARCH_LIMIT_BOX_COLOR: pygame.Color("red"), pylink.MOUSE_CURSOR_COLOR: pygame.Color("red"), 'font': pygame.Color("white"), } # Font pygame.font.init() self.font = pygame.font.SysFont("Courier New", 11) # further properties self.state = None self.pal = None self.size = (0, 0) self.set_tracker(tracker) self.last_mouse_state = -1 self.bit64 = "64bit" in platform.architecture() self.imagebuffer = self.new_array()