Skip to content

Commit

Permalink
Merge pull request #1072 from papr/v14-features
Browse files Browse the repository at this point in the history
v1.4 feature collection
  • Loading branch information
mkassner authored Feb 15, 2018
2 parents 8f748a1 + 6904db3 commit 1d59967
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 56 deletions.
12 changes: 7 additions & 5 deletions pupil_src/launchables/eye.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,12 +297,10 @@ def toggle_general_settings(collapsed):
width *= 2
height *= 2
width += icon_bar_width
width, height = session_settings.get('window_size', (width, height))

width, height = session_settings.get(
'window_size', (width, height))
main_window = glfw.glfwCreateWindow(width, height, title, None, None)
window_pos = session_settings.get(
'window_position', window_position_default)
window_pos = session_settings.get('window_position', window_position_default)
glfw.glfwSetWindowPos(main_window, window_pos[0], window_pos[1])
glfw.glfwMakeContextCurrent(main_window)
cygl.utils.init()
Expand Down Expand Up @@ -659,11 +657,15 @@ def window_should_update():
session_settings['ui_config'] = g_pool.gui.configuration
session_settings['capture_settings'] = g_pool.capture.class_name, g_pool.capture.get_init_dict()
session_settings['capture_manager_settings'] = g_pool.capture_manager.class_name, g_pool.capture_manager.get_init_dict()
session_settings['window_size'] = glfw.glfwGetWindowSize(main_window)
session_settings['window_position'] = glfw.glfwGetWindowPos(main_window)
session_settings['version'] = str(g_pool.version)
session_settings['last_pupil_detector'] = g_pool.pupil_detector.__class__.__name__
session_settings['pupil_detector_settings'] = g_pool.pupil_detector.get_settings()

session_window_size = glfw.glfwGetWindowSize(main_window)
if 0 not in session_window_size:
session_settings['window_size'] = session_window_size

session_settings.close()

g_pool.capture.deinit_ui()
Expand Down
37 changes: 27 additions & 10 deletions pupil_src/launchables/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,11 +219,16 @@ def get_dt():

g_pool.capture.playback_speed = session_settings.get('playback_speed', 1.)

width, height = session_settings.get('window_size', g_pool.capture.frame_size)
width, height = g_pool.capture.frame_size
width += icon_bar_width
width, height = session_settings.get('window_size', (width, height))

window_pos = session_settings.get('window_position', window_position_default)
window_name = "Pupil Player: {} - {}".format(meta_info["Recording Name"],
os.path.split(rec_dir)[-1])

glfw.glfwInit()
main_window = glfw.glfwCreateWindow(width, height, "Pupil Player: "+meta_info["Recording Name"]+" - "
+ rec_dir.split(os.path.sep)[-1], None, None)
main_window = glfw.glfwCreateWindow(width, height, window_name, None, None)
glfw.glfwSetWindowPos(main_window, window_pos[0], window_pos[1])
glfw.glfwMakeContextCurrent(main_window)
cygl.utils.init()
Expand Down Expand Up @@ -275,17 +280,21 @@ def purge_plugins():
g_pool.plugins.clean()

def do_export(_):
export_range = g_pool.seek_control.trim_left, g_pool.seek_control.trim_right
export_dir = os.path.join(g_pool.rec_dir, 'exports', '{}-{}'.format(*export_range))
left_idx = g_pool.seek_control.trim_left
right_idx = g_pool.seek_control.trim_right
export_range = left_idx, right_idx + 1 # exclusive range.stop

export_dir = os.path.join(g_pool.rec_dir, g_pool.seek_control.get_folder_name_from_trims())

try:
os.makedirs(export_dir)
except OSError as e:
if e.errno != errno.EEXIST:
logger.error("Could not create export dir")
raise e
else:
overwrite_warning = "Previous export for range [{}-{}] already exists - overwriting."
logger.warning(overwrite_warning.format(*export_range))
overwrite_warning = "Previous export for range [{}] already exists - overwriting."
logger.warning(overwrite_warning.format(g_pool.seek_control.get_trim_range_string()))
else:
logger.info('Created export dir at "{}"'.format(export_dir))

