Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/pupil-labs/pupil
Browse files Browse the repository at this point in the history
  • Loading branch information
mkassner committed Aug 30, 2017
2 parents fd5e8dc + 28fcb7c commit 524660d
Show file tree
Hide file tree
Showing 12 changed files with 188 additions and 37 deletions.
1 change: 1 addition & 0 deletions deployment/deploy_capture/bundle.spec
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ elif platform.system() == 'Windows':
a.binaries,
a.zipfiles,
a.datas,
[('PupilDrvInst.exe','../../pupil_external/PupilDrvInst.exe','BINARY')],
[('glfw3.dll','../../pupil_external/glfw3.dll','BINARY')],
[('OpenSans-Regular.ttf', os.path.join(package_path, 'pyglui/OpenSans-Regular.ttf'),'DATA')],
[('Roboto-Regular.ttf', os.path.join(package_path, 'pyglui/Roboto-Regular.ttf'),'DATA')],
Expand Down
1 change: 1 addition & 0 deletions deployment/deploy_service/bundle.spec
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ elif platform.system() == 'Windows':
a.binaries,
a.zipfiles,
a.datas,
[('PupilDrvInst.exe','../../pupil_external/PupilDrvInst.exe','BINARY')],
[('glfw3.dll','../../pupil_external/glfw3.dll','BINARY')],
[('OpenSans-Regular.ttf', os.path.join(package_path, 'pyglui/OpenSans-Regular.ttf'),'DATA')],
[('Roboto-Regular.ttf', os.path.join(package_path, 'pyglui/Roboto-Regular.ttf'),'DATA')],
Expand Down
Binary file added pupil_external/PupilDrvInst.exe
Binary file not shown.
4 changes: 3 additions & 1 deletion pupil_src/launchables/world.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ def world(timebase, eyes_are_alive, ipc_pub_url, ipc_sub_url,
from remote_recorder import Remote_Recorder
from audio_capture import Audio_Capture
from accuracy_visualizer import Accuracy_Visualizer
from dialation_history import Dialation_History

# UI Platform tweaks
if platform.system() == 'Linux':
Expand Down Expand Up @@ -152,7 +153,7 @@ def get_timestamp():
manager_classes += [p for p in runtime_plugins if issubclass(p, Base_Manager)]
runtime_plugins = [p for p in runtime_plugins if not issubclass(p, Base_Manager)]
user_launchable_plugins = [Audio_Capture, Pupil_Groups, Frame_Publisher, Pupil_Remote, Time_Sync, Surface_Tracker,
Annotation_Capture, Log_History, Fixation_Detector,
Annotation_Capture, Log_History, Fixation_Detector, Dialation_History,
Blink_Detection, Remote_Recorder, Accuracy_Visualizer] + runtime_plugins
system_plugins = [Log_Display, Display_Recent_Gaze, Recorder, Pupil_Data_Relay]
plugin_by_index = (system_plugins + user_launchable_plugins + calibration_plugins
Expand Down Expand Up @@ -336,6 +337,7 @@ def reset_restart():
getter=lambda: eyes_are_alive[1].value
))
selector_label = "Select to load"
user_launchable_plugins.sort(key=lambda p: p.__name__)
labels = [p.__name__.replace('_', ' ') for p in user_launchable_plugins]
user_launchable_plugins.insert(0, selector_label)
labels.insert(0, selector_label)
Expand Down
37 changes: 19 additions & 18 deletions pupil_src/shared_modules/csv_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,41 +45,42 @@ def write_key_value_file(csvfile,dictionary,append=False):
"""
writer = csv.writer(csvfile, delimiter=',')
if not append:
writer.writerow(['key','value'])
for key,val in dictionary.items():
writer.writerow([key,val])
writer.writerow(['key', 'value'])
for key, val in dictionary.items():
writer.writerow([key, val])


if __name__ == '__main__':
test = {'foo':'bar','oh':'rl","y','it was':'not me'}
test_append = {'jo':'ho'}
test = {'foo': 'bar', 'oh': 'rl"abc"y', 'it was': 'not 🚨'}
test_append = {'jo': 'ho'}
test_updated = test.copy()
test_updated.update(test_append)

testfile = '.test.csv'

# Test write+read
with open(testfile, 'w') as csvfile:
write_key_value_file(csvfile,test)
with open(testfile, 'r') as csvfile:
with open(testfile, 'w', encoding='utf-8') as csvfile:
write_key_value_file(csvfile, test)
with open(testfile, 'r', encoding='utf-8') as csvfile:
result = read_key_value_file(csvfile)
assert test == result
assert test == result, (test, result)

# Test write+append (same keys)+read
with open(testfile, 'w') as csvfile:
write_key_value_file(csvfile,test)
write_key_value_file(csvfile,test,append=True)
with open(testfile, 'r') as csvfile:
with open(testfile, 'w', encoding='utf-8') as csvfile:
write_key_value_file(csvfile, test)
write_key_value_file(csvfile, test, append=True)
with open(testfile, 'r', encoding='utf-8') as csvfile:
result = read_key_value_file(csvfile)
assert test == result

# Test write+append (different keys)+read
with open(testfile, 'w') as csvfile:
write_key_value_file(csvfile,test)
write_key_value_file(csvfile,test_append,append=True)
with open(testfile, 'r') as csvfile:
with open(testfile, 'w', encoding='utf-8') as csvfile:
write_key_value_file(csvfile, test)
write_key_value_file(csvfile, test_append, append=True)
with open(testfile, 'r', encoding='utf-8') as csvfile:
result = read_key_value_file(csvfile)
assert test_updated == result

import os
os.remove(testfile)
print('CSV Test: successful')
print('CSV Test: successful')
88 changes: 88 additions & 0 deletions pupil_src/shared_modules/dialation_history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
'''
(*)~---------------------------------------------------------------------------
Pupil - eye tracking platform
Copyright (C) 2012-2017 Pupil Labs
Distributed under the terms of the GNU
Lesser General Public License (LGPL v3.0).
See COPYING and COPYING.LESSER for license details.
---------------------------------------------------------------------------~(*)
'''

import logging
import glfw
from collections import deque
from plugin import Plugin
from pyglui import ui, graph

import gl_utils

# logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)


class Dialation_History(Plugin):
"""Pupil dialation visualization
This plugin uses the 3d model's pupil diameter
and displays it in a graph for each eye.
"""
order = .9

def __init__(self, g_pool):
super().__init__(g_pool)
self.graphs = ()
self.menu = None

def init_gui(self):
eye0_graph = graph.Bar_Graph(min_val=.0, max_val=5.)
eye0_graph.pos = (140, 230)
eye0_graph.update_rate = 5
eye0_graph.label = "id0 dia: %0.2f"

eye1_graph = graph.Bar_Graph(min_val=.0, max_val=5.)
eye1_graph.pos = (260, 230)
eye1_graph.update_rate = 5
eye1_graph.label = "id0 dia: %0.2f"

self.graphs = eye0_graph, eye1_graph
self.on_window_resize(self.g_pool.main_window, *glfw.glfwGetFramebufferSize(self.g_pool.main_window))

def close():
self.alive = False

self.menu = ui.Growing_Menu('Dialation History')
self.menu.collapsed = True
self.menu.append(ui.Button('Close', close))
self.menu.append(ui.Info_Text('Displays the recent pupil dialation in millimeters for each eye.'))
self.g_pool.sidebar.append(self.menu)

def recent_events(self, events):
for p in events['pupil_positions']:
diam = p.get('diameter_3d', 0.)
if diam > 0. and p['confidence'] > 0.6:
self.graphs[p['id']].add(diam)

def gl_display(self):
for g in self.graphs:
g.draw()

def on_window_resize(self, window, w, h):
if gl_utils.is_window_visible(window):
hdpi_factor = float(glfw.glfwGetFramebufferSize(window)[0] / glfw.glfwGetWindowSize(window)[0])
for g in self.graphs:
g.scale = hdpi_factor
g.adjust_window_size(w, h)

def get_init_dict(self):
return {}

def deinit_ui(self):
self.graphs = ()
self.g_pool.sidebar.remove(self.menu)
self.menu = None

def cleanup(self):
if self.menu:
self.deinit_ui()
2 changes: 1 addition & 1 deletion pupil_src/shared_modules/fixation_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,7 @@ def init_gui(self):
def close():
self.alive = False

self.menu = ui.Growing_Menu('Online Fixation Detector')
self.menu = ui.Growing_Menu('Fixation Detector')
self.menu.collapsed = True
self.menu.append(ui.Button('Close', close))

Expand Down
2 changes: 1 addition & 1 deletion pupil_src/shared_modules/player_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def load_meta_info(rec_dir):
def update_meta_info(rec_dir, meta_info):
logger.info('Updating meta info')
meta_info_path = os.path.join(rec_dir,"info.csv")
with open(meta_info_path,'w',newline='') as csvfile:
with open(meta_info_path,'w',newline='',encoding='utf-8') as csvfile:
csv_utils.write_key_value_file(csvfile,meta_info)


Expand Down
3 changes: 1 addition & 2 deletions pupil_src/shared_modules/square_marker_detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,7 @@ def detect_markers_robust(gray_img,grid_size,prev_markers,min_marker_perimeter=4
new_markers = []
tick -=1


if prev_img is not None and prev_markers:
if prev_img is not None and prev_img.shape == gray_img.shape and prev_markers:

new_ids = [m['id'] for m in new_markers]

Expand Down
42 changes: 30 additions & 12 deletions pupil_src/shared_modules/video_capture/file_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class File_Source(Base_Source):
timestamps (str): Path to timestamps file
"""

