Example #1
0
    def wait_for_fixation_start(self):
        """Returns starting time and position when a simulated fixation is started"""

        # function assumes a 'fixation' has started when 'gaze' position remains reasonably
        # stable for five samples in a row (same as saccade end)

        maxerr = 3  # pixels

        # wait for reasonably stable position
        xl = []  # list for last five samples (x coordinate)
        yl = []  # list for last five samples (y coordinate)
        moving = True
        while moving:
            npos = self.sample()
            xl.append(npos[0])  # add newest sample
            yl.append(npos[1])  # add newest sample
            if len(xl) == 5:
                # check if deviation is small enough
                if max(xl) - min(xl) < maxerr and max(yl) - min(yl) < maxerr:
                    moving = False
                # remove oldest sample
                xl.pop(0)
                yl.pop(0)
            # wait for a bit, to avoid immediately returning (runs go faster than mouse moves)
            clock.pause(10)

        return clock.get_time(), (xl[len(xl) - 1], yl[len(yl) - 1])
Example #2
0
	def wait_for_saccade_end(self):

		"""Returns ending time, starting and end position when a simulated saccade is ended"""

		# function assumes that a 'saccade' has ended when 'gaze' position remains reasonably
		# (i.e.: within maxerr) stable for five samples
		# for saccade start algorithm, see wait_for_fixation_start

		stime, spos = self.wait_for_saccade_start()
		maxerr = 3 # pixels
		
		# wait for reasonably stable position
		xl = [] # list for last five samples (x coordinate)
		yl = [] # list for last five samples (y coordinate)
		moving = True
		while moving:
			# check positions
			npos = self.sample()
			xl.append(npos[0]) # add newest sample
			yl.append(npos[1]) # add newest sample
			if len(xl) == 5:
				# check if deviation is small enough
				if max(xl)-min(xl) < maxerr and max(yl)-min(yl) < maxerr:
					moving = False
				# remove oldest sample
				xl.pop(0); yl.pop(0)
			# wait for a bit, to avoid immediately returning (runs go faster than mouse moves)
			clock.pause(10)

		return clock.get_time(), spos, (xl[len(xl)-1],yl[len(yl)-1])
Example #3
0
    def wait_for_saccade_end(self):
        """Returns ending time, starting and end position when a simulated saccade is ended"""

        # function assumes that a 'saccade' has ended when 'gaze' position remains reasonably
        # (i.e.: within maxerr) stable for five samples
        # for saccade start algorithm, see wait_for_fixation_start

        stime, spos = self.wait_for_saccade_start()
        maxerr = 3  # pixels

        # wait for reasonably stable position
        xl = []  # list for last five samples (x coordinate)
        yl = []  # list for last five samples (y coordinate)
        moving = True
        while moving:
            # check positions
            npos = self.sample()
            xl.append(npos[0])  # add newest sample
            yl.append(npos[1])  # add newest sample
            if len(xl) == 5:
                # check if deviation is small enough
                if max(xl) - min(xl) < maxerr and max(yl) - min(yl) < maxerr:
                    moving = False
                # remove oldest sample
                xl.pop(0)
                yl.pop(0)
            # wait for a bit, to avoid immediately returning (runs go faster than mouse moves)
            clock.pause(10)

        return clock.get_time(), spos, (xl[len(xl) - 1], yl[len(yl) - 1])
Example #4
0
	def wait_for_fixation_start(self):

		"""Returns starting time and position when a simulated fixation is started"""

		# function assumes a 'fixation' has started when 'gaze' position remains reasonably
		# stable for five samples in a row (same as saccade end)

		maxerr = 3 # pixels
		
		# wait for reasonably stable position
		xl = [] # list for last five samples (x coordinate)
		yl = [] # list for last five samples (y coordinate)
		moving = True
		while moving:
			npos = self.sample()
			xl.append(npos[0]) # add newest sample
			yl.append(npos[1]) # add newest sample
			if len(xl) == 5:
				# check if deviation is small enough
				if max(xl)-min(xl) < maxerr and max(yl)-min(yl) < maxerr:
					moving = False
				# remove oldest sample
				xl.pop(0); yl.pop(0)
			# wait for a bit, to avoid immediately returning (runs go faster than mouse moves)
			clock.pause(10)

		return clock.get_time(), (xl[len(xl)-1],yl[len(yl)-1])
Example #5
0
	def calibrate(self):

		"""Dummy calibration"""

		print("Calibration would now take place")

		clock.pause(1000)
Example #6
0
    def calibrate(self, animated=None, skip_bad_points=False):
        """Calibrates the eye tracking system
        
        arguments
        None
        
        keyword arguments
        animated    --  bool. Set to True to show a parrot animation instead
                        of calibration dots, or False to use standard points.
                        Set to None to use default option.

        skip_bad_points     --  bool. Intelligaze will skip difficult points
                                when set to True. (Default = False)

        returns
        success     --  returns True if calibration succeeded, or False if
                        not; in addition a calibration log is added to the
                        log file and some properties are updated (i.e. the
                        thresholds for detection algorithms)
        """

        # Process animated keyword argument.
        if animated is None:
            animated = self.animated_calibration
        if animated:
            img = "ANIMATION:PARROT"
        else:
            img = ""

        # Show a message.
        self.screen.clear()
        self.screen.draw_text(text="Running calibration in the foreground...",
                              fontsize=20)
        self.disp.fill(self.screen)
        self.disp.show()

        # CALIBRATION
        # Re-run the calibration until it was approved by the user.
        quited = False
        calibration_approved = False
        while not calibration_approved:
            # Wait for the calibration to finish.
            status, improve = self.alea.calibrate(image=img, \
                skip_bad_points=skip_bad_points)
            # Construct a message string.
            if status == 0:
                calib_str = "Calibration completed!"
            else:
                calib_str = "Calibration failed!"
            if improve:
                calib_str += "\n\nWARNING: IntelliGaze recommends repeating the calibration to improve accuracy."
            calib_str += "\n\n\nPress R to retry, or Space to continue."
            # Show calibration results.
            self.screen.clear()
            self.screen.draw_text(text=calib_str, fontsize=20)
            self.disp.fill(self.screen)
            self.disp.show()
            # Wait for user input.
            key = None
            while key not in ["r", "Space", "space", "q"]:
                key, keytime = self.kb.get_key(keylist=['q', 'r', 'space'],
                                               timeout=None,
                                               flush=True)
            # Process key press.
            if key in ["q", "Space", "space"]:
                calibration_approved = True
                if key == "q":
                    quited = True

        # Calibration failed if the user quited.
        if quited:
            return False

        # NOISE CALIBRATION
        # Present noise calibration instructions.
        self.screen.clear()
        self.screen.draw_text(
            text="Noise calibration. Please look at the dot, and press any key to start.",
            fontsize=20, \
            pos=(int(self.dispsize[0]/2),int(self.dispsize[1]*0.3)))
        self.screen.draw_fixation(fixtype="dot")
        self.disp.fill(self.screen)
        self.disp.show()
        # Wait for a keypress.
        key, keytime = self.kb.get_key(keylist=None, timeout=None, \
            flush=True)
        # Start with empty lists.
        err = {'LX': [], 'LY': [], 'RX': [], 'RY': []}
        var = {'LX': [], 'LY': [], 'RX': [], 'RY': []}
        # Start streaming data so that samples can be obtained.
        self.start_recording()
        self.log("noise_calibration_start")
        # Present a central fixation.
        x = int(float(self.dispsize[0]) / 2.0)
        y = int(float(self.dispsize[1]) / 2.0)
        self.screen.clear()
        self.screen.draw_fixation(fixtype="dot", pos=(x, y))
        self.disp.fill(self.screen)
        t0 = self.disp.show()
        # Collect at least 10 samples, and wait for at least 1 second.
        i = 0
        while (i < 10) or (clock.get_time() - t0 < 1000):
            # Get new sample.
            gx, gy = self.sample()
            if (gx > 0) and (gy > 0):
                i += 1
                err["LX"].append(abs(float(x) - float(gx)))
                err["LY"].append(abs(float(y) - float(gy)))
                err["RX"].append(abs(float(x) - float(gx)))
                err["RY"].append(abs(float(y) - float(gy)))
                for k in var.keys():
                    var[k].append(err[k][-1]**2)
                clock.pause(int(self.sampletime))
        # Stop streaming.
        self.log("noise_calibration_stop")
        self.stop_recording()

        # Compute the RMS noise for the calibration points.
        xnoise = (math.sqrt(sum(var['LX']) / float(len(var['LX']))) + \
            math.sqrt(sum(var['RX']) / float(len(var['RX'])))) / 2.0
        ynoise = (math.sqrt(sum(var['LY']) / float(len(var['LY']))) + \
            math.sqrt(sum(var['RY']) / float(len(var['RY'])))) / 2.0
        self.pxdsttresh = (xnoise, ynoise)

        # AFTERMATH
        # store some variables
        pixpercm = (self.dispsize[0] / float(self.screensize[0]) + \
            self.dispsize[1]/float(self.screensize[1])) / 2
        screendist = settings.SCREENDIST
        # calculate thresholds based on tracker settings
        self.accuracy = ( \
            (pix2deg(screendist, sum(err['LX']) / float(len(err['LX'])), pixpercm), \
            pix2deg(screendist, sum(err['LY']) / float(len(err['LY'])), pixpercm)), \
            (pix2deg(screendist, sum(err['RX']) / float(len(err['RX'])), pixpercm), \
            pix2deg(screendist, sum(err['RY']) / float(len(err['RY'])), pixpercm)))
        self.pxerrdist = deg2pix(screendist, self.errdist, pixpercm)
        self.pxfixtresh = deg2pix(screendist, self.fixtresh, pixpercm)
        self.pxaccuracy = ( \
            (sum(err['LX']) / float(len(err['LX'])), \
            sum(err['LY']) / float(len(err['LY']))), \
            (sum(err['RX']) / float(len(err['RX'])), \
            sum(err['RY']) / float(len(err['RY']))))
        self.pxspdtresh = deg2pix(screendist, self.spdtresh / 1000.0,
                                  pixpercm)  # in pixels per millisecond
        self.pxacctresh = deg2pix(screendist, self.accthresh / 1000.0,
                                  pixpercm)  # in pixels per millisecond**2

        # calibration report
        self.log("pygaze calibration report start")
        self.log("accuracy (degrees): LX={}, LY={}, RX={}, RY={}".format( \
            self.accuracy[0][0], self.accuracy[0][1], self.accuracy[1][0], \
            self.accuracy[1][1]))
        self.log("accuracy (in pixels): LX={}, LY={}, RX={}, RY={}".format( \
            self.pxaccuracy[0][0], self.pxaccuracy[0][1], \
            self.pxaccuracy[1][0], self.pxaccuracy[1][1]))
        self.log("precision (RMS noise in pixels): X={}, Y={}".format( \
            self.pxdsttresh[0],self.pxdsttresh[1]))
        self.log("distance between participant and display: {} cm".format( \
            screendist))
        self.log("fixation threshold: {} pixels".format(self.pxfixtresh))
        self.log("speed threshold: {} pixels/ms".format(self.pxspdtresh))
        self.log("acceleration threshold: {} pixels/ms**2".format( \
            self.pxacctresh))
        self.log("pygaze calibration report end")

        return True