Expand Down Expand Up @@ -322,9 +331,13 @@ def toggle_general_settings(collapsed):
vert_constr.append(g_pool.user_timelines)
g_pool.timelines.append(vert_constr)

def set_window_size():
f_width, f_height = g_pool.capture.frame_size
f_width += int(icon_bar_width * g_pool.gui.scale)
glfw.glfwSetWindowSize(main_window, f_width, f_height)

general_settings = ui.Growing_Menu('General', header_pos='headline')
general_settings.append(ui.Button('Reset window size',
lambda: glfw.glfwSetWindowSize(main_window, g_pool.capture.frame_size[0], g_pool.capture.frame_size[1])))
general_settings.append(ui.Button('Reset window size', set_window_size))
general_settings.append(ui.Selector('gui_user_scale', g_pool, setter=set_scale, selection=[.8, .9, 1., 1.1, 1.2]+list(np.arange(1.5, 5.1, .5)), label='Interface Size'))
general_settings.append(ui.Info_Text('Player Version: {}'.format(g_pool.version)))
general_settings.append(ui.Info_Text('Capture Version: {}'.format(meta_info['Capture Software Version'])))
Expand Down Expand Up @@ -496,9 +509,13 @@ def handle_notifications(n):
session_settings['min_data_confidence'] = g_pool.min_data_confidence
session_settings['gui_scale'] = g_pool.gui_user_scale
session_settings['ui_config'] = g_pool.gui.configuration
session_settings['window_size'] = glfw.glfwGetWindowSize(main_window)
session_settings['window_position'] = glfw.glfwGetWindowPos(main_window)
session_settings['version'] = str(g_pool.version)

session_window_size = glfw.glfwGetWindowSize(main_window)
if 0 not in session_window_size:
session_settings['window_size'] = session_window_size

session_settings.close()

# de-init all running plugins
Expand Down
10 changes: 8 additions & 2 deletions pupil_src/launchables/world.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,9 +305,10 @@ def handle_notifications(n):
'actor': p.class_name,
'doc': p.on_notify.__doc__})

width, height = session_settings.get('window_size', (1280 + icon_bar_width, 720))

# window and gl setup
glfw.glfwInit()
width, height = session_settings.get('window_size', (1280+icon_bar_width, 720))
main_window = glfw.glfwCreateWindow(width, height, "Pupil Capture - World")
window_pos = session_settings.get('window_position', window_position_default)
glfw.glfwSetWindowPos(main_window, window_pos[0], window_pos[1])
Expand Down Expand Up @@ -355,6 +356,7 @@ def set_window_size():
f_width, f_height = g_pool.capture.frame_size
f_width += int(icon_bar_width*g_pool.gui.scale)
glfw.glfwSetWindowSize(main_window, f_width, f_height)