def __init__(self, g_pool, source_path=None, timed_playback=False):
def __init__(self, g_pool, source_path=None, timed_playback=False, loop=False):
super().__init__(g_pool)

# minimal attribute set
Expand All @@ -99,6 +99,7 @@ def __init__(self, g_pool, source_path=None, timed_playback=False):
self.source_path = source_path
self.timestamps = None
self.timed_playback = timed_playback
self.loop = loop

if not source_path or not os.path.isfile(source_path):
logger.error('Init failed. Source file could not be found at `%s`'%source_path)
Expand Down Expand Up @@ -161,16 +162,19 @@ def __init__(self, g_pool, source_path=None, timed_playback=False):

loc, name = os.path.split(os.path.splitext(source_path)[0])
self._intrinsics = load_intrinsics(loc, name, self.frame_size)
self.play = True

def ensure_initialisation(fallback_func=None):
def ensure_initialisation(fallback_func=None, requires_playback=False):
from functools import wraps

def decorator(func):
@wraps(func)
def run_func(self, *args, **kwargs):
if self._initialised and self.video_stream:
return func(self, *args, **kwargs)
elif fallback_func:
# test self.play only if requires_playback is True
if not requires_playback or self.play:
return func(self, *args, **kwargs)
if fallback_func:
return fallback_func(*args, **kwargs)
else:
logger.debug('Initialisation required.')
Expand All @@ -186,6 +190,7 @@ def intrinsics(self):
return self._intrinsics