Example #7
0
    def calibrate(self, calibrate=True, validate=True):
        """Calibrates the eye tracking system
		
		arguments
		None
		
		keyword arguments
		calibrate	-- Boolean indicating if calibration should be
				   performed (default = True)
		validate	-- Boolean indicating if validation should be performed
				   (default = True)
		
		returns
		success	-- returns True if calibration succeeded, or False if
				   not; in addition a calibration log is added to the
				   log file and some properties are updated (i.e. the
				   thresholds for detection algorithms)
		"""

        # TODO:
        # add feedback for calibration (e.g. with iV_GetAccuracyImage (struct ImageStruct * imageData) for accuracy and iV_GetEyeImage for cool eye pictures)
        # example: res = iViewXAPI.iV_GetEyeImage(byref(imageData))
        # ImageStruct has four data fields:
        # imageHeight	-- int vertical size (px)
        # imageWidth	-- int horizontal size (px)
        # imageSize		-- int image data size (byte)
        # imageBuffer	-- pointer to image data (I have NO idea what format this is in)

        # configure calibration (NOT starting it)
        calibrationData = CCalibration(
            9, 1, 0, 1, 1, 0, 127, 1, 15, b""
        )  # (method (i.e.: number of points), visualization, display, speed, auto, fg, bg, shape, size, filename)

        # setup calibration
        res = iViewXAPI.iV_SetupCalibration(byref(calibrationData))
        if res != 1:
            err = errorstring(res)
            raise Exception(
                "Error in libsmi.SMItracker.calibrate: failed to setup calibration; %s"
                % err)

        # calibrate
        cres = iViewXAPI.iV_Calibrate()

        # validate if calibration returns succes
        if cres == 1:
            cerr = None
            vres = iViewXAPI.iV_Validate()
            # handle validation errors
            if vres != 1:
                verr = errorstring(vres)
            else:
                verr = None
##				# TEST #
##				res = iViewXAPI.iV_GetAccuracyImage(byref(imageData))
##				self.log("IMAGEBUFFERSTART")
##				self.log(imageData.imageBuffer)
##				self.log("IMAGEBUFFERSTOP")
##				print("Image height: %s, image width: %s, image size: %s" % (imageData.imageHeight,imageData.imageWidth, imageData.imageSize))
##				print imageData.imageBuffer
##				########
# handle calibration errors
        else:
            cerr = errorstring(cres)

        # return succes
        if cerr == None:
            print("libsmi.SMItracker.calibrate: calibration was succesful")
            if verr == None:
                print("libsmi.SMItracker.calibrate: validation was succesful")

                # present instructions
                self.disp.fill()  # clear display
                self.screen.draw_text(
                    text=
                    "Noise calibration: please look at the dot\n\n(press space to start)",
                    pos=(self.dispsize[0] / 2, int(self.dispsize[1] * 0.2)),
                    center=True)
                self.screen.draw_fixation(fixtype='dot')
                self.disp.fill(self.screen)
                self.disp.show()
                self.screen.clear()  # clear screen again

                # wait for spacepress
                self.kb.get_key(keylist=['space'], timeout=None)

                # show fixation
                self.disp.fill()
                self.screen.draw_fixation(fixtype='dot')
                self.disp.fill(self.screen)
                self.disp.show()
                self.screen.clear()

                # wait for a bit, to allow participant to fixate
                clock.pause(500)

                # get samples
                sl = [
                    self.sample()
                ]  # samplelist, prefilled with 1 sample to prevent sl[-1] from producing an error; first sample will be ignored for RMS calculation
                t0 = clock.get_time()  # starting time
                while clock.get_time() - t0 < 1000:
                    s = self.sample()  # sample
                    if s != sl[-1] and s != (-1, -1) and s != (0, 0):
                        sl.append(s)
                # calculate RMS noise
                Xvar = []
                Yvar = []
                for i in range(2, len(sl)):
                    Xvar.append((sl[i][0] - sl[i - 1][0])**2)
                    Yvar.append((sl[i][1] - sl[i - 1][1])**2)
                XRMS = (sum(Xvar) / len(Xvar))**0.5
                YRMS = (sum(Yvar) / len(Yvar))**0.5
                self.pxdsttresh = (XRMS, YRMS)

                # calculate pixels per cm
                pixpercm = (self.dispsize[0] / float(self.screensize[0]) +
                            self.dispsize[1] / float(self.screensize[1])) / 2
                # get accuracy
                res = 0
                i = 0
                while res != 1 and i < self.maxtries:  # multiple tries, in case no (valid) sample is available
                    res = iViewXAPI.iV_GetAccuracy(
                        byref(accuracyData), 0)  # 0 is for 'no visualization'
                    i += 1
                    clock.pause(int(self.sampletime))  # wait for sampletime
                if res == 1:
                    self.accuracy = (
                        (accuracyData.deviationLX, accuracyData.deviationLY),
                        (accuracyData.deviationLX, accuracyData.deviationLY)
                    )  # dsttresh = (left tuple, right tuple); tuple = (horizontal deviation, vertical deviation) in degrees of visual angle
                else:
                    err = errorstring(res)
                    print(
                        "WARNING libsmi.SMItracker.calibrate: failed to obtain accuracy data; %s"
                        % err)
                    self.accuracy = ((2, 2), (2, 2))
                    print(
                        "libsmi.SMItracker.calibrate: As an estimate, the intersample distance threshhold was set to it's default value of 2 degrees"
                    )
                # get distance from screen to eyes (information from tracker)
                res = 0
                i = 0
                while res != 1 and i < self.maxtries:  # multiple tries, in case no (valid) sample is available
                    res = iViewXAPI.iV_GetSample(byref(sampleData))
                    i += 1
                    clock.pause(int(self.sampletime))  # wait for sampletime
                if res == 1:
                    screendist = sampleData.leftEye.eyePositionZ / 10.0  # eyePositionZ is in mm; screendist is in cm
                else:
                    err = errorstring(res)
                    print(
                        "WARNING libsmi.SMItracker.calibrate: failed to obtain screen distance; %s"
                        % err)
                    screendist = settings.SCREENDIST
                    print(
                        "libsmi.SMItracker.calibrate: As an estimate, the screendistance was set to it's default value of 57 cm"
                    )
                # calculate thresholds based on tracker settings
                self.pxerrdist = deg2pix(screendist, self.errdist, pixpercm)
                self.pxfixtresh = deg2pix(screendist, self.fixtresh, pixpercm)
                self.pxaccuracy = ((deg2pix(screendist, self.accuracy[0][0],
                                            pixpercm),
                                    deg2pix(screendist, self.accuracy[0][1],
                                            pixpercm)),
                                   (deg2pix(screendist, self.accuracy[1][0],
                                            pixpercm),
                                    deg2pix(screendist, self.accuracy[1][1],
                                            pixpercm)))
                self.pxspdtresh = deg2pix(
                    screendist, self.spdtresh / 1000.0,
                    pixpercm)  # in pixels per millisecond
                self.pxacctresh = deg2pix(
                    screendist, self.accthresh / 1000.0,
                    pixpercm)  # in pixels per millisecond**2

                # calibration report
                self.log("pygaze calibration report start")
                self.log("accuracy (degrees): LX=%s, LY=%s, RX=%s, RY=%s" %
                         (self.accuracy[0][0], self.accuracy[0][1],
                          self.accuracy[1][0], self.accuracy[1][1]))
                self.log("accuracy (in pixels): LX=%s, LY=%s, RX=%s, RY=%s" %
                         (self.pxaccuracy[0][0], self.pxaccuracy[0][1],
                          self.pxaccuracy[1][0], self.pxaccuracy[1][1]))
                self.log("precision (RMS noise in pixels): X=%s, Y=%s" %
                         (self.pxdsttresh[0], self.pxdsttresh[1]))
                self.log("distance between participant and display: %s cm" %
                         screendist)
                self.log("fixation threshold: %s pixels" % self.pxfixtresh)
                self.log("speed threshold: %s pixels/ms" % self.pxspdtresh)
                self.log("acceleration threshold: %s pixels/ms**2" %
                         self.pxacctresh)
                self.log("pygaze calibration report end")

                return True

            # validation error
            else:
                print(
                    "WARNING libsmi.SMItracker.calibrate: validation was unsuccesful %s"
                    % verr)
                return False

        # calibration error
        else:
            print(
                "WARNING libsmi.SMItracker.calibrate: calibration was unsuccesful; %s"
                % cerr)
            return False
