-
Notifications
You must be signed in to change notification settings - Fork 0
/
EyeTracking.py
349 lines (280 loc) · 14.4 KB
/
EyeTracking.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pylink
import gc
import time
import os, sys
import numpy as np
######################################################################################
##################### FAIRE LA TRADUCTION !!!! #######################################
######################################################################################
spath = os.path.dirname(sys.argv[0])
if len(spath) !=0: os.chdir(spath)
eyelinktracker = pylink.EyeLink()
eyelink = pylink.getEYELINK()
Oeil_droit = 1
#Oeil_gauche = 0
#Binoculaire = 2
class EyeTracking(object):
def __init__(self, screen_width_px, screen_height_px, dot_size, N_trials, observer, datadir, timeStr):
self.screen_width_px = screen_width_px
self.screen_height_px = screen_height_px
self.dot_size = dot_size
self.N_trials = N_trials
self.mode = 'enregistrement'
self.edfFileName = "%s.EDF"%(observer) # ne doit pas contenir plus de 8 caractères -- Must contains no more than 8 characters
self.edfFileName_2 = os.path.join(datadir, self.mode + '_' + observer + '_' + timeStr + '.edf')
#######################################################
# Start + End TRIAL !!!
#######################################################
def End_trial(self):
pylink.endRealTimeMode() # Fin mode temps réel
pylink.pumpDelay(100) # ajout 100ms de donnée pour les évenement finaux
eyelink.stopRecording()
while eyelink.getkey() :
pass
def Start_trial(self, trial):
# Titre essai en cours au bas de l'écran d'eyetracker
print(trial)
message = "record_status_message 'Trial %d/%d'" %(trial + 1, self.N_trials)
eyelink.sendCommand(message)
# EyeLink Data Viewer définit le début d'un essai par le message TRIALID.
msg = "TRIALID %d" % trial
eyelink.sendMessage(msg)
msg = "!V TRIAL_VAR_DATA %d" % trial
eyelink.sendMessage(msg)
# Commutez le tracker sur ide et donnez-lui le temps d'effectuer un interrupteur de mode complet
eyelink.setOfflineMode()
pylink.msecDelay(50)
# Commencez à enregistrer des échantillons et des événements sur le fichier edf et sur le lien.
error = eyelink.startRecording(1, 1, 1, 1) # 0 si tout se passe bien !
if error :
self.End_trial()
print('error =', error)
#return error
#gc.disable() # Désactiver la collecte python pour éviter les retards TESTER ENLEVER !!!
pylink.beginRealTimeMode(100) # Commencer le mode temps réel
# Lit et supprime les événements dans la file d'attente de données jusqu'à ce qu'ils soient dans un bloc d'enregistrement.
try:
eyelink.waitForBlockStart(100,1,0)
except RuntimeError:
if pylink.getLastError()[0] == 0: # Temps d'attente expiré sans données de lien
self.End_trial()
eyelink.sendMessage("TRIAL ERROR")
print ("ERROR: No link samples received!")
return pylink.TRIAL_ERROR
else:
raise
def Fixation(self, point, tps_start_fix, win, escape_possible) :
# Tps de fixation à durée variable
duree_fixation = np.random.uniform(0.4, 0.8) # durée du point de fixation (400-800 ms)
tps_fixation = 0
escape_possible(self.mode)
# ---------------------------------------------------
# Initialiser les données d'échantillons et les variables d'entrée de bouton
nSData = None
sData = None
button = 0
eye_used = eyelink.eyeAvailable() # Déterminez quel oeil (s) est disponible
if eye_used == Oeil_droit:
eyelink.sendMessage("EYE_USED 1 RIGHT")
else:
print ("Error in getting the eye information!")
self.End_trial()
return pylink.TRIAL_ERROR
while (tps_fixation < duree_fixation) :
escape_possible(self.mode)
tps_actuel = time.time()
tps_fixation = tps_actuel - tps_start_fix
nSData = eyelink.getNewestSample() # Vérifiez la nouvelle mise à jour de l'échantillon
if(nSData != None and (sData == None or nSData.getTime() != sData.getTime())):
sData = nSData
escape_possible(self.mode)
# Détectez si le nouvel échantillon a des données pour l'oeil en cours de suivi
if eye_used == Oeil_droit and sData.isRightSample():
# Obtenir l'échantillon sous la forme d'une structure événementielle
gaze = sData.getRightEye().getGaze()
valid_gaze_pos = isinstance(gaze, (tuple, list))
if valid_gaze_pos : #Si les données sont valides, comparez la position du regard avec les limites de la fenêtre de tolérance
escape_possible(self.mode)
x_eye = gaze[0]
y_eye = gaze[1]
point.draw()
win.flip()
# Taille de la fenêtre de FIXATION
W_FW = self.dot_size + 120 # Largeur en pixels
H_FW = self.dot_size + 120 # Hauteur
diffx = abs(x_eye-self.screen_width_px/2) - W_FW/2
diffy = abs(y_eye-self.screen_height_px/2) - H_FW/2
if diffx>0 or diffy>0 :
escape_possible(self.mode)
win.flip()
tps_start_fix = time.time()
else : #Si les données sont invalides (par exemple, en cas de clignotement)
escape_possible(self.mode)
win.flip()
point.draw()
win.flip()
core.wait(0.1)
tps_start_fix = time.time()
else :
escape_possible(self.mode)
error = eyelink.isRecording()
if(error != 0) :
core.wait(0.1)
tps_start_fix = time.time()
#######################################################
# Start + End EXP !!!
#######################################################
def Start_exp(self) :
# ---------------------------------------------------
# point de départ de l'expérience
# ---------------------------------------------------
pylink.openGraphics((self.screen_width_px, self.screen_height_px),32) # Initialise les graphiques
eyelink.openDataFile(self.edfFileName) # Ouvre le fichier EDF.
# réinitialise les touches et réglez le mode de suivi en mode hors connexion.
pylink.flushGetkeyQueue()
eyelink.setOfflineMode()
# Définit le système de coordonnées d'affichage et envoie un message à cet effet au fichier EDF;
eyelink.sendCommand("screen_pixel_coords = 0 0 %d %d" %(self.screen_width_px - 1, self.screen_height_px - 1))
eyelink.sendMessage("DISPLAY_COORDS 0 0 %d %d" %(self.screen_width_px - 1, self.screen_height_px - 1))
# ---------------------------------------------------
# NETOYER ??? version = 3
# ---------------------------------------------------
tracker_software_ver = 0
eyelink_ver = eyelink.getTrackerVersion()
if eyelink_ver == 3:
tvstr = eyelink.getTrackerVersionString()
vindex = tvstr.find("EYELINK CL")
tracker_software_ver = int(float(tvstr[(vindex + len("EYELINK CL")):].strip()))
if eyelink_ver>=2:
eyelink.sendCommand("select_parser_configuration 0")
if eyelink_ver == 2: # Éteignez les caméras scenelink
eyelink.sendCommand("scene_camera_gazemap = NO")
else:
eyelink.sendCommand("saccade_velocity_threshold = 35")
eyelink.sendCommand("saccade_acceleration_threshold = 9500")
# Définir le contenu du fichier EDF
eyelink.sendCommand("file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT")
if tracker_software_ver>=4:
eyelink.sendCommand("file_sample_data = LEFT,RIGHT,GAZE,AREA,GAZERES,STATUS,HTARGET,INPUT")
else:
eyelink.sendCommand("file_sample_data = LEFT,RIGHT,GAZE,AREA,GAZERES,STATUS,INPUT")
# Définir les données du lien (utilisé pour le curseur du regard)
eyelink.sendCommand("link_event_filter = LEFT,RIGHT,FIXATION,FIXUPDATE,SACCADE,BLINK,BUTTON,INPUT")
if tracker_software_ver>=4:
eyelink.sendCommand("link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,HTARGET,INPUT")
else:
eyelink.sendCommand("link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT")
#############################
# Calibration
#############################
pylink.setCalibrationColors((255, 255, 255),(128, 128, 128)) # Définit couleur de la cible d'étalonnage (blanc) et de l'arrière-plan (gris)
pylink.setTargetSize(self.screen_width_px//70, self.screen_width_px//300) # Définit taille de la cible d'étalonnage
pylink.setCalibrationSounds("", "", "")
pylink.setDriftCorrectSounds("", "off", "off")
def End_exp(self):
# Transfert et nettoyage de fichiers!
eyelink.setOfflineMode()
pylink.msecDelay(500)
# Fermez le fichier et transférez-le sur Display PC
eyelink.closeDataFile()
eyelink.receiveDataFile(self.edfFileName, self.edfFileName_2)
eyelink.close()
# Fermer les graphiques de l'expérience
pylink.closeGraphics()
def fin_enregistrement(self):
#eyelink.sendMessage("TRIAL_RESULT %d" % button) # VOIR POUR BUTTON OU IL EST DEFINI !!!
ret_value = eyelink.getRecordingStatus() # état de l'enregistrement de sortie
self.End_trial()
#gc.enable() # Réactivez la collecte python pour nettoyer la mémoire à la fin de l'essai
pylink.endRealTimeMode()
return ret_value
#######################################################
# Check Calibration + Correction !!!
#######################################################
def check(self):
if(not eyelink.isConnected() or eyelink.breakPressed()):
self.End_trial()
self.End_exp()
# break
eyelink.flushKeybuttons(0) # Réinitialise touches et boutons sur tracker
error = eyelink.isRecording() # Vérifiez d'abord si l'enregistrement est interrompu
if error != 0:
self.End_trial()
print ('error =', error)
if(eyelink.breakPressed()): # Vérifie fin du programme ou les touches ALT-F4 ou CTRL-C
eyelink.sendMessage("EXPERIMENT ABORTED")
print("EXPERIMENT ABORTED")
self.End_trial()
self.End_exp()
elif(eyelink.escapePressed()): # Vérifier si escape pressé
eyelink.sendMessage("TRIAL ABORTED")
print("TRIAL ABORTED")
self.End_trial()
def check_trial(self, ret_value) :
if (ret_value == pylink.TRIAL_OK):
eyelink.sendMessage("TRIAL OK")
print("TRIAL OK")
#break
elif (ret_value == pylink.SKIP_TRIAL):
eyelink.sendMessage("TRIAL ABORTED")
print("TRIAL ABORTED")
#break
elif (ret_value == pylink.ABORT_EXPT):
eyelink.sendMessage("EXPERIMENT ABORTED")
print("EXPERIMENT ABORTED")
elif (ret_value == pylink.REPEAT_TRIAL):
eyelink.sendMessage("TRIAL REPEATED")
print("TRIAL REPEATED")
else:
eyelink.sendMessage("TRIAL ERROR")
print("TRIAL ERROR")
#break
def calibration(self) :
eyelink.doTrackerSetup() # configuration du suivi
def drift_correction(self) :
try:
eyelink.doDriftCorrect((self.screen_width_px/2), (self.screen_height_px/2), 1, 1)
except:
eyelink.doTrackerSetup()
pylink.msecDelay(50)
#######################################################
# Stimulus + Target !!!
#######################################################
def StimulusON(self, tps_start_fix):
eyelink.sendMessage('StimulusOn')
eyelink.sendMessage("%d DISPLAY ON" %tps_start_fix)
eyelink.sendMessage("SYNCTIME %d" %tps_start_fix)
eyelink.sendCommand("clear_screen 0")
# ---------------------------------------------------
# BOITE FIXATION
# ---------------------------------------------------
colour = 7 # blanc
x = self.screen_width_px/2
y = self.screen_height_px/2
# Taille de la fenêtre de FIXATION
W_FW = self.dot_size + 120 # Largeur en pixels
H_FW = self.dot_size + 120 # Hauteur
boite_fixation = [x-W_FW/2, y-H_FW/2, x+W_FW/2, y+H_FW/2]
eyelink.sendCommand('draw_box %d %d %d %d %d'%(boite_fixation[0], boite_fixation[1], boite_fixation[2], boite_fixation[3], colour))
def StimulusOFF(self) :
eyelink.sendCommand("clear_screen 0") # Efface la boîte de l'écran Eyelink
eyelink.sendMessage('StimulusOff')
def TargetON(self) :
eyelink.sendMessage('TargetOn')
# ---------------------------------------------------
# BOITE MOUVEMENT
# ---------------------------------------------------
colour = 7 # blanc
x = self.screen_width_px/2
y = self.screen_height_px/2
# Taille de la fenêtre de MOUVEMENT
W_MW = 2*(0.9*(self.screen_width_px/2)) # Largeur en pixels (égale à 2 * longueur déplacement cible)
H_MW = 200 # Hauteur
boite_mouvement = [x-W_MW/2, y-H_MW/2, x+W_MW/2, y+H_MW/2]
eyelink.sendCommand('draw_box %d %d %d %d %d'%(boite_mouvement[0], boite_mouvement[1], boite_mouvement[2], boite_mouvement[3], colour))
def TargetOFF(self) :
eyelink.sendMessage('TargetOff')
eyelink.sendCommand("clear_screen 0") # Efface la boîte de l'écran Eyelink
#######################################################