general_settings.append(ui.Button('Reset window size', set_window_size))
general_settings.append(ui.Selector('audio_mode', audio, selection=audio.audio_modes))
general_settings.append(ui.Selector('detection_mapping_mode',
Expand Down Expand Up @@ -507,13 +509,17 @@ def window_should_update():
session_settings['loaded_plugins'] = g_pool.plugins.get_initializers()
session_settings['gui_scale'] = g_pool.gui_user_scale
session_settings['ui_config'] = g_pool.gui.configuration
session_settings['window_size'] = glfw.glfwGetWindowSize(main_window)
session_settings['window_position'] = glfw.glfwGetWindowPos(main_window)
session_settings['version'] = str(g_pool.version)
session_settings['eye0_process_alive'] = eyes_are_alive[0].value
session_settings['eye1_process_alive'] = eyes_are_alive[1].value
session_settings['detection_mapping_mode'] = g_pool.detection_mapping_mode
session_settings['audio_mode'] = audio.audio_mode

session_window_size = glfw.glfwGetWindowSize(main_window)
if 0 not in session_window_size:
session_settings['window_size'] = session_window_size

session_settings.close()

# de-init all running plugins
Expand Down
2 changes: 1 addition & 1 deletion pupil_src/shared_modules/annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def export_annotations(self, export_range, export_dir):
return

start, end = export_range
annotations_in_section = [a for a in self.annotations_list if start <= a['index'] <= end]
annotations_in_section = [a for a in self.annotations_list if start <= a['index'] < end]
csv_keys = self.parse_csv_keys(annotations_in_section)

with open(os.path.join(export_dir, 'annotations.csv'), 'w', encoding='utf-8', newline='') as csvfile:
Expand Down
2 changes: 1 addition & 1 deletion pupil_src/shared_modules/blink_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def export(self, export_range, export_dir):
'filter_response', 'base_data')

start, end = export_range
blinks_in_section = [b for b in self.g_pool.blinks if start <= b['index'] <= end]
blinks_in_section = [b for b in self.g_pool.blinks if start <= b['index'] < end]

with open(os.path.join(export_dir, 'blinks.csv'), 'w',
encoding='utf-8', newline='') as csvfile:
Expand Down
40 changes: 27 additions & 13 deletions pupil_src/shared_modules/fixation_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import numpy as np
import cv2

from bisect import bisect_left, bisect_right
from scipy.spatial.distance import pdist
from collections import deque
from itertools import chain
Expand Down Expand Up @@ -239,13 +240,20 @@ def set_max_duration(new_value):
self.notify_all({'subject': 'fixation_detector.should_recalculate', 'delay': 1.})

def jump_next_fixation(_):
ts = self.last_frame_ts
for f in self.g_pool.fixations:
if f['timestamp'] > ts:
self.g_pool.capture.seek_to_frame(f['mid_frame_index'])
self.g_pool.new_seek = True
return
logger.error('No further fixation available')
cur_idx = self.last_frame_idx
all_idc = [f['mid_frame_index'] for f in self.g_pool.fixations]
# wrap-around index
tar_fix = bisect_right(all_idc, cur_idx) % len(all_idc)
self.g_pool.capture.seek_to_frame(self.g_pool.fixations[tar_fix]['mid_frame_index'])
self.g_pool.new_seek = True

def jump_prev_fixation(_):
cur_idx = self.last_frame_idx
all_idc = [f['mid_frame_index'] for f in self.g_pool.fixations]
# wrap-around index
tar_fix = (bisect_left(all_idc, cur_idx) - 1) % len(all_idc)
self.g_pool.capture.seek_to_frame(self.g_pool.fixations[tar_fix]['mid_frame_index'])
self.g_pool.new_seek = True

for help_block in self.__doc__.split('\n\n'):
help_str = help_block.replace('\n', ' ').replace(' ', '').strip()
Expand All @@ -263,17 +271,23 @@ def jump_next_fixation(_):
self.current_fixation_details = ui.Info_Text('')
self.menu.append(self.current_fixation_details)

self.add_button = ui.Thumb('jump_next_fixation', setter=jump_next_fixation,
self.next_fix_button = ui.Thumb('jump_next_fixation', setter=jump_next_fixation,
getter=lambda: False, label=chr(0xe044), hotkey='f',
label_font='pupil_icons')
self.add_button.status_text = 'Next Fixation'
self.g_pool.quickbar.append(self.add_button)
self.next_fix_button.status_text = 'Next Fixation'
self.g_pool.quickbar.append(self.next_fix_button)

self.prev_fix_button = ui.Thumb('jump_prev_fixation', setter=jump_prev_fixation,
getter=lambda: False, label=chr(0xe045), hotkey='F',
label_font='pupil_icons')
self.prev_fix_button.status_text = 'Previous Fixation'
self.g_pool.quickbar.append(self.prev_fix_button)

def deinit_ui(self):
self.remove_menu()
self.current_fixation_details = None
self.g_pool.quickbar.remove(self.add_button)
self.add_button = None
self.g_pool.quickbar.remove(self.next_fix_button)
self.next_fix_button = None

def cleanup(self):
if self.bg_task:
Expand Down Expand Up @@ -344,7 +358,7 @@ def recent_events(self, events):
if not frame:
return

self.last_frame_ts = frame.timestamp
self.last_frame_idx = frame.index
events['fixations'] = self.g_pool.fixations_by_frame[frame.index]
if self.show_fixations:
for f in self.g_pool.fixations_by_frame[frame.index]:
Expand Down
6 changes: 3 additions & 3 deletions pupil_src/shared_modules/offline_surface_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ def save_surface_statsics_to_file(self, export_range, export_dir):
csv_writer =csv.writer(csvfile, delimiter=',')
csv_writer.writerow(('frame_idx','timestamp','m_to_screen','m_from_screen','detected_markers'))
for idx,ts,ref_srf_data in zip(range(len(self.g_pool.timestamps)),self.g_pool.timestamps,s.cache):
if in_mark <= idx <= out_mark:
if in_mark <= idx < out_mark:
if ref_srf_data is not None and ref_srf_data is not False:
csv_writer.writerow( (idx,ts,ref_srf_data['m_to_screen'],ref_srf_data['m_from_screen'],ref_srf_data['detected_markers']) )

Expand All @@ -478,7 +478,7 @@ def save_surface_statsics_to_file(self, export_range, export_dir):
csv_writer.writerow(('world_timestamp', 'world_frame_idx', 'gaze_timestamp',
'x_norm', 'y_norm', 'x_scaled', 'y_scaled', 'on_srf', 'confidence'))
for idx, ts, ref_srf_data in zip(range(len(self.g_pool.timestamps)), self.g_pool.timestamps, s.cache):
if in_mark <= idx <= out_mark:
if in_mark <= idx < out_mark:
if ref_srf_data is not None and ref_srf_data is not False:
for gp in s.gaze_on_srf_by_frame_idx(idx, ref_srf_data['m_from_screen']):
csv_writer.writerow((ts, idx, gp['base_data']['timestamp'],
Expand All @@ -495,7 +495,7 @@ def save_surface_statsics_to_file(self, export_range, export_dir):
'y_scaled', 'on_srf'))
fixations_on_surface = []
for idx,ref_srf_data in zip(range(len(self.g_pool.timestamps)),s.cache):
if in_mark <= idx <= out_mark:
if in_mark <= idx < out_mark:
if ref_srf_data is not None and ref_srf_data is not False:
for f in s.fixations_on_srf_by_frame_idx(idx,ref_srf_data['m_from_screen']):
fixations_on_surface.append(f)
Expand Down
65 changes: 50 additions & 15 deletions pupil_src/shared_modules/seek_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
---------------------------------------------------------------------------~(*)
'''

from bisect import bisect_left as bisect
from bisect import bisect_left

from pyglui import ui
from plugin import System_Plugin_Base
Expand Down Expand Up @@ -65,7 +65,9 @@ def play(self):

@play.setter
def play(self, new_state):
if new_state and self.current_ts >= self.g_pool.timestamps[-10]:
if new_state and self.current_ts == self.trim_right_ts:
self.g_pool.capture.seek_to_frame(self.trim_left)
elif new_state and self.current_ts >= self.g_pool.timestamps[-10]:
self.g_pool.capture.seek_to_frame(0) # avoid pause set by hitting trimmark pause.
logger.warning("End of video - restart at beginning.")
self.g_pool.capture.play = new_state
Expand All @@ -76,7 +78,7 @@ def trim_left_ts(self):

@trim_left_ts.setter
def trim_left_ts(self, val):
self.trim_left = bisect(self.g_pool.timestamps, val, hi=self.trim_right-1)
self.trim_left = bisect_left(self.g_pool.timestamps, val, hi=self.trim_right-1)

@property
def trim_right_ts(self):
Expand All @@ -85,7 +87,8 @@ def trim_right_ts(self):
@trim_right_ts.setter
def trim_right_ts(self, val):
# left + 1 <= right <= frame_count -1
self.trim_right = bisect(self.g_pool.timestamps, val, lo=self.trim_left+1)
self.trim_right = bisect_left(self.g_pool.timestamps, val, lo=self.trim_left+1)
self.trim_right = min(self.trim_right, len(self.g_pool.timestamps) - 1)

@property
def current_ts(self):
Expand All @@ -95,7 +98,7 @@ def current_ts(self):
def current_ts(self, val):
if self.current_ts != val:
try:
val_idx = bisect(self.g_pool.timestamps, val)
val_idx = bisect_left(self.g_pool.timestamps, val)
self.g_pool.capture.seek_to_frame(val_idx)
except:
import traceback as tb
Expand Down Expand Up @@ -144,14 +147,46 @@ def set_trim_range(self, mark_range):
self.trim_left, self.trim_right = mark_range

def get_trim_range_string(self):
return '{} - {}'.format(self.trim_left, self.trim_right)

def set_trim_range_string(self, str):
time_fmt = ''
min_ts = self.g_pool.timestamps[0]
for ts in (self.trim_left_ts, self.trim_right_ts):
ts -= min_ts
minutes = ts // 60
seconds = ts - (minutes * 60.)
micro_seconds_e1 = int((seconds - int(seconds)) * 1e3)
time_fmt += '{:02.0f}:{:02d}.{:03d} - '.format(abs(minutes), int(seconds), micro_seconds_e1)
return time_fmt[:-3]

def set_trim_range_string(self, range_str):
try:
in_m, out_m = str.split('-')
in_m = int(in_m)
out_m = int(out_m)
self.trim_left = in_m
self.trim_right = out_m
except:
logger.warning("Setting Trimmarks via string failed.")
range_list = range_str.split('-')
assert len(range_list) == 2

def convert_to_ts(time_str):
time_list = time_str.split(':')
assert len(time_list) == 2
minutes, seconds = map(float, time_list)
return minutes * 60 + seconds + self.g_pool.timestamps[0]

left, right = map(convert_to_ts, range_list)
assert left < right
# setting right trim first ensures previous assertion even if
# g_pool.timestamps[-1] < left < right. The possibility is
# desirable, because it enables to set the range to the maximum
# value without knowing the actual maximum value.
self.trim_right_ts = right
self.trim_left_ts = left

except (AssertionError, ValueError):
logger.warning('Invalid trim range entered')

def get_folder_name_from_trims(self):
time_fmt = ''
min_ts = self.g_pool.timestamps[0]
for ts in (self.trim_left_ts, self.trim_right_ts):
ts -= min_ts
minutes = ts // 60
seconds = ts - (minutes * 60.)
micro_seconds_e1 = int((seconds - int(seconds)) * 1e3)
time_fmt += '{:02.0f}_{:02d}_{:03d}-'.format(abs(minutes), int(seconds), micro_seconds_e1)
return time_fmt[:-1]
9 changes: 7 additions & 2 deletions pupil_src/shared_modules/service_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,11 +182,16 @@ def cleanup(self):
del self.texture

def get_init_dict(self):
return {'window_size': glfw.glfwGetWindowSize(self.g_pool.main_window),
'window_position': glfw.glfwGetWindowPos(self.g_pool.main_window),
sess = {'window_position': glfw.glfwGetWindowPos(self.g_pool.main_window),
'gui_scale': self.g_pool.gui_user_scale,
'ui_config': self.g_pool.gui.configuration}

session_window_size = glfw.glfwGetWindowSize(self.g_pool.main_window)
if 0 not in session_window_size:
sess['window_size'] = session_window_size

return sess

def start_stop_eye(self, eye_id, make_alive):
if make_alive:
n = {'subject': 'eye_process.should_start.{}'.format(eye_id), 'eye_id': eye_id}
Expand Down
Loading

0 comments on commit 1d59967

Please sign in to comment.