Example #8
0
    def calibrate(self):
        """See pygaze._eyetracker.baseeyetracker.BaseEyeTracker"""

        if self.recording:
            raise Exception(
                "Error in libeyelink.libeyelink.calibrate(): Trying to "
                "calibrate after recording has started!")

        # # # # #
        # EyeLink calibration and validation

        # attempt calibrate; confirm abort when esc pressed
        while True:
            self.eyelink_graphics.esc_pressed = False
            pylink.getEYELINK().doTrackerSetup()
            if not self.eyelink_graphics.esc_pressed:
                break
            self.confirm_abort_experiment()

        # If we are using the built-in EyeLink event detection, we don't need
        # the RMS calibration routine.
        if self.eventdetection == 'native':
            return

        # # # # #
        # RMS calibration

        # present instructions
        self.display.fill()  # clear display
        self.scr.draw_text(text= \
         "Noise calibration: please look at the dot\n\n(press space to start)",
         pos=(self.resolution[0]/2, int(self.resolution[1]*0.2)),
         center=True, fontsize=self.fontsize)
        self.scr.draw_fixation(fixtype='dot')
        self.display.fill(self.scr)
        self.display.show()
        self.scr.clear()  # clear screen again

        # wait for spacepress
        self.kb.get_key(keylist=['space'], timeout=None)

        # start recording
        self.log("PYGAZE RMS CALIBRATION START")
        self.start_recording()

        # show fixation
        self.display.fill()
        self.scr.draw_fixation(fixtype='dot')
        self.display.fill(self.scr)
        self.display.show()
        self.scr.clear()

        # wait for a bit, to allow participant to fixate
        clock.pause(500)

        # get samples
        # samplelist, prefilled with 1 sample to prevent sl[-1] from producing
        # an error; first sample will be ignored for RMS calculation
        sl = [self.sample()]
        t0 = clock.get_time()  # starting time
        while clock.get_time() - t0 < 1000:
            s = self.sample()  # sample
            if s != sl[-1] and s != (-1, -1) and s != (0, 0):
                sl.append(s)

        # stop recording
        self.log("PYGAZE RMS CALIBRATION END")
        self.stop_recording()

        # calculate RMS noise
        Xvar = []
        Yvar = []
        for i in range(2, len(sl)):
            Xvar.append((sl[i][0] - sl[i - 1][0])**2)
            Yvar.append((sl[i][1] - sl[i - 1][1])**2)
        XRMS = (sum(Xvar) / len(Xvar))**0.5
        YRMS = (sum(Yvar) / len(Yvar))**0.5
        self.pxdsttresh = (XRMS, YRMS)

        # recalculate thresholds (degrees to pixels)
        self.pxfixtresh = deg2pix(self.screendist, self.fixtresh,
                                  self.pixpercm)
        self.pxspdtresh = deg2pix(
            self.screendist, self.spdtresh,
            self.pixpercm) / 1000.0  # in pixels per millisecons
        self.pxacctresh = deg2pix(
            self.screendist, self.accthresh,
            self.pixpercm) / 1000.0  # in pixels per millisecond**2
Example #9
0
    def calibrate(self):
        """Dummy calibration"""

        print("Calibration would now take place")
        clock.pause(1000)
Example #10
0
    def calibrate(self):
        #self.screen.clear()
        #self.screen.draw_text(
        #    text="Calibrate EyeTracker",
        #    fontsize=20)
        #self.disp.fill(self.screen)
        #self.disp.show()

        if (not self._recording.is_set()):
            resultTracking = self.api.requestTracking(0)
            if (resultTracking != ELApi.ReturnStart.SUCCESS):
                raise Exception("unable to start eye tracker")

        resultCalibrate = self.api.calibrate(0)
        if (resultCalibrate != ELApi.ReturnCalibrate.SUCCESS):
            self.api.unrequestTracking()
            self.errorbeep.play()
            raise Exception("Calibration failed = {}".format(errorstringCalibrate(resultCalibrate)))
        self._calibrated.set()

        # NOISE CALIBRATION
        self.screen.clear()
        self.screen.draw_text(
            text="Noise calibration. Please look at the dot, and press any key to start.",
            fontsize=20, \
            pos=(int(self.dispsize[0]/2),int(self.dispsize[1]*0.3)))
        x = int(float(self.dispsize[0]) / 2.0)
        y = int(float(self.dispsize[1]) / 2.0)
        self.screen.draw_fixation(fixtype="dot", pos=(x,y))
        self.disp.fill(self.screen)
        self.disp.show()
        self.kb.get_key(keylist=None, timeout=None, flush=True)

        # wait for a bit, to allow participant to fixate
        clock.pause(500)

        # get distance to screen
        screendist = 0
        i = 0
        while screendist == 0 and i < self.maxtries:
            i = i+1
            self.sampleLock.acquire()
            if (self.lastSample is not None):
                if self.eye_used != 1 and self.lastSample.eyePositionLeftZ != ELInvalidValue:
                    screendist = self.lastSample.eyePositionLeftZ / 10.0 # eyePositionZ is in mm; screendist is in cm
                elif self.eye_used != 0 and self.lastSample.eyePositionRightZ != ELInvalidValue:
                    screendist = self.lastSample.eyePositionRightZ / 10.0
            self.sampleLock.release()
            clock.pause(int(self.sampleTime))
        if i >= self.maxtries:
            self.api.unrequestTracking()
            self.errorbeep.play()
            raise Exception("unable to receive gaze data for noise calibration")

        # get samples
        sl = [self.sample()] # samplelist, prefilled with 1 sample to prevent sl[-1] from producing an error; first sample will be ignored for RMS calculation
        t0 = clock.get_time() # starting time
        while clock.get_time() - t0 < 1000:
            s = self.sample() # sample
            if s[0] != -1 and s[1] != -1 and s[0] != ELInvalidValue and s[1] != ELInvalidValue:
                sl.append(s)
            clock.pause(int(self.sampleTime))
        if (len(sl) < 2):
            if (not self._recording.is_set()):
                self.api.unrequestTracking()
            return False

        # calculate RMS noise
        Xvar = []
        Yvar = []
        Xmean = 0.
        Ymean = 0.
        for i in range(2,len(sl)):
            Xvar.append((sl[i][0]-sl[i-1][0])**2)
            Yvar.append((sl[i][1]-sl[i-1][1])**2)
            Xmean += sl[i][0]
            Ymean += sl[i][1]
        XRMS = (sum(Xvar) / len(Xvar))**0.5
        YRMS = (sum(Yvar) / len(Yvar))**0.5
        Xmean = Xmean / (len(sl)-2)
        Ymean = Ymean / (len(sl)-2)
        self.pxdsttresh = (XRMS, YRMS)

        # calculate pixels per cm
        pixpercm = (self.dispsize[0]/float(self.screensize[0]) + self.dispsize[1]/float(self.screensize[1])) / 2

        # get accuracy
        accuracyPxX = abs( Xmean - x )
        accuracyPxY = abs( Ymean - y )
        self.accuracy = ( pix2deg(screendist, accuracyPxX, pixpercm), \
                          pix2deg(screendist, accuracyPxY, pixpercm) )

        # calculate thresholds based on tracker settings
        self.pxfixtresh = deg2pix(screendist, self.fixtresh, pixpercm)
        self.pxaccuracy = (accuracyPxX, accuracyPxY )
        self.pxspdtresh = deg2pix(screendist, self.spdtresh/1000.0, pixpercm) # in pixels per millisecond
        self.pxacctresh = deg2pix(screendist, self.accthresh/1000.0, pixpercm) # in pixels per millisecond**2

        ## log
        self.log("pygaze calibration")
        self.log("accuracy (degrees) = X={}, Y={}".format( \
            self.accuracy[0], self.accuracy[1] ))
        self.log("accuracy (in pixels) = X={}, Y={}".format( \
            self.pxaccuracy[0], self.pxaccuracy[1]))
        self.log("precision (RMS noise in pixels) = X={}, Y={}".format( \
            self.pxdsttresh[0], self.pxdsttresh[1]))
        self.log("distance between participant and display = {} cm".format(screendist))
        self.log("fixation threshold = {} pixels".format(self.pxfixtresh))
        self.log("speed threshold = {} pixels/ms".format(self.pxspdtresh))
        self.log("acceleration threshold = {} pixels/ms**2".format(self.pxacctresh))
        
        if (not self._recording.is_set()):
            self.api.unrequestTracking()
        return True
Example #11
0
	def calibrate(self, calibrate=True, validate=True):

		"""Calibrates the eye tracking system
		
		arguments
		None
		
		keyword arguments
		calibrate	-- Boolean indicating if calibration should be
				   performed (default = True)
		validate	-- Boolean indicating if validation should be performed
				   (default = True)
		
		returns
		success	-- returns True if calibration succeeded, or False if
				   not; in addition a calibration log is added to the
				   log file and some properties are updated (i.e. the
				   thresholds for detection algorithms)
		"""

		# TODO:
		# add feedback for calibration (e.g. with iV_GetAccuracyImage (struct ImageStruct * imageData) for accuracy and iV_GetEyeImage for cool eye pictures)
		# example: res = iViewXAPI.iV_GetEyeImage(byref(imageData))
		# ImageStruct has four data fields:
		# imageHeight	-- int vertical size (px)
		# imageWidth	-- int horizontal size (px)
		# imageSize		-- int image data size (byte)
		# imageBuffer	-- pointer to image data (I have NO idea what format this is in)

		# configure calibration (NOT starting it)
		calibrationData = CCalibration(9, 1, 0, 1, 1, 0, 127, 1, 15, b"") # (method (i.e.: number of points), visualization, display, speed, auto, fg, bg, shape, size, filename)

		# setup calibration
		res = iViewXAPI.iV_SetupCalibration(byref(calibrationData))
		if res != 1:
			err = errorstring(res)
			raise Exception("Error in libsmi.SMItracker.calibrate: failed to setup calibration; %s" % err)

		# calibrate
		cres = iViewXAPI.iV_Calibrate()
			
		# validate if calibration returns succes
		if cres == 1:
			cerr = None
			vres = iViewXAPI.iV_Validate()
			# handle validation errors
			if vres != 1:
				verr = errorstring(vres)
			else:
				verr = None
##				# TEST #
##				res = iViewXAPI.iV_GetAccuracyImage(byref(imageData))
##				self.log("IMAGEBUFFERSTART")
##				self.log(imageData.imageBuffer)
##				self.log("IMAGEBUFFERSTOP")
##				print("Image height: %s, image width: %s, image size: %s" % (imageData.imageHeight,imageData.imageWidth, imageData.imageSize))
##				print imageData.imageBuffer
##				########
		# handle calibration errors
		else:
			cerr = errorstring(cres)

		# return succes
		if cerr == None:
			print("libsmi.SMItracker.calibrate: calibration was succesful")
			if verr == None:
				print("libsmi.SMItracker.calibrate: validation was succesful")

				# present instructions
				self.disp.fill() # clear display
				self.screen.draw_text(text="Noise calibration: please look at the dot\n\n(press space to start)", pos=(self.dispsize[0]/2, int(self.dispsize[1]*0.2)), center=True)
				self.screen.draw_fixation(fixtype='dot')
				self.disp.fill(self.screen)
				self.disp.show()
				self.screen.clear() # clear screen again

				# wait for spacepress
				self.kb.get_key(keylist=['space'], timeout=None)

				# show fixation
				self.disp.fill()
				self.screen.draw_fixation(fixtype='dot')
				self.disp.fill(self.screen)
				self.disp.show()
				self.screen.clear()

				# wait for a bit, to allow participant to fixate
				clock.pause(500)

				# get samples
				sl = [self.sample()] # samplelist, prefilled with 1 sample to prevent sl[-1] from producing an error; first sample will be ignored for RMS calculation
				t0 = clock.get_time() # starting time
				while clock.get_time() - t0 < 1000:
					s = self.sample() # sample
					if s != sl[-1] and s != (-1,-1) and s != (0,0):
						sl.append(s)
				# calculate RMS noise
				Xvar = []
				Yvar = []
				for i in range(2,len(sl)):
					Xvar.append((sl[i][0]-sl[i-1][0])**2)
					Yvar.append((sl[i][1]-sl[i-1][1])**2)
				XRMS = (sum(Xvar) / len(Xvar))**0.5
				YRMS = (sum(Yvar) / len(Yvar))**0.5
				self.pxdsttresh = (XRMS, YRMS)

				# calculate pixels per cm
				pixpercm = (self.dispsize[0]/float(self.screensize[0]) + self.dispsize[1]/float(self.screensize[1])) / 2
				# get accuracy
				res = 0; i = 0
				while res != 1 and i < self.maxtries: # multiple tries, in case no (valid) sample is available
					res = iViewXAPI.iV_GetAccuracy(byref(accuracyData),0) # 0 is for 'no visualization'
					i += 1
					clock.pause(int(self.sampletime)) # wait for sampletime
				if res == 1:
					self.accuracy = ((accuracyData.deviationLX,accuracyData.deviationLY), (accuracyData.deviationLX,accuracyData.deviationLY)) # dsttresh = (left tuple, right tuple); tuple = (horizontal deviation, vertical deviation) in degrees of visual angle
				else:
					err = errorstring(res)
					print("WARNING libsmi.SMItracker.calibrate: failed to obtain accuracy data; %s" % err)
					self.accuracy = ((2,2),(2,2))
					print("libsmi.SMItracker.calibrate: As an estimate, the intersample distance threshhold was set to it's default value of 2 degrees")
				# get distance from screen to eyes (information from tracker)
				res = 0; i = 0
				while res != 1 and i < self.maxtries: # multiple tries, in case no (valid) sample is available
					res = iViewXAPI.iV_GetSample(byref(sampleData))
					i += 1
					clock.pause(int(self.sampletime)) # wait for sampletime
				if res == 1:
					screendist = sampleData.leftEye.eyePositionZ / 10.0 # eyePositionZ is in mm; screendist is in cm
				else:
					err = errorstring(res)
					print("WARNING libsmi.SMItracker.calibrate: failed to obtain screen distance; %s" % err)
					screendist = SCREENDIST
					print("libsmi.SMItracker.calibrate: As an estimate, the screendistance was set to it's default value of 57 cm")
				# calculate thresholds based on tracker settings
				self.pxerrdist = deg2pix(screendist, self.errdist, pixpercm)
				self.pxfixtresh = deg2pix(screendist, self.fixtresh, pixpercm)
				self.pxaccuracy = ((deg2pix(screendist, self.accuracy[0][0], pixpercm),deg2pix(screendist, self.accuracy[0][1], pixpercm)), (deg2pix(screendist, self.accuracy[1][0], pixpercm),deg2pix(screendist, self.accuracy[1][1], pixpercm)))
				self.pxspdtresh = deg2pix(screendist, self.spdtresh/1000.0, pixpercm) # in pixels per millisecond
				self.pxacctresh = deg2pix(screendist, self.accthresh/1000.0, pixpercm) # in pixels per millisecond**2

				# calibration report
				self.log("pygaze calibration report start")
				self.log("accuracy (degrees): LX=%s, LY=%s, RX=%s, RY=%s" % (self.accuracy[0][0],self.accuracy[0][1],self.accuracy[1][0],self.accuracy[1][1]))
				self.log("accuracy (in pixels): LX=%s, LY=%s, RX=%s, RY=%s" % (self.pxaccuracy[0][0],self.pxaccuracy[0][1],self.pxaccuracy[1][0],self.pxaccuracy[1][1]))
				self.log("precision (RMS noise in pixels): X=%s, Y=%s" % (self.pxdsttresh[0],self.pxdsttresh[1]))
				self.log("distance between participant and display: %s cm" % screendist)
				self.log("fixation threshold: %s pixels" % self.pxfixtresh)
				self.log("speed threshold: %s pixels/ms" % self.pxspdtresh)
				self.log("acceleration threshold: %s pixels/ms**2" % self.pxacctresh)
				self.log("pygaze calibration report end")

				return True

			# validation error
			else:
				print("WARNING libsmi.SMItracker.calibrate: validation was unsuccesful %s" % verr)
				return False

		# calibration error
		else:
			print("WARNING libsmi.SMItracker.calibrate: calibration was unsuccesful; %s" % cerr)
			return False
Example #12
0
	def calibrate(self):

		"""Calibrates the eye tracking system
		
		arguments
		None
		
		keyword arguments
		None

		returns
		success	-- returns True if calibration succeeded, or False if
				   not; in addition a calibration log is added to the
				   log file and some properties are updated (i.e. the
				   thresholds for detection algorithms)
		"""
		
		# CALIBRATION
		# determine the calibration points
		calibpoints = []
		for x in [0.1,0.5,0.9]:
			for y in [0.1,0.5,0.9]:
				calibpoints.append((int(x*self.dispsize[0]),int(y*self.dispsize[1])))
		random.shuffle(calibpoints)
		
		# show a message
		self.screen.clear()
		self.screen.draw_text(text="Press Space to start the calibration or Q to quit.")
		self.disp.fill(self.screen)
		self.disp.show()
		
		# wait for keyboard input
		key, keytime = self.kb.get_key(keylist=['q','space'], timeout=None, flush=True)
		if key == 'q':
			quited = True
		else:
			quited = False
		
		# run until the user is statisfied, or quits
		calibrated = False
		calibresult = None
		while not quited and not calibrated:
			# start a new calibration
			self.eyetribe.calibration.start(pointcount=len(calibpoints))
			
			# loop through calibration points
			for cpos in calibpoints:
				self.draw_calibration_target(cpos[0], cpos[1])
				# wait for a bit to allow participant to start looking at
				# the calibration point (#TODO: space press?)
				clock.pause(1000)
				# start calibration of point
				self.eyetribe.calibration.pointstart(cpos[0],cpos[1])
				# wait for a second
				clock.pause(1000)
				# stop calibration of this point
				result = self.eyetribe.calibration.pointend()
				# the final calibration point returns a dict (does it?)
				if type(result) == dict:
					calibresult = copy.deepcopy(result)
				# check if the Q key has been pressed
				if self.kb.get_key(keylist=['q'],timeout=10,flush=False)[0] == 'q':
					# abort calibration
					self.eyetribe.calibration.abort()
					# set quited variable and break this for loop
					quited = True
					break
			
			# retry option if the calibration was aborted			
			if quited:
				# show retry message
				self.screen.clear()
				self.screen.draw_text("Calibration aborted. Press Space to restart, or 'Q' to quit.")
				self.disp.fill(self.screen)
				self.disp.show()
				# get input
				key, keytime = self.kb.get_key(keylist=['q','space'], timeout=None, flush=True)
				if key == 'space':
					# unset quited Boolean
					quited = False
				# skip further processing
				continue

			# get the calibration result if it was not obtained yet
			if type(calibresult) != dict:
				# empty display
				self.disp.fill()
				self.disp.show()
				# allow for a bit of calculation time
				clock.pause(2000)
				# get the result
				calibresult = self.eyetribe._tracker.get_calibresult()

			# results
			# clear the screen
			self.screen.clear()
			# draw results for each point
			if type(calibresult) == dict:
				for p in calibresult['calibpoints']:
					# only draw the point if data was obtained
					if p['state'] > 0:
						# draw the mean error
						self.screen.draw_circle(colour=(252,233,79), pos=(p['cpx'],p['cpy']), r=p['mepix'], pw=0, fill=True)
						# draw the point
						self.screen.draw_fixation(fixtype='dot', colour=(115,210,22), pos=(p['cpx'],p['cpy']))
						# draw the estimated point
						self.screen.draw_fixation(fixtype='dot', colour=(32,74,135), pos=(p['mecpx'],p['mecpy']))
						# annotate accuracy
						self.screen.draw_text(text=str(p['acd']), pos=(p['cpx']+10,p['cpy']+10), fontsize=12)
					# if no data was obtained, draw the point in red
					else:
						self.screen.draw_fixation(fixtype='dot', colour=(204,0,0), pos=(p['cpx'],p['cpy']))
				# draw box for averages
				self.screen.draw_rect(colour=(238,238,236), x=int(self.dispsize[0]*0.15), y=int(self.dispsize[1]*0.2), w=400, h=200, pw=0, fill=True)
				# draw result
				if calibresult['result']:
					self.screen.draw_text(text="calibration is successful", colour=(115,210,22), pos=(int(self.dispsize[0]*0.25),int(self.dispsize[1]*0.25)), fontsize=12)
				else:
					self.screen.draw_text(text="calibration failed", colour=(204,0,0), pos=(int(self.dispsize[0]*0.25),int(self.dispsize[1]*0.25)), fontsize=12)
				# draw average accuracy
				self.screen.draw_text(text="average error = %.2f degrees" % (calibresult['deg']), colour=(211,215,207), pos=(int(self.dispsize[0]*0.25),int(self.dispsize[1]*0.25+20)), fontsize=12)
				# draw input options
				self.screen.draw_text(text="Press Space to continue, or 'R' to restart.", colour=(211,215,207), pos=(int(self.dispsize[0]*0.25),int(self.dispsize[1]*0.25+40)), fontsize=12)
			else:
				self.screen.draw_text(text="Calibration failed, press 'R' to try again.")
			# show the results
			self.disp.fill(self.screen)
			self.disp.show()
			# wait for input
			key, keytime = self.kb.get_key(keylist=['space','r'], timeout=None, flush=True)
			# process input
			if key == 'space':
				calibrated = True

		# calibration failed if the user quited
		if quited:
			return False

		# NOISE CALIBRATION
		# get all error estimates (pixels)
		var = []
		for p in calibresult['calibpoints']:
			# only draw the point if data was obtained
			if p['state'] > 0:
				var.append(p['mepix'])
		noise = sum(var) / float(len(var))
		self.pxdsttresh = (noise, noise)
				
		# AFTERMATH
		# store some variables
		pixpercm = (self.dispsize[0]/float(self.screensize[0]) + self.dispsize[1]/float(self.screensize[1])) / 2
		screendist = SCREENDIST
		# calculate thresholds based on tracker settings
		self.accuracy = ((calibresult['Ldeg'],calibresult['Ldeg']), (calibresult['Rdeg'],calibresult['Rdeg'])) 
		self.pxerrdist = deg2pix(screendist, self.errdist, pixpercm)
		self.pxfixtresh = deg2pix(screendist, self.fixtresh, pixpercm)
		self.pxaccuracy = ((deg2pix(screendist, self.accuracy[0][0], pixpercm),deg2pix(screendist, self.accuracy[0][1], pixpercm)), (deg2pix(screendist, self.accuracy[1][0], pixpercm),deg2pix(screendist, self.accuracy[1][1], pixpercm)))
		self.pxspdtresh = deg2pix(screendist, self.spdtresh/1000.0, pixpercm) # in pixels per millisecond
		self.pxacctresh = deg2pix(screendist, self.accthresh/1000.0, pixpercm) # in pixels per millisecond**2

		# calibration report
		self.log("pygaze calibration report start")
		self.log("accuracy (degrees): LX=%s, LY=%s, RX=%s, RY=%s" % (self.accuracy[0][0],self.accuracy[0][1],self.accuracy[1][0],self.accuracy[1][1]))
		self.log("accuracy (in pixels): LX=%s, LY=%s, RX=%s, RY=%s" % (self.pxaccuracy[0][0],self.pxaccuracy[0][1],self.pxaccuracy[1][0],self.pxaccuracy[1][1]))
		self.log("precision (RMS noise in pixels): X=%s, Y=%s" % (self.pxdsttresh[0],self.pxdsttresh[1]))
		self.log("distance between participant and display: %s cm" % screendist)
		self.log("fixation threshold: %s pixels" % self.pxfixtresh)
		self.log("speed threshold: %s pixels/ms" % self.pxspdtresh)
		self.log("acceleration threshold: %s pixels/ms**2" % self.pxacctresh)
		self.log("pygaze calibration report end")

		return True
