[solved] Eye-Tracking Experiment Question - Gaze Contingent Stimuli Presentation
Hey guys,
I want to get some guidance on how to implement an eye-tracking paradigm in which stimuli are presented for a free viewing period for three seconds, and stimuli presentation does not continue until the participant fixates on a particular image (i.e., for 100 ms). After this point, the stimuli on the opposite side of the screen appears with a shape wrapped around it and the participant must look at it and press a key indicating what kind of shape it is. I have a few questions:
First, regarding the wait_for_fixation_start function: how is a fixation defined? Can I modify it so a fixation is considered to be greater than or equal to 100 ms?
Second, can the wait_for_fixation_start function be modified to wait for not just any fixation, but for a fixation within a specific region of interest on the screen (i.e., one of the images)? Or must I use an inline script and the sample function in some sort of loop to wait for the eyes to linger in a specified part of the screen for 100ms?
Third, with regard to interest area creation for an ellipse, do the two (x,y) coordinate pairs specified in the command refer to the top/height parts of the ellipse or the side/width parts of the ellipse? I want to specify interest areas for my oval-shaped face stimuli. Also, is there a way to test the actual area covered by the interest area in terms of screen coordinates so I can make sure it's covering the area I think it's covering?
Thanks again for all of your help!
-Ricky
Comments
Hi Ricky,
The
wait_for_fixation_start()
function, and the otherwait_for_[X]
functions, simply use the EyeLink's internal event detection algorithms. These are described in the EyeLink manual (in the section The EyeLink On-Line Parser). However, it appears that only the saccade detection algorithm is described and not the fixation detection. (They are not exactly the inverse of each other, as you might think.) So if you want to know the exact algorithm, I would post a question on the SR Support forum.Well... aside from the point above, you cannot really detect the start of a fixation based on how long the fixation will last, right? You can filter fixations afterwards based on duration, of course, either online or during the analysis.
You will indeed need to implement this with an
inline_script
. This isn't very difficult though, and feel free to post specific questions here.I assume you are talking about this line from the tutorial:
I believe the numbers reflect (x1, y1, x2, y2). So the second pair (400, 300) are coordinates, and not dimensions. However, specifying ROIs in this way is actually specific for the EyeLink DataViewer. To OpenSesame, this is just an arbitrary message that is written to the
.edf
file. To find out exactly which commands you can send, and how they are interpreted by the DataViewer, I would refer to the DataViewer manual, again available from the SR Support forum.Cheers!
Sebastiaan
Check out SigmundAI.eu for our OpenSesame AI assistant!
Hey Sebastian,
Thanks for the useful info. I've got an algorithm in mind to do what I need and I wanted to run it by you.
First, I'm going to use an inline script to enable fixation updates at 100ms so I can detect fixation duration in the target area in real-time before moving forward with stimuli presentation. I have a script with the following commands that is executed after calibration.
-Enable fixation updates
-Enable writing of fixation update events to .edf
self.experiment.eyelink.send_command("file_event_filter = LEFT,RIGHT,FIXATION,FIXUPDATE,SACCADE,BLINK,MESSAGE,BUTTON")
-Enable sending of fixation updates over link for use in experiment to guide stimuli presentation
self.experiment.eyelink.send_command("link_event_filter = LEFT,RIGHT,FIXATION,FIXUPDATE,SACCADE,BLINK,BUTTON")
-Make fixation update interval 100ms
self.experiment.eyelink.send_command("fixation_update_interval = 100")
self.experiment.eyelink.send_command("fixation_update_accumulate = 100")
The above scripts runs, but FIXUPDATE events don't seem to appear in the .edf output. Should I be changing the .py files directly, or should the above inline script code suffice?
Below is an algorithm I think will do what I need. At a specified time during the experiment, the program waits for the user to fixate for at least 100ms on a predefined oval area of the screen (either the left or right, depending upon trial type). Once this happens, stimuli presentation continues. Below is code I think will do the job.
-Await fixation on target stimuli
-Set fixation flag to false
fixated = false
self.experiment.eyelink.log("Starting wait for fixation period")
while fixated = false:
-wait for the beginning of a fixation and get the coordinates
-I'm unsure here how to store the x,y gaze coord data that the wait_for_fixation_start function returns?
start_pos = wait_for_fixation_start()
-fixate_on is a trial variable that indicates whether the left or
right side of the screen has the target stimuli for this particular trial
if fixate_on = "left":
-use formula for an ellipse to see if the fixation is inside the target
-ctrx and ctry are the center points of the left ellipse stimuli, a and b are
the width and height dimensions
if (((start_pos.x - ctrx)^2/a^2) + ((start_pos.y - ctry)^2/b^2)) <=1:
-Fixation position is inside image
-I'm not sure how to get the coordinates out of the float_data
from the fixation update event?
data = wait_for_event("pylink.FIXUPDATE")
-Make sure that at the end of the 100ms fixation update the
gaze is still in the target ellipse image
if (((data.x - ctrx)^2/a^2) + ((data.y - ctry)^2/b^2)) <= 1:
fixated = true
else:
-do the same thing as above only the ctrx and ctry coords are for the right ellipse stimuli
I'm not sure if the second coordinate check after the update event is necessary, but the manual suggests that it will send an update at the end of a fixation too, so I want to make sure the gaze is still in the target stimuli just in case that happens.
Let me know if you see any obvious problems with this.
As always, I appreciate all of the help!
-Ricky
I have never used fixation updates myself (I generally poll gaze samples), but based on the manual this does seem like it should do the trick. The
FIXUPDATE
events should also appear in the.edf
file though. So if they don't, I would check on the SR Support forum what the problem might be. For example, it could be that the fact thatlibeyelink
already specifies afile_event_filter
andlink_event_filter
prevents these settings from being specified again. I'm not sure: This is EyeLink-specific stuff, and not all behavior is fully specified in the manual.The logic in your script seems to be ok, as far as I can tell. But it's not even remotely close to syntactically valid Python! For example,
^
is a bitwise exclusive or, and not an exponentiation, which is**
Similarly,=
is an assignment, to compare to values, you need==
. And the statementdata = wait_for_event("pylink.FIXUPDATE")
passes the string 'pylink.FIXUPDATE' to the functionwait_for_event()
, and not thepylink
attributeFIXUPDATE
.So what I would do is write a working script (i.e. one that doesn't cause syntax errors) that follows the basic logic that you have outlined here and see whether it functions as expected. If it doesn't, you can paste the script here and we can take it from there. To familiarize yourself with the basic Python syntax, I would walk through one of the tutorials described on the documentation site:
Good luck!
Sebastiaan
Edit: Just to get you started with the most tricky part, you should be able to get a FIXUPDATE event approximately like this (see the PyLink manual).
Check out SigmundAI.eu for our OpenSesame AI assistant!
Hey Sebastian,
OpenSesame is telling me that the name pylink is not defined when I try to pass pylink.FIXUPDATE to the wait_for_event function...I'm using OpenSesame Portable and Edwin provided me with a new download recently because pylink was not importing correctly...it imports correctly now obviously, since I can connect to and calibrate the tracker, but it looks like it's still not recognizing pylink...?
Did you specifically import pylink within the inline_script in which you try to pass pylink.FIXUPDATE? To add to Sebastiaans snippet:
Hey guys,
I've got everything working well now, and I'll include the code shortly. I used the getStartGaze/getEndGaze functions and it seems to be working correctly - what's the difference between those functions and the PPD one you included in your sample? Just curious...
Thanks guys!
-Ricky
Ow right, I simply misread the manual. the
getStartPPD()
function gets information about the angular resolution. You indeed wantgetStartGaze()
andgetEndGaze()
.Good to hear that you got it working!
Check out SigmundAI.eu for our OpenSesame AI assistant!
Hey guys,
First, I wanted to include the code below for reference in case any future readers need to implement a similar paradigm: