def init_capture(self,uid,size,fps): if uid is not None: self.capture = uvc.Capture(uid) else: self.capture = Fake_Capture() self.uid = uid self.frame_size = size self.frame_rate = fps if 'C930e' in self.capture.name: logger.debug('Timestamp offset for c930 applied: -0.1sec') self.ts_offset = -0.1 else: self.ts_offset = 0.0 #UVC setting quirks: controls_dict = dict([(c.display_name,c) for c in self.capture.controls]) try: controls_dict['Auto Focus'].value = 0 except KeyError: pass if "Pupil Cam1" in self.capture.name or "USB2.0 Camera" in self.capture.name: self.capture.bandwidth_factor = 1.3 if "ID0" in self.capture.name or "ID1" in self.capture.name: try: # Auto Exposure Priority = 1 leads to reduced framerates under low light and corrupt timestamps. controls_dict['Auto Exposure Priority'].value = 1 except KeyError: pass try: controls_dict['Absolute Exposure Time'].value = 59 except KeyError: pass try: controls_dict['Auto Focus'].value = 0 except KeyError: pass
def init_capture(self,uid): self.uid = uid if uid is not None: self.capture = uvc.Capture(uid) else: self.capture = Fake_Capture() if 'C930e' in self.capture.name: logger.debug('Timestamp offset for c930 applied: -0.1sec') self.ts_offset = -0.1 else: self.ts_offset = 0.0 #UVC setting quirks: controls_dict = dict([(c.display_name,c) for c in self.capture.controls]) try: controls_dict['Auto Focus'].value = 0 except KeyError: pass if "Pupil Cam1" in self.capture.name or "USB2.0 Camera" in self.capture.name: self.capture.bandwidth_factor = 1.8 if "ID0" in self.capture.name or "ID1" in self.capture.name: self.capture.bandwidth_factor = 1.3 try: controls_dict['Auto Exposure Priority'].value = 1 except KeyError: pass try: controls_dict['Saturation'].value = 0 except KeyError: pass try: controls_dict['Absolute Exposure Time'].value = 63 except KeyError: pass try: controls_dict['Auto Focus'].value = 0 except KeyError: pass
class Camera_Capture(object): """ Camera Capture encapsulates the videoInput class """ deviceSettings = None captureSettings = None device = None stream = None menu = None sidebar = None width = 640 height = 480 preferred_fps = 30 fps_mediatype_map = None # list of tuples in form of: (fps, media_type_index) readSetting = None _frame = None _is_initialized = False _failed_inits = 0 @property def settings(self): settings = {} settings['name'] = self.name settings['frame_rate'] = self.frame_rate settings['frame_size'] = self.frame_size return settings @settings.setter def settings(self, settings): self.frame_size = settings['frame_size'] self.frame_rate = settings['frame_rate'] @property def name(self): if self.uid is not None: return str(self.device['name']) else: return "Fake Capture" @property def actual_width(self): if self.uid is not None: return self.stream.listMediaType[ self.deviceSettings.indexMediaType].width else: return self.width @property def actual_height(self): if self.uid is not None: return self.stream.listMediaType[ self.deviceSettings.indexMediaType].height else: return self.height @property def src_id(self): return self.uid def __init__(self, uid, size=(640, 480), fps=None, timebase=None): self.init_capture(uid, size, fps) def re_init_capture(self, uid, size=(640, 480), fps=None): if self.sidebar is None: self._close_device() self.init_capture(uid, size, fps) else: self.deinit_gui() self._close_device() self.init_capture(uid, size, fps) self.init_gui(self.sidebar) self.menu.collapsed = False def init_capture(self, uid, size=(640, 480), fps=None, timebase=None): self.uid = uid if uid is not None: # validate parameter UID devices = device_list( ) # TODO: read list only once (initially) to save runtime for device in devices: print if device['uid'] == uid: break if device['uid'] != uid: msg = ERR_INIT_FAIL + "UID of camera was not found." logger.error(msg) self.init_capture(None, size, fps, timebase) return # validate parameter SIZE if not len(size) == 2: msg = ERR_INIT_FAIL + "Parameter 'size' must have length 2." logger.error(msg) self.init_capture(None, size, fps, timebase) return # setting up device self.device = device self.deviceSettings = vi.DeviceSettings() self.deviceSettings.symbolicLink = self.device['uid'] self.deviceSettings.indexStream = 0 self.deviceSettings.indexMediaType = 0 self.captureSettings = vi.CaptureSettings() self.captureSettings.readMode = vi.ReadMode.SYNC self.captureSettings.videoFormat = vi.CaptureVideoFormat.RGB32 self.stream = self.device['handle'].listStream[ self.deviceSettings.indexStream] # set timebase if timebase == None: logger.debug("Capture will run with default system timebase") self.timebase = 0 else: logger.debug( "Capture will run with app wide adjustable timebase") self.timebase = timebase # set properties self.width = size[0] self.height = size[1] self.preferred_fps = fps self._initFrameRates() self._initMediaTypeId() # robust camera initialization self.context = _getVideoInputInstance() while True: res = self.context.setupDevice(self.deviceSettings, self.captureSettings) if res != vi.ResultCode.OK: self._failed_inits += 1 msg = ERR_INIT_FAIL + "Fall back to Fake Capture. Error code: %d" % ( res) if self._failed_inits < MAX_RETRY_INIT_CAMERA: logger.warning( "Retry initializing camera: {0}/{1}: ".format( self._failed_inits, MAX_RETRY_INIT_CAMERA) + msg) else: logger.error(msg) self._failed_inits = 0 self.init_capture(None, size, fps, timebase) return sleep(0.25) else: break # creating frame buffer and initializing capture settings frame = np.empty((self.actual_height * self.actual_width * 4), dtype=np.uint8) self.readSetting = vi.ReadSetting() self.readSetting.symbolicLink = self.deviceSettings.symbolicLink self.readSetting.setNumpyArray(frame) frame.shape = (self.actual_height, self.actual_width, -1) self._frame = frame logger.debug( "Successfully set up device: %s @ %dx%dpx %dfps (mediatype %d)" % (self.name, self.actual_height, self.actual_width, self.frame_rate, self.deviceSettings.indexMediaType)) else: self.device = Fake_Capture() self.width = size[0] self.height = size[1] self.preferred_fps = fps self._is_initialized = True self._failed_inits = 0 def get_frame(self): if self.uid is not None: res = self.context.readPixels(self.readSetting) if res == vi.ResultCode.READINGPIXELS_REJECTED_TIMEOUT: for n in range(MAX_RETRY_GRABBING_FRAMES): logger.warning( "Retry reading frame: {0}/{1}. Error code: {2}".format( n + 1, MAX_RETRY_GRABBING_FRAMES, res)) res = self.context.readPixels(self.readSetting) if res == vi.ResultCode.READINGPIXELS_DONE: break if res != vi.ResultCode.READINGPIXELS_DONE: msg = "Could not read frame. Fall back to Fake Capture. Error code: %d" % ( res) logger.error(msg) self.re_init_capture(None, self.frame_size, self.preferred_fps) return self.get_frame() frame = Frame(self.get_now() - self.timebase, self._frame) return frame else: return self.device.get_frame_robust() @property def frame_rate(self): if self.uid is not None: return self.stream.listMediaType[ self.deviceSettings.indexMediaType].MF_MT_FRAME_RATE else: return self.preferred_fps @frame_rate.setter def frame_rate(self, preferred_fps): self.re_init_capture(self.uid, (self.width, self.height), preferred_fps) @property def available_frame_rates(self): fps_list = [] for fps, _ in self.fps_mediatype_map: fps_list.append(fps) return fps_list @property def frame_size(self): return (self.actual_width, self.actual_height) @frame_size.setter def frame_size(self, size): self.re_init_capture(self.uid, size, self.preferred_fps) @property def available_frame_sizes(self): size_list = [] for size in self.size_mediatype_map: size_list.append(size) return size_list @property def jpeg_support(self): return False def get_now(self): return time() def get_timestamp(): return self.get_now() - self.timebase.value def init_gui(self, sidebar): def gui_init_cam(uid): logger.debug("selected new device: " + str(uid)) self.re_init_capture(uid, (self.width, self.height), self.preferred_fps) def gui_get_cam(): return self.uid def gui_get_frame_size(): return self.frame_size def gui_set_frame_size(new_size): self.frame_size = new_size def gui_get_frame_rate(): return self.frame_rate def gui_set_frame_rate(new_fps): self.frame_rate = new_fps #create the menu entry self.menu = ui.Growing_Menu(label='Camera Settings') self.menu.append(ui.Info_Text("Device: " + self.name)) # TODO: refresh button for capture list. Make properties that refresh on reading... cams = device_list() cam_names = ['Fake Capture'] + [str(c["name"]) for c in cams] cam_devices = [None] + [c["uid"] for c in cams] self.menu.append( ui.Selector('device', self, selection=cam_devices, labels=cam_names, label='Capture Device', getter=gui_get_cam, setter=gui_init_cam)) #hardware_ts_switch = ui.Switch('use_hw_ts',self,label='use hardware timestamps') #hardware_ts_switch.read_only = True #self.menu.append(hardware_ts_switch) #self.menu.append(ui.Selector('frame_size', selection=self.available_frame_sizes, label='Frame Size', getter=gui_get_frame_size, setter=gui_set_frame_size)) if self.uid is not None: self.menu.append( ui.Info_Text("Resolution: {0} x {1} pixels".format( self.actual_width, self.actual_height))) self.menu.append( ui.Selector('frame_rate', selection=self.available_frame_rates, label='Frame Rate', getter=gui_get_frame_rate, setter=gui_set_frame_rate)) # for control in self.controls: # c = None # ctl_name = control['name'] # # we use closures as setters and getters for each control element # def make_setter(control): # def fn(val): # self.capture.set_control(control['id'],val) # control['value'] = self.capture.get_control(control['id']) # return fn # def make_getter(control): # def fn(): # return control['value'] # return fn # set_ctl = make_setter(control) # get_ctl = make_getter(control) # #now we add controls # if control['type']=='bool': # c = ui.Switch(ctl_name,getter=get_ctl,setter=set_ctl) # elif control['type']=='int': # c = ui.Slider(ctl_name,getter=get_ctl,min=control['min'],max=control['max'], # step=control['step'], setter=set_ctl) # elif control['type']=="menu": # if control['menu'] is None: # selection = range(control['min'],control['max']+1,control['step']) # labels = selection # else: # selection = [value for name,value in control['menu'].iteritems()] # labels = [name for name,value in control['menu'].iteritems()] # c = ui.Selector(ctl_name,getter=get_ctl,selection=selection,labels = labels,setter=set_ctl) # else: # pass # if control['disabled']: # c.read_only = True # if ctl_name == 'Exposure, Auto Priority': # # the controll should always be off. we set it to 0 on init (see above) # c.read_only = True # if c is not None: # self.menu.append(c) # self.menu.append(ui.Button("refresh",gui_update_from_device)) # self.menu.append(ui.Button("load defaults",gui_load_defaults)) self.menu.collapsed = True self.sidebar = sidebar #add below geneal settings self.sidebar.insert(1, self.menu) def deinit_gui(self): if self.menu: self.sidebar.remove(self.menu) self.menu = None def close(self): self.deinit_gui() self._close_device() def _close_device(self): if self.uid is None: return if self._is_initialized: self._is_initialized = False res = self.context.closeDevice(self.deviceSettings) if res != vi.ResultCode.OK: msg = "Error while closing the capture device. Error code: %s" % res logger.error(msg) raise CameraCaptureError(msg) def _initFrameRates(self): """ Reads out possible frame-rates for a given resolution and stores result as internal frame-rate map. """ self.fps_mediatype_map = [] tmp_fps_values = [] self.size_mediatype_map = [] media_types = self.stream.listMediaType for mt, i in zip(media_types, range(len(media_types))): size_tuple = (mt.width, mt.height) # add distinct resolutions if not size_tuple in self.size_mediatype_map: self.size_mediatype_map.append(size_tuple) if mt.width == self.width and mt.height == self.height: # add distinct frame-rate options if not mt.MF_MT_FRAME_RATE in tmp_fps_values: tmp_fps_values.append(mt.MF_MT_FRAME_RATE) self.fps_mediatype_map.append((mt.MF_MT_FRAME_RATE, i)) if not self.fps_mediatype_map: msg = ERR_INIT_FAIL + "Capture device does not support resolution: %d x %d" % ( self.width, self.height) logger.error(msg) raise CameraCaptureError(msg) logger.debug( "found %d media types for given resolution: %s" % (len(self.fps_mediatype_map), str(self.fps_mediatype_map))) self.fps_mediatype_map.sort() def _initMediaTypeId(self): """ Selects device by setting media-type ID based on previously initialized frame-rate map. """ match = None # choose highest framerate if none is given if self.preferred_fps is None: match = self.fps_mediatype_map[-1] else: # choose best match (fps +/- .25 is ok) for fps, i in self.fps_mediatype_map: if abs(fps - self.preferred_fps) < .25: match = (fps, i) break # if none is found, choose highest framerate if match is None: logger.warn( "Capture device does not support preferred frame-rate %d" % (self.preferred_fps)) match = self.fps_mediatype_map[-1] self.deviceSettings.indexMediaType = match[1]
def init_capture(self, uid, size=(640, 480), fps=None, timebase=None): self.uid = uid if uid is not None: # validate parameter UID devices = device_list( ) # TODO: read list only once (initially) to save runtime for device in devices: print if device['uid'] == uid: break if device['uid'] != uid: msg = ERR_INIT_FAIL + "UID of camera was not found." logger.error(msg) self.init_capture(None, size, fps, timebase) return # validate parameter SIZE if not len(size) == 2: msg = ERR_INIT_FAIL + "Parameter 'size' must have length 2." logger.error(msg) self.init_capture(None, size, fps, timebase) return # setting up device self.device = device self.deviceSettings = vi.DeviceSettings() self.deviceSettings.symbolicLink = self.device['uid'] self.deviceSettings.indexStream = 0 self.deviceSettings.indexMediaType = 0 self.captureSettings = vi.CaptureSettings() self.captureSettings.readMode = vi.ReadMode.SYNC self.captureSettings.videoFormat = vi.CaptureVideoFormat.RGB32 self.stream = self.device['handle'].listStream[ self.deviceSettings.indexStream] # set timebase if timebase == None: logger.debug("Capture will run with default system timebase") self.timebase = 0 else: logger.debug( "Capture will run with app wide adjustable timebase") self.timebase = timebase # set properties self.width = size[0] self.height = size[1] self.preferred_fps = fps self._initFrameRates() self._initMediaTypeId() # robust camera initialization self.context = _getVideoInputInstance() while True: res = self.context.setupDevice(self.deviceSettings, self.captureSettings) if res != vi.ResultCode.OK: self._failed_inits += 1 msg = ERR_INIT_FAIL + "Fall back to Fake Capture. Error code: %d" % ( res) if self._failed_inits < MAX_RETRY_INIT_CAMERA: logger.warning( "Retry initializing camera: {0}/{1}: ".format( self._failed_inits, MAX_RETRY_INIT_CAMERA) + msg) else: logger.error(msg) self._failed_inits = 0 self.init_capture(None, size, fps, timebase) return sleep(0.25) else: break # creating frame buffer and initializing capture settings frame = np.empty((self.actual_height * self.actual_width * 4), dtype=np.uint8) self.readSetting = vi.ReadSetting() self.readSetting.symbolicLink = self.deviceSettings.symbolicLink self.readSetting.setNumpyArray(frame) frame.shape = (self.actual_height, self.actual_width, -1) self._frame = frame logger.debug( "Successfully set up device: %s @ %dx%dpx %dfps (mediatype %d)" % (self.name, self.actual_height, self.actual_width, self.frame_rate, self.deviceSettings.indexMediaType)) else: self.device = Fake_Capture() self.width = size[0] self.height = size[1] self.preferred_fps = fps self._is_initialized = True self._failed_inits = 0
class Camera_Capture(object): """ Camera Capture is a class that encapsualtes uvc.Capture: - adds UI elements - adds timestamping sanitization fns. """ def __init__(self, uid, timebase=None): if timebase == None: logger.debug("Capture will run with default system timebase") self.timebase = c_double(0) elif hasattr(timebase, 'value'): logger.debug("Capture will run with app wide adjustable timebase") self.timebase = timebase else: logger.error( "Invalid timebase variable type. Will use default system timebase" ) self.timebase = c_double(0) self.sidebar = None self.menu = None self.init_capture(uid) def re_init_capture(self, uid): current_size = self.capture.frame_size current_fps = self.capture.frame_rate self.capture = None #recreate the bar with new values menu_conf = self.menu.configuration self.deinit_gui() self.init_capture(uid) self.frame_size = current_size self.frame_rate = current_fps self.init_gui(self.sidebar) self.menu.configuration = menu_conf def init_capture(self, uid): self.uid = uid if uid is not None: self.capture = uvc.Capture(uid) else: self.capture = Fake_Capture() if 'C930e' in self.capture.name: logger.debug('Timestamp offset for c930 applied: -0.1sec') self.ts_offset = -0.1 else: self.ts_offset = 0.0 #UVC setting quirks: controls_dict = dict([(c.display_name, c) for c in self.capture.controls]) try: controls_dict['Auto Focus'].value = 0 except KeyError: pass if "Pupil Cam1" in self.capture.name or "USB2.0 Camera" in self.capture.name: self.capture.bandwidth_factor = 1.3 if "ID0" in self.capture.name or "ID1" in self.capture.name: try: controls_dict['Auto Exposure Priority'].value = 1 except KeyError: pass try: controls_dict['Absolute Exposure Time'].value = 59 except KeyError: pass try: controls_dict['Auto Focus'].value = 0 except KeyError: pass def get_frame(self): try: frame = self.capture.get_frame_robust() except: raise CameraCaptureError("Could not get frame from %s" % self.uid) timestamp = self.get_now() + self.ts_offset timestamp -= self.timebase.value frame.timestamp = timestamp return frame def get_now(self): return time() def get_timestamp(self): return self.get_now() - self.timebase.value @property def frame_rate(self): return self.capture.frame_rate @frame_rate.setter def frame_rate(self, new_rate): #closest match for rate rates = [abs(r - new_rate) for r in self.capture.frame_rates] best_rate_idx = rates.index(min(rates)) rate = self.capture.frame_rates[best_rate_idx] if rate != new_rate: logger.warning( "%sfps capture mode not available at (%s) on '%s'. Selected %sfps. " % (new_rate, self.capture.frame_size, self.capture.name, rate)) self.capture.frame_rate = rate @property def settings(self): settings = {} settings['name'] = self.capture.name settings['frame_rate'] = self.frame_rate settings['frame_size'] = self.frame_size settings['uvc_controls'] = {} for c in self.capture.controls: settings['uvc_controls'][c.display_name] = c.value return settings @settings.setter def settings(self, settings): self.frame_size = settings['frame_size'] self.frame_rate = settings['frame_rate'] for c in self.capture.controls: try: c.value = settings['uvc_controls'][c.display_name] except KeyError as e: logger.info('No UVC setting "%s" found from settings.' % c.display_name) @property def frame_size(self): return self.capture.frame_size @frame_size.setter def frame_size(self, new_size): #closest match for size sizes = [abs(r[0] - new_size[0]) for r in self.capture.frame_sizes] best_size_idx = sizes.index(min(sizes)) size = self.capture.frame_sizes[best_size_idx] if size != new_size: logger.warning( "%s resolution capture mode not available. Selected %s." % (new_size, size)) self.capture.frame_size = size @property def name(self): return self.capture.name @property def jpeg_support(self): if self.capture.__class__ is Fake_Capture: return False else: return True def init_gui(self, sidebar): #lets define some helper functions: def gui_load_defaults(): for c in self.capture.controls: try: c.value = c.def_val except: pass def set_size(new_size): self.frame_size = new_size menu_conf = self.menu.configuration self.deinit_gui() self.init_gui(self.sidebar) self.menu.configuration = menu_conf def gui_update_from_device(): for c in self.capture.controls: c.refresh() def gui_init_cam_by_uid(requested_id): if requested_id is None: self.re_init_capture(None) else: for cam in uvc.device_list(): if cam['uid'] == requested_id: if is_accessible(requested_id): self.re_init_capture(requested_id) else: logger.error( "The selected Camera is already in use or blocked." ) return logger.warning( "could not reinit capture, src_id not valid anymore") return #create the menu entry self.menu = ui.Growing_Menu(label='Camera Settings') cameras = uvc.device_list() camera_names = ['Fake Capture'] + [c['name'] for c in cameras] camera_ids = [None] + [c['uid'] for c in cameras] self.menu.append( ui.Selector('uid', self, selection=camera_ids, labels=camera_names, label='Capture Device', setter=gui_init_cam_by_uid)) sensor_control = ui.Growing_Menu(label='Sensor Settings') sensor_control.append( ui.Info_Text( "Do not change these during calibration or recording!")) sensor_control.collapsed = False image_processing = ui.Growing_Menu(label='Image Post Processing') image_processing.collapsed = True sensor_control.append( ui.Selector('frame_size', self, setter=set_size, selection=self.capture.frame_sizes, label='Resolution')) sensor_control.append( ui.Selector('frame_rate', self, selection=self.capture.frame_rates, label='Framerate')) for control in self.capture.controls: c = None ctl_name = control.display_name #now we add controls if control.d_type == bool: c = ui.Switch('value', control, label=ctl_name, on_val=control.max_val, off_val=control.min_val) elif control.d_type == int: c = ui.Slider('value', control, label=ctl_name, min=control.min_val, max=control.max_val, step=control.step) elif type(control.d_type) == dict: selection = [ value for name, value in control.d_type.iteritems() ] labels = [name for name, value in control.d_type.iteritems()] c = ui.Selector('value', control, label=ctl_name, selection=selection, labels=labels) else: pass # if control['disabled']: # c.read_only = True # if ctl_name == 'Exposure, Auto Priority': # # the controll should always be off. we set it to 0 on init (see above) # c.read_only = True if c is not None: if control.unit == 'processing_unit': image_processing.append(c) else: sensor_control.append(c) self.menu.append(sensor_control) if image_processing.elements: self.menu.append(image_processing) self.menu.append(ui.Button("refresh", gui_update_from_device)) self.menu.append(ui.Button("load defaults", gui_load_defaults)) self.sidebar = sidebar #add below geneal settings self.sidebar.insert(1, self.menu) def deinit_gui(self): if self.menu: self.sidebar.remove(self.menu) self.menu = None def close(self): self.deinit_gui() # self.capture.close() del self.capture logger.info("Capture released")
class Camera_Capture(object): """ Camera Capture is a class that encapsualtes uvc.Capture: - adds UI elements - adds timestamping sanitization fns. """ def __init__(self,uid,timebase=None): if timebase == None: logger.debug("Capture will run with default system timebase") self.timebase = c_double(0) elif hasattr(timebase,'value'): logger.debug("Capture will run with app wide adjustable timebase") self.timebase = timebase else: logger.error("Invalid timebase variable type. Will use default system timebase") self.timebase = c_double(0) self.sidebar = None self.menu = None self.init_capture(uid) def re_init_capture(self,uid): current_size = self.capture.frame_size current_fps = self.capture.frame_rate self.capture = None if self.menu: #recreate the bar with new values menu_conf = self.menu.configuration else: menu_conf = None self.deinit_gui() self.init_capture(uid) self.frame_size = current_size self.frame_rate = current_fps if menu_conf: self.init_gui(self.sidebar) self.menu.configuration = menu_conf def _re_init_capture_by_name(self,name): for x in range(4): devices = device_list() for d in devices: if d['name'] == name: logger.info("Found device.%s."%name) self.re_init_capture(d['uid']) return logger.warning('Could not find Camera %s during re initilization.'%name) sleep(1.5) raise CameraCaptureError('Could not find Camera %s during re initilization.'%name) def init_capture(self,uid): self.uid = uid if uid is None: self.capture = Fake_Capture() else: self.capture = uvc.Capture(uid) if 'C930e' in self.capture.name: logger.debug('Timestamp offset for c930 applied: -0.1sec') self.ts_offset = -0.1 else: self.ts_offset = 0.0 #UVC setting quirks: controls_dict = dict([(c.display_name,c) for c in self.capture.controls]) try: controls_dict['Auto Focus'].value = 0 except KeyError: pass if "Pupil Cam1" in self.capture.name or "USB2.0 Camera" in self.capture.name: self.capture.bandwidth_factor = 1.8 if "ID0" in self.capture.name or "ID1" in self.capture.name: self.capture.bandwidth_factor = 1.3 try: controls_dict['Auto Exposure Priority'].value = 0 except KeyError: pass try: # print controls_dict['Auto Exposure Mode'].value controls_dict['Auto Exposure Mode'].value = 1 except KeyError as e: pass try: controls_dict['Saturation'].value = 0 except KeyError: pass try: controls_dict['Absolute Exposure Time'].value = 63 except KeyError: pass try: controls_dict['Backlight Compensation'].value = 2 except KeyError: pass try: controls_dict['Gamma'].value = 100 except KeyError: pass else: self.capture.bandwidth_factor = 1.8 try: controls_dict['Auto Exposure Priority'].value = 1 except KeyError: pass if "C525" in self.capture.name or "B525" in self.capture.name or "C920" in self.capture.name: self.capture.bandwidth_factor = 4.0 try: controls_dict['Auto Focus'].value = 0 except KeyError: pass def get_frame(self): try: frame = self.capture.get_frame_robust() except uvc.CaptureError as e: try: self._re_init_capture_by_name(self.capture.name) frame = self.capture.get_frame_robust() except uvc.CaptureError as e: raise CameraCaptureError("Could not get frame from %s"%self.uid) timestamp = self.get_now()+self.ts_offset timestamp -= self.timebase.value frame.timestamp = timestamp return frame def get_now(self): return uvc.get_time_monotonic() def get_timestamp(self): return self.get_now()-self.timebase.value @property def frame_rate(self): return self.capture.frame_rate @frame_rate.setter def frame_rate(self,new_rate): #closest match for rate rates = [ abs(r-new_rate) for r in self.capture.frame_rates ] best_rate_idx = rates.index(min(rates)) rate = self.capture.frame_rates[best_rate_idx] if rate != new_rate: logger.warning("%sfps capture mode not available at (%s) on '%s'. Selected %sfps. "%(new_rate,self.capture.frame_size,self.capture.name,rate)) self.capture.frame_rate = rate @property def settings(self): settings = {} settings['name'] = self.capture.name settings['frame_rate'] = self.frame_rate settings['frame_size'] = self.frame_size settings['uvc_controls'] = {} for c in self.capture.controls: settings['uvc_controls'][c.display_name] = c.value return settings @settings.setter def settings(self,settings): self.frame_size = settings['frame_size'] self.frame_rate = settings['frame_rate'] for c in self.capture.controls: try: c.value = settings['uvc_controls'][c.display_name] except KeyError as e: logger.debug('No UVC setting "%s" found from settings.'%c.display_name) @property def frame_size(self): return self.capture.frame_size @frame_size.setter def frame_size(self,new_size): #closest match for size sizes = [ abs(r[0]-new_size[0]) for r in self.capture.frame_sizes ] best_size_idx = sizes.index(min(sizes)) size = self.capture.frame_sizes[best_size_idx] if size != new_size: logger.warning("%s resolution capture mode not available. Selected %s."%(new_size,size)) self.capture.frame_size = size if hasattr(self,'on_frame_size_change'): self.on_frame_size_change(size) @property def name(self): return self.capture.name @property def jpeg_support(self): if self.capture.__class__ is Fake_Capture: return False else: return True def init_gui(self,sidebar): #lets define some helper functions: def gui_load_defaults(): for c in self.capture.controls: try: c.value = c.def_val except: pass def set_size(new_size): self.frame_size = new_size menu_conf = self.menu.configuration self.deinit_gui() self.init_gui(self.sidebar) self.menu.configuration = menu_conf def gui_update_from_device(): for c in self.capture.controls: c.refresh() def gui_init_cam_by_uid(requested_id): if requested_id is None: self.re_init_capture(None) else: for cam in uvc.device_list(): if cam['uid'] == requested_id: if is_accessible(requested_id): self.re_init_capture(requested_id) else: logger.error("The selected Camera is already in use or blocked.") return logger.warning("could not reinit capture, src_id not valid anymore") return #create the menu entry self.menu = ui.Growing_Menu(label='Camera Settings') cameras = uvc.device_list() camera_names = ['Fake Capture']+[c['name'] for c in cameras] camera_ids = [None]+[c['uid'] for c in cameras] self.menu.append(ui.Selector('uid',self,selection=camera_ids,labels=camera_names,label='Capture device', setter=gui_init_cam_by_uid) ) sensor_control = ui.Growing_Menu(label='Sensor Settings') sensor_control.append(ui.Info_Text("Do not change these during calibration or recording!")) sensor_control.collapsed=False image_processing = ui.Growing_Menu(label='Image Post Processing') image_processing.collapsed=True sensor_control.append(ui.Selector('frame_size',self,setter=set_size, selection=self.capture.frame_sizes,label='Resolution' ) ) sensor_control.append(ui.Selector('frame_rate',self, selection=self.capture.frame_rates,label='Frame rate' ) ) for control in self.capture.controls: c = None ctl_name = control.display_name #now we add controls if control.d_type == bool : c = ui.Switch('value',control,label=ctl_name, on_val=control.max_val, off_val=control.min_val) elif control.d_type == int: c = ui.Slider('value',control,label=ctl_name,min=control.min_val,max=control.max_val,step=control.step) elif type(control.d_type) == dict: selection = [value for name,value in control.d_type.iteritems()] labels = [name for name,value in control.d_type.iteritems()] c = ui.Selector('value',control, label = ctl_name, selection=selection,labels = labels) else: pass # if control['disabled']: # c.read_only = True # if ctl_name == 'Exposure, Auto Priority': # # the controll should always be off. we set it to 0 on init (see above) # c.read_only = True if c is not None: if control.unit == 'processing_unit': image_processing.append(c) else: sensor_control.append(c) self.menu.append(sensor_control) if image_processing.elements: self.menu.append(image_processing) self.menu.append(ui.Button("refresh",gui_update_from_device)) self.menu.append(ui.Button("load defaults",gui_load_defaults)) self.sidebar = sidebar #add below geneal settings self.sidebar.insert(1,self.menu) def deinit_gui(self): if self.menu: self.sidebar.remove(self.menu) self.menu = None def close(self): self.deinit_gui() # self.capture.close() del self.capture
class Camera_Capture(object): """ Camera Capture encapsulates the videoInput class """ deviceSettings = None captureSettings = None device = None stream = None menu = None sidebar = None width = 640 height = 480 preferred_fps = 30 fps_mediatype_map = None # list of tuples in form of: (fps, media_type_index) readSetting = None _frame = None _is_initialized = False _failed_inits = 0 @property def settings(self): settings = {} settings['name'] = self.name settings['frame_rate'] = self.frame_rate settings['frame_size'] = self.frame_size return settings @settings.setter def settings(self,settings): self.frame_size = settings['frame_size'] self.frame_rate = settings['frame_rate'] @property def name(self): if self.uid is not None: return str(self.device['name']) else: return "Fake Capture" @property def actual_width(self): if self.uid is not None: return self.stream.listMediaType[self.deviceSettings.indexMediaType].width else: return self.width @property def actual_height(self): if self.uid is not None: return self.stream.listMediaType[self.deviceSettings.indexMediaType].height else: return self.height @property def src_id(self): return self.uid def __init__(self, uid, size=(640,480), fps=None, timebase=None): self.init_capture(uid, size, fps) def re_init_capture(self, uid, size=(640,480), fps=None): if self.sidebar is None: self._close_device() self.init_capture(uid, size, fps) else: self.deinit_gui() self._close_device() self.init_capture(uid, size, fps) self.init_gui(self.sidebar) self.menu.collapsed = False def init_capture(self, uid, size=(640,480), fps=None, timebase=None): self.uid = uid if uid is not None: # validate parameter UID devices = device_list() # TODO: read list only once (initially) to save runtime for device in devices: print if device['uid'] == uid: break if device['uid'] != uid: msg = ERR_INIT_FAIL + "UID of camera was not found." logger.error(msg) self.init_capture(None, size, fps, timebase) return # validate parameter SIZE if not len(size) == 2: msg = ERR_INIT_FAIL + "Parameter 'size' must have length 2." logger.error(msg) self.init_capture(None, size, fps, timebase) return # setting up device self.device = device self.deviceSettings = vi.DeviceSettings() self.deviceSettings.symbolicLink = self.device['uid'] self.deviceSettings.indexStream = 0 self.deviceSettings.indexMediaType = 0 self.captureSettings = vi.CaptureSettings() self.captureSettings.readMode = vi.ReadMode.SYNC self.captureSettings.videoFormat = vi.CaptureVideoFormat.RGB32 self.stream = self.device['handle'].listStream[self.deviceSettings.indexStream] # set timebase if timebase == None: logger.debug("Capture will run with default system timebase") self.timebase = 0 else: logger.debug("Capture will run with app wide adjustable timebase") self.timebase = timebase # set properties self.width = size[0] self.height = size[1] self.preferred_fps = fps self._initFrameRates() self._initMediaTypeId() # robust camera initialization self.context = _getVideoInputInstance() while True: res = self.context.setupDevice(self.deviceSettings, self.captureSettings) if res != vi.ResultCode.OK: self._failed_inits += 1 msg = ERR_INIT_FAIL + "Fall back to Fake Capture. Error code: %d" %(res) if self._failed_inits < MAX_RETRY_INIT_CAMERA: logger.warning("Retry initializing camera: {0}/{1}: ".format(self._failed_inits, MAX_RETRY_INIT_CAMERA) + msg) else: logger.error(msg) self._failed_inits = 0 self.init_capture(None, size, fps, timebase) return sleep(0.25) else: break # creating frame buffer and initializing capture settings frame = np.empty((self.actual_height * self.actual_width * 4), dtype=np.uint8) self.readSetting = vi.ReadSetting() self.readSetting.symbolicLink = self.deviceSettings.symbolicLink self.readSetting.setNumpyArray(frame) frame.shape = (self.actual_height, self.actual_width, -1) self._frame = frame logger.debug("Successfully set up device: %s @ %dx%dpx %dfps (mediatype %d)" %(self.name, self.actual_height, self.actual_width, self.frame_rate, self.deviceSettings.indexMediaType)) else: self.device = Fake_Capture() self.width = size[0] self.height = size[1] self.preferred_fps = fps self._is_initialized = True self._failed_inits = 0 def get_frame(self): if self.uid is not None: res = self.context.readPixels(self.readSetting) if res == vi.ResultCode.READINGPIXELS_REJECTED_TIMEOUT: for n in range(MAX_RETRY_GRABBING_FRAMES): logger.warning("Retry reading frame: {0}/{1}. Error code: {2}".format(n+1, MAX_RETRY_GRABBING_FRAMES, res)) res = self.context.readPixels(self.readSetting) if res == vi.ResultCode.READINGPIXELS_DONE: break if res != vi.ResultCode.READINGPIXELS_DONE: msg = "Could not read frame. Fall back to Fake Capture. Error code: %d" %(res) logger.error(msg) self.re_init_capture(None, self.frame_size, self.preferred_fps) return self.get_frame() frame = Frame(self.get_now() - self.timebase, self._frame) return frame else: return self.device.get_frame_robust() @property def frame_rate(self): if self.uid is not None: return self.stream.listMediaType[self.deviceSettings.indexMediaType].MF_MT_FRAME_RATE else: return self.preferred_fps @frame_rate.setter def frame_rate(self, preferred_fps): self.re_init_capture(self.uid, (self.width, self.height), preferred_fps) @property def available_frame_rates(self): fps_list = [] for fps, _ in self.fps_mediatype_map: fps_list.append(fps) return fps_list @property def frame_size(self): return (self.actual_width, self.actual_height) @frame_size.setter def frame_size(self, size): self.re_init_capture( self.uid, size, self.preferred_fps) @property def available_frame_sizes(self): size_list = [] for size in self.size_mediatype_map: size_list.append(size) return size_list @property def jpeg_support(self): return False def get_now(self): return time() def get_timestamp(): return self.get_now()-self.timebase.value def init_gui(self,sidebar): def gui_init_cam(uid): logger.debug("selected new device: " + str(uid)) self.re_init_capture(uid, (self.width, self.height), self.preferred_fps) def gui_get_cam(): return self.uid def gui_get_frame_size(): return self.frame_size def gui_set_frame_size(new_size): self.frame_size = new_size def gui_get_frame_rate(): return self.frame_rate def gui_set_frame_rate(new_fps): self.frame_rate = new_fps #create the menu entry self.menu = ui.Growing_Menu(label='Camera Settings') self.menu.append(ui.Info_Text("Device: " + self.name)) # TODO: refresh button for capture list. Make properties that refresh on reading... cams = device_list() cam_names = ['Fake Capture'] + [str(c["name"]) for c in cams] cam_devices = [None] + [c["uid"] for c in cams] self.menu.append(ui.Selector('device',self,selection=cam_devices,labels=cam_names,label='Capture Device', getter=gui_get_cam, setter=gui_init_cam)) #hardware_ts_switch = ui.Switch('use_hw_ts',self,label='use hardware timestamps') #hardware_ts_switch.read_only = True #self.menu.append(hardware_ts_switch) #self.menu.append(ui.Selector('frame_size', selection=self.available_frame_sizes, label='Frame Size', getter=gui_get_frame_size, setter=gui_set_frame_size)) if self.uid is not None: self.menu.append(ui.Info_Text("Resolution: {0} x {1} pixels".format(self.actual_width, self.actual_height))) self.menu.append(ui.Selector('frame_rate', selection=self.available_frame_rates, label='Frame Rate', getter=gui_get_frame_rate, setter=gui_set_frame_rate)) # for control in self.controls: # c = None # ctl_name = control['name'] # # we use closures as setters and getters for each control element # def make_setter(control): # def fn(val): # self.capture.set_control(control['id'],val) # control['value'] = self.capture.get_control(control['id']) # return fn # def make_getter(control): # def fn(): # return control['value'] # return fn # set_ctl = make_setter(control) # get_ctl = make_getter(control) # #now we add controls # if control['type']=='bool': # c = ui.Switch(ctl_name,getter=get_ctl,setter=set_ctl) # elif control['type']=='int': # c = ui.Slider(ctl_name,getter=get_ctl,min=control['min'],max=control['max'], # step=control['step'], setter=set_ctl) # elif control['type']=="menu": # if control['menu'] is None: # selection = range(control['min'],control['max']+1,control['step']) # labels = selection # else: # selection = [value for name,value in control['menu'].iteritems()] # labels = [name for name,value in control['menu'].iteritems()] # c = ui.Selector(ctl_name,getter=get_ctl,selection=selection,labels = labels,setter=set_ctl) # else: # pass # if control['disabled']: # c.read_only = True # if ctl_name == 'Exposure, Auto Priority': # # the controll should always be off. we set it to 0 on init (see above) # c.read_only = True # if c is not None: # self.menu.append(c) # self.menu.append(ui.Button("refresh",gui_update_from_device)) # self.menu.append(ui.Button("load defaults",gui_load_defaults)) self.menu.collapsed = True self.sidebar = sidebar #add below geneal settings self.sidebar.insert(1,self.menu) def deinit_gui(self): if self.menu: self.sidebar.remove(self.menu) self.menu = None def close(self): self.deinit_gui() self._close_device() def _close_device(self): if self.uid is None: return if self._is_initialized: self._is_initialized = False res = self.context.closeDevice(self.deviceSettings) if res != vi.ResultCode.OK: msg = "Error while closing the capture device. Error code: %s" %res logger.error(msg) raise CameraCaptureError(msg) def _initFrameRates(self): """ Reads out possible frame-rates for a given resolution and stores result as internal frame-rate map. """ self.fps_mediatype_map = [] tmp_fps_values = [] self.size_mediatype_map = [] media_types = self.stream.listMediaType for mt, i in zip(media_types, range(len(media_types))): size_tuple = (mt.width, mt.height) # add distinct resolutions if not size_tuple in self.size_mediatype_map: self.size_mediatype_map.append(size_tuple) if mt.width == self.width and mt.height == self.height: # add distinct frame-rate options if not mt.MF_MT_FRAME_RATE in tmp_fps_values: tmp_fps_values.append(mt.MF_MT_FRAME_RATE) self.fps_mediatype_map.append((mt.MF_MT_FRAME_RATE, i)) if not self.fps_mediatype_map: msg = ERR_INIT_FAIL + "Capture device does not support resolution: %d x %d"% (self.width, self.height) logger.error(msg) raise CameraCaptureError(msg) logger.debug("found %d media types for given resolution: %s" %(len(self.fps_mediatype_map), str(self.fps_mediatype_map))) self.fps_mediatype_map.sort() def _initMediaTypeId(self): """ Selects device by setting media-type ID based on previously initialized frame-rate map. """ match = None # choose highest framerate if none is given if self.preferred_fps is None: match = self.fps_mediatype_map[-1] else: # choose best match (fps +/- .25 is ok) for fps, i in self.fps_mediatype_map: if abs(fps-self.preferred_fps) < .25: match = (fps, i) break # if none is found, choose highest framerate if match is None: logger.warn("Capture device does not support preferred frame-rate %d"% (self.preferred_fps)) match = self.fps_mediatype_map[-1] self.deviceSettings.indexMediaType = match[1]
def init_capture(self, uid, size=(640,480), fps=None, timebase=None): self.uid = uid if uid is not None: # validate parameter UID devices = device_list() # TODO: read list only once (initially) to save runtime for device in devices: print if device['uid'] == uid: break if device['uid'] != uid: msg = ERR_INIT_FAIL + "UID of camera was not found." logger.error(msg) self.init_capture(None, size, fps, timebase) return # validate parameter SIZE if not len(size) == 2: msg = ERR_INIT_FAIL + "Parameter 'size' must have length 2." logger.error(msg) self.init_capture(None, size, fps, timebase) return # setting up device self.device = device self.deviceSettings = vi.DeviceSettings() self.deviceSettings.symbolicLink = self.device['uid'] self.deviceSettings.indexStream = 0 self.deviceSettings.indexMediaType = 0 self.captureSettings = vi.CaptureSettings() self.captureSettings.readMode = vi.ReadMode.SYNC self.captureSettings.videoFormat = vi.CaptureVideoFormat.RGB32 self.stream = self.device['handle'].listStream[self.deviceSettings.indexStream] # set timebase if timebase == None: logger.debug("Capture will run with default system timebase") self.timebase = 0 else: logger.debug("Capture will run with app wide adjustable timebase") self.timebase = timebase # set properties self.width = size[0] self.height = size[1] self.preferred_fps = fps self._initFrameRates() self._initMediaTypeId() # robust camera initialization self.context = _getVideoInputInstance() while True: res = self.context.setupDevice(self.deviceSettings, self.captureSettings) if res != vi.ResultCode.OK: self._failed_inits += 1 msg = ERR_INIT_FAIL + "Fall back to Fake Capture. Error code: %d" %(res) if self._failed_inits < MAX_RETRY_INIT_CAMERA: logger.warning("Retry initializing camera: {0}/{1}: ".format(self._failed_inits, MAX_RETRY_INIT_CAMERA) + msg) else: logger.error(msg) self._failed_inits = 0 self.init_capture(None, size, fps, timebase) return sleep(0.25) else: break # creating frame buffer and initializing capture settings frame = np.empty((self.actual_height * self.actual_width * 4), dtype=np.uint8) self.readSetting = vi.ReadSetting() self.readSetting.symbolicLink = self.deviceSettings.symbolicLink self.readSetting.setNumpyArray(frame) frame.shape = (self.actual_height, self.actual_width, -1) self._frame = frame logger.debug("Successfully set up device: %s @ %dx%dpx %dfps (mediatype %d)" %(self.name, self.actual_height, self.actual_width, self.frame_rate, self.deviceSettings.indexMediaType)) else: self.device = Fake_Capture() self.width = size[0] self.height = size[1] self.preferred_fps = fps self._is_initialized = True self._failed_inits = 0
class Camera_Capture(object): """ Camera Capture is a class that encapsualtes uvc.Capture: - adds UI elements - adds timestamping sanitization fns. """ def __init__(self, uid, timebase=None): if timebase == None: logger.debug("Capture will run with default system timebase") self.timebase = c_double(0) elif hasattr(timebase, "value"): logger.debug("Capture will run with app wide adjustable timebase") self.timebase = timebase else: logger.error("Invalid timebase variable type. Will use default system timebase") self.timebase = c_double(0) self.sidebar = None self.menu = None self.init_capture(uid, size=(1280, 720), fps=30) def re_init_capture(self, uid): current_size = self.capture.frame_size current_fps = self.capture.frame_rate self.capture = None # recreate the bar with new values menu_conf = self.menu.configuration self.deinit_gui() self.init_capture(uid, current_size, current_fps) self.init_gui(self.sidebar) self.menu.configuration = menu_conf def init_capture(self, uid, size, fps): if uid is not None: self.capture = uvc.Capture(uid) else: self.capture = Fake_Capture() self.uid = uid self.frame_size = size self.frame_rate = fps if "C930e" in self.capture.name: logger.debug("Timestamp offset for c930 applied: -0.1sec") self.ts_offset = -0.1 else: self.ts_offset = 0.0 # UVC setting quirks: controls_dict = dict([(c.display_name, c) for c in self.capture.controls]) try: controls_dict["Auto Focus"].value = 0 except KeyError: pass if "Pupil Cam1" in self.capture.name or "USB2.0 Camera" in self.capture.name: self.capture.bandwidth_factor = 1.3 if "ID0" in self.capture.name or "ID1" in self.capture.name: try: # Auto Exposure Priority = 1 leads to reduced framerates under low light and corrupt timestamps. controls_dict["Auto Exposure Priority"].value = 1 except KeyError: pass try: controls_dict["Absolute Exposure Time"].value = 59 except KeyError: pass try: controls_dict["Auto Focus"].value = 0 except KeyError: pass def get_frame(self): try: frame = self.capture.get_frame_robust() except: raise CameraCaptureError("Could not get frame from %s" % self.uid) timestamp = self.get_now() + self.ts_offset timestamp -= self.timebase.value frame.timestamp = timestamp return frame def get_now(self): return time() def get_timestamp(): return self.get_now() - self.timebase.value @property def frame_rate(self): return self.capture.frame_rate @frame_rate.setter def frame_rate(self, new_rate): # closest match for rate rates = [abs(r - new_rate) for r in self.capture.frame_rates] best_rate_idx = rates.index(min(rates)) rate = self.capture.frame_rates[best_rate_idx] if rate != new_rate: logger.warning( "%sfps capture mode not available at (%s) on '%s'. Selected %sfps. " % (new_rate, self.capture.frame_size, self.capture.name, rate) ) self.capture.frame_rate = rate @property def settings(self): settings = {} settings["name"] = self.capture.name settings["frame_rate"] = self.frame_rate settings["uvc_controls"] = {} for c in self.capture.controls: settings["uvc_controls"][c.display_name] = c.value return settings @settings.setter def settings(self, settings): try: self.frame_rate = settings["frame_rate"] except KeyError: pass if settings.get("name", "") == self.capture.name: for c in self.capture.controls: try: c.value = settings["uvc_controls"][c.display_name] except KeyError as e: logger.warning('Could not set UVC setting "%s" from last session.' % c.display_name) @property def frame_size(self): return self.capture.frame_size @frame_size.setter def frame_size(self, new_size): self.capture.frame_size = filter_sizes(self.name, new_size) @property def name(self): return self.capture.name def init_gui(self, sidebar): # lets define some helper functions: def gui_load_defaults(): for c in self.capture.controls: try: c.value = c.def_val except: pass def gui_update_from_device(): for c in self.capture.controls: c.refresh() def gui_init_cam_by_uid(requested_id): if requested_id is None: self.re_init_capture(None) else: for cam in uvc.device_list(): if cam["uid"] == requested_id: if is_accessible(requested_id): self.re_init_capture(requested_id) else: logger.error("The selected Camera is already in use or blocked.") return logger.warning("could not reinit capture, src_id not valid anymore") return # create the menu entry self.menu = ui.Growing_Menu(label="Camera Settings") cameras = uvc.device_list() camera_names = ["Fake Capture"] + [c["name"] for c in cameras] camera_ids = [None] + [c["uid"] for c in cameras] self.menu.append( ui.Selector( "uid", self, selection=camera_ids, labels=camera_names, label="Capture Device", setter=gui_init_cam_by_uid, ) ) sensor_control = ui.Growing_Menu(label="Sensor Settings") sensor_control.collapsed = False image_processing = ui.Growing_Menu(label="Image Post Processing") image_processing.collapsed = True sensor_control.append( ui.Selector("frame_rate", self, selection=self.capture.frame_rates, label="Frames per second") ) for control in self.capture.controls: c = None ctl_name = control.display_name # now we add controls if control.d_type == bool: c = ui.Switch("value", control, label=ctl_name, on_val=control.max_val, off_val=control.min_val) elif control.d_type == int: c = ui.Slider( "value", control, label=ctl_name, min=control.min_val, max=control.max_val, step=control.step ) elif type(control.d_type) == dict: selection = [value for name, value in control.d_type.iteritems()] labels = [name for name, value in control.d_type.iteritems()] c = ui.Selector("value", control, label=ctl_name, selection=selection, labels=labels) else: pass # if control['disabled']: # c.read_only = True # if ctl_name == 'Exposure, Auto Priority': # # the controll should always be off. we set it to 0 on init (see above) # c.read_only = True if c is not None: if control.unit == "processing_unit": image_processing.append(c) else: sensor_control.append(c) self.menu.append(sensor_control) if image_processing.elements: self.menu.append(image_processing) self.menu.append(ui.Button("refresh", gui_update_from_device)) self.menu.append(ui.Button("load defaults", gui_load_defaults)) self.sidebar = sidebar # add below geneal settings self.sidebar.insert(1, self.menu) def deinit_gui(self): if self.menu: self.sidebar.remove(self.menu) self.menu = None def close(self): self.deinit_gui() # self.capture.close() del self.capture logger.info("Capture released")