Example #13
0
    def calibrate(self, pre_calib_wait=500, calib_wait=1000):
        """Calibrates the eye tracking system

        returns
        success	-- returns True if calibration succeeded, or False if
                   not; in addition a calibration log is added to the
                   log file and some properties are updated (i.e. the
                   thresholds for detection algorithms)
        """
        def get_psychopy_pos(x, y):
            return (x - self.dispsize[0] / 2), (y - self.dispsize[1] / 2)

        # CALIBRATION
        # determine the calibration points
        calibpoints = []
        margin = int(self.dispsize[1] / 10)
        gap_x = int((self.dispsize[0] - 2 * margin) / 3)
        gap_y = int((self.dispsize[1] - 2 * margin) / 3)
        for x in range(4):
            for y in range(4):
                calibpoints.append((margin + gap_x * x, margin + gap_y * y))
        random.shuffle(calibpoints)  # shuffle two lists together

        # show a message
        black_bg = visual.Rect(self.presenter.window,
                               width=2.1,
                               height=2.1,
                               fillColor='black')
        self.presenter.show_instructions(
            'Press space to calibrate\n\nFollow circles with your eyes',
            other_stim=[black_bg],
            next_instr_text=None)
        quited = False

        # Pause the processing of samples during the calibration.
        # self.eyetribe._pause_sample_processing()
        # run until the user is statisfied, or quits
        calibrated = False
        calibresult = None
        while not quited and not calibrated:

            # Clear the existing calibration.
            if self.eyetribe._tracker.get_iscalibrated():
                self.eyetribe._lock.acquire(True)
                self.eyetribe.calibration.clear()
                self.eyetribe._lock.release()

            # Wait for a bit.
            clock.pause(1500)

            # start a new calibration
            if not self.eyetribe._tracker.get_iscalibrating():
                self.eyetribe._lock.acquire(True)
                self.eyetribe.calibration.start(pointcount=len(calibpoints))
                self.eyetribe._lock.release()

            # loop through calibration points
            for cpos in calibpoints:
                # Check whether the calibration is already done.
                # (Not sure how or why, but for some reason some data
                # can persist between calbrations, and the tracker will
                # simply stop allowing further pointstart requests.)
                if self.eyetribe._tracker.get_iscalibrated():
                    break

                # Draw a calibration target.
                point = get_psychopy_pos(cpos[0], cpos[1])
                self.draw_calibration_target(point[0], point[1], black_bg)
                # wait for a bit to allow participant to start looking at
                # the calibration point
                clock.pause(pre_calib_wait)
                # start calibration of point
                self.eyetribe._lock.acquire(True)
                self.eyetribe.calibration.pointstart(cpos[0], cpos[1])
                self.eyetribe._lock.release()
                # wait for a second
                clock.pause(calib_wait)
                # stop calibration of this point
                self.eyetribe._lock.acquire(True)
                self.eyetribe.calibration.pointend()
                self.eyetribe._lock.release()

            # empty display
            self.presenter.draw_stimuli_for_duration([black_bg], duration=None)
            # allow for a bit of calculation time
            # (this is waaaaaay too much)
            clock.pause(1000)
