A filter that records state information from Tobii Eyetrackers into state variables.
Griffin Milsap (email@example.com), Jeremy Hill (firstname.lastname@example.org).
06/09/2011: Initial public release; 06/10/2011: Hacked in scale and offset for gaze data;
Source Code Revisions
- Initial development: 3318
- Tested under: 3318
- Known to compile under: 3318
- Broken since: --
- Upgrade SDK version to 3.0 (once it's out of beta)
- Include a fixation/monitor filter
- Maybe a Qt calibration app
In many cases, an experiment may require data about where the participant is looking. In these experiments, an eyetracker is the only way to gather data relating to gaze position and eye location. There are many eyetracking methods currently on the market, but many of these require the subject to hold their head steady -- often while strapped to a structure of some sort. The Tobii eyetrackers require no such restriction so they were a natural choice when it came to interfacing with BCI2000.
Integration into BCI2000
Compile the extension into your source module by enabling contributed extensions in your CMake configuration. You can do this by going into your root build folder and deleting
CMakeCache.txt and re-running the project batch file, or by running
cmake -i and enabling BUILD_EYETRACKERLOGGER. Once the extension is built into the source module, enable it by starting the source module with the
--LogEyetracker=1 command line argument.
Usage and Calibration
Set up the eyetracker as detailed in the documentation that came with your device. The device will connect to your machine and communicate through the ethernet port. As such, it'd be wise to disconnect and turn off any other networking devices while using the eyetracker. It is possible that your network request could go out over a different network interface if you're not careful which makes for a troubleshooting nightmare. When you start the source module, ensure that the
--LogEyetracker=1 command line parameter is set. Run the Eyetracker Browser utility which came with your eyetracker drivers and use it to locate the device on your local network. Copy the network address to the clipboard and paste it in the
NetworkLocation parameter within BCI2000. If the listed port is different, put that in the
Port parameter in the BCI2000 operator.
Calibration can now occur. Calibration should be done per subject per sitting. Re-calibration is not necessary between runs, but any time that the subject changes eyewear, makeup, or position, or if the lighting conditions change it should be re-calibrated. A good rule of thumb would be to recalibrate at the start of every session. Once a calibration is performed, it is saved in the Tobii device until the next calibration (even if there's a power loss).
BCI2000 does not provide any way to calibrate the eyetracker. This should be done using the Tobii SDK sample application. The SDK can be obtained here: http://www.tobii.com/analysis-and-research/global/products/software/tobii-software-development-kit/. The 2.0 or Beta 3.0 SDK can be used to calibrate the device. When installed, open
C:\Program Files\Tobii\Tobii Eye Tracker SDK 2.0.1\samples\ for the 2.0.1 SDK or
C:\Program Files\Tobii\SDK\Samples\ for the 3.0 Beta SDK and find the "Eye Tracker Components C++" or "EyetrackerComponents.Cpp" sample project. There should be a pre-built executable in this directory or a "prebuild" directory which can be used to run a calibration. Use the network address from the Eyetracker Browser and follow the instructions to calibrate and test your device. When you're done you can close the calibration utility and run BCI2000 - which will use the calibration saved on the device.
If you're using an T60/T120 or any future Tobii eyetracker with an attached display, you'll probably have it in a dual screen setup showing different things on both monitors - either extended or dualview. Typically, the Tobii monitor is used to present the task to the subject and the other monitor is used for the experimenter to control BCI2000 from. This can present a bit of a problem when calibrating because the sample application will only run on your "primary display" which may or may not be your Tobii monitor. Either set your primary display to be the Tobii screen or temporarily set the screen configuration into "clone" mode. A different screen resolution or aspect ratio does not impact the Tobii calibration process.
Once the device is calibrated, it can be used reliably in BCI2000. The logger will report information about eye validity in a text visualization window and feed states into the system.
The eyetracker is configured in the Source tab within the EyetrackerLogger section. The configurable parameters are:
LogEyetracker- Enables/Disables logging of Eyetracker states
NetworkLocation- The network address of the Eyetracker given by the Tobii Eyetracker Browser
Port- The port that the Tobii communicates over - Tobii default is 4455
LogGazeData- Enables/Disables logging of gaze data
LogEyePos- Enables/Disables logging of eye position (as seen from the camera)
LogPupilSize- Enables/Disables logging of pupil size (very rough)
LogEyeDist- Enables/Disables logging of the distance from the screen to the eyes (again, rough)
GazeScale- Scales the incoming gaze data first
GazeOffset- Offsets the incoming gaze data after scaling
Note: GazeScale and GazeOffset are quick hacks to address an issue with gaze data being clamped around the edges of the screen. The eyetracker gives back values which are between 0.0 and 1.0 for onscreen gaze but supports looking slightly offscreen by allowing gaze data returned to go above 1.0 and below 0.0. BCI2000 needs this scaled between 0.0 and 1.0 before the gaze data is multiplied by 65535 for storage in the 16 bit state. These two parameters account for this scaling and offset and prevents the clamping from happening as often as it would otherwise. These parameters will be removed once BCI2000 supports typed states.
The following code retreives the actual ~(0.0-1.0) range that the eyetracker outputs directly (assuming you've scaled and offset the signal to avoid clipping) from each eye and averages it to find a gaze position.
float x = State( "EyetrackerLeftEyeGazeX" ) + State( "EyetrackerRightEyeGazeX" ); x /= ( 2.0f * 65535.0f ); float y = State( "EyetrackerLeftEyeGazeY" ) + State( "EyetrackerRightEyeGazeY" ); y /= ( 2.0f * 65535.0f ); x -= ( float )Parameter( "GazeOffset" ); x /= ( float )Parameter( "GazeScale" ); y -= ( float )Parameter( "GazeOffset" ); y /= ( float )Parameter( "GazeScale" );
Unless otherwise specified, all states are prefixed with
Eyetracker<Left/Right>Eye which corresponds with each individual eye. The EyetrackerLogger extension does not support subjects with more than two eyes at the moment.
The eye gaze position (where - on the screen - the subject is looking) is returned from the Tobii SDK as 32 bit floating point numbers which (roughly) range from 0.0 to 1.0. They are multiplied by 65535 and stored as 16 bit integers in these states if the
LogGazeData parameter is enabled. (0,0) corresponds to the top left of the screen, (65535,65535) corresponds to the right bottom of the screen. -- See EyetrackerStatesOK.
The eye position relative to the camera in 2D space is returned if
LogEyePos is enabled. Again, these are returned from the library as floating point numbers from 0.0 to 1.0 and are scaled to 16 bit integer values from 0 to 65535. (0,0) corresponds to the top left of the camera's view, and (65535,65535) corresponds to the bottom right of the camera's view.
The pupil size in mm is saved in this state if
LogPupilSize is enabled. It corresponds to the length of the longest chord drawn from one side of the pupil to the other. The size will change depending on the eye position and distance from the screen. Although it is given in mm, it would be best to use this as a relative measurement.
The distance between the screen and the eyes in mm is saved in this state if
LogEyeDist is enabled. This measurement is an approximation. The actual measurement will depend on whether or not the test subject is wearing glasses or not.
This state is a number from 0 to 4 and is documented in the Tobii SDK manual. It is repeated here for convenience.
- 0 - The eye tracker is certain that the data for this eye is right. There is no risk of confusing data from the other eye.
- 1 - The eye tracker has only recorded one eye and made some assumptions and estimations regarding which is the left and which is the right eye. However, it is still very likely that the assumption made is correct. The validity code for the other eye is in this case always set to 3.
- 2 - The eye tracker has only recorded one eye, and has no way of determining which one is the left eye and which one is the right eye. The validity code for both eyes is set to 2.
- 3 - The eye tracker is fairly confident that the actual gaze data belongs to the other eye. The other eye will always have validity code 1.
- 4 - The actual gaze data is missing or definitely belonging to the other eye.
|Code (Right - Left)||Description|
|0 - 0||Both eyes found. Data is valid for both eyes.|
|0 - 4 or 4 - 0||One eye found. Gaze data is the same for both eyes.|
|1 - 3 or 3 - 1||One eye found. Gaze data is the same for both eyes.|
|2 - 2||One eye found. Gaze data is the same for both eyes.|
|4 - 4||No eye found. Gaze data for both eyes are invalid.|
It'd probably be wise to remove all data points with a validity state of 2 or higher while running your analysis.
Early versions of the extension didn't take into account that the library may return a number greater than 1.0 or less than 0.0. This resulted in "pac-man" style wrap around of gaze coordinates in 2.0 and crashes in 3.0. If the output from the library is out of bounds, it is clamped to the boundaries and the "EyetrackerStatesOK" parameter is changed. A value of "1" corresponds to valid gaze data, a value of "0" corresponds to invalid "clamped" gaze data. Use the "GazeOffset" and "GazeScale" parameters to avoid clamping. Those parameters scale and offset the data so that when it does go out of range, it can still be fit into the 16 bit state.