Source code for gazelib.conversion.utils
# -*- coding: utf-8 -*-
[docs]class ConversionException(Exception):
pass
[docs]def estimate_sampling_interval(times):
'''
Return mean interval in the given list of times.
Return None if times is empty or contains only one element.
'''
n = 0
dt_sum = 0.0
prev_t = 0.0
first = True
for t in times:
if first:
first = False
else:
dt_sum += t - prev_t
prev_t = t
n += 1
if n < 2:
return None
return dt_sum / (n - 1)
[docs]def split_to_ranges_at_change_in_value(gd, value_converter, time_converter):
'''
Similar to gazelib.legacy.igazelib.split_at_change_in_value but
returns also starting and ending times. The start time is equal to
the end time of the previous range. Invalid values do not cause splits.
Parameters:
gd:
gazedata as list of dicts
value_converter:
A function that converts from raw row to the value
used for comparison and also in yielded dicts.
Must raise ValueError or TypeError if value cannot be converted.
On ValueError or TypeError the row will be skipped.
time_converter:
A function that converts from raw row to times used
in ranges.
Must return integer.
Must raise ValueError or TypeError if time cannot be converted.
On ValueError or TypeError the row will be skipped.
Yields dicts::
{
'start': <integer, start time of range, inclusive>,
'end': <integer, end time of range, exclusive>,
'value': <the value of the range>,
'first': <first point in range>
}
'''
# Estimate sample interval (i.e. 1 / sampling rate)
times = map(time_converter, gd)
sample_interval = int(round(estimate_sampling_interval(times)))
cur_time = None
previous_value = None
event_to_yield = None
for index, gazepoint in enumerate(gd):
try:
cur_value = value_converter(gazepoint)
cur_time = time_converter(gazepoint)
# cur_time must be last because otherwise it would not always
# point to the time of last valid row. If last rows are
# invalid, the last event should still have correct time.
except (ValueError, TypeError):
# Skip invalid but remember the previous value.
# This way we do not split at invalid values.
continue
# Iterate until value differs from the previous
if cur_value == previous_value:
continue
# Assert: different valid value observed
# If first row, do not store dict but start the first dict.
if event_to_yield is None:
event_to_yield = {
'start': cur_time,
'value': cur_value,
'first': gazepoint
}
else:
# Range dict finished
event_to_yield['end'] = cur_time
yield event_to_yield
# Start new one
event_to_yield = {
'start': cur_time,
'value': cur_value,
'first': gazepoint
}
previous_value = cur_value
# Handle the last event
if event_to_yield is not None:
event_to_yield['end'] = cur_time + sample_interval
yield event_to_yield
[docs]class ExperimentConfiguration(object):
def __init__(self, raw_dict):
self.raw = raw_dict
[docs] def get_trial_configuration(self, trial_config_id):
'''
Available trial configuration ids are:
'calibration_movie', 'calibration_stimuli',
'SRT1', 'SRT2', 'SRT3', 'SRT4', 'SRT5'
'''
raw = next(cf for cf in self.raw if cf['name'] == trial_config_id)
return TrialConfiguration(raw)
[docs]class TrialConfiguration(object):
def __init__(self, raw_dict):
self.raw = raw_dict
[docs] def get_image_name(self, index):
'''
Retrieves an image name from 'images' list at given index.
'''
return self.raw['images'][index]
[docs] def get_aoi_rectangle(self, aoi_index):
'''
Returns AoI in gazelib/geom/rectangle format.
'''
aoi = self.raw['aois'][aoi_index]
# Convert to gazelib/geom/rectangle
rect = [aoi[0], aoi[2], aoi[1], aoi[3]]
return rect