Example #14
0
    def calibrate(self, calibrate=True, validate=True):
        """Calibrates the eye tracker.

        arguments
        None

        keyword arguments
        calibrate	--	Boolean indicating if calibration should be
                    performed (default = True).
        validate	--	Boolean indicating if validation should be performed
                    (default = True).

        returns
        success	--	returns True if calibration succeeded, or False if
                    not; in addition a calibration log is added to the
                    log file and some properties are updated (i.e. the
                    thresholds for detection algorithms)
        """
        self._write_enabled = False
        self.start_recording()
        self.screen.set_background_colour(colour=(0, 0, 0))

        if calibrate:
            origin = (int(self.disp.dispsize[0] / 4), int(self.disp.dispsize[1] / 4))
            size = (int(2 * self.disp.dispsize[0] / 4), int(2 * self.disp.dispsize[1] / 4))

            while not self.kb.get_key(keylist=['space'], flush=False)[0]:
                gaze_sample = copy.copy(self.gaze[-1])

                self.screen.clear()

                validity_colour = (255, 0, 0)

                if gaze_sample['right_gaze_origin_validity'] and gaze_sample['left_gaze_origin_validity']:
                    left_validity = 0.15 < gaze_sample['left_gaze_origin_in_trackbox_coordinate_system'][2] < 0.85
                    right_validity = 0.15 < gaze_sample['right_gaze_origin_in_trackbox_coordinate_system'][2] < 0.85
                    if left_validity and right_validity:
                        validity_colour = (0, 255, 0)

                self.screen.draw_text(text="When correctly positioned press \'space\' to start the calibration.", pos=(int(self.disp.dispsize[0] / 2), int(self.disp.dispsize[1] * 0.1)), colour=(255, 255, 255), fontsize=20)
                self.screen.draw_line(colour=validity_colour, spos=origin, epos=(origin[0] + size[0], origin[1]), pw=1)
                self.screen.draw_line(colour=validity_colour, spos=origin, epos=(origin[0], origin[1] + size[1]), pw=1)
                self.screen.draw_line(colour=validity_colour, spos=(origin[0], origin[1] + size[1]), epos=(origin[0] + size[0], origin[1] + size[1]), pw=1)
                self.screen.draw_line(colour=validity_colour, spos=(origin[0] + size[0], origin[1] + size[1]), epos=(origin[0] + size[0], origin[1]), pw=1)

                right_eye, left_eye, distance = None, None, []
                if gaze_sample['right_gaze_origin_validity']:
                    distance.append(round(gaze_sample['right_gaze_origin_in_user_coordinate_system'][2] / 10, 1))
                    right_eye = ((1 - gaze_sample['right_gaze_origin_in_trackbox_coordinate_system'][0]) * size[0] + origin[0],
                                gaze_sample['right_gaze_origin_in_trackbox_coordinate_system'][1] * size[1] + origin[1])
                    self.screen.draw_circle(colour=validity_colour, pos=right_eye, r=int(self.disp.dispsize[0] / 100), pw=5, fill=True)

                if gaze_sample['left_gaze_origin_validity']:
                    distance.append(round(gaze_sample['left_gaze_origin_in_user_coordinate_system'][2] / 10, 1))
                    left_eye = ((1 - gaze_sample['left_gaze_origin_in_trackbox_coordinate_system'][0]) * size[0] + origin[0],
                                gaze_sample['left_gaze_origin_in_trackbox_coordinate_system'][1] * size[1] + origin[1])
                    self.screen.draw_circle(colour=validity_colour, pos=left_eye, r=int(self.disp.dispsize[0] / 100), pw=5, fill=True)

                self.screen.draw_text(text="Current distance to the eye tracker: {0} cm.".format(self._mean(distance)), pos=(int(self.disp.dispsize[0] / 2), int(self.disp.dispsize[1] * 0.9)), colour=(255, 255, 255), fontsize=20)

                self.disp.fill(self.screen)
                self.disp.show()

            # # # # # #
            # # calibration

            if not self.eyetracker:
                print("WARNING! libtobii.TobiiProTracker.calibrate: no eye trackers found for the calibration!")
                self.stop_recording()
                return False

            calibration = tr.ScreenBasedCalibration(self.eyetracker)

            calibrating = True


            while calibrating:
                calibration.enter_calibration_mode()
                for point in self.points_to_calibrate:
                    self.screen.clear()
		    # CDP : Changement couleur
                    #self.screen.draw_circle(colour='yellow', pos=point, r=int(self.disp.dispsize[0] / 100.0), pw=5, fill=True)
                    #self.screen.draw_circle(colour=(255, 0, 0), pos=point, r=int(self.disp.dispsize[0] / 400.0), pw=5, fill=True)
                    #self.disp.fill(self.screen)
                    #self.disp.show()

                    # Wait a little for user to focus.
		    # CDP : Ajout sasie clavier
                    #clock.pause(1000)

                    self.ReduceBall(point,30,'yellow')

                    pressed_key = self.kb.get_key(keylist=['space', 'r'], flush=True, timeout=None)


                    normalized_point = self._px_2_norm(point)

                    if calibration.collect_data(normalized_point[0], normalized_point[1]) != tr.CALIBRATION_STATUS_SUCCESS:
                        # Try again if it didn't go well the first time.
                        # Not all eye tracker models will fail at this point, but instead fail on ComputeAndApply.
                        calibration.collect_data(normalized_point[0], normalized_point[1])

                self.screen.clear()
                self.screen.draw_text("Calculating calibration result....", colour=(255, 255, 255), fontsize=20)
                self.disp.fill(self.screen)
                self.disp.show()

                calibration_result = calibration.compute_and_apply()


                calibration.leave_calibration_mode()

                print "Compute and apply returned {0} and collected at {1} points.".\
                    format(calibration_result.status, len(calibration_result.calibration_points))

                if calibration_result.status != tr.CALIBRATION_STATUS_SUCCESS:
                    self.stop_recording()
                    print("WARNING! libtobii.TobiiProTracker.calibrate: Calibration was unsuccessful!")
                    return False

                self.screen.clear()
                for point in calibration_result.calibration_points:
                    self.screen.draw_circle(colour=(255, 255, 255), pos=self._norm_2_px(point.position_on_display_area), r=self.disp.dispsize[0] / 200, pw=1, fill=False)
                    for sample in point.calibration_samples:
                        if sample.left_eye.validity == tr.VALIDITY_VALID_AND_USED:
                            self.screen.draw_circle(colour=(255, 0, 0), pos=self._norm_2_px(sample.left_eye.position_on_display_area), r=self.disp.dispsize[0] / 450, pw=self.disp.dispsize[0] / 450, fill=False)
                            self.screen.draw_line(colour=(255, 0, 0), spos=self._norm_2_px(point.position_on_display_area), epos=self._norm_2_px(sample.left_eye.position_on_display_area), pw=1)
                        if sample.right_eye.validity == tr.VALIDITY_VALID_AND_USED:
                            self.screen.draw_circle(colour=(0, 0, 255), pos=self._norm_2_px(sample.right_eye.position_on_display_area), r=self.disp.dispsize[0] / 450, pw=self.disp.dispsize[0] / 450, fill=False)
                            self.screen.draw_line(colour=(0, 0, 255), spos=self._norm_2_px(point.position_on_display_area), epos=self._norm_2_px(sample.right_eye.position_on_display_area), pw=1)

                self.screen.draw_text("Press the \'R\' key to recalibrate or \'Space\' to continue....", pos=(0.5 * self.disp.dispsize[0], 0.95 * self.disp.dispsize[1]), colour=(255, 255, 255), fontsize=20)

                self.screen.draw_text("Left Eye", pos=(0.5 * self.disp.dispsize[0], 0.01 * self.disp.dispsize[1]), colour=(255, 0, 0), fontsize=20)
                self.screen.draw_text("Right Eye", pos=(0.5 * self.disp.dispsize[0], 0.03 * self.disp.dispsize[1]), colour=(0, 0, 255), fontsize=20)

                self.disp.fill(self.screen)
                self.disp.show()

                pressed_key = self.kb.get_key(keylist=['space', 'r'], flush=True, timeout=None)

                if pressed_key[0] == 'space':
                    calibrating = False

        if validate:
            # # # show menu
            self.screen.clear()
            self.screen.draw_text(text="Press space to start validation", colour=(255, 255, 255), fontsize=20)
            self.disp.fill(self.screen)
            self.disp.show()

            # # # wait for spacepress
            self.kb.get_key(keylist=['space'], flush=True, timeout=None)

            # # # # # #
            # # validation

            # # # arrays for data storage
            lxacc, lyacc, rxacc, ryacc = [], [], [], []

            # # loop through all calibration positions
            for pos in self.points_to_calibrate:
                # show validation point
                self.screen.clear()
                self.screen.draw_fixation(fixtype='dot', pos=pos, colour=(255, 255, 255))
                self.disp.fill(self.screen)
                self.disp.show()

                # allow user some time to gaze at dot
                clock.pause(1000)

                lxsamples, lysamples, rxsamples, rysamples = [], [], [], []
                for sample in self.gaze:
                    if sample["left_gaze_point_validity"]:
                        gaze_point = self._norm_2_px(sample["left_gaze_point_on_display_area"])
                        lxsamples.append(abs(gaze_point[0] - pos[0]))
                        lysamples.append(abs(gaze_point[1] - pos[1]))
                    if sample["right_gaze_point_validity"]:
                        gaze_point = self._norm_2_px(sample["right_gaze_point_on_display_area"])
                        rxsamples.append(abs(gaze_point[0] - pos[0]))
                        rysamples.append(abs(gaze_point[1] - pos[1]))

                # calculate mean deviation
                lxacc.append(self._mean(lxsamples))
                lyacc.append(self._mean(lysamples))
                rxacc.append(self._mean(rxsamples))
                ryacc.append(self._mean(rysamples))

                # wait for a bit to slow down validation process a bit
                clock.pause(1000)

            # calculate mean accuracy
            self.pxaccuracy = [(self._mean(lxacc), self._mean(lyacc)), (self._mean(rxacc), self._mean(ryacc))]

            # sample rate
            # calculate intersample times
            timestamps = []
            gaze_samples = copy.copy(self.gaze)
            for i in xrange(0, len(gaze_samples) - 1):
                timestamps.append((gaze_samples[i + 1]['system_time_stamp'] - gaze_samples[i]['system_time_stamp']) / 1000.0)

            # mean intersample time
            self.sampletime = self._mean(timestamps)
            self.samplerate = int(1000.0 / self.sampletime)

            # # # # # #
            # # RMS noise

            # # present instructions
            self.screen.clear()
            self.screen.draw_text(text="Noise calibration: please look at the dot\n\n(press space to start)", pos=(self.disp.dispsize[0] / 2, int(self.disp.dispsize[1] * 0.2)), colour=(255, 255, 255), fontsize=20)
            self.screen.draw_fixation(fixtype='dot', colour=(255, 255, 255))
            self.disp.fill(self.screen)
            self.disp.show()

            # # wait for spacepress
            self.kb.get_key(keylist=['space'], flush=True, timeout=None)

            # # show fixation
            self.screen.draw_fixation(fixtype='dot', colour=(255, 255, 255))
            self.disp.fill(self.screen)
            self.disp.show()
            self.screen.clear()

            # # wait for a bit, to allow participant to fixate
            clock.pause(500)

            # # get samples
            sl = [self.sample()]  # samplelist, prefilled with 1 sample to prevent sl[-1] from producing an error; first sample will be ignored for RMS calculation
            t0 = clock.get_time()  # starting time
            while clock.get_time() - t0 < 1000:
                s = self.sample()  # sample
                if s != sl[-1] and self.is_valid_sample(s) and s != (0, 0):
                    sl.append(s)

            # # calculate RMS noise
            Xvar, Yvar = [], []
            for i in xrange(2, len(sl)):
                Xvar.append((sl[i][0] - sl[i - 1][0])**2)
                Yvar.append((sl[i][1] - sl[i - 1][1])**2)
            XRMS = (self._mean(Xvar))**0.5
            YRMS = (self._mean(Yvar))**0.5
            self.pxdsttresh = (XRMS, YRMS)

            # # # # # # #
            # # # calibration report

            # # # # recalculate thresholds (degrees to pixels)
            self.pxfixtresh = self._deg2pix(self.screendist, self.fixtresh, self.pixpercm)
            self.pxspdtresh = self._deg2pix(self.screendist, self.spdtresh / 1000.0, self.pixpercm)  # in pixels per millisecons
            self.pxacctresh = self._deg2pix(self.screendist, self.accthresh / 1000.0, self.pixpercm)  # in pixels per millisecond**2

            data_to_write = ''
            data_to_write += "pygaze calibration report start\n"
            data_to_write += "samplerate: %s Hz\n" % self.samplerate
            data_to_write += "sampletime: %s ms\n" % self.sampletime
            data_to_write += "accuracy (in pixels): LX=%s, LY=%s, RX=%s, RY=%s\n" % (self.pxaccuracy[0][0], self.pxaccuracy[0][1], self.pxaccuracy[1][0], self.pxaccuracy[1][1])
            data_to_write += "precision (RMS noise in pixels): X=%s, Y=%s\n" % (self.pxdsttresh[0], self.pxdsttresh[1])
            data_to_write += "distance between participant and display: %s cm\n" % self.screendist
            data_to_write += "fixation threshold: %s pixels\n" % self.pxfixtresh
            data_to_write += "speed threshold: %s pixels/ms\n" % self.pxspdtresh
            data_to_write += "accuracy threshold: %s pixels/ms**2\n" % self.pxacctresh
            data_to_write += "pygaze calibration report end\n"

            # # # # write report to log
            #self.datafile.write(data_to_write)

            self.screen.clear()
            self.screen.draw_text(text=data_to_write, pos=(self.disp.dispsize[0] / 2, int(self.disp.dispsize[1] / 2)), colour=(255, 255, 255), fontsize=20)
            self.disp.fill(self.screen)
            self.disp.show()

            self.kb.get_key(keylist=['space'], flush=True, timeout=None)

        self.stop_recording()
        self._write_enabled = True

        return True
    def preCalibrate(self):
        """Helps position the infant while playing a video.
        returns
            Boolean indicating whether the positioning is done (True: 'space' has been pressed)
        """
        self._write_enabled = False
        self.start_recording()

        # origin: top-left corner of precalibration box; size: tuple of lengths for box sides
        origin = (int(self.disp.dispsize[0] / 4),
                  int(self.disp.dispsize[1] / 4))
        size = (int(2 * self.disp.dispsize[0] / 4),
                int(2 * self.disp.dispsize[1] / 4))

        # Initialise a PsychoPy MovieStim
        mov = visual.MovieStim3(self.video_win, c.CALIBVIDEO, flipVert=False)

        #        print("------------> Pre-calibration process started.")
        print(
            "\t-> When correctly positioned, press \'space\' to start the calibration."
        )

        while mov.status != visual.FINISHED:
            if not self.gaze:
                continue

            self.screen.clear()

            # Add the MovieStim to a PyGaze Screen instance.
            self.screen.screen.append(mov)

            # self.gaze.append(gaze_data), gaze_data is the data structure provided by Tobii
            gaze_sample = copy.copy(self.gaze[-1])  # latest gazepoint

            validity_colour = (255, 0, 0)

            if gaze_sample['right_gaze_origin_validity'] and gaze_sample[
                    'left_gaze_origin_validity']:
                left_validity = 0.15 < gaze_sample[
                    'left_gaze_origin_in_trackbox_coordinate_system'][2] < 0.85
                right_validity = 0.15 < gaze_sample[
                    'right_gaze_origin_in_trackbox_coordinate_system'][2] < 0.85
                if left_validity and right_validity:
                    validity_colour = (0, 255, 0)

            self.screen.draw_line(colour=validity_colour,
                                  spos=origin,
                                  epos=(origin[0] + size[0], origin[1]),
                                  pw=1)
            self.screen.draw_line(colour=validity_colour,
                                  spos=origin,
                                  epos=(origin[0], origin[1] + size[1]),
                                  pw=1)
            self.screen.draw_line(colour=validity_colour,
                                  spos=(origin[0], origin[1] + size[1]),
                                  epos=(origin[0] + size[0],
                                        origin[1] + size[1]),
                                  pw=1)
            self.screen.draw_line(colour=validity_colour,
                                  spos=(origin[0] + size[0],
                                        origin[1] + size[1]),
                                  epos=(origin[0] + size[0], origin[1]),
                                  pw=1)

            right_eye, left_eye, distance = None, None, []
            if gaze_sample['right_gaze_origin_validity']:
                distance.append(
                    round(
                        gaze_sample[
                            'right_gaze_origin_in_user_coordinate_system'][2] /
                        10, 1))
                right_pos = gaze_sample[
                    'right_gaze_origin_in_trackbox_coordinate_system']
                right_eye = ((1 - right_pos[0]) * size[0] + origin[0],
                             right_pos[1] * size[1] + origin[1])
                self.screen.draw_circle(colour=validity_colour,
                                        pos=right_eye,
                                        r=int(self.disp.dispsize[0] / 100),
                                        pw=5,
                                        fill=True)

            if gaze_sample['left_gaze_origin_validity']:
                distance.append(
                    round(
                        gaze_sample[
                            'left_gaze_origin_in_user_coordinate_system'][2] /
                        10, 1))
                left_pos = gaze_sample[
                    'left_gaze_origin_in_trackbox_coordinate_system']
                left_eye = ((1 - left_pos[0]) * size[0] + origin[0],
                            left_pos[1] * size[1] + origin[1])
                self.screen.draw_circle(colour=validity_colour,
                                        pos=left_eye,
                                        r=int(self.disp.dispsize[0] / 100),
                                        pw=5,
                                        fill=True)

            self.screen.draw_text(
                text="Current distance to the eye tracker: {0} cm.".format(
                    self._mean(distance)),
                pos=(int(self.disp.dispsize[0] / 2),
                     int(self.disp.dispsize[1] * 0.9)),
                colour=(255, 255, 255),
                fontsize=20)

            self.disp.fill(self.screen)
            self.disp.show()

            key = self._getKeyPress()
            if key == "space":
                break

        # because looping doesn't seem to work
        if mov.status != visual.FINISHED:
            # pause and discard video for the audio to stop as well
            mov.pause()
            self.screen.screen.remove(mov)
            #video_win.close()
            del mov
            self.screen.clear()
            clock.pause(1000)
            return True
        else:
            return False
