Skip to content

Commit

Permalink
Merge pull request #1101 from papr/v1.5-milestone
Browse files Browse the repository at this point in the history
 v1.5 features and bug fixes, part 2
  • Loading branch information
mkassner authored Feb 27, 2018
2 parents b0a5eb1 + 5c5f4d6 commit 5c8ca99
Show file tree
Hide file tree
Showing 9 changed files with 61 additions and 44 deletions.
10 changes: 8 additions & 2 deletions pupil_src/launchables/eye.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,10 @@ def replace_source(source_class_name, source_settings):
g_pool.capture.init_ui()
if g_pool.writer:
logger.info("Done recording.")
g_pool.writer.release()
try:
g_pool.writer.release()
except RuntimeError:
logger.error('No eye video recorded')
g_pool.writer = None

g_pool.replace_source = replace_source # for ndsi capture
Expand Down Expand Up @@ -494,7 +497,10 @@ def window_should_update():
elif subject == 'recording.stopped':
if g_pool.writer:
logger.info("Done recording.")
g_pool.writer.release()
try:
g_pool.writer.release()
except RuntimeError:
logger.error('No eye video recorded')
g_pool.writer = None
elif subject.startswith('meta.should_doc'):
ipc_socket.notify({
Expand Down
14 changes: 4 additions & 10 deletions pupil_src/shared_modules/av_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,16 +179,14 @@ def write_video_frame(self, input_frame):
break # wait for next image

def close(self):
# flush encoder
while 1:
# only flush encoder if there has been at least one frame
while self.configured:
packet = self.video_stream.encode()
if packet:
self.container.mux(packet)
else:
break

self.container.close()
logger.debug("Closed media container")
self.container.close() # throws RuntimeError if no frames were written
write_timestamps(self.file_loc, self.timestamps)

def release(self):
Expand Down Expand Up @@ -239,11 +237,7 @@ def write_video_frame(self, input_frame):
self.timestamps.append(input_frame.timestamp)

def close(self):
try:
self.container.close()
except(RuntimeError):
logger.error("Media file does not contain any frames.")
logger.debug("Closed media container")
self.container.close() # throws RuntimeError if no frames were written
write_timestamps(self.file_loc, self.timestamps)

def release(self):
Expand Down
16 changes: 8 additions & 8 deletions pupil_src/shared_modules/calibration_routines/gaze_mappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def __init__(self, g_pool):
super().__init__(g_pool)

def _map_monocular(self, p):
return {'topic': 'gaze.2d.{}'.format(p['id']),
return {'topic': 'gaze.2d.{}.'.format(p['id']),
'norm_pos': p['norm_pos'],
'confidence': p['confidence'],
'timestamp': p['timestamp'],
Expand All @@ -161,7 +161,7 @@ def __init__(self, g_pool, params):

def _map_monocular(self, p):
gaze_point = self.map_fn(p['norm_pos'])
return {'topic': 'gaze.2d.{}'.format(p['id']),
return {'topic': 'gaze.2d.{}.'.format(p['id']),
'norm_pos': gaze_point,
'confidence': p['confidence'],
'id': p['id'],
Expand All @@ -183,7 +183,7 @@ def __init__(self, g_pool, params0, params1):

def _map_monocular(self, p):
gaze_point = self.map_fns[p['id']](p['norm_pos'])
return {'topic': 'gaze.2d.{}'.format(p['id']),
return {'topic': 'gaze.2d.{}.'.format(p['id']),
'norm_pos': gaze_point,
'confidence': p['confidence'],
'id': p['id'],
Expand Down Expand Up @@ -224,15 +224,15 @@ def _map_binocular(self, p0, p1):
(gaze_point_eye0[1] + gaze_point_eye1[1])/2.)
confidence = (p0['confidence'] + p1['confidence'])/2.
ts = (p0['timestamp'] + p1['timestamp'])/2.
return {'topic': 'gaze.2d.2',
return {'topic': 'gaze.2d.01.',
'norm_pos': gaze_point,
'confidence': confidence,
'timestamp': ts,
'base_data': [p0, p1]}

def _map_monocular(self, p):
gaze_point = self.map_fn_fallback[p['id']](p['norm_pos'])
return {'topic': 'gaze.2d.2',
return {'topic': 'gaze.2d.{}.'.format(p['id']),
'norm_pos': gaze_point,
'confidence': p['confidence'],
'timestamp': p['timestamp'],
Expand Down Expand Up @@ -297,7 +297,7 @@ def _map_monocular(self,p):
gaze_3d = self.toWorld(gaze_point)
normal_3d = np.dot( self.rotation_matrix, np.array( p['circle_3d']['normal'] ) )

g = { 'topic': 'gaze.2d.2',
g = { 'topic': 'gaze.3d.{}.'.format(p['id']),
'norm_pos': image_point,
'eye_center_3d': eye_center.tolist(),
'gaze_normal_3d': normal_3d.tolist(),
Expand Down Expand Up @@ -410,7 +410,7 @@ def _map_monocular(self, p):

normal_3d = self.rotation_matricies[p_id] @ np.array(p['circle_3d']['normal'])

g = {'topic': 'gaze.3d.2',
g = {'topic': 'gaze.3d.{}.'.format(p_id),
'eye_centers_3d': {p['id']: eye_center.tolist()},
'gaze_normals_3d': {p['id']: normal_3d.tolist()},
'gaze_point_3d': gaze_3d.tolist(),
Expand Down Expand Up @@ -493,7 +493,7 @@ def _map_binocular(self, p0, p1):

confidence = min(p0['confidence'],p1['confidence'])
ts = (p0['timestamp'] + p1['timestamp'])/2.
g = {'topic': 'gaze.3d.2',
g = {'topic': 'gaze.3d.01.',
'eye_centers_3d': {0: s0_center.tolist(), 1: s1_center.tolist()},
'gaze_normals_3d': {0: s0_normal.tolist(), 1: s1_normal.tolist()},
'gaze_point_3d': nearest_intersection_point.tolist(),
Expand Down
18 changes: 12 additions & 6 deletions pupil_src/shared_modules/player_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ def update_recording_to_recent(rec_dir):
if rec_version < VersionFormat('1.4'):
update_recording_v13_v14(rec_dir)

# Do this independent of rec_version
check_for_worldless_recording(rec_dir)

# How to extend:
# if rec_version < VersionFormat('FUTURE FORMAT'):
# update_recording_v081_to_FUTURE(rec_dir)
Expand Down Expand Up @@ -460,6 +463,15 @@ def update_recording_v0915_v13(rec_dir):

def update_recording_v13_v14(rec_dir):
logger.info("Updating recording from v1.3 to v1.4")
meta_info_path = os.path.join(rec_dir, "info.csv")
with open(meta_info_path, 'r', encoding='utf-8') as csvfile:
meta_info = csv_utils.read_key_value_file(csvfile)
meta_info['Data Format Version'] = 'v1.4'
update_meta_info(rec_dir, meta_info)


def check_for_worldless_recording(rec_dir):
logger.info("Checking for world-less recording")
valid_ext = ('.mp4', '.mkv', '.avi', '.h264', '.mjpeg')
existing_videos = [f for f in glob.glob(os.path.join(rec_dir, 'world.*'))
if os.path.splitext(f)[1] in valid_ext]
Expand Down Expand Up @@ -488,12 +500,6 @@ def update_recording_v13_v14(rec_dir):
save_object({'frame_rate': frame_rate, 'frame_size': (1280, 720), 'version': 0},
os.path.join(rec_dir, 'world.fake'))

meta_info_path = os.path.join(rec_dir, "info.csv")
with open(meta_info_path, 'r', encoding='utf-8') as csvfile:
meta_info = csv_utils.read_key_value_file(csvfile)
meta_info['Data Format Version'] = 'v1.4'
update_meta_info(rec_dir, meta_info)


def update_recording_bytes_to_unicode(rec_dir):
logger.info("Updating recording from bytes to unicode.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ cdef inline convertTo3DPythonResult( Detector3DResult& result, object frame )
py_result['sphere'] = sphere

if str(result.projectedSphere.center[0]) == 'nan':
projectedSphere = {'axes': (0,0), 'angle': 90.0, 'center': (0,0)}
projectedSphere = {'axes': (0.,0.), 'angle': 90.0, 'center': (0.,0.)}
else:
projectedSphere = {}
projectedSphere['center'] = (result.projectedSphere.center[0] + frame.width / 2.0 ,frame.height / 2.0 - result.projectedSphere.center[1])
Expand Down
25 changes: 15 additions & 10 deletions pupil_src/shared_modules/recorder.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class Recorder(System_Plugin_Base):

def __init__(self, g_pool, session_name=get_auto_name(), rec_dir=None,
user_info={'name': '', 'additional_field': 'change_me'},
info_menu_conf={}, show_info_menu=False, record_eye=False,
info_menu_conf={}, show_info_menu=False, record_eye=True,
raw_jpeg=True):
super().__init__(g_pool)
# update name if it was autogenerated.
Expand Down Expand Up @@ -167,9 +167,10 @@ def on_notify(self, notification):
Reacts to notifications:
``recording.should_start``: Starts a new recording session.
fields: 'session_name' change session name
use `/` to att dirs.
fields:
- 'session_name' change session name
start with `/` to ingore the rec base dir and start from root instead.
- `record_eye` boolean that indicates recording of the eyes, defaults to current setting
``recording.should_stop``: Stops current recording session
Emits notifications:
Expand All @@ -188,9 +189,8 @@ def on_notify(self, notification):
elif notification['subject'] == 'recording.should_start':
if self.running:
logger.info('Recording already running!')
elif not self.g_pool.capture.online:
logger.error("Current world capture is offline. Please reconnect or switch to fake capture")
else:
self.record_eye = notification.get('record_eye', self.record_eye)
if notification.get("session_name", ""):
self.set_session_name(notification["session_name"])
self.start()
Expand Down Expand Up @@ -295,7 +295,7 @@ def close_info_menu(self):
self.g_pool.gui.remove(self.info_menu)
self.info_menu = None

def recent_events(self,events):
def recent_events(self, events):
if self.running:
for key, data in events.items():
if key not in ('dt', 'frame', 'depth_frame'):
Expand All @@ -316,8 +316,15 @@ def recent_events(self,events):

def stop(self):
# explicit release of VideoWriter
self.writer.release()
self.writer = None
try:
self.writer.release()
except RuntimeError:
logger.error("No world video recorded")
else:
logger.debug("Closed media container")
self.g_pool.capture.intrinsics.save(self.rec_path, custom_name='world')
finally:
self.writer = None

save_object(self.data, os.path.join(self.rec_path, "pupil_data"))

Expand All @@ -327,8 +334,6 @@ def stop(self):
except:
logger.info("No surface_definitions data found. You may want this if you do marker tracking.")

self.g_pool.capture.intrinsics.save(self.rec_path, custom_name='world')

try:
with open(self.meta_info_path, 'a', newline='') as csvfile:
csv_utils.write_key_value_file(csvfile, {
Expand Down
6 changes: 5 additions & 1 deletion pupil_src/shared_modules/seek_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ def play(self, new_state):
if new_state and self.current_ts == self.trim_right_ts:
self.g_pool.capture.seek_to_frame(self.trim_left)
self.g_pool.new_seek = True
elif new_state and self.current_ts >= self.g_pool.timestamps[-10]:

# Sometimes there is less video frames than timestamps. The rewind
# logic needs to catch these cases but work for recordings with less
# than 10 frames
elif new_state and self.current_ts >= self.g_pool.timestamps[-10:][0]:
self.g_pool.capture.seek_to_frame(0)
self.g_pool.new_seek = True
logger.warning("End of video - restart at beginning.")
Expand Down
2 changes: 1 addition & 1 deletion pupil_src/shared_modules/video_capture/ndsi_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

try:
from ndsi import __version__
assert __version__ >= '0.3.4'
assert __version__ >= '0.3.5'
from ndsi import __protocol_version__
except (ImportError, AssertionError):
raise Exception("pyndsi version is to old. Please upgrade")
Expand Down
12 changes: 7 additions & 5 deletions pupil_src/shared_modules/video_capture/uvc_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def __init__(self, g_pool, frame_size, frame_rate, name=None, preferred_names=()
self.name_backup = (self.name,)
self.frame_size_backup = frame_size
self.frame_rate_backup = frame_rate
self.backup_uvc_controls = {}

def verify_drivers(self):
import os
Expand Down Expand Up @@ -202,12 +203,12 @@ def _re_init_capture(self, uid):
self.configure_capture(current_size, current_fps, current_uvc_controls)
self.update_menu()

def _init_capture(self, uid):
def _init_capture(self, uid, backup_uvc_controls={}):
self.uvc_capture = uvc.Capture(uid)
self.configure_capture(self.frame_size_backup, self.frame_rate_backup, self._get_uvc_controls())
self.configure_capture(self.frame_size_backup, self.frame_rate_backup, backup_uvc_controls)
self.update_menu()

def _re_init_capture_by_names(self, names):
def _re_init_capture_by_names(self, names, backup_uvc_controls={}):
# burn-in test specific. Do not change text!
self.devices.update()
for d in self.devices:
Expand All @@ -217,7 +218,7 @@ def _re_init_capture_by_names(self, names):
if self.uvc_capture:
self._re_init_capture(d['uid'])
else:
self._init_capture(d['uid'])
self._init_capture(d['uid'], backup_uvc_controls)
return
raise InitialisationError('Could not find Camera {} during re initilization.'.format(names))

Expand All @@ -226,9 +227,10 @@ def _restart_logic(self):
if self.uvc_capture:
logger.warning("Capture failed to provide frames. Attempting to reinit.")
self.name_backup = (self.uvc_capture.name,)
self.backup_uvc_controls = self._get_uvc_controls()
self.uvc_capture = None
try:
self._re_init_capture_by_names(self.name_backup)
self._re_init_capture_by_names(self.name_backup, self.backup_uvc_controls)
except (InitialisationError, uvc.InitError):
time.sleep(0.02)
self.update_menu()
Expand Down

0 comments on commit 5c8ca99

Please sign in to comment.