psychopy.org | Reference | Downloads | Github

Pygaze's Area Of Interest (AOI) function equivalent in Psychopy

I’m trying to build a gaze contingent experiment where the trial in a visual search paradigm proceeds to the next when fixated on the target for sometime. I used PyGaze’s AOI function to design the experiment. But the eye tracker (pupil labs) doesn’t support Pygaze and was asked to do the same in psychopy. I’m grateful for suggestions.

import os
import random
import xlrd
import numpy as np
import time
 
from pygaze.defaults import *
from constants import *
from pygaze.display import Display
from pygaze.screen import Screen
from pygaze.eyetracker import EyeTracker
from pygaze.keyboard import Keyboard
from pygaze.time import Time
from pygaze.logfile import Logfile
from pygaze.plugins.aoi import AOI
 
workbook = xlrd.open_workbook('inputfile.xlsx')
sheet = workbook.sheet_by_name('Sheet4')
 
# # # # #
# 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 Timer
timer = Time()
 
# create a new logfile
gazeposlog = Logfile(filename="gazeposlog")
log = Logfile(filename="data")
log.write(["Trial","SequenceNo","Start","End","Duration","Searchtime","Gazefix","Actualgazefix"])
 
sequence = np.random.permutation(9)
 
fixation = "fixation.png"
for i in range(0,9):
    scr.clear()
    scr.draw_image(fixation)
    disp.fill(scr)
    fix = disp.show()
    tracker.start_recording()
    pointer=1
    fixdur=0
    fixstamp = [None]*1000
    while fixdur<1000:
        gazepos = tracker.sample()
        fixstamp[0]=disp.show()
        if (DISPSIZE[0]/2 - 25)<gazepos[0]<(DISPSIZE[0]/2 +25) and (DISPSIZE[1]/2 - 25)<gazepos[1]<(DISPSIZE[1]/2 +25):
            fixstamp[pointer] = disp.show()
            fixdur = fixstamp[pointer]-fixstamp[1]
            pointer = pointer + 1
         
    stimulus = "stimuli/" + sheet.cell(sequence[i],4).value + ".jpg"
    scr.draw_image(stimulus)
    disp.fill(scr)
    t1 = disp.show()
    key = None
     
    x = (DISPSIZE[0]/2 - 25 - 683) + int(sheet.cell(sequence[i], 5).value) # centre minus half of the image width, plus kitten head X position in image
    y = (DISPSIZE[1]/2 - 25 - 384) + int(sheet.cell(sequence[i], 6).value) # centre minus half of the image height, plus kitten head Y position in image
    x1 = (DISPSIZE[0]/2 - 25 - 683) + int(sheet.cell(sequence[i], 7).value) # centre minus half of the image width, plus kitten head X position in image
    y1 = (DISPSIZE[1]/2 - 25 - 384) + int(sheet.cell(sequence[i], 8).value)
    x2 = (DISPSIZE[0]/2 - 25 - 683) + int(sheet.cell(sequence[i], 9).value) # centre minus half of the image width, plus kitten head X position in image
    y2 = (DISPSIZE[1]/2 - 25 - 384) + int(sheet.cell(sequence[i], 10).value)
    x3 = (DISPSIZE[0]/2 - 25 - 683) + int(sheet.cell(sequence[i], 11).value) # centre minus half of the image width, plus kitten head X position in image
    y3 = (DISPSIZE[1]/2 - 25 - 384) + int(sheet.cell(sequence[i], 12).value)
     
    aoi = AOI('rectangle',(x,y),(50,50))
    aoi1 = AOI('rectangle',(x1,y1),(50,50))
    aoi2 = AOI('rectangle',(x2,y2),(50,50))
    aoi3 = AOI('rectangle',(x3,y3),(50,50))
     
    start=0
    end=0
    duration = 0
    ts=[]
    gpx=[]
    gpy=[]
    while duration<1000:
        key, presstime = kb.get_key(keylist=['space'],timeout=1)
        if key == 'space':
            break
         
        # get gaze position
        timestamp = disp.show()
        gazepos = tracker.sample()
        # check if the gaze position is within the aoi
        if aoi.contains(gazepos) or aoi1.contains(gazepos) or aoi2.contains(gazepos) or aoi3.contains(gazepos) :
            end = disp.show()
        else:
            start = disp.show()
         
        ts.append(timestamp)
        gpx.append(gazepos[0])
        gpy.append(gazepos[1])
        duration=end-start
         
    sr=(sequence[i]+1)%9
    if x<gazepos[0]<(x+50):
        if 1<=sr<=4:
            gazefix = sheet.cell(sequence[i], 3).value
        elif (sr==5 or sr==7 or sr==0):
            gazefix = "TL"
        elif sr==6:
            gazefix = "BL"
        elif sr==8:
            gazefix = "TR"
    elif x1<gazepos[0]<(x1+50):
        if (sr==5 or sr==0):
            gazefix = "TR"
        elif (sr==6 or sr==8):
            gazefix = "BR"
        elif sr==7:
            gazefix = "BL"
    elif x2<gazepos[0]<(x2+50):
        gazefix = "BL"
    elif x3<gazepos[0]<(x3+50):
        gazefix = "BR"
    gazeposlog.write(["Trial",i,"timestamp",ts])
    gazeposlog.write(["Trial",i,"gazepos_x",gpx])
    gazeposlog.write(["Trial",i,"gazepos_y",gpy])
    log.write([i,sequence[i],start,end,duration,start-t1,gazefix,sheet.cell(sequence[i], 3).value])
     
 
 
 
tracker.stop_recording()
 
log.close()
gazeposlog.close()
tracker.close()
disp.close()
timer.expend()

PsychoPy has the ShapeStim class for creating geometrical shapes (and has convenience sub-classes like Rect, Circle, etc):

http://www.psychopy.org/api/visual.html

You would define them similarly to the way you do above, by providing coordinates for the vertices. The ShapeStim class has a .contains() function just as in your code above. The shape wouldn’t actually need to be drawn on screen for this function to be used.