Demonstration

Picture Free-viewing Task

In the previous section, we gained a preliminary understanding of the basic workflow of Pupilio. Now, we can implement a simple picture free-view task. This demonstration will guide you through the main usage of Pupilio.

PyGame implementation:

#!/usr/bin/env python

# Copyright (c) 2024, Hangzhou Deep Gaze Science and Technology Co., Ltd
# All Rights Reserved
#
# For use by  Hangzhou Deep Gaze Science and Technology Co., Ltd customers
# only. Redistribution and use in source and binary forms, with or without
# modification, are NOT permitted.
#
# Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the distribution.
#
# Neither name of  Hangzhou Deep Gaze Sci & Tech Ltd nor the name of 
# contributors may be used to endorse or promote products derived from 
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# DESCRIPTION:
# This is a demo showing how to use deep gaze pythonic library
# In this script, we connect to the tracker, perform a calibration, 
# validate the calibration results, then we subscribe to the sample data
# stream, with which we constantly update the position of a gaze cursor

# Author: Gancheng Zhu
# Last updated: 10/28/2024 by ZW

# Load libraries
import os
import time
import pygame
from pygame.locals import *
from pupilio import Pupilio

# use the Pygame library for graphics, first init pygame and open a full screen window
pygame.init()
scn_width, scn_height = (1920, 1080)
win = pygame.display.set_mode((scn_width, scn_height), FULLSCREEN|HWSURFACE)

# instantiate a tracker object
pupil_io = Pupilio()

# create a task session, and set a session name
# The session name must contain only letters, digits or underscores without any special characters.
pupil_io.create_session(session_name="deepgaze_demo")

# set 'validate' to True if you would like to verify the calibration results
pupil_io.calibration_draw(validate=True, hands_free=False, screen=win)

#  start retrieving gaze
pupil_io.start_sampling()
pygame.time.wait(100)  # sleep for 100 ms so the tracker cache some sample

# A free viewing task, in which we show a picture and overlay the gaze cursor
img_folder = 'images'
images = ['gray_grid.jpg', 'west_lake.jpg', 'old_town.jpg']

# show the images one by one in a loop, press a ENTER key to exit the program
for _img in images:
    # show the image on screen
    win.fill((128, 128, 128))
    im = pygame.image.load(os.path.join(img_folder, _img))
    win.blit(im, (0, 0))
    pygame.display.flip()
    # send a trigger to record in the eye movement data to mark picture onset
    pupil_io.set_trigger(202)

    # now lets show the gaze point, press any key to close the window
    got_key = False
    max_duration = 10000
    t_start = pygame.time.get_ticks()
    pygame.event.clear()  # clear all cached events if there were any
    gx, gy = -65536, -65536
    while not (got_key or (pygame.time.get_ticks() - t_start)>=max_duration):
        # get the newest gaze position
        left, right, bino = pupil_io.get_current_gaze()
        status, gx, gy = bino
        gx = int(gx)
        gy = int(gy)

        # check key presses
        for ev in pygame.event.get():
            if ev.type == KEYDOWN:
                if ev.key == K_RETURN:
                    got_key = True
                # if ev.key == K_SPACE:

        # update the visual (image and cursor)
        win.blit(im, (0,0))
        pygame.draw.circle(win, (0, 255, 0), (gx, gy), 50, 5)  # cursor for the left eye
        pygame.display.flip()

# stop sampling
pygame.time.wait(100)  # sleep for 100 ms to capture ending samples
pupil_io.stop_sampling()

# save the sample data to file
data_dir = "./data"
if not os.path.exists(data_dir):
    os.makedirs(data_dir)

file_name = "deepgaze_demo.csv"
pupil_io.save_data(os.path.join(data_dir, file_name))

# release the tracker instance
pupil_io.release()

# quit pygame
pygame.quit()

PsychoPy implementation:

#!/usr/bin/env python

# Copyright (c) 2024, Hangzhou Deep Gaze Science and Technology Co., Ltd
# All Rights Reserved
#
# For use by  Hangzhou Deep Gaze Science and Technology Co., Ltd customers
# only. Redistribution and use in source and binary forms, with or without
# modification, are NOT permitted.
#
# Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the distribution.
#
# Neither name of  Hangzhou Deep Gaze Sci & Tech Ltd nor the name of
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# DESCRIPTION:
# This is a demo showing how to use deep gaze pythonic library
# In this script, we connect to the tracker, perform a calibration,
# validate the calibration results, then we subscribe to the sample data
# stream, with which we constantly update the position of a gaze cursor

# Author: Gancheng Zhu
# Last updated: 10/28/2024 by ZW

# Load libraries
import os
from psychopy import visual, core, event
from pupilio import Pupilio

# use the Psychopy library for graphics, first open a full screen window
scn_width, scn_height = (1920, 1080)
win = visual.Window((scn_width, scn_height), fullscr=True, units='pix')

# instantiate a tracker object
pupil_io = Pupilio()

# create a task session, and set a session name
# The session name must contain only letters, 
# digits or underscores without any special characters.
pupil_io.create_session(session_name="deepgaze_demo")

# set 'validate' to True if you would like to verify the calibration results
pupil_io.calibration_draw(screen=win, validate=True, hands_free=False)

#  start retrieving gaze
pupil_io.start_sampling()
core.wait(0.1)  # sleep for 100 ms so the tracker cache some sample

# A free viewing task, in which we show a picture and overlay the gaze cursor
img_folder = 'images'
images = ['gray_grid.jpg', 'west_lake.jpg', 'old_town.jpg']

# show the images one by one in a loop, press a ENTER key to exit the program
for _img in images:
    # show the image on screen
    im = visual.ImageStim(win, os.path.join(img_folder, _img))
    im.draw()
    win.flip()
    # send a trigger to record in the eye movement data to mark picture onset
    pupil_io.set_trigger(202)

    # now lets show the gaze point, press any key to close the window
    got_key = False
    max_duration = 10
    t_start = core.getTime()
    event.clearEvents()  # clear all cached events if there were any
    gx, gy = -65536, -65536
    while not (got_key or (core.getTime() - t_start)>=max_duration):
        # get the newest gaze position
        left, right, bino = pupil_io.get_current_gaze()
        status, gx, gy = bino
        # convert to psychopy coordinates, screen center = (0,0)
        gx = gx -960
        gy = 540 - gy

        # check keyboard events
        if event.getKeys():
            got_key = True

        # redraw the screen
        win.color =(0,0,0)
        im.draw()
        gaze_cursor = visual.Circle(win, 50, pos=(gx, gy), lineColor=(-1, 1, -1), lineWidth=5, fillColor=None)
        gaze_cursor.draw()
        win.flip()

# stop sampling
core.wait(0.1)  # sleep for 100 ms to capture ending samples
pupil_io.stop_sampling()

# save the sample data to file
data_dir = "./data"
if not os.path.exists(data_dir):
    os.makedirs(data_dir)

file_name = "deepgaze_demo.csv"
pupil_io.save_data(os.path.join(data_dir, file_name))

# release the tracker instance
pupil_io.release()

# quit pygame
core.quit()