def export(should_terminate,frames_to_export,current_frame, data_dir,start_frame=None,end_frame=None,plugin_initializers=[],out_file_path=None): logger = logging.getLogger(__name__+' with pid: '+str(os.getpid()) ) #parse and load data dir info video_path = data_dir + "/world.avi" timestamps_path = data_dir + "/timestamps.npy" gaze_positions_path = data_dir + "/gaze_positions.npy" record_path = data_dir + "/world_viz.avi" #parse info.csv file with open(data_dir + "/info.csv") as info: meta_info = dict( ((line.strip().split('\t')) for line in info.readlines() ) ) rec_version = meta_info["Capture Software Version"] rec_version_int = int(filter(type(rec_version).isdigit, rec_version)[:3])/100 #(get major,minor,fix of version) logger.debug("Exporting a video from recording with version: %s , %s"%(rec_version,rec_version_int)) #load gaze information gaze_list = np.load(gaze_positions_path) timestamps = np.load(timestamps_path) #correlate data positions_by_frame = correlate_gaze(gaze_list,timestamps) # Initialize capture, check if it works cap = autoCreateCapture(video_path,timestamps=timestamps_path) if cap is None: logger.error("Did not receive valid Capture") return width,height = cap.get_size() #Out file path verification, we do this before but if one uses a seperate tool, this will kick in. if out_file_path is None: out_file_path = os.path.join(data_dir, "world_viz.avi") else: file_name = os.path.basename(out_file_path) dir_name = os.path.dirname(out_file_path) if not dir_name: dir_name = data_dir if not file_name: file_name = 'world_viz.avi' out_file_path = os.path.expanduser(os.path.join(dir_name,file_name)) if os.path.isfile(out_file_path): logger.warning("Video out file already exsists. I will overwrite!") os.remove(out_file_path) logger.debug("Saving Video to %s"%out_file_path) #Trim mark verification #make sure the trim marks (start frame, endframe) make sense: We define them like python list slices,thus we can test them like such. trimmed_timestamps = timestamps[start_frame:end_frame] if len(trimmed_timestamps)==0: logger.warn("Start and end frames are set such that no video will be exported.") return False if start_frame == None: start_frame = 0 #these two vars are shared with the lauching process and give an job lenght and progress report. frames_to_export.value = len(trimmed_timestamps) current_frame.value = 0 logger.debug("Will export from frame %s to frame %s. This means I will export %s frames."%(start_frame,start_frame+frames_to_export.value,frames_to_export.value)) #lets get the avg. framerate for our slice of video: fps = float(len(trimmed_timestamps))/(trimmed_timestamps[-1] - trimmed_timestamps[0]) logger.debug("Framerate of export video is %s"%fps) #setup of writer writer = cv2.VideoWriter(out_file_path, cv2.cv.CV_FOURCC(*'DIVX'), fps, (width,height)) cap.seek_to_frame(start_frame) start_time = time() plugins = [] g = Temp() g.plugins = plugins g.app = 'exporter' # load plugins from initializers: for initializer in plugin_initializers: name, args = initializer logger.debug("Loading plugin: %s with settings %s"%(name, args)) try: p = plugin_by_name[name](g,**args) plugins.append(p) except: logger.warning("Plugin '%s' failed to load." %name) while frames_to_export.value - current_frame.value > 0: if should_terminate.value: logger.warning("User aborted export. Exported %s frames to %s."%(current_frame.value,out_file_path)) #explicit release of VideoWriter writer.release() writer = None return False new_frame = cap.get_frame() #end of video logic: pause at last frame. if not new_frame: logger.error("Could not read all frames.") #explicit release of VideoWriter writer.release() writer = None return False else: frame = new_frame #new positons and events current_pupil_positions = positions_by_frame[frame.index] events = None # allow each Plugin to do its work. for p in plugins: p.update(frame,current_pupil_positions,events) # # render gl visual feedback from loaded plugins # for p in plugins: # p.gl_display(frame) writer.write(frame.img) current_frame.value +=1 writer.release() writer = None duration = time()-start_time effective_fps = float(current_frame.value)/duration logger.info("Export done: Exported %s frames to %s. This took %s seconds. Exporter ran at %s frames per second"%(current_frame.value,out_file_path,duration,effective_fps)) return True
def export(should_terminate,frames_to_export,current_frame, rec_dir,start_frame=None,end_frame=None,plugin_initializers=[],out_file_path=None): logger = logging.getLogger(__name__+' with pid: '+str(os.getpid()) ) #parse info.csv file with open(rec_dir + "/info.csv") as info: meta_info = dict( ((line.strip().split('\t')) for line in info.readlines() ) ) rec_version = read_rec_version(meta_info) logger.debug("Exporting a video from recording with version: %s"%rec_version) if rec_version < VersionFormat('0.4'): video_path = rec_dir + "/world.avi" timestamps_path = rec_dir + "/timestamps.npy" else: video_path = rec_dir + "/world.mkv" timestamps_path = rec_dir + "/world_timestamps.npy" gaze_positions_path = rec_dir + "/gaze_positions.npy" #load gaze information gaze_list = np.load(gaze_positions_path) timestamps = np.load(timestamps_path) #correlate data if rec_version < VersionFormat('0.4'): positions_by_frame = correlate_gaze_legacy(gaze_list,timestamps) else: positions_by_frame = correlate_gaze(gaze_list,timestamps) cap = autoCreateCapture(video_path,timestamps=timestamps_path) width,height = cap.get_size() #Out file path verification, we do this before but if one uses a seperate tool, this will kick in. if out_file_path is None: out_file_path = os.path.join(rec_dir, "world_viz.mp4") else: file_name = os.path.basename(out_file_path) dir_name = os.path.dirname(out_file_path) if not dir_name: dir_name = rec_dir if not file_name: file_name = 'world_viz.mp4' out_file_path = os.path.expanduser(os.path.join(dir_name,file_name)) if os.path.isfile(out_file_path): logger.warning("Video out file already exsists. I will overwrite!") os.remove(out_file_path) logger.debug("Saving Video to %s"%out_file_path) #Trim mark verification #make sure the trim marks (start frame, endframe) make sense: We define them like python list slices,thus we can test them like such. trimmed_timestamps = timestamps[start_frame:end_frame] if len(trimmed_timestamps)==0: logger.warn("Start and end frames are set such that no video will be exported.") return False if start_frame == None: start_frame = 0 #these two vars are shared with the lauching process and give a job length and progress report. frames_to_export.value = len(trimmed_timestamps) current_frame.value = 0 logger.debug("Will export from frame %s to frame %s. This means I will export %s frames."%(start_frame,start_frame+frames_to_export.value,frames_to_export.value)) #setup of writer writer = AV_Writer(out_file_path) cap.seek_to_frame(start_frame) start_time = time() g = Global_Container() g.app = 'exporter' g.rec_dir = rec_dir g.rec_version = rec_version g.timestamps = timestamps g.gaze_list = gaze_list g.positions_by_frame = positions_by_frame g.plugins = Plugin_List(g,plugin_by_name,plugin_initializers) while frames_to_export.value - current_frame.value > 0: if should_terminate.value: logger.warning("User aborted export. Exported %s frames to %s."%(current_frame.value,out_file_path)) #explicit release of VideoWriter writer.close() writer = None return False try: frame = cap.get_frame() except EndofVideoFileError: break events = {} #new positons and events events['pupil_positions'] = positions_by_frame[frame.index] # allow each Plugin to do its work. for p in g.plugins: p.update(frame,events) writer.write_video_frame(frame) current_frame.value +=1 writer.close() writer = None duration = time()-start_time effective_fps = float(current_frame.value)/duration logger.info("Export done: Exported %s frames to %s. This took %s seconds. Exporter ran at %s frames per second"%(current_frame.value,out_file_path,duration,effective_fps)) return True
def main(): # Callback functions def on_resize(window,w, h): active_window = glfwGetCurrentContext() glfwMakeContextCurrent(window) hdpi_factor = glfwGetFramebufferSize(window)[0]/glfwGetWindowSize(window)[0] w,h = w*hdpi_factor, h*hdpi_factor g_pool.gui.update_window(w,h) g_pool.gui.collect_menus() graph.adjust_size(w,h) adjust_gl_view(w,h) glfwMakeContextCurrent(active_window) for p in g_pool.plugins: p.on_window_resize(window,w,h) def on_key(window, key, scancode, action, mods): g_pool.gui.update_key(key,scancode,action,mods) def on_char(window,char): g_pool.gui.update_char(char) def on_button(window,button, action, mods): g_pool.gui.update_button(button,action,mods) pos = glfwGetCursorPos(window) pos = normalize(pos,glfwGetWindowSize(main_window)) pos = denormalize(pos,(frame.img.shape[1],frame.img.shape[0]) ) # Position in img pixels for p in g_pool.plugins: p.on_click(pos,button,action) def on_pos(window,x, y): hdpi_factor = float(glfwGetFramebufferSize(window)[0]/glfwGetWindowSize(window)[0]) x,y = x*hdpi_factor,y*hdpi_factor g_pool.gui.update_mouse(x,y) def on_scroll(window,x,y): g_pool.gui.update_scroll(x,y*y_scroll_factor) def on_close(window): glfwSetWindowShouldClose(main_window,True) logger.debug('Process closing from window') try: rec_dir = sys.argv[1] except: #for dev, supply hardcoded dir: rec_dir = '/Users/mkassner/Desktop/Marker_Tracking_Demo_Recording/' if os.path.isdir(rec_dir): logger.debug("Dev option: Using hadcoded data dir.") else: if getattr(sys, 'frozen', False): logger.warning("You did not supply a data directory when you called this script! \ \nPlease drag a Pupil recoding directory onto the launch icon.") else: logger.warning("You did not supply a data directory when you called this script! \ \nPlease supply a Pupil recoding directory as first arg when calling Pupil Player.") return if not is_pupil_rec_dir(rec_dir): logger.error("You did not supply a dir with the required files inside.") return # load session persistent settings session_settings = Persistent_Dict(os.path.join(user_dir,"user_settings")) #backwards compatibility fn. patch_meta_info(rec_dir) #parse info.csv file meta_info_path = rec_dir + "/info.csv" with open(meta_info_path) as info: meta_info = dict( ((line.strip().split('\t')) for line in info.readlines() ) ) rec_version = read_rec_version(meta_info) if rec_version < VersionFormat('0.4'): video_path = rec_dir + "/world.avi" timestamps_path = rec_dir + "/timestamps.npy" else: video_path = rec_dir + "/world.mkv" timestamps_path = rec_dir + "/world_timestamps.npy" gaze_positions_path = rec_dir + "/gaze_positions.npy" #load gaze information gaze_list = np.load(gaze_positions_path) timestamps = np.load(timestamps_path) #correlate data if rec_version < VersionFormat('0.4'): positions_by_frame = correlate_gaze_legacy(gaze_list,timestamps) else: positions_by_frame = correlate_gaze(gaze_list,timestamps) # Initialize capture cap = autoCreateCapture(video_path,timestamps=timestamps_path) if isinstance(cap,FakeCapture): logger.error("could not start capture.") return width,height = session_settings.get('window_size',cap.get_size()) window_pos = session_settings.get('window_position',(0,0)) # not yet using this one. # Initialize glfw glfwInit() main_window = glfwCreateWindow(width, height, "Pupil Player: "+meta_info["Recording Name"]+" - "+ rec_dir.split(os.path.sep)[-1], None, None) glfwMakeContextCurrent(main_window) cygl.utils.init() # Register callbacks main_window glfwSetWindowSizeCallback(main_window,on_resize) glfwSetWindowCloseCallback(main_window,on_close) glfwSetKeyCallback(main_window,on_key) glfwSetCharCallback(main_window,on_char) glfwSetMouseButtonCallback(main_window,on_button) glfwSetCursorPosCallback(main_window,on_pos) glfwSetScrollCallback(main_window,on_scroll) # create container for globally scoped vars (within world) g_pool = Global_Container() g_pool.app = 'player' g_pool.version = get_version(version_file) g_pool.capture = cap g_pool.timestamps = timestamps g_pool.gaze_list = gaze_list g_pool.positions_by_frame = positions_by_frame g_pool.play = False g_pool.new_seek = True g_pool.user_dir = user_dir g_pool.rec_dir = rec_dir g_pool.rec_version = rec_version g_pool.meta_info = meta_info def next_frame(_): try: cap.seek_to_frame(cap.get_frame_index()) except FileSeekError: pass g_pool.new_seek = True def prev_frame(_): try: cap.seek_to_frame(cap.get_frame_index()-2) except FileSeekError: pass g_pool.new_seek = True def set_scale(new_scale): g_pool.gui.scale = new_scale g_pool.gui.collect_menus() def get_scale(): return g_pool.gui.scale def open_plugin(plugin): if plugin == "Select to load": return logger.debug('Open Plugin: %s'%plugin) new_plugin = plugin(g_pool) g_pool.plugins.add(new_plugin) def purge_plugins(): for p in g_pool.plugins: if p.__class__ in user_launchable_plugins: p.alive=False g_pool.plugins.clean() g_pool.gui = ui.UI() g_pool.gui.scale = session_settings.get('gui_scale',1) g_pool.main_menu = ui.Scrolling_Menu("Settings",pos=(-350,20),size=(300,300)) g_pool.main_menu.append(ui.Button("quit",lambda: on_close(None))) g_pool.main_menu.configuration = session_settings.get('main_menu_config',{}) g_pool.main_menu.append(ui.Slider('scale', setter=set_scale,getter=get_scale,step = .05,min=0.75,max=2.5,label='Interface Size')) g_pool.main_menu.append(ui.Info_Text('Player Version: %s'%g_pool.version)) g_pool.main_menu.append(ui.Info_Text('Recording Version: %s'%rec_version)) g_pool.main_menu.append(ui.Selector('Open plugin', selection = user_launchable_plugins, labels = [p.__name__.replace('_',' ') for p in user_launchable_plugins], setter= open_plugin, getter = lambda: "Select to load")) g_pool.main_menu.append(ui.Button('Close all plugins',purge_plugins)) g_pool.main_menu.append(ui.Button('Reset window size',lambda: glfwSetWindowSize(main_window,cap.get_size()[0],cap.get_size()[1])) ) g_pool.quickbar = ui.Stretching_Menu('Quick Bar',(0,100),(120,-100)) g_pool.play_button = ui.Thumb('play',g_pool,label='Play',hotkey=GLFW_KEY_SPACE) g_pool.play_button.on_color[:] = (0,1.,.0,.8) g_pool.forward_button = ui.Thumb('forward',getter = lambda: False,setter= next_frame, hotkey=GLFW_KEY_RIGHT) g_pool.backward_button = ui.Thumb('backward',getter = lambda: False, setter = prev_frame, hotkey=GLFW_KEY_LEFT) g_pool.quickbar.extend([g_pool.play_button,g_pool.forward_button,g_pool.backward_button]) g_pool.gui.append(g_pool.quickbar) g_pool.gui.append(g_pool.main_menu) #we always load these plugins system_plugins = [('Trim_Marks',{}),('Seek_Bar',{})] default_plugins = [('Scan_Path',{}),('Vis_Polyline',{}),('Vis_Circle',{}),('Export_Launcher',{})] previous_plugins = session_settings.get('loaded_plugins',default_plugins) g_pool.plugins = Plugin_List(g_pool,plugin_by_name,system_plugins+previous_plugins) for p in g_pool.plugins: if p.class_name == 'Trim_Marks': g_pool.trim_marks = p break #set the last saved window size on_resize(main_window, *glfwGetWindowSize(main_window)) glfwSetWindowPos(main_window,0,0) # gl_state settings basic_gl_setup() g_pool.image_tex = create_named_texture((height,width,3)) #set up performace graphs: pid = os.getpid() ps = psutil.Process(pid) ts = cap.get_now()-.03 cpu_graph = graph.Bar_Graph() cpu_graph.pos = (20,110) cpu_graph.update_fn = ps.get_cpu_percent cpu_graph.update_rate = 5 cpu_graph.label = 'CPU %0.1f' fps_graph = graph.Bar_Graph() fps_graph.pos = (140,110) fps_graph.update_rate = 5 fps_graph.label = "%0.0f REC FPS" pupil_graph = graph.Bar_Graph(max_val=1.0) pupil_graph.pos = (260,110) pupil_graph.update_rate = 5 pupil_graph.label = "Confidence: %0.2f" while not glfwWindowShouldClose(main_window): #grab new frame if g_pool.play or g_pool.new_seek: try: new_frame = cap.get_frame() except EndofVideoFileError: #end of video logic: pause at last frame. g_pool.play=False if g_pool.new_seek: display_time = new_frame.timestamp g_pool.new_seek = False update_graph = True else: update_graph = False frame = new_frame.copy() events = {} #new positons we make a deepcopy just like the image is a copy. events['pupil_positions'] = deepcopy(positions_by_frame[frame.index]) if update_graph: #update performace graphs for p in events['pupil_positions']: pupil_graph.add(p['confidence']) t = new_frame.timestamp if ts != t: dt,ts = t-ts,t fps_graph.add(1./dt) g_pool.play_button.status_text = str(frame.index) #always update the CPU graph cpu_graph.update() # allow each Plugin to do its work. for p in g_pool.plugins: p.update(frame,events) #check if a plugin need to be destroyed g_pool.plugins.clean() # render camera image glfwMakeContextCurrent(main_window) make_coord_system_norm_based() update_named_texture(g_pool.image_tex,frame.img) draw_named_texture(g_pool.image_tex) make_coord_system_pixel_based(frame.img.shape) # render visual feedback from loaded plugins for p in g_pool.plugins: p.gl_display() graph.push_view() fps_graph.draw() cpu_graph.draw() pupil_graph.draw() graph.pop_view() g_pool.gui.update() #present frames at appropriate speed wait_time = frame.timestamp - display_time display_time = frame.timestamp try: spent_time = time()-timestamp sleep(wait_time-spent_time) except: pass timestamp = time() glfwSwapBuffers(main_window) glfwPollEvents() session_settings['loaded_plugins'] = g_pool.plugins.get_initializers() session_settings['gui_scale'] = g_pool.gui.scale session_settings['main_menu_config'] = g_pool.main_menu.configuration session_settings['window_size'] = glfwGetWindowSize(main_window) session_settings['window_position'] = glfwGetWindowPos(main_window) session_settings.close() # de-init all running plugins for p in g_pool.plugins: p.alive = False g_pool.plugins.clean() cap.close() glfwDestroyWindow(main_window) glfwTerminate() logger.debug("Process done")
def main(): # Callback functions def on_resize(window,w, h): active_window = glfwGetCurrentContext() glfwMakeContextCurrent(window) adjust_gl_view(w,h,window) norm_size = normalize((w,h),glfwGetWindowSize(window)) fb_size = denormalize(norm_size,glfwGetFramebufferSize(window)) atb.TwWindowSize(*map(int,fb_size)) glfwMakeContextCurrent(active_window) for p in g.plugins: p.on_window_resize(window,w,h) def on_key(window, key, scancode, action, mods): if not atb.TwEventKeyboardGLFW(key,action): if action == GLFW_PRESS: pass def on_char(window,char): if not atb.TwEventCharGLFW(char,1): pass def on_button(window,button, action, mods): if not atb.TwEventMouseButtonGLFW(button,action): pos = glfwGetCursorPos(window) pos = normalize(pos,glfwGetWindowSize(main_window)) pos = denormalize(pos,(frame.img.shape[1],frame.img.shape[0]) ) # Position in img pixels for p in g.plugins: p.on_click(pos,button,action) def on_pos(window,x, y): norm_pos = normalize((x,y),glfwGetWindowSize(window)) fb_x,fb_y = denormalize(norm_pos,glfwGetFramebufferSize(window)) if atb.TwMouseMotion(int(fb_x),int(fb_y)): pass def on_scroll(window,x,y): if not atb.TwMouseWheel(int(x)): pass def on_close(window): glfwSetWindowShouldClose(main_window,True) logger.debug('Process closing from window') try: rec_dir = sys.argv[1] except: #for dev, supply hardcoded dir: rec_dir = '/Users/mkassner/Desktop/Marker_Tracking_Demo_Recording/' if os.path.isdir(rec_dir): logger.debug("Dev option: Using hadcoded data dir.") else: if getattr(sys, 'frozen', False): logger.warning("You did not supply a data directory when you called this script! \ \nPlease drag a Pupil recoding directory onto the launch icon.") else: logger.warning("You did not supply a data directory when you called this script! \ \nPlease supply a Pupil recoding directory as first arg when calling Pupil Player.") return if not is_pupil_rec_dir(rec_dir): logger.error("You did not supply a dir with the required files inside.") return #backwards compatibility fn. patch_meta_info(rec_dir) #parse and load data folder info video_path = rec_dir + "/world.avi" timestamps_path = rec_dir + "/timestamps.npy" gaze_positions_path = rec_dir + "/gaze_positions.npy" meta_info_path = rec_dir + "/info.csv" #parse info.csv file with open(meta_info_path) as info: meta_info = dict( ((line.strip().split('\t')) for line in info.readlines() ) ) rec_version = meta_info["Capture Software Version"] rec_version_float = int(filter(type(rec_version).isdigit, rec_version)[:3])/100. #(get major,minor,fix of version) logger.debug("Recording version: %s , %s"%(rec_version,rec_version_float)) #load gaze information gaze_list = np.load(gaze_positions_path) timestamps = np.load(timestamps_path) #correlate data positions_by_frame = correlate_gaze(gaze_list,timestamps) # load session persistent settings session_settings = Persistent_Dict(os.path.join(user_dir,"user_settings")) def load(var_name,default): return session_settings.get(var_name,default) def save(var_name,var): session_settings[var_name] = var # Initialize capture cap = autoCreateCapture(video_path,timestamps=timestamps_path) if isinstance(cap,FakeCapture): logger.error("could not start capture.") return width,height = cap.get_size() # Initialize glfw glfwInit() main_window = glfwCreateWindow(width, height, "Pupil Player: "+meta_info["Recording Name"]+" - "+ rec_dir.split(os.path.sep)[-1], None, None) glfwMakeContextCurrent(main_window) # Register callbacks main_window glfwSetWindowSizeCallback(main_window,on_resize) glfwSetWindowCloseCallback(main_window,on_close) glfwSetKeyCallback(main_window,on_key) glfwSetCharCallback(main_window,on_char) glfwSetMouseButtonCallback(main_window,on_button) glfwSetCursorPosCallback(main_window,on_pos) glfwSetScrollCallback(main_window,on_scroll) # create container for globally scoped varfs (within world) g = Temp() g.plugins = [] g.play = False g.new_seek = True g.user_dir = user_dir g.rec_dir = rec_dir g.app = 'player' g.timestamps = timestamps g.positions_by_frame = positions_by_frame # helpers called by the main atb bar def update_fps(): old_time, bar.timestamp = bar.timestamp, time() dt = bar.timestamp - old_time if dt: bar.fps.value += .1 * (1. / dt - bar.fps.value) def set_window_size(mode,data): width,height = cap.get_size() ratio = (1,.75,.5,.25)[mode] w,h = int(width*ratio),int(height*ratio) glfwSetWindowSize(main_window,w,h) data.value=mode # update the bar.value def get_from_data(data): """ helper for atb getter and setter use """ return data.value def get_play(): return g.play def set_play(value): g.play = value def next_frame(): try: cap.seek_to_frame(cap.get_frame_index()) except FileSeekError: pass g.new_seek = True def prev_frame(): try: cap.seek_to_frame(cap.get_frame_index()-2) except FileSeekError: pass g.new_seek = True def open_plugin(selection,data): if plugin_by_index[selection] not in additive_plugins: for p in g.plugins: if isinstance(p,plugin_by_index[selection]): return g.plugins = [p for p in g.plugins if p.alive] logger.debug('Open Plugin: %s'%name_by_index[selection]) new_plugin = plugin_by_index[selection](g) g.plugins.append(new_plugin) g.plugins.sort(key=lambda p: p.order) if hasattr(new_plugin,'init_gui'): new_plugin.init_gui() # save the value for atb bar data.value=selection def get_from_data(data): """ helper for atb getter and setter use """ return data.value atb.init() # add main controls ATB bar bar = atb.Bar(name = "Controls", label="Controls", help="Scene controls", color=(50, 50, 50), alpha=100,valueswidth=150, text='light', position=(10, 10),refresh=.1, size=(300, 160)) bar.next_atb_pos = (10,220) bar.fps = c_float(0.0) bar.timestamp = time() bar.window_size = c_int(load("window_size",0)) window_size_enum = atb.enum("Display Size",{"Full":0, "Medium":1,"Half":2,"Mini":3}) bar.version = create_string_buffer(version,512) bar.recording_version = create_string_buffer(rec_version,512) bar.add_var("fps", bar.fps, step=1., readonly=True) bar._fps = c_float(cap.get_fps()) bar.add_var("recoding fps",bar._fps,readonly=True) bar.add_var("display size", vtype=window_size_enum,setter=set_window_size,getter=get_from_data,data=bar.window_size) bar.add_var("play",vtype=c_bool,getter=get_play,setter=set_play,key="space") bar.add_button('step next',next_frame,key='right') bar.add_button('step prev',prev_frame,key='left') bar.add_var("frame index",vtype=c_int,getter=lambda:cap.get_frame_index()-1 ) bar.plugin_to_load = c_int(0) plugin_type_enum = atb.enum("Plug In",index_by_name) bar.add_var("plugin",setter=open_plugin,getter=get_from_data,data=bar.plugin_to_load, vtype=plugin_type_enum) bar.add_var("version of recording",bar.recording_version, readonly=True, help="version of the capture software used to make this recording") bar.add_var("version of player",bar.version, readonly=True, help="version of the Pupil Player") bar.add_button("exit", on_close,data=main_window,key="esc") #set the last saved window size set_window_size(bar.window_size.value,bar.window_size) on_resize(main_window, *glfwGetWindowSize(main_window)) glfwSetWindowPos(main_window,0,0) #we always load these plugins g.plugins.append(Export_Launcher(g,data_dir=rec_dir,frame_count=len(timestamps))) g.plugins.append(Seek_Bar(g,capture=cap)) g.trim_marks = Trim_Marks(g,capture=cap) g.plugins.append(g.trim_marks) #these are loaded based on user settings for initializer in load('plugins',[]): name, args = initializer logger.debug("Loading plugin: %s with settings %s"%(name, args)) try: p = plugin_by_name[name](g,**args) g.plugins.append(p) except: logger.warning("Plugin '%s' failed to load from settings file." %name) if load('plugins',"_") == "_": #lets load some default if we dont have presets g.plugins.append(Scan_Path(g)) g.plugins.append(Vis_Polyline(g)) g.plugins.append(Vis_Circle(g)) # g.plugins.append(Vis_Light_Points(g)) #sort by exec order g.plugins.sort(key=lambda p: p.order) #init gui for p in g.plugins: if hasattr(p,'init_gui'): p.init_gui() # gl_state settings basic_gl_setup() g.image_tex = create_named_texture((height,width,3)) while not glfwWindowShouldClose(main_window): update_fps() #grab new frame if g.play or g.new_seek: try: new_frame = cap.get_frame() except EndofVideoFileError: #end of video logic: pause at last frame. g.play=False if g.new_seek: display_time = new_frame.timestamp g.new_seek = False frame = new_frame.copy() #new positons and events we make a deepcopy just like the image is a copy. current_pupil_positions = deepcopy(positions_by_frame[frame.index]) events = [] # allow each Plugin to do its work. for p in g.plugins: p.update(frame,current_pupil_positions,events) #check if a plugin need to be destroyed g.plugins = [p for p in g.plugins if p.alive] # render camera image glfwMakeContextCurrent(main_window) make_coord_system_norm_based() draw_named_texture(g.image_tex,frame.img) make_coord_system_pixel_based(frame.img.shape) # render visual feedback from loaded plugins for p in g.plugins: p.gl_display() #present frames at appropriate speed wait_time = frame.timestamp - display_time display_time = frame.timestamp try: spent_time = time()-timestamp sleep(wait_time-spent_time) except: pass timestamp = time() atb.draw() glfwSwapBuffers(main_window) glfwPollEvents() plugin_save = [] for p in g.plugins: try: p_initializer = p.get_class_name(),p.get_init_dict() plugin_save.append(p_initializer) except AttributeError: #not all plugins need to be savable, they will not have the init dict. # any object without a get_init_dict method will throw this exception. pass # de-init all running plugins for p in g.plugins: p.alive = False #reading p.alive actually runs plug-in cleanup _ = p.alive save('plugins',plugin_save) save('window_size',bar.window_size.value) session_settings.close() cap.close() bar.destroy() glfwDestroyWindow(main_window) glfwTerminate() logger.debug("Process done")
def main(): # Callback functions def on_resize(window, w, h): active_window = glfwGetCurrentContext() glfwMakeContextCurrent(window) hdpi_factor = glfwGetFramebufferSize(window)[0] / glfwGetWindowSize( window)[0] w, h = w * hdpi_factor, h * hdpi_factor g_pool.gui.update_window(w, h) g_pool.gui.collect_menus() graph.adjust_size(w, h) adjust_gl_view(w, h) glfwMakeContextCurrent(active_window) for p in g_pool.plugins: p.on_window_resize(window, w, h) def on_key(window, key, scancode, action, mods): g_pool.gui.update_key(key, scancode, action, mods) def on_char(window, char): g_pool.gui.update_char(char) def on_button(window, button, action, mods): g_pool.gui.update_button(button, action, mods) pos = glfwGetCursorPos(window) pos = normalize(pos, glfwGetWindowSize(main_window)) pos = denormalize( pos, (frame.img.shape[1], frame.img.shape[0])) # Position in img pixels for p in g_pool.plugins: p.on_click(pos, button, action) def on_pos(window, x, y): hdpi_factor = float( glfwGetFramebufferSize(window)[0] / glfwGetWindowSize(window)[0]) x, y = x * hdpi_factor, y * hdpi_factor g_pool.gui.update_mouse(x, y) def on_scroll(window, x, y): g_pool.gui.update_scroll(x, y * y_scroll_factor) def on_close(window): glfwSetWindowShouldClose(main_window, True) logger.debug('Process closing from window') try: rec_dir = sys.argv[1] except: #for dev, supply hardcoded dir: rec_dir = '/Users/mkassner/Desktop/Marker_Tracking_Demo_Recording/' if os.path.isdir(rec_dir): logger.debug("Dev option: Using hadcoded data dir.") else: if getattr(sys, 'frozen', False): logger.warning( "You did not supply a data directory when you called this script! \ \nPlease drag a Pupil recoding directory onto the launch icon." ) else: logger.warning( "You did not supply a data directory when you called this script! \ \nPlease supply a Pupil recoding directory as first arg when calling Pupil Player." ) return if not is_pupil_rec_dir(rec_dir): logger.error( "You did not supply a dir with the required files inside.") return # load session persistent settings session_settings = Persistent_Dict(os.path.join(user_dir, "user_settings")) #backwards compatibility fn. patch_meta_info(rec_dir) #parse info.csv file meta_info_path = rec_dir + "/info.csv" with open(meta_info_path) as info: meta_info = dict( ((line.strip().split('\t')) for line in info.readlines())) rec_version = read_rec_version(meta_info) if rec_version < VersionFormat('0.4'): video_path = rec_dir + "/world.avi" timestamps_path = rec_dir + "/timestamps.npy" else: video_path = rec_dir + "/world.mkv" timestamps_path = rec_dir + "/world_timestamps.npy" gaze_positions_path = rec_dir + "/gaze_positions.npy" #load gaze information gaze_list = np.load(gaze_positions_path) timestamps = np.load(timestamps_path) #correlate data if rec_version < VersionFormat('0.4'): positions_by_frame = correlate_gaze_legacy(gaze_list, timestamps) else: positions_by_frame = correlate_gaze(gaze_list, timestamps) # Initialize capture cap = autoCreateCapture(video_path, timestamps=timestamps_path) if isinstance(cap, FakeCapture): logger.error("could not start capture.") return width, height = session_settings.get('window_size', cap.get_size()) window_pos = session_settings.get('window_position', (0, 0)) # not yet using this one. # Initialize glfw glfwInit() main_window = glfwCreateWindow( width, height, "Pupil Player: " + meta_info["Recording Name"] + " - " + rec_dir.split(os.path.sep)[-1], None, None) glfwMakeContextCurrent(main_window) cygl.utils.init() # Register callbacks main_window glfwSetWindowSizeCallback(main_window, on_resize) glfwSetWindowCloseCallback(main_window, on_close) glfwSetKeyCallback(main_window, on_key) glfwSetCharCallback(main_window, on_char) glfwSetMouseButtonCallback(main_window, on_button) glfwSetCursorPosCallback(main_window, on_pos) glfwSetScrollCallback(main_window, on_scroll) # create container for globally scoped vars (within world) g_pool = Global_Container() g_pool.app = 'player' g_pool.version = get_version(version_file) g_pool.capture = cap g_pool.timestamps = timestamps g_pool.gaze_list = gaze_list g_pool.positions_by_frame = positions_by_frame g_pool.play = False g_pool.new_seek = True g_pool.user_dir = user_dir g_pool.rec_dir = rec_dir g_pool.rec_version = rec_version g_pool.meta_info = meta_info def next_frame(_): try: cap.seek_to_frame(cap.get_frame_index()) except FileSeekError: pass g_pool.new_seek = True def prev_frame(_): try: cap.seek_to_frame(cap.get_frame_index() - 2) except FileSeekError: pass g_pool.new_seek = True def set_scale(new_scale): g_pool.gui.scale = new_scale g_pool.gui.collect_menus() def get_scale(): return g_pool.gui.scale def open_plugin(plugin): if plugin == "Select to load": return logger.debug('Open Plugin: %s' % plugin) new_plugin = plugin(g_pool) g_pool.plugins.add(new_plugin) def purge_plugins(): for p in g_pool.plugins: if p.__class__ in user_launchable_plugins: p.alive = False g_pool.plugins.clean() g_pool.gui = ui.UI() g_pool.gui.scale = session_settings.get('gui_scale', 1) g_pool.main_menu = ui.Scrolling_Menu("Settings", pos=(-350, 20), size=(300, 300)) g_pool.main_menu.append(ui.Button("quit", lambda: on_close(None))) g_pool.main_menu.configuration = session_settings.get( 'main_menu_config', {}) g_pool.main_menu.append( ui.Slider('scale', setter=set_scale, getter=get_scale, step=.05, min=0.75, max=2.5, label='Interface Size')) g_pool.main_menu.append(ui.Info_Text('Player Version: %s' % g_pool.version)) g_pool.main_menu.append(ui.Info_Text('Recording Version: %s' % rec_version)) g_pool.main_menu.append( ui.Selector('Open plugin', selection=user_launchable_plugins, labels=[ p.__name__.replace('_', ' ') for p in user_launchable_plugins ], setter=open_plugin, getter=lambda: "Select to load")) g_pool.main_menu.append(ui.Button('Close all plugins', purge_plugins)) g_pool.main_menu.append( ui.Button( 'Reset window size', lambda: glfwSetWindowSize(main_window, cap.get_size()[0], cap.get_size()[1]))) g_pool.quickbar = ui.Stretching_Menu('Quick Bar', (0, 100), (120, -100)) g_pool.play_button = ui.Thumb('play', g_pool, label='Play', hotkey=GLFW_KEY_SPACE) g_pool.play_button.on_color[:] = (0, 1., .0, .8) g_pool.forward_button = ui.Thumb('forward', getter=lambda: False, setter=next_frame, hotkey=GLFW_KEY_RIGHT) g_pool.backward_button = ui.Thumb('backward', getter=lambda: False, setter=prev_frame, hotkey=GLFW_KEY_LEFT) g_pool.quickbar.extend( [g_pool.play_button, g_pool.forward_button, g_pool.backward_button]) g_pool.gui.append(g_pool.quickbar) g_pool.gui.append(g_pool.main_menu) #we always load these plugins system_plugins = [('Trim_Marks', {}), ('Seek_Bar', {})] default_plugins = [('Scan_Path', {}), ('Vis_Polyline', {}), ('Vis_Circle', {}), ('Export_Launcher', {})] previous_plugins = session_settings.get('loaded_plugins', default_plugins) g_pool.plugins = Plugin_List(g_pool, plugin_by_name, system_plugins + previous_plugins) for p in g_pool.plugins: if p.class_name == 'Trim_Marks': g_pool.trim_marks = p break #set the last saved window size on_resize(main_window, *glfwGetWindowSize(main_window)) glfwSetWindowPos(main_window, 0, 0) # gl_state settings basic_gl_setup() g_pool.image_tex = create_named_texture((height, width, 3)) #set up performace graphs: pid = os.getpid() ps = psutil.Process(pid) ts = cap.get_now() - .03 cpu_graph = graph.Bar_Graph() cpu_graph.pos = (20, 110) cpu_graph.update_fn = ps.get_cpu_percent cpu_graph.update_rate = 5 cpu_graph.label = 'CPU %0.1f' fps_graph = graph.Bar_Graph() fps_graph.pos = (140, 110) fps_graph.update_rate = 5 fps_graph.label = "%0.0f REC FPS" pupil_graph = graph.Bar_Graph(max_val=1.0) pupil_graph.pos = (260, 110) pupil_graph.update_rate = 5 pupil_graph.label = "Confidence: %0.2f" while not glfwWindowShouldClose(main_window): #grab new frame if g_pool.play or g_pool.new_seek: try: new_frame = cap.get_frame() except EndofVideoFileError: #end of video logic: pause at last frame. g_pool.play = False if g_pool.new_seek: display_time = new_frame.timestamp g_pool.new_seek = False update_graph = True else: update_graph = False frame = new_frame.copy() events = {} #new positons we make a deepcopy just like the image is a copy. events['pupil_positions'] = deepcopy(positions_by_frame[frame.index]) if update_graph: #update performace graphs for p in events['pupil_positions']: pupil_graph.add(p['confidence']) t = new_frame.timestamp if ts != t: dt, ts = t - ts, t fps_graph.add(1. / dt) g_pool.play_button.status_text = str(frame.index) #always update the CPU graph cpu_graph.update() # allow each Plugin to do its work. for p in g_pool.plugins: p.update(frame, events) #check if a plugin need to be destroyed g_pool.plugins.clean() # render camera image glfwMakeContextCurrent(main_window) make_coord_system_norm_based() update_named_texture(g_pool.image_tex, frame.img) draw_named_texture(g_pool.image_tex) make_coord_system_pixel_based(frame.img.shape) # render visual feedback from loaded plugins for p in g_pool.plugins: p.gl_display() graph.push_view() fps_graph.draw() cpu_graph.draw() pupil_graph.draw() graph.pop_view() g_pool.gui.update() #present frames at appropriate speed wait_time = frame.timestamp - display_time display_time = frame.timestamp try: spent_time = time() - timestamp sleep(wait_time - spent_time) except: pass timestamp = time() glfwSwapBuffers(main_window) glfwPollEvents() session_settings['loaded_plugins'] = g_pool.plugins.get_initializers() session_settings['gui_scale'] = g_pool.gui.scale session_settings['main_menu_config'] = g_pool.main_menu.configuration session_settings['window_size'] = glfwGetWindowSize(main_window) session_settings['window_position'] = glfwGetWindowPos(main_window) session_settings.close() # de-init all running plugins for p in g_pool.plugins: p.alive = False g_pool.plugins.clean() cap.close() glfwDestroyWindow(main_window) glfwTerminate() logger.debug("Process done")
def export(should_terminate, frames_to_export, current_frame, data_dir, start_frame=None, end_frame=None, plugin_initializers=[], out_file_path=None): logger = logging.getLogger(__name__ + ' with pid: ' + str(os.getpid())) #parse and load data dir info video_path = data_dir + "/world.avi" timestamps_path = data_dir + "/timestamps.npy" gaze_positions_path = data_dir + "/gaze_positions.npy" record_path = data_dir + "/world_viz.avi" #parse info.csv file with open(data_dir + "/info.csv") as info: meta_info = dict( ((line.strip().split('\t')) for line in info.readlines())) rec_version = meta_info["Capture Software Version"] rec_version_int = int( filter(type(rec_version).isdigit, rec_version)[:3]) / 100 #(get major,minor,fix of version) logger.debug("Exporting a video from recording with version: %s , %s" % (rec_version, rec_version_int)) #load gaze information gaze_list = np.load(gaze_positions_path) timestamps = np.load(timestamps_path) #correlate data positions_by_frame = correlate_gaze(gaze_list, timestamps) # Initialize capture, check if it works cap = autoCreateCapture(video_path, timestamps=timestamps_path) if cap is None: logger.error("Did not receive valid Capture") return width, height = cap.get_size() #Out file path verification, we do this before but if one uses a seperate tool, this will kick in. if out_file_path is None: out_file_path = os.path.join(data_dir, "world_viz.avi") else: file_name = os.path.basename(out_file_path) dir_name = os.path.dirname(out_file_path) if not dir_name: dir_name = data_dir if not file_name: file_name = 'world_viz.avi' out_file_path = os.path.expanduser(os.path.join(dir_name, file_name)) if os.path.isfile(out_file_path): logger.warning("Video out file already exsists. I will overwrite!") os.remove(out_file_path) logger.debug("Saving Video to %s" % out_file_path) #Trim mark verification #make sure the trim marks (start frame, endframe) make sense: We define them like python list slices,thus we can test them like such. trimmed_timestamps = timestamps[start_frame:end_frame] if len(trimmed_timestamps) == 0: logger.warn( "Start and end frames are set such that no video will be exported." ) return False if start_frame == None: start_frame = 0 #these two vars are shared with the lauching process and give an job lenght and progress report. frames_to_export.value = len(trimmed_timestamps) current_frame.value = 0 logger.debug( "Will export from frame %s to frame %s. This means I will export %s frames." % (start_frame, start_frame + frames_to_export.value, frames_to_export.value)) #lets get the avg. framerate for our slice of video: fps = float(len(trimmed_timestamps)) / (trimmed_timestamps[-1] - trimmed_timestamps[0]) logger.debug("Framerate of export video is %s" % fps) #setup of writer writer = cv2.VideoWriter(out_file_path, cv2.cv.CV_FOURCC(*'DIVX'), fps, (width, height)) cap.seek_to_frame(start_frame) start_time = time() plugins = [] g = Temp() g.plugins = plugins g.app = 'exporter' # load plugins from initializers: for initializer in plugin_initializers: name, args = initializer logger.debug("Loading plugin: %s with settings %s" % (name, args)) try: p = plugin_by_name[name](g, **args) plugins.append(p) except: logger.warning("Plugin '%s' could not be loaded in exporter." % name) while frames_to_export.value - current_frame.value > 0: if should_terminate.value: logger.warning("User aborted export. Exported %s frames to %s." % (current_frame.value, out_file_path)) #explicit release of VideoWriter writer.release() writer = None return False new_frame = cap.get_frame() #end of video logic: pause at last frame. if not new_frame: logger.error("Could not read all frames.") #explicit release of VideoWriter writer.release() writer = None return False else: frame = new_frame #new positons and events current_pupil_positions = positions_by_frame[frame.index] events = None # allow each Plugin to do its work. for p in plugins: p.update(frame, current_pupil_positions, events) # # render gl visual feedback from loaded plugins # for p in plugins: # p.gl_display(frame) writer.write(frame.img) current_frame.value += 1 writer.release() writer = None duration = time() - start_time effective_fps = float(current_frame.value) / duration logger.info( "Export done: Exported %s frames to %s. This took %s seconds. Exporter ran at %s frames per second" % (current_frame.value, out_file_path, duration, effective_fps)) return True
def main(): # Callback functions def on_resize(window, w, h): active_window = glfwGetCurrentContext() glfwMakeContextCurrent(window) adjust_gl_view(w, h) atb.TwWindowSize(w, h) glfwMakeContextCurrent(active_window) def on_key(window, key, scancode, action, mods): if not atb.TwEventKeyboardGLFW(key, action): if action == GLFW_PRESS: if key == GLFW_KEY_ESCAPE: pass def on_char(window, char): if not atb.TwEventCharGLFW(char, 1): pass def on_button(window, button, action, mods): if not atb.TwEventMouseButtonGLFW(button, action): pos = glfwGetCursorPos(window) pos = normalize(pos, glfwGetWindowSize(main_window)) pos = denormalize(pos, (frame.img.shape[1], frame.img.shape[0])) # Position in img pixels for p in g.plugins: p.on_click(pos, button, action) def on_pos(window, x, y): if atb.TwMouseMotion(int(x), int(y)): pass def on_scroll(window, x, y): if not atb.TwMouseWheel(int(x)): pass def on_close(window): glfwSetWindowShouldClose(window) logger.info('Process closing from window') try: rec_dir = sys.argv[1] except: #for dev, supply hardcoded dir: rec_dir = "/Users/mkassner/Desktop/2014_01_21/000" if os.path.isdir(rec_dir): logger.debug("Dev option: Using hadcoded data dir.") else: if getattr(sys, 'frozen', False): logger.warning( "You did not supply a data directory when you called this script! \ \nPlease drag a Pupil recoding directory onto the launch icon." ) else: logger.warning( "You did not supply a data directory when you called this script! \ \nPlease supply a Pupil recoding directory as first arg when calling Pupil Player." ) return if not is_pupil_rec_dir(rec_dir): logger.error( "You did not supply a dir with the required files inside.") return #backwards compatibility fn. patch_meta_info(rec_dir) #parse and load data folder info video_path = rec_dir + "/world.avi" timestamps_path = rec_dir + "/timestamps.npy" gaze_positions_path = rec_dir + "/gaze_positions.npy" meta_info_path = rec_dir + "/info.csv" #parse info.csv file with open(meta_info_path) as info: meta_info = dict( ((line.strip().split('\t')) for line in info.readlines())) rec_version = meta_info["Capture Software Version"] try: rec_version_float = int( filter(type(rec_version).isdigit, rec_version)[:3]) / 100. #(get major,minor,fix of version) logger.debug("Recording version: %s , %s" % (rec_version, rec_version_float)) except ValueError: logger.warning( "Recoding version not valid and unknown. Reported version: '%s'" % rec_version) rec_version_float = 0 #load gaze information gaze_list = np.load(gaze_positions_path) timestamps = np.load(timestamps_path) #correlate data positions_by_frame = correlate_gaze(gaze_list, timestamps) # load session persistent settings session_settings = shelve.open(os.path.join(user_dir, "user_settings"), protocol=2) def load(var_name, default): return session_settings.get(var_name, default) def save(var_name, var): session_settings[var_name] = var # Initialize capture, check if it works cap = autoCreateCapture(video_path, timestamps=timestamps_path) if cap is None: logger.error("Did not receive valid Capture") return width, height = cap.get_size() # Initialize glfw glfwInit() main_window = glfwCreateWindow( width, height, "Pupil Player: " + meta_info["Recording Name"] + " - " + rec_dir.split(os.path.sep)[-1], None, None) glfwMakeContextCurrent(main_window) # Register callbacks main_window glfwSetWindowSizeCallback(main_window, on_resize) glfwSetWindowCloseCallback(main_window, on_close) glfwSetKeyCallback(main_window, on_key) glfwSetCharCallback(main_window, on_char) glfwSetMouseButtonCallback(main_window, on_button) glfwSetCursorPosCallback(main_window, on_pos) glfwSetScrollCallback(main_window, on_scroll) # gl_state settings basic_gl_setup() # create container for globally scoped vars (within world) g = Temp() g.plugins = [] g.play = False g.new_seek = True g.user_dir = user_dir g.rec_dir = rec_dir g.app = 'player' # helpers called by the main atb bar def update_fps(): old_time, bar.timestamp = bar.timestamp, time() dt = bar.timestamp - old_time if dt: bar.fps.value += .1 * (1. / dt - bar.fps.value) def set_window_size(mode, data): width, height = cap.get_size() ratio = (1, .75, .5, .25)[mode] w, h = int(width * ratio), int(height * ratio) glfwSetWindowSize(main_window, w, h) data.value = mode # update the bar.value def get_from_data(data): """ helper for atb getter and setter use """ return data.value def get_play(): return g.play def set_play(value): g.play = value def open_plugin(selection, data): if plugin_by_index[selection] not in additive_plugins: for p in g.plugins: if isinstance(p, plugin_by_index[selection]): return g.plugins = [p for p in g.plugins if p.alive] logger.debug('Open Plungin: %s' % name_by_index[selection]) new_plugin = plugin_by_index[selection](g) g.plugins.append(new_plugin) g.plugins.sort(key=lambda p: p.order) if hasattr(new_plugin, 'init_gui'): new_plugin.init_gui() # save the value for atb bar data.value = selection def get_from_data(data): """ helper for atb getter and setter use """ return data.value atb.init() # add main controls ATB bar bar = atb.Bar(name="Controls", label="Controls", help="Scene controls", color=(50, 50, 50), alpha=100, valueswidth=150, text='light', position=(10, 10), refresh=.1, size=(300, 160)) bar.next_atb_pos = (10, 220) bar.fps = c_float(0.0) bar.timestamp = time() bar.window_size = c_int(load("window_size", 0)) window_size_enum = atb.enum("Display Size", { "Full": 0, "Medium": 1, "Half": 2, "Mini": 3 }) bar.version = create_string_buffer(version, 512) bar.recording_version = create_string_buffer(rec_version, 512) bar.add_var("fps", bar.fps, step=1., readonly=True) bar._fps = c_float(cap.get_fps()) bar.add_var("recoding fps", bar._fps, readonly=True) bar.add_var("display size", vtype=window_size_enum, setter=set_window_size, getter=get_from_data, data=bar.window_size) bar.add_var("play", vtype=c_bool, getter=get_play, setter=set_play, key="space") bar.add_var("next frame", getter=cap.get_frame_index) bar.plugin_to_load = c_int(0) plugin_type_enum = atb.enum("Plug In", index_by_name) bar.add_var("plugin", setter=open_plugin, getter=get_from_data, data=bar.plugin_to_load, vtype=plugin_type_enum) bar.add_var( "version of recording", bar.recording_version, readonly=True, help="version of the capture software used to make this recording") bar.add_var("version of player", bar.version, readonly=True, help="version of the Pupil Player") bar.add_button("exit", on_close, data=main_window, key="esc") #set the last saved window size set_window_size(bar.window_size.value, bar.window_size) on_resize(main_window, *glfwGetFramebufferSize(main_window)) glfwSetWindowPos(main_window, 0, 0) #we always load these plugins g.plugins.append( Export_Launcher(g, data_dir=rec_dir, frame_count=len(timestamps))) g.plugins.append(Seek_Bar(g, capture=cap)) #these are loaded based on user settings for initializer in load('plugins', []): name, args = initializer logger.debug("Loading plugin: %s with settings %s" % (name, args)) try: p = plugin_by_name[name](g, **args) g.plugins.append(p) except: logger.warning("Plugin '%s' failed to load from settings file." % name) if load('plugins', "_") == "_": #lets load some default if we dont have presets g.plugins.append(Scan_Path(g)) g.plugins.append(Vis_Polyline(g)) g.plugins.append(Vis_Circle(g)) # g.plugins.append(Vis_Light_Points(g)) #sort by exec order g.plugins.sort(key=lambda p: p.order) #init gui for p in g.plugins: if hasattr(p, 'init_gui'): p.init_gui() while not glfwWindowShouldClose(main_window): update_fps() #grab new frame if g.play or g.new_seek: test_frame = cap.get_frame() #end of video logic: pause at last frame. if not test_frame: g.play = False else: new_frame = test_frame if g.new_seek: display_time = new_frame.timestamp g.new_seek = False frame = new_frame.copy() #new positons and events current_pupil_positions = positions_by_frame[frame.index][:] events = [] # allow each Plugin to do its work. for p in g.plugins: p.update(frame, current_pupil_positions, events) #check if a plugin need to be destroyed g.plugins = [p for p in g.plugins if p.alive] # render camera image glfwMakeContextCurrent(main_window) draw_gl_texture(frame.img) # render visual feedback from loaded plugins for p in g.plugins: p.gl_display() #present frames at appropriate speed wait_time = frame.timestamp - display_time display_time = frame.timestamp try: spent_time = time() - timestamp sleep(wait_time - spent_time) except: pass timestamp = time() atb.draw() glfwSwapBuffers(main_window) glfwPollEvents() plugin_save = [] for p in g.plugins: if hasattr(p, 'get_init_dict'): p_initializer = p.get_class_name(), p.get_init_dict() plugin_save.append(p_initializer) # de-init all running plugins for p in g.plugins: p.alive = False #reading p.alive actually runs plug-in cleanup _ = p.alive save('plugins', plugin_save) save('window_size', bar.window_size.value) session_settings.close() cap.close() bar.destroy() glfwDestroyWindow(main_window) glfwTerminate() logger.debug("Process done")
def export(should_terminate, frames_to_export, current_frame, rec_dir, start_frame=None, end_frame=None, plugin_initializers=[], out_file_path=None): logger = logging.getLogger(__name__ + ' with pid: ' + str(os.getpid())) #parse info.csv file with open(rec_dir + "/info.csv") as info: meta_info = dict( ((line.strip().split('\t')) for line in info.readlines())) rec_version = read_rec_version(meta_info) logger.debug("Exporting a video from recording with version: %s" % rec_version) if rec_version < VersionFormat('0.4'): video_path = rec_dir + "/world.avi" timestamps_path = rec_dir + "/timestamps.npy" else: video_path = rec_dir + "/world.mkv" timestamps_path = rec_dir + "/world_timestamps.npy" gaze_positions_path = rec_dir + "/gaze_positions.npy" #load gaze information gaze_list = np.load(gaze_positions_path) timestamps = np.load(timestamps_path) #correlate data if rec_version < VersionFormat('0.4'): positions_by_frame = correlate_gaze_legacy(gaze_list, timestamps) else: positions_by_frame = correlate_gaze(gaze_list, timestamps) cap = autoCreateCapture(video_path, timestamps=timestamps_path) width, height = cap.get_size() #Out file path verification, we do this before but if one uses a seperate tool, this will kick in. if out_file_path is None: out_file_path = os.path.join(rec_dir, "world_viz.mp4") else: file_name = os.path.basename(out_file_path) dir_name = os.path.dirname(out_file_path) if not dir_name: dir_name = rec_dir if not file_name: file_name = 'world_viz.mp4' out_file_path = os.path.expanduser(os.path.join(dir_name, file_name)) if os.path.isfile(out_file_path): logger.warning("Video out file already exsists. I will overwrite!") os.remove(out_file_path) logger.debug("Saving Video to %s" % out_file_path) #Trim mark verification #make sure the trim marks (start frame, endframe) make sense: We define them like python list slices,thus we can test them like such. trimmed_timestamps = timestamps[start_frame:end_frame] if len(trimmed_timestamps) == 0: logger.warn( "Start and end frames are set such that no video will be exported." ) return False if start_frame == None: start_frame = 0 #these two vars are shared with the lauching process and give a job length and progress report. frames_to_export.value = len(trimmed_timestamps) current_frame.value = 0 logger.debug( "Will export from frame %s to frame %s. This means I will export %s frames." % (start_frame, start_frame + frames_to_export.value, frames_to_export.value)) #setup of writer writer = AV_Writer(out_file_path) cap.seek_to_frame(start_frame) start_time = time() g = Global_Container() g.app = 'exporter' g.rec_dir = rec_dir g.rec_version = rec_version g.timestamps = timestamps g.gaze_list = gaze_list g.positions_by_frame = positions_by_frame g.plugins = Plugin_List(g, plugin_by_name, plugin_initializers) while frames_to_export.value - current_frame.value > 0: if should_terminate.value: logger.warning("User aborted export. Exported %s frames to %s." % (current_frame.value, out_file_path)) #explicit release of VideoWriter writer.close() writer = None return False try: frame = cap.get_frame() except EndofVideoFileError: break events = {} #new positons and events events['pupil_positions'] = positions_by_frame[frame.index] # allow each Plugin to do its work. for p in g.plugins: p.update(frame, events) writer.write_video_frame(frame) current_frame.value += 1 writer.close() writer = None duration = time() - start_time effective_fps = float(current_frame.value) / duration logger.info( "Export done: Exported %s frames to %s. This took %s seconds. Exporter ran at %s frames per second" % (current_frame.value, out_file_path, duration, effective_fps)) return True