@property
@ensure_initialisation(fallback_func=lambda: (640, 480))
def frame_size(self):
return int(self.video_stream.format.width), int(self.video_stream.format.height)

Expand All @@ -198,6 +203,7 @@ def get_init_dict(self):
settings = super().get_init_dict()
settings['source_path'] = self.source_path
settings['timed_playback'] = self.timed_playback
settings['loop'] = self.loop
return settings

@property
Expand Down Expand Up @@ -246,9 +252,14 @@ def get_frame(self):
logger.debug('Frame index not consistent.')
break
if not frame:
logger.info("End of videofile %s %s"%(self.current_frame_idx,len(self.timestamps)))
raise EndofVideoFileError('Reached end of videofile')

if self.loop:
logger.info('Looping enabled. Seeking to beginning.')
self.seek_to_frame(0)
self.target_frame_idx = 0
return self.get_frame()
else:
logger.info("End of videofile %s %s"%(self.current_frame_idx,len(self.timestamps)))
raise EndofVideoFileError('Reached end of videofile')
try:
timestamp = self.timestamps[index]
except IndexError:
Expand All @@ -268,14 +279,14 @@ def wait(self,frame):
self.display_time = frame.timestamp - time()
sleep(self.slowdown)

@ensure_initialisation(fallback_func=lambda evt: sleep(0.05))
def recent_events(self,events):
@ensure_initialisation(fallback_func=lambda evt: sleep(0.05), requires_playback=True)
def recent_events(self, events):
try:
frame = self.get_frame()
except EndofVideoFileError:
logger.info('Video has ended.')
self.notify_all({"subject":'file_source.video_finished', 'source_path':self.source_path})
self._initialised = False
self.notify_all({"subject":'file_source.video_finished', 'source_path': self.source_path})
self.play = False
else:
self._recent_frame = frame
events['frame'] = frame
Expand Down Expand Up @@ -314,13 +325,20 @@ def init_gui(self):
from pyglui import ui
ui_elements = []
ui_elements.append(ui.Info_Text("Running Capture with '%s' as src"%self.source_path))
ui_elements.append(ui.Slider('slowdown',self,min=0,max=1.0))
ui_elements.append(ui.Slider('slowdown', self, min=0, max=1.0))