# loop through points
for i in range(len(CALIBPOINTS)):
	# get coordinate
	x, y = CALIBPOINTS[i]
	# draw calibration point
	scr.clear()
	scr.draw_fixation(fixtype='dot', pos=(x,y))
	disp.fill(scr)
	# start recording
	tracker.start_recording()
	tracker.log("VALIDATION_TRIALSTART, trialnr=%d, x=%d, y=%d" % (i,x,y))
	# show display
	disp.show()
	tracker.log("validation_point_on")
	# allow for a bit of time so the subject can fixate the target
	clock.pause(1000)
	tracker.log("validation_point_fix")
	# wait for a bit
	clock.pause(POINTTIME)
	# clear screen
	scr.clear()
	disp.fill(scr)
	disp.show()
	# stop recording
	tracker.log("validation_point_off")
	tracker.stop_recording()
	# inter-trial interval
	clock.pause(ITI)

# pause screen
scr.clear()
Example #17
0
    def calibrate(self):
        """See pygaze._eyetracker.baseeyetracker.BaseEyeTracker"""

        while True:
            if self.recording:
                raise Exception(
                    "Error in libeyelink.libeyelink.calibrate(): Trying to "
                    "calibrate after recording has started!")

            # # # # #
            # EyeLink calibration and validation

            # attempt calibrate; confirm abort when esc pressed
            while True:
                self.eyelink_graphics.esc_pressed = False
                pylink.getEYELINK().doTrackerSetup()
                if not self.eyelink_graphics.esc_pressed:
                    break
                self.confirm_abort_experiment()

            # If we are using the built-in EyeLink event detection, we don't need
            # the RMS calibration routine.
            if self.eventdetection == 'native':
                return

            # # # # #
            # RMS calibration
            while True:
                # present instructions
                self.display.fill()  # clear display
                self.scr.draw_text(text= \
                 "Noise calibration: please look at the dot\n\n(press space to start)",
                 pos=(self.resolution[0]/2, int(self.resolution[1]*0.2)),
                 center=True, fontsize=self.fontsize)
                self.scr.draw_fixation(fixtype='dot')
                self.display.fill(self.scr)
                self.display.show()
                self.scr.clear()  # clear screen again

                # wait for spacepress
                self.kb.get_key(keylist=['space'], timeout=None)

                # start recording
                self.log("PYGAZE RMS CALIBRATION START")
                self.start_recording()

                # show fixation
                self.display.fill()
                self.scr.draw_fixation(fixtype='dot')
                self.display.fill(self.scr)
                self.display.show()
                self.scr.clear()

                # wait for a bit, to allow participant to fixate
                clock.pause(500)

                # get samples
                # samplelist, prefilled with 1 sample to prevent sl[-1] from producing
                # an error; first sample will be ignored for RMS calculation
                sl = [self.sample()]
                t0 = clock.get_time()  # starting time
                while clock.get_time() - t0 < 1000:
                    s = self.sample()  # sample
                    if s != sl[-1] and s != (-1, -1) and s != (0, 0):
                        sl.append(s)

                # stop recording
                self.log("PYGAZE RMS CALIBRATION END")
                self.stop_recording()

                # calculate RMS noise
                Xvar = []
                Yvar = []
                for i in range(2, len(sl)):
                    Xvar.append((sl[i][0] - sl[i - 1][0])**2)
                    Yvar.append((sl[i][1] - sl[i - 1][1])**2)
                if Xvar and Yvar:  # check if properly recorded to avoid risk of division by zero error
                    XRMS = (sum(Xvar) / len(Xvar))**0.5
                    YRMS = (sum(Yvar) / len(Yvar))**0.5
                    self.pxdsttresh = (XRMS, YRMS)

                    # recalculate thresholds (degrees to pixels)
                    self.pxfixtresh = deg2pix(self.screendist, self.fixtresh,
                                              self.pixpercm)
                    self.pxspdtresh = deg2pix(
                        self.screendist, self.spdtresh,
                        self.pixpercm) / 1000.0  # in pixels per millisecons
                    self.pxacctresh = deg2pix(
                        self.screendist, self.accthresh,
                        self.pixpercm) / 1000.0  # in pixels per millisecond**2
                    return
                else:  # if nothing recorded, display message saying so
                    self.display.fill()
                    self.scr.draw_text(text = \
                     "Noise calibration failed.\n\nPress r to retry,\nor press space to return to calibration screen.", \
                     pos=(self.resolution[0]/2, int(self.resolution[1]*0.2)), \
                     center=True, fontsize=self.fontsize)
                    self.display.fill(self.scr)
                    self.display.show()
                    self.scr.clear()
                    # wait for space or r press, if r restart noise calibration, if space return to calibration menu
                    keypressed = self.kb.get_key(keylist=['space', 'r'],
                                                 timeout=None)
                    if keypressed[0] == 'space':
                        break
