Sample 02: Using units (for PsychoPy)¶
What does this sample do?¶
This sample shows how to use 'units' option when using PsychoPy. Codes are the same as those of Sample 01: Controlling SimpleGazeTracker from GazeParser.TrackingTools except units.
Note that units of gaze positions are always 'pix' in the SimpleGazeTracker datafile. In the sample script, calibration target locations are defined as following.
calTargetPos = [[ 0, 0],
[-0.6,-0.6],[-0.6, 0],[-0.6,0.6],
[ 0,-0.6],[ 0, 0],[ 0,0.6],
[ 0.6,-0.6],[ 0.6, 0],[ 0.6,0.6]]
tracker.setCalibrationScreen(win)
tracker.setCalibrationTargetPositions(calarea, calTargetPos,units='norm')
setCalibrationTargetPositions()
. translates
calibation target positions into 'pix' before sending them to the SimpleGazeTracker.
As a result, calibration positions are recorded in 'pix' in the SimpleGazeTracker data file as following.:
#CALPOINT,-307.000000,0.000000
#CALPOINT,-307.000000,-230.000000
#CALPOINT,307.000000,230.000000
#CALPOINT,0.000000,-230.000000
#CALPOINT,-307.000000,230.000000
#CALPOINT,307.000000,0.000000
#CALPOINT,307.000000,-230.000000
#CALPOINT,0.000000,0.000000
#CALPOINT,0.000000,230.000000
Comparing these outputs with the script, 0.6 'norm' corresponds to 307 'pix' in horizontal direction and 230 'pix' in vertical direction.
In the SimpleGazeTracker data file, units of recorded gaze positions are also 'pix'.:
2.525,-4.1,-19.3
19.093,-3.9,-18.8
35.692,-3.9,-22.3
52.301,-4.7,-14.0
68.924,0.1,-18.8
85.546,-0.6,-15.0
102.175,2.1,-25.4
118.800,2.1,-17.4
135.421,-0.7,-21.9
152.057,-3.0,-14.7
168.669,-1.9,-22.7
185.294,-4.3,-30.5
201.919,-8.0,-22.7
218.542,-4.9,-23.5
235.170,-5.3,-21.2
251.680,-1.2,-21.8
268.296,-9.0,-15.2
getEyePosition()
receives gaze position in 'pix' and converts to desirable units.
In this sample, units of gaze positions are converted to 'norm' and are output to the local log file.:
trial1
getSpatialError: 0.1786,0.0234,-0.1771
SentAt,ReceivedAt,Lag,TargetX,TargetY,EyeX,EyeY
0.0,0.6,0.5,0.0000,0.0000,-0.0020,-0.0391
14.0,14.3,0.3,0.0000,0.0000,0.0039,-0.0651
18.3,18.6,0.3,0.0000,0.0000,0.0039,-0.0651
33.6,33.9,0.3,0.0000,0.0000,0.0039,-0.0443
50.3,50.6,0.3,0.0000,0.0000,-0.0020,-0.0573
66.9,67.3,0.3,0.0000,0.0000,-0.0059,-0.0391
83.7,84.0,0.3,0.0000,0.0000,-0.0039,-0.0599
100.3,100.7,0.3,0.0000,0.0000,-0.0078,-0.0781
117.0,117.3,0.3,0.0000,0.0000,-0.0156,-0.0599
133.7,134.0,0.3,0.0000,0.0000,-0.0098,-0.0599
150.4,150.7,0.3,0.0000,0.0000,-0.0098,-0.0547
167.1,167.4,0.3,0.0000,0.0000,-0.0020,-0.0573
183.7,184.1,0.3,0.0000,0.0000,-0.0176,-0.0391
200.4,200.8,0.3,0.0000,0.0000,-0.0176,-0.0521
217.1,217.4,0.3,0.0000,0.0000,0.0000,-0.0469
233.8,234.1,0.3,0.0000,0.0000,-0.0254,-0.0651
250.5,250.8,0.3,0.0000,0.0000,-0.0156,-0.0521
Codes (PsychoPy)¶
Lines modified from sample01_PsychoPy.py are highlighted.
import psychopy.visual
import psychopy.event
import psychopy.core
import sys
import random
import GazeParser.TrackingTools
import wx
class FileWindow(wx.Frame):
def __init__(self,parent,id,title):
wx.Frame.__init__(self,parent,id,title)
panel = wx.Panel(self,wx.ID_ANY)
vbox = wx.BoxSizer(wx.VERTICAL)
filenameBox = wx.BoxSizer(wx.HORIZONTAL)
filenameBox.Add(wx.StaticText(panel,wx.ID_ANY,'Datafile name',size=(160,30)),0)
self.filenameEdit = wx.TextCtrl(panel,wx.ID_ANY)
filenameBox.Add(self.filenameEdit,1)
filenameBox.Add(wx.StaticText(panel,wx.ID_ANY,'.csv'),0)
vbox.Add(filenameBox, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 10)
addressBox = wx.BoxSizer(wx.HORIZONTAL)
addressBox.Add(wx.StaticText(panel,wx.ID_ANY,'SimpleGazeTracker address',size=(160,30)),0)
self.addressEdit = wx.TextCtrl(panel,wx.ID_ANY)
self.addressEdit.SetValue('192.168.1.1')
addressBox.Add(self.addressEdit,1)
vbox.Add(addressBox, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 10)
imgsizeBox = wx.BoxSizer(wx.HORIZONTAL)
imgsizeBox.Add(wx.StaticText(panel,wx.ID_ANY,'Capture image size',size=(160,30)),0)
self.imgsizeEdit = wx.TextCtrl(panel,wx.ID_ANY)
self.imgsizeEdit.SetValue('640,480')
imgsizeBox.Add(self.imgsizeEdit,1)
vbox.Add(imgsizeBox, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 10)
isdummyBox = wx.BoxSizer(wx.HORIZONTAL)
self.isdummyCheck = wx.CheckBox(panel,wx.ID_ANY,'Use dummy mode (for standalone debug)')
isdummyBox.Add(self.isdummyCheck)
vbox.Add(isdummyBox, 0, wx.ALIGN_CENTER | wx.CENTER, 10)
vbox.Add((-1, 25))
okBox = wx.BoxSizer(wx.HORIZONTAL)
okButton = wx.Button(panel,wx.ID_ANY, 'Ok', size=(70, 30))
self.Bind(wx.EVT_BUTTON, self.quitfunc, okButton)
okBox.Add(okButton)
vbox.Add(okBox, 0, wx.ALIGN_CENTER | wx.CENTER, 10)
panel.SetSizer(vbox)
self.Show(True)
def quitfunc(self, event):
global FileWindowValues
filename = self.filenameEdit.GetValue()
address = self.addressEdit.GetValue()
imgsize = self.imgsizeEdit.GetValue()
isdummy = self.isdummyCheck.GetValue()
FileWindowValues = {'filename':filename,'address':address,'imgsize':imgsize,'isdummy':isdummy}
self.Close(True)
FileWindowValues = {}
application = wx.App(False)
fw = FileWindow(None,wx.ID_ANY,"Sample01_PsychoPy")
application.MainLoop()
dataFileName = FileWindowValues['filename']
fp = open(dataFileName+'_local.csv','w')
xy = FileWindowValues['imgsize'].split(',')
cameraX = int(xy[0])
cameraY = int(xy[1])
tracker = GazeParser.TrackingTools.getController(backend='PsychoPy',dummy=FileWindowValues['isdummy'])
tracker.setReceiveImageSize((cameraX,cameraY))
tracker.connect(FileWindowValues['address'])
win = psychopy.visual.Window(size=(1024,768),units='norm')
tracker.openDataFile(dataFileName+'.csv')
tracker.sendSettings(GazeParser.config.getParametersAsDict())
calarea = [-0.8,-0.8,0.8,0.8]
calTargetPos = [[ 0, 0],
[-0.6,-0.6],[-0.6, 0],[-0.6,0.6],
[ 0,-0.6],[ 0, 0],[ 0,0.6],
[ 0.6,-0.6],[ 0.6, 0],[ 0.6,0.6]]
tracker.setCalibrationScreen(win)
tracker.setCalibrationTargetPositions(calarea, calTargetPos,units='norm')
while True:
res = tracker.calibrationLoop()
if res=='q':
sys.exit(0)
if tracker.isCalibrationFinished():
break
stim = psychopy.visual.Rect(win, width=0.03, height=0.04, units='norm')
marker = psychopy.visual.Rect(win, width=0.009, height=0.012, units='norm', fillColor=(1,1,0),lineWidth=0.1)
trialClock = psychopy.core.Clock()
for tr in range(2):
error = tracker.getSpatialError(message='Press space key', units='norm')
targetPositionList = [(0.1*random.randint(-3,3),0.1*random.randint(-3,3)) for i in range(10)]
targetPositionList.insert(0,(0,0))
currentPosition = 0
previousPosition = 0
stim.setPos(targetPositionList[currentPosition])
marker.setPos(targetPositionList[currentPosition])
waitkeypress = True
while waitkeypress:
if 'space' in psychopy.event.getKeys():
waitkeypress = False
stim.draw()
win.flip()
tracker.startRecording(message='trial'+str(tr+1))
tracker.sendMessage('STIM %s %s'%targetPositionList[currentPosition])
data = []
trialClock.reset()
while True:
currentTime = trialClock.getTime()
currentPosition = int(currentTime)
if currentPosition>=len(targetPositionList):
break
targetPosition = targetPositionList[currentPosition]
if previousPosition != currentPosition:
tracker.sendMessage('STIM %s %s'%targetPosition)
previousPosition = currentPosition
preGet = trialClock.getTime()
eyePos= tracker.getEyePosition(units='norm')
postGet = trialClock.getTime()
if not eyePos[0] == None:
data.append((1000*preGet,1000*postGet,1000*(postGet-preGet),
targetPosition[0],targetPosition[1],eyePos[0],eyePos[1]))
marker.setPos((eyePos[0],eyePos[1]))
else:
data.append((1000*preGet,1000*postGet,1000*(postGet-preget),
targetPosition[0],targetPosition[1],-65536,-65536))
keyList = psychopy.event.getKeys()
if 'space' in keyList:
tracker.sendMessage('press space')
stim.setPos(targetPosition)
stim.draw()
marker.draw()
win.flip()
tracker.stopRecording(message='end trial')
fp.write('trial%d\n' % (tr+1))
if error[0] != None:
fp.write('getSpatialError: %.4f,%.4f,%.4f\n' % (error[0],error[-1][0],error[-1][1]))
else:
fp.write('getSpatialError: None\n')
fp.write('SentAt,ReceivedAt,Lag,TargetX,TargetY,EyeX,EyeY\n')
for d in data:
fp.write('%.1f,%.1f,%.1f,%.4f,%.4f,%.4f,%.4f\n' % d)
fp.flush()
tracker.closeDataFile()
fp.close()