def toggle_looping(val):
self.loop = val
if val:
self.play = True
ui_elements.append(ui.Switch('loop', self, setter=toggle_looping))
self.g_pool.capture_source_menu.extend(ui_elements)

@property
def jpeg_support(self):
return False


class File_Manager(Base_Manager):
"""Summary
Expand Down
36 changes: 36 additions & 0 deletions pupil_src/shared_modules/video_capture/uvc_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,16 @@ class UVC_Source(Base_Source):
Camera Capture is a class that encapsualtes uvc.Capture:
"""
def __init__(self, g_pool, frame_size, frame_rate, name=None, preferred_names=(), uid=None, uvc_controls={}):
import platform

super().__init__(g_pool)
self.uvc_capture = None
self._restart_in = 3
assert name or preferred_names or uid

if platform.system() == 'Windows':
self.verify_drivers()

self.devices = uvc.Device_List()

devices_by_name = {dev['name']: dev for dev in self.devices}
Expand Down Expand Up @@ -84,6 +89,37 @@ def __init__(self, g_pool, frame_size, frame_rate, name=None, preferred_names=()
self.frame_size_backup = frame_size
self.frame_rate_backup = frame_rate

def verify_drivers(self):
import time
import os
import subprocess

DEV_HW_IDS = [(0x05A3, 0x9230, "Pupil Cam1 ID0"), (0x05A3, 0x9231, "Pupil Cam1 ID1"), (0x05A3, 0x9232, "Pupil Cam1 ID2"), (0x046D, 0x0843, "Logitech Webcam C930e"), (0x17EF,0x480F, "Lenovo Integrated Camera")]
ids_present = 0;
ids_to_install = [];
for id in DEV_HW_IDS:
cmd_str_query = 'PupilDrvInst.exe --vid {} --pid {}'.format(id[0], id[1])
print('Running ', cmd_str_query)
proc = subprocess.Popen(cmd_str_query)
proc.wait()
if proc.returncode == 2:
ids_present += 1
ids_to_install.append(id)
cmd_str_inst = "Start-Process PupilDrvInst.exe -Wait -WorkingDirectory \\\\\\\"{}\\\\\\\" -argument ''--vid {} --pid {} --desc \\\\\\\"{}\\\\\\\" --vendor \\\\\\\"Pupil Labs\\\\\\\" --inst'' ;"
work_dir = os.getcwd()
# print('work_dir = ', work_dir)
if ids_present > 0:
cmd_str = 'Remove-Item {}\\win_drv -recurse;'.format(work_dir)
for id in ids_to_install:
cmd_str += cmd_str_inst.format(work_dir, id[0],id[1], id[2])
logger.warning('Updating drivers, please wait...');
full_str = "'" + cmd_str + "'"
elevation_cmd = 'powershell.exe Start-Process powershell.exe -WorkingDirectory \\\"{}\\\" -WindowStyle hidden -Wait -argument {} -Verb runAs'.format(work_dir, full_str)
#print(elevation_cmd)
proc = subprocess.Popen(elevation_cmd)
proc.wait()
logger.warning('Done updating drivers!')

def configure_capture(self, frame_size, frame_rate, uvc_controls):
# Set camera defaults. Override with previous settings afterwards
if 'C930e' in self.uvc_capture.name:
Expand Down
Loading

0 comments on commit 524660d

Please sign in to comment.