def start(self): self.timestamps = [] self.data = {'pupil_positions':[],'gaze_positions':[]} self.pupil_pos_list = [] self.gaze_pos_list = [] self.frame_count = 0 self.running = True self.menu.read_only = True self.start_time = time() session = os.path.join(self.rec_dir, self.session_name) try: os.makedirs(session) logger.debug("Created new recordings session dir %s"%session) except: logger.debug("Recordings session dir %s already exists, using it." %session) # set up self incrementing folder within session folder counter = 0 while True: self.rec_path = os.path.join(session, "%03d/" % counter) try: os.mkdir(self.rec_path) logger.debug("Created new recording dir %s"%self.rec_path) break except: logger.debug("We dont want to overwrite data, incrementing counter & trying to make new data folder") counter += 1 self.meta_info_path = os.path.join(self.rec_path, "info.csv") with open(self.meta_info_path, 'w') as f: f.write("Recording Name\t"+self.session_name+ "\n") f.write("Start Date\t"+ strftime("%d.%m.%Y", localtime(self.start_time))+ "\n") f.write("Start Time\t"+ strftime("%H:%M:%S", localtime(self.start_time))+ "\n") if self.audio_src != 'No Audio': audio_path = os.path.join(self.rec_path, "world.wav") self.audio_writer = Audio_Capture(self.audio_devices_dict[self.audio_src],audio_path) else: self.audio_writer = None if self.raw_jpeg and "uvc_capture" in str(self.g_pool.capture.__class__): self.video_path = os.path.join(self.rec_path, "world.mp4") self.writer = JPEG_Writer(self.video_path,int(self.g_pool.capture.frame_rate)) # elif 1: # self.writer = av_writer.AV_Writer(self.video_path) else: self.video_path = os.path.join(self.rec_path, "world.mkv") self.writer = CV_Writer(self.video_path, float(self.g_pool.capture.frame_rate), self.g_pool.capture.frame_size) # positions path to eye process if self.record_eye: for tx in self.g_pool.eye_tx: tx.send((self.rec_path,self.raw_jpeg)) if self.show_info_menu: self.open_info_menu()
def start(self): self.timestamps = [] self.data = {'pupil_positions':[],'gaze_positions':[]} self.pupil_pos_list = [] self.gaze_pos_list = [] self.frame_count = 0 self.running = True self.menu.read_only = True self.start_time = time() session = os.path.join(self.rec_dir, self.session_name) try: os.makedirs(session) logger.debug("Created new recordings session dir %s"%session) except: logger.debug("Recordings session dir %s already exists, using it." %session) # set up self incrementing folder within session folder counter = 0 while True: self.rec_path = os.path.join(session, "%03d/" % counter) try: os.mkdir(self.rec_path) logger.debug("Created new recording dir %s"%self.rec_path) break except: logger.debug("We dont want to overwrite data, incrementing counter & trying to make new data folder") counter += 1 self.meta_info_path = os.path.join(self.rec_path, "info.csv") with open(self.meta_info_path, 'w') as f: f.write("Recording Name\t"+self.session_name+ "\n") f.write("Start Date\t"+ strftime("%d.%m.%Y", localtime(self.start_time))+ "\n") f.write("Start Time\t"+ strftime("%H:%M:%S", localtime(self.start_time))+ "\n") if self.audio_src != 'No Audio': audio_path = os.path.join(self.rec_path, "world.wav") self.audio_writer = Audio_Capture(self.audio_devices_dict[self.audio_src],audio_path) else: self.audio_writer = None self.video_path = os.path.join(self.rec_path, "world.mkv") if self.raw_jpeg and "uvc_capture" in str(self.g_pool.capture.__class__): self.writer = JPEG_Dumper(self.video_path) # elif 1: # self.writer = av_writer.AV_Writer(self.video_path) else: self.writer = CV_Writer(self.video_path, float(self.g_pool.capture.frame_rate), self.g_pool.capture.frame_size) # positions path to eye process if self.record_eye: for tx in self.g_pool.eye_tx: tx.send((self.rec_path,self.raw_jpeg)) if self.show_info_menu: self.open_info_menu()
def eye(g_pool, cap_src, cap_size, pipe_to_world, eye_id=0): """ Creates a window, gl context. Grabs images from a capture. Streams Pupil coordinates into g_pool.pupil_queue """ # modify the root logger for this process logger = logging.getLogger() # remove inherited handlers logger.handlers = [] # create file handler which logs even debug messages fh = logging.FileHandler(os.path.join(g_pool.user_dir, 'eye%s.log' % eye_id), mode='w') # fh.setLevel(logging.DEBUG) # create console handler with a higher log level ch = logging.StreamHandler() ch.setLevel(logger.level + 10) # create formatter and add it to the handlers formatter = logging.Formatter( 'Eye' + str(eye_id) + ' Process: %(asctime)s - %(name)s - %(levelname)s - %(message)s') fh.setFormatter(formatter) formatter = logging.Formatter( 'EYE' + str(eye_id) + ' Process [%(levelname)s] %(name)s : %(message)s') ch.setFormatter(formatter) # add the handlers to the logger logger.addHandler(fh) logger.addHandler(ch) # create logger for the context of this function logger = logging.getLogger(__name__) #UI Platform tweaks if platform.system() == 'Linux': scroll_factor = 10.0 window_position_default = (600, 300 * eye_id) elif platform.system() == 'Windows': scroll_factor = 1.0 window_position_default = (600, 31 + 300 * eye_id) else: scroll_factor = 1.0 window_position_default = (600, 300 * eye_id) # Callback functions def on_resize(window, w, h): if not g_pool.iconified: active_window = glfwGetCurrentContext() glfwMakeContextCurrent(window) g_pool.gui.update_window(w, h) graph.adjust_size(w, h) adjust_gl_view(w, h) glfwMakeContextCurrent(active_window) 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_iconify(window, iconified): g_pool.iconified = iconified def on_button(window, button, action, mods): if g_pool.display_mode == 'roi': if action == GLFW_RELEASE and u_r.active_edit_pt: u_r.active_edit_pt = False return # if the roi interacts we dont what the gui to interact as well elif action == GLFW_PRESS: pos = glfwGetCursorPos(window) pos = normalize(pos, glfwGetWindowSize(main_window)) if g_pool.flip: pos = 1 - pos[0], 1 - pos[1] pos = denormalize( pos, (frame.width, frame.height)) # Position in img pixels if u_r.mouse_over_edit_pt(pos, u_r.handle_size + 40, u_r.handle_size + 40): return # if the roi interacts we dont what the gui to interact as well g_pool.gui.update_button(button, action, mods) def on_pos(window, x, y): hdpi_factor = float( glfwGetFramebufferSize(window)[0] / glfwGetWindowSize(window)[0]) g_pool.gui.update_mouse(x * hdpi_factor, y * hdpi_factor) if u_r.active_edit_pt: pos = normalize((x, y), glfwGetWindowSize(main_window)) if g_pool.flip: pos = 1 - pos[0], 1 - pos[1] pos = denormalize(pos, (frame.width, frame.height)) u_r.move_vertex(u_r.active_pt_idx, pos) def on_scroll(window, x, y): g_pool.gui.update_scroll(x, y * scroll_factor) def on_close(window): g_pool.quit.value = True logger.info('Process closing from window') # load session persistent settings session_settings = Persistent_Dict( os.path.join(g_pool.user_dir, 'user_settings_eye%s' % eye_id)) if session_settings.get("version", VersionFormat('0.0')) < g_pool.version: logger.info( "Session setting are from older version of this app. I will not use those." ) session_settings.clear() # Initialize capture cap = autoCreateCapture(cap_src, timebase=g_pool.timebase) default_settings = {'frame_size': cap_size, 'frame_rate': 30} previous_settings = session_settings.get('capture_settings', None) if previous_settings and previous_settings['name'] == cap.name: cap.settings = previous_settings else: cap.settings = default_settings # Test capture try: frame = cap.get_frame() except CameraCaptureError: logger.error("Could not retrieve image from capture") cap.close() return #signal world that we are ready to go pipe_to_world.send('eye%s process ready' % eye_id) # any object we attach to the g_pool object *from now on* will only be visible to this process! # vars should be declared here to make them visible to the code reader. g_pool.iconified = False g_pool.capture = cap g_pool.flip = session_settings.get('flip', False) g_pool.display_mode = session_settings.get('display_mode', 'camera_image') g_pool.display_mode_info_text = { 'camera_image': "Raw eye camera image. This uses the least amount of CPU power", 'roi': "Click and drag on the blue circles to adjust the region of interest. The region should be a small as possible but big enough to capture to pupil in its movements", 'algorithm': "Algorithm display mode overlays a visualization of the pupil detection parameters on top of the eye video. Adjust parameters with in the Pupil Detection menu below." } # g_pool.draw_pupil = session_settings.get('draw_pupil',True) u_r = UIRoi(frame.img.shape) u_r.set(session_settings.get('roi', u_r.get())) writer = None pupil_detector = Canny_Detector(g_pool) # UI callback functions def set_scale(new_scale): g_pool.gui.scale = new_scale g_pool.gui.collect_menus() def set_display_mode_info(val): g_pool.display_mode = val g_pool.display_mode_info.text = g_pool.display_mode_info_text[val] # Initialize glfw glfwInit() if g_pool.binocular: title = "Binocular eye %s" % eye_id else: title = 'Eye' width, height = session_settings.get('window_size', (frame.width, frame.height)) main_window = glfwCreateWindow(width, height, title, None, None) window_pos = session_settings.get('window_position', window_position_default) glfwSetWindowPos(main_window, window_pos[0], window_pos[1]) glfwMakeContextCurrent(main_window) cygl_init() # gl_state settings basic_gl_setup() g_pool.image_tex = Named_Texture() g_pool.image_tex.update_from_frame(frame) glfwSwapInterval(0) #setup GUI g_pool.gui = ui.UI() g_pool.gui.scale = session_settings.get('gui_scale', 1) g_pool.sidebar = ui.Scrolling_Menu("Settings", pos=(-300, 0), size=(0, 0), header_pos='left') general_settings = ui.Growing_Menu('General') general_settings.append( ui.Slider('scale', g_pool.gui, setter=set_scale, step=.05, min=1., max=2.5, label='Interface Size')) general_settings.append( ui.Button( 'Reset window size', lambda: glfwSetWindowSize(main_window, frame.width, frame.height))) general_settings.append( ui.Selector('display_mode', g_pool, setter=set_display_mode_info, selection=['camera_image', 'roi', 'algorithm'], labels=['Camera Image', 'ROI', 'Algorithm'], label="Mode")) general_settings.append( ui.Switch('flip', g_pool, label='Flip image display')) g_pool.display_mode_info = ui.Info_Text( g_pool.display_mode_info_text[g_pool.display_mode]) general_settings.append(g_pool.display_mode_info) g_pool.sidebar.append(general_settings) g_pool.gui.append(g_pool.sidebar) g_pool.gui.append( ui.Hot_Key("quit", setter=on_close, getter=lambda: True, label="X", hotkey=GLFW_KEY_ESCAPE)) # let the camera add its GUI g_pool.capture.init_gui(g_pool.sidebar) # let detector add its GUI pupil_detector.init_gui(g_pool.sidebar) # Register callbacks main_window glfwSetFramebufferSizeCallback(main_window, on_resize) glfwSetWindowCloseCallback(main_window, on_close) glfwSetWindowIconifyCallback(main_window, on_iconify) 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) #set the last saved window size on_resize(main_window, *glfwGetWindowSize(main_window)) # load last gui configuration g_pool.gui.configuration = session_settings.get('ui_config', {}) #set up performance graphs pid = os.getpid() ps = psutil.Process(pid) ts = frame.timestamp cpu_graph = graph.Bar_Graph() cpu_graph.pos = (20, 130) cpu_graph.update_fn = ps.cpu_percent cpu_graph.update_rate = 5 cpu_graph.label = 'CPU %0.1f' fps_graph = graph.Bar_Graph() fps_graph.pos = (140, 130) fps_graph.update_rate = 5 fps_graph.label = "%0.0f FPS" #create a timer to control window update frequency window_update_timer = timer(1 / 60.) def window_should_update(): return next(window_update_timer) # Event loop while not g_pool.quit.value: # Get an image from the grabber try: frame = cap.get_frame() except CameraCaptureError: logger.error("Capture from Camera Failed. Stopping.") break except EndofVideoFileError: logger.warning("Video File is done. Stopping") break #update performace graphs t = frame.timestamp dt, ts = t - ts, t try: fps_graph.add(1. / dt) except ZeroDivisionError: pass cpu_graph.update() ### RECORDING of Eye Video (on demand) ### # Setup variables and lists for recording if pipe_to_world.poll(): command, raw_mode = pipe_to_world.recv() if command is not None: record_path = command logger.info("Will save eye video to: %s" % record_path) timestamps_path = os.path.join(record_path, "eye%s_timestamps.npy" % eye_id) if raw_mode and hasattr(frame, 'jpeg_buffer'): video_path = os.path.join(record_path, "eye%s.mp4" % eye_id) writer = JPEG_Writer(video_path, cap.frame_rate) else: video_path = os.path.join(record_path, "eye%s.mkv" % eye_id) writer = CV_Writer(video_path, float(cap.frame_rate), cap.frame_size) timestamps = [] else: logger.info("Done recording.") writer.release() writer = None np.save(timestamps_path, np.asarray(timestamps)) del timestamps if writer: writer.write_video_frame(frame) timestamps.append(frame.timestamp) # pupil ellipse detection result = pupil_detector.detect( frame, user_roi=u_r, visualize=g_pool.display_mode == 'algorithm') result['id'] = eye_id # stream the result g_pool.pupil_queue.put(result) # GL drawing if window_should_update(): if not g_pool.iconified: glfwMakeContextCurrent(main_window) clear_gl_screen() # switch to work in normalized coordinate space if g_pool.display_mode == 'algorithm': g_pool.image_tex.update_from_ndarray(frame.img) elif g_pool.display_mode in ('camera_image', 'roi'): g_pool.image_tex.update_from_ndarray(frame.gray) else: pass make_coord_system_norm_based(g_pool.flip) g_pool.image_tex.draw() # switch to work in pixel space make_coord_system_pixel_based((frame.height, frame.width, 3), g_pool.flip) if result['confidence'] > 0: if result.has_key('axes'): pts = cv2.ellipse2Poly((int( result['center'][0]), int(result['center'][1])), (int(result['axes'][0] / 2), int(result['axes'][1] / 2)), int(result['angle']), 0, 360, 15) cygl_draw_polyline(pts, 1, cygl_rgba(1., 0, 0, .5)) cygl_draw_points([result['center']], size=20, color=cygl_rgba(1., 0., 0., .5), sharpness=1.) # render graphs graph.push_view() fps_graph.draw() cpu_graph.draw() graph.pop_view() # render GUI g_pool.gui.update() #render the ROI if g_pool.display_mode == 'roi': u_r.draw(g_pool.gui.scale) #update screen glfwSwapBuffers(main_window) glfwPollEvents() # END while running # in case eye recording was still runnnig: Save&close if writer: logger.info("Done recording eye.") writer = None np.save(timestamps_path, np.asarray(timestamps)) glfwRestoreWindow(main_window) #need to do this for windows os # save session persistent settings session_settings['gui_scale'] = g_pool.gui.scale session_settings['roi'] = u_r.get() session_settings['flip'] = g_pool.flip session_settings['display_mode'] = g_pool.display_mode session_settings['ui_config'] = g_pool.gui.configuration session_settings['capture_settings'] = g_pool.capture.settings session_settings['window_size'] = glfwGetWindowSize(main_window) session_settings['window_position'] = glfwGetWindowPos(main_window) session_settings['version'] = g_pool.version session_settings.close() pupil_detector.cleanup() g_pool.gui.terminate() glfwDestroyWindow(main_window) glfwTerminate() cap.close() #flushing queue in case world process did not exit gracefully while not g_pool.pupil_queue.empty(): g_pool.pupil_queue.get() g_pool.pupil_queue.close() logger.debug("Process done")
class Recorder(Plugin): """Capture Recorder""" 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, audio_src="No Audio", raw_jpeg=False, ): super(Recorder, self).__init__(g_pool) # update name if it was autogenerated. if session_name.startswith("20") and len(session_name) == 10: session_name = get_auto_name() if rec_dir: self.set_rec_dir(rec_dir) else: # lets make a rec dir next to the user dir base_dir = self.g_pool.user_dir.rsplit(os.path.sep, 1)[0] self.rec_dir = os.path.join(base_dir, "recordings") if not os.path.isdir(self.rec_dir): os.mkdir(self.rec_dir) self.raw_jpeg = raw_jpeg self.order = 0.9 self.record_eye = record_eye self.session_name = session_name self.audio_devices_dict = Audio_Input_Dict() if audio_src in self.audio_devices_dict.keys(): self.audio_src = audio_src else: self.audio_src = "No Audio" self.running = False self.menu = None self.button = None self.user_info = user_info self.show_info_menu = show_info_menu self.info_menu = None self.info_menu_conf = info_menu_conf self.height, self.width = self.g_pool.capture.frame_size def get_init_dict(self): d = {} d["record_eye"] = self.record_eye d["audio_src"] = self.audio_src d["session_name"] = self.session_name d["user_info"] = self.user_info d["info_menu_conf"] = self.info_menu_conf d["show_info_menu"] = self.show_info_menu d["rec_dir"] = self.rec_dir d["raw_jpeg"] = self.raw_jpeg return d def init_gui(self): self.menu = ui.Growing_Menu("Recorder") self.g_pool.sidebar.insert(3, self.menu) self.menu.append( ui.Info_Text( 'Pupil recordings are saved like this: "path_to_recordings/recording_session_name/nnn" where "nnn" is an increasing number to avoid overwrites. You can use "/" in your session name to create subdirectories.' ) ) self.menu.append( ui.Info_Text( 'Recordings are saved to "~/pupil_recordings". You can change the path here but note that invalid input will be ignored.' ) ) self.menu.append(ui.Text_Input("rec_dir", self, setter=self.set_rec_dir, label="Path to recordings")) self.menu.append( ui.Text_Input("session_name", self, setter=self.set_session_name, label="Recording session name") ) self.menu.append( ui.Switch("show_info_menu", self, on_val=True, off_val=False, label="Request additional user info") ) self.menu.append( ui.Selector( "raw_jpeg", self, selection=[True, False], labels=["bigger file, less CPU", "smaller file, more CPU"], label="compression", ) ) self.menu.append(ui.Info_Text("Recording the raw eye video is optional. We use it for debugging.")) self.menu.append(ui.Switch("record_eye", self, on_val=True, off_val=False, label="Record eye")) self.menu.append(ui.Selector("audio_src", self, selection=self.audio_devices_dict.keys())) self.button = ui.Thumb("running", self, setter=self.toggle, label="Record", hotkey="r") self.button.on_color[:] = (1, 0.0, 0.0, 0.8) self.g_pool.quickbar.insert(1, self.button) def deinit_gui(self): if self.menu: self.g_pool.sidebar.remove(self.menu) self.menu = None if self.button: self.g_pool.quickbar.remove(self.button) self.button = None def toggle(self, _=None): if self.running: self.stop() else: self.start() def get_rec_time_str(self): rec_time = gmtime(time() - self.start_time) return strftime("%H:%M:%S", rec_time) def start(self): self.timestamps = [] self.data = {"pupil_positions": [], "gaze_positions": []} self.pupil_pos_list = [] self.gaze_pos_list = [] self.frame_count = 0 self.running = True self.menu.read_only = True self.start_time = time() session = os.path.join(self.rec_dir, self.session_name) try: os.makedirs(session) logger.debug("Created new recordings session dir %s" % session) except: logger.debug("Recordings session dir %s already exists, using it." % session) # set up self incrementing folder within session folder counter = 0 while True: self.rec_path = os.path.join(session, "%03d/" % counter) try: os.mkdir(self.rec_path) logger.debug("Created new recording dir %s" % self.rec_path) break except: logger.debug("We dont want to overwrite data, incrementing counter & trying to make new data folder") counter += 1 self.meta_info_path = os.path.join(self.rec_path, "info.csv") with open(self.meta_info_path, "w") as f: f.write("Recording Name\t" + self.session_name + "\n") f.write("Start Date\t" + strftime("%d.%m.%Y", localtime(self.start_time)) + "\n") f.write("Start Time\t" + strftime("%H:%M:%S", localtime(self.start_time)) + "\n") if self.audio_src != "No Audio": audio_path = os.path.join(self.rec_path, "world.wav") self.audio_writer = Audio_Capture(self.audio_devices_dict[self.audio_src], audio_path) else: self.audio_writer = None if self.raw_jpeg and "uvc_capture" in str(self.g_pool.capture.__class__): self.video_path = os.path.join(self.rec_path, "world.mp4") self.writer = JPEG_Writer(self.video_path, int(self.g_pool.capture.frame_rate)) # elif 1: # self.writer = av_writer.AV_Writer(self.video_path) else: self.video_path = os.path.join(self.rec_path, "world.mkv") self.writer = CV_Writer( self.video_path, float(self.g_pool.capture.frame_rate), self.g_pool.capture.frame_size ) # positions path to eye process if self.record_eye: for tx in self.g_pool.eye_tx: tx.send((self.rec_path, self.raw_jpeg)) if self.show_info_menu: self.open_info_menu() def open_info_menu(self): self.info_menu = ui.Growing_Menu("additional Recording Info", size=(300, 300), pos=(300, 300)) self.info_menu.configuration = self.info_menu_conf def populate_info_menu(): self.info_menu.elements[:-2] = [] for name in self.user_info.iterkeys(): self.info_menu.insert(0, ui.Text_Input(name, self.user_info)) def set_user_info(new_string): self.user_info = new_string populate_info_menu() populate_info_menu() self.info_menu.append( ui.Info_Text( 'Use the *user info* field to add/remove additional fields and their values. The format must be a valid Python dictionary. For example -- {"key":"value"}. You can add as many fields as you require. Your custom fields will be saved for your next session.' ) ) self.info_menu.append(ui.Text_Input("user_info", self, setter=set_user_info, label="User info")) self.g_pool.gui.append(self.info_menu) def close_info_menu(self): if self.info_menu: self.info_menu_conf = self.info_menu.configuration self.g_pool.gui.remove(self.info_menu) self.info_menu = None def update(self, frame, events): if self.running: self.data["pupil_positions"] += events["pupil_positions"] self.data["gaze_positions"] += events["gaze_positions"] self.timestamps.append(frame.timestamp) self.writer.write_video_frame(frame) # self.writer.write_video_frame_yuv422(frame) self.frame_count += 1 # cv2.putText(frame.img, "Frame %s"%self.frame_count,(200,200), cv2.FONT_HERSHEY_SIMPLEX,1,(255,100,100)) for p in events["pupil_positions"]: pupil_pos = p["timestamp"], p["confidence"], p["id"], p["norm_pos"][0], p["norm_pos"][1], p["diameter"] self.pupil_pos_list.append(pupil_pos) for g in events.get("gaze_positions", []): gaze_pos = g["timestamp"], g["confidence"], g["norm_pos"][0], g["norm_pos"][1] self.gaze_pos_list.append(gaze_pos) self.button.status_text = self.get_rec_time_str() def stop(self): # explicit release of VideoWriter self.writer.release() self.writer = None if self.record_eye: for tx in self.g_pool.eye_tx: try: tx.send((None, None)) except: logger.warning("Could not stop eye-recording. Please report this bug!") save_object(self.data, os.path.join(self.rec_path, "pupil_data")) gaze_list_path = os.path.join(self.rec_path, "gaze_positions.npy") np.save(gaze_list_path, np.asarray(self.gaze_pos_list)) pupil_list_path = os.path.join(self.rec_path, "pupil_positions.npy") np.save(pupil_list_path, np.asarray(self.pupil_pos_list)) timestamps_path = os.path.join(self.rec_path, "world_timestamps.npy") ts = sanitize_timestamps(np.array(self.timestamps)) np.save(timestamps_path, ts) try: copy2( os.path.join(self.g_pool.user_dir, "surface_definitions"), os.path.join(self.rec_path, "surface_definitions"), ) except: logger.info("No surface_definitions data found. You may want this if you do marker tracking.") try: copy2( os.path.join(self.g_pool.user_dir, "cal_pt_cloud.npy"), os.path.join(self.rec_path, "cal_pt_cloud.npy") ) except: logger.warning("No calibration data found. Please calibrate first.") try: copy2( os.path.join(self.g_pool.user_dir, "camera_matrix.npy"), os.path.join(self.rec_path, "camera_matrix.npy"), ) copy2(os.path.join(self.g_pool.user_dir, "dist_coefs.npy"), os.path.join(self.rec_path, "dist_coefs.npy")) except: logger.info("No camera intrinsics found.") try: with open(self.meta_info_path, "a") as f: f.write("Duration Time\t" + self.get_rec_time_str() + "\n") if self.g_pool.binocular: f.write("Eye Mode\tbinocular\n") else: f.write("Eye Mode\tmonocular\n") f.write("Duration Time\t" + self.get_rec_time_str() + "\n") f.write("World Camera Frames\t" + str(self.frame_count) + "\n") f.write("World Camera Resolution\t" + str(self.width) + "x" + str(self.height) + "\n") f.write("Capture Software Version\t%s\n" % self.g_pool.version) if platform.system() == "Windows": username = os.environ["USERNAME"] sysname, nodename, release, version, machine, _ = platform.uname() else: username = os.getlogin() try: sysname, nodename, release, version, machine = os.uname() except: sysname, nodename, release, version, machine = sys.platform, None, None, None, None f.write("User\t" + username + "\n") f.write("Platform\t" + sysname + "\n") f.write("Machine\t" + nodename + "\n") f.write("Release\t" + release + "\n") f.write("Version\t" + version + "\n") except Exception: logger.exception("Could not save metadata. Please report this bug!") try: with open(os.path.join(self.rec_path, "user_info.csv"), "w") as f: for name, val in self.user_info.iteritems(): f.write("%s\t%s\n" % (name, val)) except Exception: logger.exception("Could not save userdata. Please report this bug!") self.close_info_menu() if self.audio_writer: self.audio_writer = None self.running = False self.menu.read_only = False self.button.status_text = "" def cleanup(self): """gets called when the plugin get terminated. either volunatily or forced. """ if self.running: self.stop() self.deinit_gui() def set_rec_dir(self, val): try: n_path = os.path.expanduser(val) logger.debug("Expanded user path.") except: n_path = val if not n_path: logger.warning("Please specify a path.") elif not os.path.isdir(n_path): logger.warning("This is not a valid path.") # elif not os.access(n_path, os.W_OK): elif not writable_dir(n_path): logger.warning("Do not have write access to '%s'." % n_path) else: self.rec_dir = n_path def set_session_name(self, val): if not val: self.session_name = get_auto_name() else: if "/" in val: logger.warning("You session name with create one or more subdirectories") self.session_name = val
class Recorder(Plugin): """Capture Recorder""" 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, audio_src = 'No Audio',raw_jpeg=False): super(Recorder, self).__init__(g_pool) #update name if it was autogenerated. if session_name.startswith('20') and len(session_name)==10: session_name = get_auto_name() if rec_dir: self.set_rec_dir(rec_dir) else: #lets make a rec dir next to the user dir base_dir = self.g_pool.user_dir.rsplit(os.path.sep,1)[0] self.rec_dir = os.path.join(base_dir,'recordings') if not os.path.isdir(self.rec_dir): os.mkdir(self.rec_dir) self.raw_jpeg = raw_jpeg self.order = .9 self.record_eye = record_eye self.session_name = session_name self.audio_devices_dict = Audio_Input_Dict() if audio_src in self.audio_devices_dict.keys(): self.audio_src = audio_src else: self.audio_src = 'No Audio' self.running = False self.menu = None self.button = None self.user_info = user_info self.show_info_menu = show_info_menu self.info_menu = None self.info_menu_conf = info_menu_conf self.height, self.width = self.g_pool.capture.frame_size def get_init_dict(self): d = {} d['record_eye'] = self.record_eye d['audio_src'] = self.audio_src d['session_name'] = self.session_name d['user_info'] = self.user_info d['info_menu_conf'] = self.info_menu_conf d['show_info_menu'] = self.show_info_menu d['rec_dir'] = self.rec_dir d['raw_jpeg'] = self.raw_jpeg return d def init_gui(self): self.menu = ui.Growing_Menu('Recorder') self.g_pool.sidebar.insert(3,self.menu) self.menu.append(ui.Info_Text('Pupil recordings are saved like this: "path_to_recordings/recording_session_name/nnn" where "nnn" is an increasing number to avoid overwrites. You can use "/" in your session name to create subdirectories.')) self.menu.append(ui.Info_Text('Recordings are saved to "~/pupil_recordings". You can change the path here but note that invalid input will be ignored.')) self.menu.append(ui.Text_Input('rec_dir',self,setter=self.set_rec_dir,label='Path to recordings')) self.menu.append(ui.Text_Input('session_name',self,setter=self.set_session_name,label='Recording session name')) self.menu.append(ui.Switch('show_info_menu',self,on_val=True,off_val=False,label='Request additional user info')) self.menu.append(ui.Selector('raw_jpeg',self,selection = [True,False], labels=["bigger file, less CPU", "smaller file, more CPU"],label='compression')) self.menu.append(ui.Info_Text('Recording the raw eye video is optional. We use it for debugging.')) self.menu.append(ui.Switch('record_eye',self,on_val=True,off_val=False,label='Record eye')) self.menu.append(ui.Selector('audio_src',self, selection=self.audio_devices_dict.keys())) self.button = ui.Thumb('running',self,setter=self.toggle,label='Record',hotkey='r') self.button.on_color[:] = (1,.0,.0,.8) self.g_pool.quickbar.insert(1,self.button) def deinit_gui(self): if self.menu: self.g_pool.sidebar.remove(self.menu) self.menu = None if self.button: self.g_pool.quickbar.remove(self.button) self.button = None def toggle(self, _=None): if self.running: self.stop() else: self.start() def get_rec_time_str(self): rec_time = gmtime(time()-self.start_time) return strftime("%H:%M:%S", rec_time) def start(self): self.timestamps = [] self.data = {'pupil_positions':[],'gaze_positions':[]} self.pupil_pos_list = [] self.gaze_pos_list = [] self.frame_count = 0 self.running = True self.menu.read_only = True self.start_time = time() session = os.path.join(self.rec_dir, self.session_name) try: os.makedirs(session) logger.debug("Created new recordings session dir %s"%session) except: logger.debug("Recordings session dir %s already exists, using it." %session) # set up self incrementing folder within session folder counter = 0 while True: self.rec_path = os.path.join(session, "%03d/" % counter) try: os.mkdir(self.rec_path) logger.debug("Created new recording dir %s"%self.rec_path) break except: logger.debug("We dont want to overwrite data, incrementing counter & trying to make new data folder") counter += 1 self.meta_info_path = os.path.join(self.rec_path, "info.csv") with open(self.meta_info_path, 'w') as f: f.write("Recording Name\t"+self.session_name+ "\n") f.write("Start Date\t"+ strftime("%d.%m.%Y", localtime(self.start_time))+ "\n") f.write("Start Time\t"+ strftime("%H:%M:%S", localtime(self.start_time))+ "\n") if self.audio_src != 'No Audio': audio_path = os.path.join(self.rec_path, "world.wav") self.audio_writer = Audio_Capture(self.audio_devices_dict[self.audio_src],audio_path) else: self.audio_writer = None if self.raw_jpeg and "uvc_capture" in str(self.g_pool.capture.__class__): self.video_path = os.path.join(self.rec_path, "world.mp4") self.writer = JPEG_Writer(self.video_path,int(self.g_pool.capture.frame_rate)) # elif 1: # self.writer = av_writer.AV_Writer(self.video_path) else: self.video_path = os.path.join(self.rec_path, "world.mkv") self.writer = CV_Writer(self.video_path, float(self.g_pool.capture.frame_rate), self.g_pool.capture.frame_size) # positions path to eye process if self.record_eye: for tx in self.g_pool.eye_tx: tx.send((self.rec_path,self.raw_jpeg)) if self.show_info_menu: self.open_info_menu() def open_info_menu(self): self.info_menu = ui.Growing_Menu('additional Recording Info',size=(300,300),pos=(300,300)) self.info_menu.configuration = self.info_menu_conf def populate_info_menu(): self.info_menu.elements[:-2] = [] for name in self.user_info.iterkeys(): self.info_menu.insert(0,ui.Text_Input(name,self.user_info)) def set_user_info(new_string): self.user_info = new_string populate_info_menu() populate_info_menu() self.info_menu.append(ui.Info_Text('Use the *user info* field to add/remove additional fields and their values. The format must be a valid Python dictionary. For example -- {"key":"value"}. You can add as many fields as you require. Your custom fields will be saved for your next session.')) self.info_menu.append(ui.Text_Input('user_info',self,setter=set_user_info,label="User info")) self.g_pool.gui.append(self.info_menu) def close_info_menu(self): if self.info_menu: self.info_menu_conf = self.info_menu.configuration self.g_pool.gui.remove(self.info_menu) self.info_menu = None def update(self,frame,events): if self.running: self.data['pupil_positions'] += events['pupil_positions'] self.data['gaze_positions'] += events['gaze_positions'] self.timestamps.append(frame.timestamp) self.writer.write_video_frame(frame) # self.writer.write_video_frame_yuv422(frame) self.frame_count += 1 # cv2.putText(frame.img, "Frame %s"%self.frame_count,(200,200), cv2.FONT_HERSHEY_SIMPLEX,1,(255,100,100)) for p in events['pupil_positions']: pupil_pos = p['timestamp'],p['confidence'],p['id'],p['norm_pos'][0],p['norm_pos'][1],p['diameter'] self.pupil_pos_list.append(pupil_pos) for g in events.get('gaze_positions',[]): gaze_pos = g['timestamp'],g['confidence'],g['norm_pos'][0],g['norm_pos'][1] self.gaze_pos_list.append(gaze_pos) self.button.status_text = self.get_rec_time_str() def stop(self): #explicit release of VideoWriter self.writer.release() self.writer = None if self.record_eye: for tx in self.g_pool.eye_tx: try: tx.send((None,None)) except: logger.warning("Could not stop eye-recording. Please report this bug!") save_object(self.data,os.path.join(self.rec_path, "pupil_data")) gaze_list_path = os.path.join(self.rec_path, "gaze_positions.npy") np.save(gaze_list_path,np.asarray(self.gaze_pos_list)) pupil_list_path = os.path.join(self.rec_path, "pupil_positions.npy") np.save(pupil_list_path,np.asarray(self.pupil_pos_list)) timestamps_path = os.path.join(self.rec_path, "world_timestamps.npy") ts = sanitize_timestamps(np.array(self.timestamps)) np.save(timestamps_path,ts) try: copy2(os.path.join(self.g_pool.user_dir,"surface_definitions"),os.path.join(self.rec_path,"surface_definitions")) except: logger.info("No surface_definitions data found. You may want this if you do marker tracking.") try: copy2(os.path.join(self.g_pool.user_dir,"cal_pt_cloud.npy"),os.path.join(self.rec_path,"cal_pt_cloud.npy")) except: logger.warning("No calibration data found. Please calibrate first.") try: copy2(os.path.join(self.g_pool.user_dir,"camera_matrix.npy"),os.path.join(self.rec_path,"camera_matrix.npy")) copy2(os.path.join(self.g_pool.user_dir,"dist_coefs.npy"),os.path.join(self.rec_path,"dist_coefs.npy")) except: logger.info("No camera intrinsics found.") try: with open(self.meta_info_path, 'a') as f: f.write("Duration Time\t"+ self.get_rec_time_str()+ "\n") if self.g_pool.binocular: f.write("Eye Mode\tbinocular\n") else: f.write("Eye Mode\tmonocular\n") f.write("Duration Time\t"+ self.get_rec_time_str()+ "\n") f.write("World Camera Frames\t"+ str(self.frame_count)+ "\n") f.write("World Camera Resolution\t"+ str(self.width)+"x"+str(self.height)+"\n") f.write("Capture Software Version\t%s\n"%self.g_pool.version) if platform.system() == "Windows": username = os.environ["USERNAME"] sysname, nodename, release, version, machine, _ = platform.uname() else: username = os.getlogin() try: sysname, nodename, release, version, machine = os.uname() except: sysname, nodename, release, version, machine = sys.platform,None,None,None,None f.write("User\t"+username+"\n") f.write("Platform\t"+sysname+"\n") f.write("Machine\t"+nodename+"\n") f.write("Release\t"+release+"\n") f.write("Version\t"+version+"\n") except Exception: logger.exception("Could not save metadata. Please report this bug!") try: with open(os.path.join(self.rec_path, "user_info.csv"), 'w') as f: for name,val in self.user_info.iteritems(): f.write("%s\t%s\n"%(name,val)) except Exception: logger.exception("Could not save userdata. Please report this bug!") self.close_info_menu() if self.audio_writer: self.audio_writer = None self.running = False self.menu.read_only = False self.button.status_text = '' def cleanup(self): """gets called when the plugin get terminated. either volunatily or forced. """ if self.running: self.stop() self.deinit_gui() def set_rec_dir(self,val): try: n_path = os.path.expanduser(val) logger.debug("Expanded user path.") except: n_path = val if not n_path: logger.warning("Please specify a path.") elif not os.path.isdir(n_path): logger.warning("This is not a valid path.") # elif not os.access(n_path, os.W_OK): elif not writable_dir(n_path): logger.warning("Do not have write access to '%s'."%n_path) else: self.rec_dir = n_path def set_session_name(self, val): if not val: self.session_name = get_auto_name() else: if '/' in val: logger.warning('You session name with create one or more subdirectories') self.session_name = val
class Recorder(Plugin): """Capture Recorder""" 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, audio_src = 'No Audio', raw_jpeg=False): super(Recorder, self).__init__(g_pool) self.states = ['start', 'want', 'pick'] self.all_qr = {'termo1' : 75, 'light1' : False, 'light2' : False, 'light3': False} self.this_state = 'start' self.start_state = time() self.qr_codes = {} self.gaze_x = -1 self.gaze_y = -1 self.avg_col = 0 self.choice = "" #update name if it was autogenerated. if session_name.startswith('20') and len(session_name)==10: session_name = get_auto_name() base_dir = self.g_pool.user_dir.rsplit(os.path.sep,1)[0] default_rec_dir = os.path.join(base_dir, 'recordings') if rec_dir and rec_dir != default_rec_dir and self.verify_path(rec_dir): self.rec_dir = rec_dir else: try: os.makedirs(default_rec_dir) except OSError as e: if 'File exists' in '%s'%e: pass else: logger.error("Could not create Rec dir") raise e else: logger.info('Created standard Rec dir at "%s"'%default_rec_dir) self.rec_dir = default_rec_dir # if rec_dir and rec_dir != default_rec_dir and self.verify_path(rec_dir): # self.rec_dir = rec_dir # else: # #lets make a rec dir next to the user dir # base_dir = self.g_pool.user_dir.rsplit(os.path.sep,1)[0] # self.rec_dir = os.path.join(base_dir,'recordings') # if not os.path.isdir(self.rec_dir): # os.mkdir(self.rec_dir) self.raw_jpeg = raw_jpeg self.order = .9 self.record_eye = record_eye self.session_name = session_name self.audio_devices_dict = Audio_Input_Dict() if audio_src in self.audio_devices_dict.keys(): self.audio_src = audio_src else: self.audio_src = 'No Audio' self.running = False self.menu = None self.button = None self.user_info = user_info self.show_info_menu = show_info_menu self.info_menu = None self.info_menu_conf = info_menu_conf self.height, self.width = self.g_pool.capture.frame_size def get_init_dict(self): d = {} d['record_eye'] = self.record_eye d['audio_src'] = self.audio_src d['session_name'] = self.session_name d['user_info'] = self.user_info d['info_menu_conf'] = self.info_menu_conf d['show_info_menu'] = self.show_info_menu d['rec_dir'] = self.rec_dir d['raw_jpeg'] = self.raw_jpeg # if self.menu: # d['menu_conf'] = self.menu.configuration # else: # d['menu_conf'] = self.menu_conf return d def init_gui(self): self.menu = ui.Growing_Menu('Recorder') # self.menu.configuration = self.menu_conf self.g_pool.sidebar.insert(3,self.menu) self.menu.append(ui.Info_Text('Pupil recordings are saved like this: "path_to_recordings/recording_session_name/nnn" where "nnn" is an increasing number to avoid overwrites. You can use "/" in your session name to create subdirectories.')) self.menu.append(ui.Info_Text('Recordings are saved to "~/pupil_recordings". You can change the path here but note that invalid input will be ignored.')) self.menu.append(ui.Text_Input('rec_dir',self,setter=self.set_rec_dir,label='Path to recordings')) self.menu.append(ui.Text_Input('session_name',self,setter=self.set_session_name,label='Recording session name')) self.menu.append(ui.Switch('show_info_menu',self,on_val=True,off_val=False,label='Request additional user info')) self.menu.append(ui.Selector('raw_jpeg',self,selection = [True,False], labels=["bigger file, less CPU", "smaller file, more CPU"],label='compression')) self.menu.append(ui.Info_Text('Recording the raw eye video is optional. We use it for debugging.')) self.menu.append(ui.Switch('record_eye',self,on_val=True,off_val=False,label='Record eye')) self.menu.append(ui.Selector('audio_src',self, selection=self.audio_devices_dict.keys())) self.button = ui.Thumb('running',self,setter=self.toggle,label='QR',hotkey='r') self.button.on_color[:] = (1,.0,.0,.8) self.g_pool.quickbar.insert(1,self.button) def deinit_gui(self): if self.menu: # self.menu_conf = self.menu.configuration self.g_pool.sidebar.remove(self.menu) self.menu = None if self.button: self.g_pool.quickbar.remove(self.button) self.button = None def toggle(self, _=None): if self.running: self.stop() else: self.start() def get_rec_time_str(self): rec_time = gmtime(time()-self.start_time) return strftime("%H:%M:%S", rec_time) def start(self): self.timestamps = [] # add self.data self.data = {'pupil_positions':[], 'gaze_positions': []} self.pupil_list = [] self.gaze_list = [] self.frame_count = 0 self.running = True self.menu.read_only = True self.start_time = time() self.start_state = time() session = os.path.join(self.rec_dir, self.session_name) try: os.makedirs(session) logger.debug("Created new recordings session dir %s"%session) except: logger.debug("Recordings session dir %s already exists, using it." %session) # set up self incrementing folder within session folder counter = 0 while True: self.rec_path = os.path.join(session, "%03d/" % counter) try: os.mkdir(self.rec_path) logger.debug("Created new recording dir %s"%self.rec_path) break except: logger.debug("We dont want to overwrite data, incrementing counter & trying to make new data folder") counter += 1 self.meta_info_path = os.path.join(self.rec_path, "info.csv") with open(self.meta_info_path, 'w') as f: f.write("Recording Name\t"+self.session_name+ "\n") f.write("Start Date\t"+ strftime("%d.%m.%Y", localtime(self.start_time))+ "\n") f.write("Start Time\t"+ strftime("%H:%M:%S", localtime(self.start_time))+ "\n") if self.audio_src != 'No Audio': audio_path = os.path.join(self.rec_path, "world.wav") self.audio_writer = Audio_Capture(self.audio_devices_dict[self.audio_src],audio_path) else: self.audio_writer = None if self.raw_jpeg and "uvc_capture" in str(self.g_pool.capture.__class__): self.video_path = os.path.join(self.rec_path, "world.mp4") self.writer = JPEG_Writer(self.video_path,int(self.g_pool.capture.frame_rate)) # elif 1: # self.writer = av_writer.AV_Writer(self.video_path) else: self.video_path = os.path.join(self.rec_path, "world.mkv") self.writer = CV_Writer(self.video_path, float(self.g_pool.capture.frame_rate), self.g_pool.capture.frame_size) # positions path to eye process if self.record_eye: for tx in self.g_pool.eye_tx: tx.send((self.rec_path,self.raw_jpeg)) if self.show_info_menu: self.open_info_menu() # self.video_path = os.path.join(self.rec_path, "world.mkv") # self.writer = cv2.VideoWriter(self.video_path, int(cv2.cv.CV_FOURCC(*'DIVX')), float(self.g_pool.capture.frame_rate), (1280,720)) # # positions path to eye process # if self.record_eye: # for tx in self.g_pool.eye_tx: # tx.send(self.rec_path) # if self.show_info_menu: # self.open_info_menu() ############### def open_info_menu(self): self.info_menu = ui.Growing_Menu('additional Recording Info',size=(300,300),pos=(300,300)) self.info_menu.configuration = self.info_menu_conf def populate_info_menu(): self.info_menu.elements[:-2] = [] for name in self.user_info.iterkeys(): self.info_menu.insert(0,ui.Text_Input(name,self.user_info)) def set_user_info(new_string): self.user_info = new_string populate_info_menu() populate_info_menu() self.info_menu.append(ui.Info_Text('Use the *user info* field to add/remove additional fields and their values. The format must be a valid Python dictionary. For example -- {"key":"value"}. You can add as many fields as you require. Your custom fields will be saved for your next session.')) self.info_menu.append(ui.Text_Input('user_info',self,setter=set_user_info,label="User info")) self.g_pool.gui.append(self.info_menu) def close_info_menu(self): if self.info_menu: self.info_menu_conf = self.info_menu.configuration self.g_pool.gui.remove(self.info_menu) self.info_menu = None def update(self,frame,events): if self.running: # update data self.data['pupil_positions'] += events['pupil_positions'] self.data['gaze_positions'] += events['gaze_positions'] #cv2.putText(frame.img, "Frame %s"%self.frame_count,(200,200), cv2.FONT_HERSHEY_SIMPLEX,1,(255,100,100) cv2.putText(frame.img, self.this_state,(200,100), cv2.FONT_HERSHEY_SIMPLEX,1,(255,100,100)) if self.this_state == 'pick': cv2.putText(frame.img, self.choice,(300,100), cv2.FONT_HERSHEY_SIMPLEX,1,(100,100,255)) QRs = qr_detect(frame) for p in events['pupil_positions']: pupil_pos = p['timestamp'],p['confidence'],p['id'],p['norm_pos'][0],p['norm_pos'][1],p['diameter'] self.pupil_list.append(pupil_pos) c = 0 avg_x = -1 avg_y = -1 for g in events.get('gaze_positions',[]): gaze_pos = g['timestamp'],g['confidence'],g['norm_pos'][0],g['norm_pos'][1] avg_x = (avg_x * c + g['norm_pos'][0]) / (c+1) avg_y = (avg_y * c + g['norm_pos'][0]) / (c+1) c = c + 1 self.gaze_list.append(gaze_pos) self.timestamps.append(frame.timestamp) avg_col = 0 self.stateT(frame, QRs, avg_x, avg_y, avg_col) self.writer.write_video_frame(frame) self.frame_count += 1 self.button.status_text = self.get_rec_time_str() def stop(self): #explicit release of VideoWriter self.writer.release() self.writer = None if self.record_eye: for tx in self.g_pool.eye_tx: try: tx.send(None) except: logger.warning("Could not stop eye-recording. Please report this bug!") save_object(self.data, os.path.join(self.rec_path, "pupil_data")) gaze_list_path = os.path.join(self.rec_path, "gaze_positions.npy") np.save(gaze_list_path,np.asarray(self.gaze_list)) pupil_list_path = os.path.join(self.rec_path, "pupil_positions.npy") np.save(pupil_list_path,np.asarray(self.pupil_list)) timestamps_path = os.path.join(self.rec_path, "world_timestamps.npy") ts = sanitize_timestamps(np.array(self.timestamps)) np.save(timestamps_path,ts) try: copy2(os.path.join(self.g_pool.user_dir,"surface_definitions"),os.path.join(self.rec_path,"surface_definitions")) except: logger.info("No surface_definitions data found. You may want this if you do marker tracking.") try: copy2(os.path.join(self.g_pool.user_dir,"cal_pt_cloud.npy"),os.path.join(self.rec_path,"cal_pt_cloud.npy")) except: logger.warning("No calibration data found. Please calibrate first.") try: copy2(os.path.join(self.g_pool.user_dir,"camera_matrix.npy"),os.path.join(self.rec_path,"camera_matrix.npy")) copy2(os.path.join(self.g_pool.user_dir,"dist_coefs.npy"),os.path.join(self.rec_path,"dist_coefs.npy")) except: logger.info("No camera intrinsics found.") try: with open(self.meta_info_path, 'a') as f: f.write("Duration Time\t"+ self.get_rec_time_str()+ "\n") if self.g_pool.binocular: f.write("Eye Mode\tbinocular\n") else: f.write("Eye Mode\tmonocular\n") f.write("Duration Time\t"+ self.get_rec_time_str()+ "\n") f.write("World Camera Frames\t"+ str(self.frame_count)+ "\n") f.write("World Camera Resolution\t"+ str(self.width)+"x"+str(self.height)+"\n") f.write("Capture Software Version\t%s\n"%self.g_pool.version) if platform.system() == "Windows": username = os.environ["USERNAME"] sysname, nodename, release, version, machine, _ = platform.uname() else: username = os.getlogin() try: sysname, nodename, release, version, machine = os.uname() except: sysname, nodename, release, version, machine = sys.platform,None,None,None,None f.write("User\t"+username+"\n") f.write("Platform\t"+sysname+"\n") f.write("Machine\t"+nodename+"\n") f.write("Release\t"+release+"\n") f.write("Version\t"+version+"\n") except Exception: logger.exception("Could not save metadata. Please report this bug!") try: with open(os.path.join(self.rec_path, "user_info.csv"), 'w') as f: for name,val in self.user_info.iteritems(): f.write("%s\t%s\n"%(name,val)) except Exception: logger.exception("Could not save userdata. Please report this bug!") self.close_info_menu() if self.audio_writer: self.audio_writer = None self.running = False self.menu.read_only = False self.button.status_text = '' def cleanup(self): """gets called when the plugin get terminated. either volunatily or forced. """ if self.running: self.stop() self.deinit_gui() def verify_path(self, val): try: n_path = os.path.expanduser(val) logger.debug("Expanded user path.") except: n_path = val if not n_path: logger.waring("Please specify a path.") return False elif not os.path.isdir(n_path): logger.warning("This is not a valid path.") return False elif not writable_dir(n_path): logger.warning("Do not have write access to '%s'." %n_path) return False else: return n_path def set_rec_dir(self,val): # try: # n_path = os.path.expanduser(val) # logger.debug("Expanded user path.") # except: # n_path = val # if not n_path: # logger.warning("Please specify a path.") # elif not os.path.isdir(n_path): # logger.warning("This is not a valid path.") # # elif not os.access(n_path, os.W_OK): # elif not writable_dir(n_path): # logger.warning("Do not have write access to '%s'."%n_path) # else: # self.rec_dir = n_path n_path = self.verify_path(val) if n_path: self.rec_dir = n_path def set_session_name(self, val): if not val: self.session_name = get_auto_name() else: if '/' in val: logger.warning('You session name with create one or more subdirectories') self.session_name = val def stateT(self, frame, QRs, agaze_x, agaze_y, avg_col): if time() - self.start_time < 1: self.gaze_x = agaze_x self.gaze_y = agaze_y if self.this_state == 'start': if time() - self.start_state < 2: self.qr_codes = {} return True if self.Nochange_gaze(agaze_x, agaze_y, avg_col): self.this_state = 'want' self.start_state = time() self.gaze_x = agaze_x self.gaze_y = agaze_y return True if self.this_state == 'want': if time() - self.start_state < 2: self.qr_codes.update(QRs) return True if self.Nochange_gaze(agaze_x, agaze_y, avg_col) and self.qr_codes: self.this_state = 'pick' self.choice = self.choose(frame) #print self.qr_codes, self.choice else: self.this_state = 'start' self.start_state = time() self.gaze_x = agaze_x self.gaze_y = agaze_y return True if self.this_state == 'pick': if time() - self.start_state < 4: return True if self.Nochange_gaze(agaze_x, agaze_y, avg_col): del self.qr_codes[self.choice] if not self.qr_codes: self.this_state = 'start' else: self.choice = self.choose(frame) #print self.choice else: print self.choice, " chosen" self.this_state = 'start' self.start_state = time() self.gaze_x = agaze_x self.gaze_y = agaze_y return True def Nochange_gaze(self, agaze_x, agaze_y, avg_col): #print self.gaze_x, self.gaze_y, agaze_x, agaze_y return (abs(self.gaze_x - agaze_x) < .1 and abs(self.gaze_y - agaze_y) < .1) def choose(self, frame): h = frame.img.shape[0] w = frame.img.shape[1] gaze_x = self.gaze_x gaze_y = self.gaze_y if (gaze_x < 0 or gaze_y < 0): gaze_x = .5 gaz_y = .5 k = self.qr_codes.keys() chosen = k[0] min_dist = 1000000 for name, mc in self.qr_codes.items(): dis = (mc[0] - gaze_x * w) * (mc[0] - gaze_x * w) + (mc[1] - gaze_y * h) * (mc[1] - gaze_y * h) if dis < min_dist: min_dist = dis chosen = name return chosen