Example #18
0
    def calibrate(self):
        """Calibrates the eye tracking system
        
        arguments
        None
        
        keyword arguments
        None

        returns
        success    -- returns True if calibration succeeded, or False if
                   not; in addition a calibration log is added to the
                   log file and some properties are updated (i.e. the
                   thresholds for detection algorithms)
        """

        # CALIBRATION
        # determine the calibration points
        calibpoints = []
        for x in [0.1, 0.5, 0.9]:
            for y in [0.1, 0.5, 0.9]:
                calibpoints.append(
                    (int(x * self.dispsize[0]), int(y * self.dispsize[1])))
        random.shuffle(calibpoints)

        # show a message
        self.screen.clear()
        self.screen.draw_text(
            text="Press Space to calibrate, S to skip, and Q to quit",
            fontsize=20)
        self.disp.fill(self.screen)
        self.disp.show()

        # wait for keyboard input
        key, keytime = self.kb.get_key(keylist=['q', 's', 'space'],
                                       timeout=None,
                                       flush=True)
        if key == 's':
            return True
        if key == 'q':
            quited = True
        else:
            quited = False

        # Pause the processing of samples during the calibration.
#        self.eyetribe._pause_sample_processing()
# run until the user is statisfied, or quits
        calibrated = False
        calibresult = None
        while not quited and not calibrated:

            # Clear the existing calibration.
            if self.eyetribe._tracker.get_iscalibrated():
                self.eyetribe._lock.acquire(True)
                self.eyetribe.calibration.clear()
                self.eyetribe._lock.release()

            # Wait for a bit.
            clock.pause(1500)

            # start a new calibration
            if not self.eyetribe._tracker.get_iscalibrating():
                self.eyetribe._lock.acquire(True)
                self.eyetribe.calibration.start(pointcount=len(calibpoints))
                self.eyetribe._lock.release()

            # loop through calibration points
            for cpos in calibpoints:
                # Check whether the calibration is already done.
                # (Not sure how or why, but for some reason some data
                # can persist between calbrations, and the tracker will
                # simply stop allowing further pointstart requests.)
                if self.eyetribe._tracker.get_iscalibrated():
                    break

                # Draw a calibration target.
                self.draw_calibration_target(cpos[0], cpos[1])
                # wait for a bit to allow participant to start looking at
                # the calibration point (#TODO: space press?)
                clock.pause(settings.EYETRIBEPRECALIBDUR)
                # start calibration of point
                self.eyetribe._lock.acquire(True)
                self.eyetribe.calibration.pointstart(cpos[0], cpos[1])
                self.eyetribe._lock.release()
                # wait for a second
                clock.pause(settings.EYETRIBECALIBDUR)
                # stop calibration of this point
                self.eyetribe._lock.acquire(True)
                self.eyetribe.calibration.pointend()
                self.eyetribe._lock.release()
                # check if the Q key has been pressed
                if self.kb.get_key(keylist=['q'], timeout=10,
                                   flush=False)[0] == 'q':
                    # abort calibration
                    self.eyetribe._lock.acquire(True)
                    self.eyetribe.calibration.abort()
                    self.eyetribe._lock.release()
                    # set quited variable and break this for loop
                    quited = True
                    break

            # retry option if the calibration was aborted
            if quited:
                # show retry message
                self.screen.clear()
                self.screen.draw_text(
                    "Calibration aborted. Press Space to restart or 'Q' to quit",
                    fontsize=20)
                self.disp.fill(self.screen)
                self.disp.show()
                # get input
                key, keytime = self.kb.get_key(keylist=['q', 'space'],
                                               timeout=None,
                                               flush=True)
                if key == 'space':
                    # unset quited Boolean
                    quited = False
                # skip further processing
                continue

            # empty display
            self.disp.fill()
            self.disp.show()
            # allow for a bit of calculation time
            # (this is waaaaaay too much)
            clock.pause(1000)
            # get the calibration result
            self.eyetribe._lock.acquire(True)
            calibresult = self.eyetribe._tracker.get_calibresult()
            self.eyetribe._lock.release()

            # results
            # clear the screen
            self.screen.clear()
            # draw results for each point
            if type(calibresult) == dict:
                for p in calibresult['calibpoints']:
                    # only draw the point if data was obtained
                    if p['state'] > 0:
                        # draw the mean error
                        # self.screen.draw_circle(colour=(252,233,79),
                        #     pos=(p['cpx'],p['cpy']), r=p['mepix'], pw=0,
                        #     fill=True)
                        self.screen.draw_line(spos=(p['cpx'], p['cpy']),
                                              epos=(p['mecpx'], p['mecpy']),
                                              pw=2)
                        # draw the point
                        self.screen.draw_fixation(fixtype='dot',
                                                  colour=(115, 210, 22),
                                                  pos=(p['cpx'], p['cpy']))
                        # draw the estimated point
                        self.screen.draw_fixation(fixtype='dot',
                                                  colour=(32, 74, 135),
                                                  pos=(p['mecpx'], p['mecpy']))
                        # annotate accuracy
                        self.screen.draw_text(text="{}".format(\
                            round(p['acd'], ndigits=2)),
                            pos=(p['cpx']+10,p['cpy']+10), fontsize=20)
                    # if no data was obtained, draw the point in red
                    else:
                        self.screen.draw_fixation(fixtype='dot',
                                                  colour=(204, 0, 0),
                                                  pos=(p['cpx'], p['cpy']))
                # draw box for averages
                # self.screen.draw_rect(colour=(238,238,236), x=int(self.dispsize[0]*0.15), y=int(self.dispsize[1]*0.2), w=400, h=200, pw=0, fill=True)
                # draw result
                if calibresult['result']:
                    self.screen.draw_text(text="Calibration successful",
                                          colour=(0, 255, 0),
                                          pos=(int(self.dispsize[0] * 0.5),
                                               int(self.dispsize[1] * 0.25)),
                                          fontsize=20)
                else:
                    self.screen.draw_text(text="Calibration failed",
                                          colour=(255, 0, 0),
                                          pos=(int(self.dispsize[0] * 0.5),
                                               int(self.dispsize[1] * 0.25)),
                                          fontsize=20)
                # draw average accuracy
                self.screen.draw_text(
                    text="Average error = {} degrees".format(round(\
                    calibresult['deg'], ndigits=2)), \
                    pos=(int(self.dispsize[0]*0.5),int(self.dispsize[1]*0.25+30)),
                    fontsize=20)
                # draw input options
                self.screen.draw_text(
                    text="Press Space to continue or 'R' to restart",
                    pos=(int(self.dispsize[0] * 0.5),
                         int(self.dispsize[1] * 0.25 + 60)),
                    fontsize=20)
            else:
                self.screen.draw_text(
                    text="Calibration failed. Press 'R' to try again.",
                    fontsize=20)
            # show the results
            self.disp.fill(self.screen)
            self.disp.show()
            # wait for input
            key, keytime = self.kb.get_key(keylist=['space', 'r'],
                                           timeout=None,
                                           flush=True)
            # process input
            if key == 'space':
                calibrated = True

        # Continue the processing of samples after the calibration.
#        self.eyetribe._unpause_sample_processing()

# calibration failed if the user quited
        if quited:
            return False

        # NOISE CALIBRATION
        # get all error estimates (pixels)
        var = []
        for p in calibresult['calibpoints']:
            # only draw the point if data was obtained
            if p['state'] > 0:
                var.append(p['mepix'])
        noise = sum(var) / float(len(var))
        self.pxdsttresh = (noise, noise)

        # AFTERMATH
        # store some variables
        pixpercm = (self.dispsize[0] / float(self.screensize[0]) +
                    self.dispsize[1] / float(self.screensize[1])) / 2
        screendist = settings.SCREENDIST
        # calculate thresholds based on tracker settings
        self.accuracy = ((calibresult['Ldeg'], calibresult['Ldeg']),
                         (calibresult['Rdeg'], calibresult['Rdeg']))
        self.pxerrdist = deg2pix(screendist, self.errdist, pixpercm)
        self.pxfixtresh = deg2pix(screendist, self.fixtresh, pixpercm)
        self.pxaccuracy = ((deg2pix(screendist, self.accuracy[0][0], pixpercm),
                            deg2pix(screendist, self.accuracy[0][1],
                                    pixpercm)),
                           (deg2pix(screendist, self.accuracy[1][0], pixpercm),
                            deg2pix(screendist, self.accuracy[1][1],
                                    pixpercm)))
        self.pxspdtresh = deg2pix(screendist, self.spdtresh / 1000.0,
                                  pixpercm)  # in pixels per millisecond
        self.pxacctresh = deg2pix(screendist, self.accthresh / 1000.0,
                                  pixpercm)  # in pixels per millisecond**2

        # calibration report
        self.log("pygaze calibration report start")
        self.log("accuracy (degrees): LX={}, LY={}, RX={}, RY={}".format(
            self.accuracy[0][0], self.accuracy[0][1], self.accuracy[1][0], \
            self.accuracy[1][1]))
        self.log("accuracy (in pixels): LX={}, LY={}, RX={}, RY={}".format( \
            self.pxaccuracy[0][0], self.pxaccuracy[0][1], \
            self.pxaccuracy[1][0], self.pxaccuracy[1][1]))
        self.log("precision (RMS noise in pixels): X={}, Y={}".format( \
            self.pxdsttresh[0], self.pxdsttresh[1]))
        self.log("distance between participant and display: {} cm".format( \
            screendist))
        self.log("fixation threshold: {} pixels".format(self.pxfixtresh))
        self.log("speed threshold: {} pixels/ms".format(self.pxspdtresh))
        self.log("acceleration threshold: {} pixels/ms**2".format( \
            self.pxacctresh))
        self.log("pygaze calibration report end")

        return True