Loading openflexure_microscope/camera/pi.py +77 −75 Original line number Diff line number Diff line Loading @@ -41,34 +41,29 @@ from .set_picamera_gain import set_analog_gain, set_digital_gain # Handle config and picamera settings CONFIG_KEYS = { 'stream_resolution': (832, 624), 'image_resolution': (2592, 1944), # Default for PiCamera v1. Overridden in __init__ 'numpy_resolution': (1312, 976), 'jpeg_quality': 75, 'stream_resolution': tuple, 'image_resolution': tuple, 'numpy_resolution': tuple, 'analog_gain': float, 'digital_gain': float, 'jpeg_quality': int, 'picamera_settings': { 'exposure_mode': None, 'awb_mode': None, 'awb_gains': None, 'framerate': None, 'shutter_speed': None, 'saturation': None, 'analog_gain': None, 'digital_gain': None, 'lens_shading_table': None, 'exposure_mode': str, 'awb_mode': str, 'awb_gains': tuple, 'framerate': int, 'shutter_speed': float, 'saturation': float, 'lens_shading_table': np.ndarray, }, } # MAIN CLASS class StreamingCamera(BaseCamera): """Raspberry Pi camera implementation of StreamingCamera. Args: config (dict): Dictionary of config parameters to apply on init. If None, will default to basic config. """ def __init__(self, config: dict = None): global CONFIG_KEYS """Raspberry Pi camera implementation of StreamingCamera.""" def __init__(self): # Run BaseCamera init BaseCamera.__init__(self) # Attach to Pi camera Loading @@ -83,17 +78,11 @@ class StreamingCamera(BaseCamera): # Reset variable states self.set_zoom(1.0) # Populate config and settings with all available keys self.config.update(CONFIG_KEYS) # Update config based on PiCamera parameters self.config.update({ 'image_resolution': tuple(self.camera.MAX_RESOLUTION) }) # Load config dictionary if passed if config: self.apply_config(config) # Update config properties self.image_resolution = tuple(self.camera.MAX_RESOLUTION) self.stream_resolution = (832, 624) self.numpy_resolution = (1312, 976) self.jpeg_quality = 75 # Create an empty stream self.stream = io.BytesIO() Loading Loading @@ -127,26 +116,47 @@ class StreamingCamera(BaseCamera): """ return hasattr(self.camera, "lens_shading_table") @property def digital_gain(self): # TODO: Handle missing picamera? return self.camera.digital_gain @digital_gain.setter def digital_gain(self, value): set_digital_gain(self.camera, value) @property def analog_gain(self): # TODO: Handle missing picamera? return self.camera.analog_gain @analog_gain.setter def analog_gain(self, value): set_analog_gain(self.camera, value) def read_config(self): """ Return config dictionary of the StreamingCamera. """ global CONFIG_KEYS conf_dict = { 'picamera_settings': {}, } # PiCamera parameters (obtained directly from PiCamera object) for key, _ in CONFIG_KEYS['picamera_settings'].items(): # PiCamera parameters for key in CONFIG_KEYS['picamera_settings']: try: value = getattr(self.camera, key) logging.debug("{}: {}".format(key, value)) conf_dict['picamera_settings'][key] = value except AttributeError: logging.debug("Unable to read PiCamera attribute {}".format(key)) # StreamingCamera parameters (obtained from StreamingCamera _config) # StreamingCamera parameters for key in CONFIG_KEYS: if (key != 'picamera_settings') and (key in self.config): conf_dict[key] = self.config[key] if (key != 'picamera_settings') and hasattr(self, key): conf_dict[key] = getattr(self, key) return conf_dict Loading @@ -160,8 +170,12 @@ class StreamingCamera(BaseCamera): Args: config (dict): Dictionary of config parameters. """ global CONFIG_KEYS # TODO: Include timing and batching logic when applying PiCamera settings paused_stream = False logging.debug("Applying config:") logging.debug(config) with self.lock: Loading @@ -177,32 +191,22 @@ class StreamingCamera(BaseCamera): self.stop_stream_recording() # Pause stream paused_stream = True # Remember to unpause stream when done # PiCamera parameters (applied directly to PiCamera object) # PiCamera parameters if 'picamera_settings' in config: # If new settings are given for key, value in config['picamera_settings'].items(): # For each given setting if hasattr(self.camera, key): self.config['picamera_settings'][key] = None # Add the key to the list of returned settings if key not in CONFIG_KEYS['picamera_settings']: CONFIG_KEYS['picamera_settings'][key] = type(value) logging.debug("Setting parameter {}: {}".format(key, config['picamera_settings'][key])) # Handle special attributes: if key == 'digital_gain': set_digital_gain(self.camera, value) elif key == 'analog_gain': set_analog_gain(self.camera, value) elif key == "shutter_speed": # TODO: use types from CONFIG_KEYS? @jtc42 self.camera.shutter_speed = int(value) else: setattr(self.camera, key, value) # Write setting to camera # StreamingCamera parameters (applied via StreamingCamera config) # StreamingCamera parameters for key, value in config.items(): # For each provided setting if key != 'picamera_settings': # We already handled this if key not in CONFIG_KEYS.keys(): logging.warn("{} is not in the streaming camera settings dictionary - adding it.") #continue #TODO: filter settings somehow? if (key != 'picamera_settings') and hasattr(self, key): if key not in CONFIG_KEYS: CONFIG_KEYS[key] = type(value) logging.debug("Setting parameter {}: {}".format(key, value)) self.config[key] = value setattr(self, key, value) # If stream was paused to update config, unpause if paused_stream: Loading Loading @@ -237,7 +241,7 @@ class StreamingCamera(BaseCamera): # LAUNCH ACTIONS def start_preview(self, fullscreen=True, window=None) -> bool: def start_preview(self, fullscreen=True, window=None): """Start the on board GPU camera preview.""" logging.info("Starting the GPU preview") Loading @@ -260,7 +264,7 @@ class StreamingCamera(BaseCamera): except picamera.exc.PiCameraValueError as e: logging.error("Suppressed a ValueError exception in start_preview. Exception: {}".format(e)) def stop_preview(self) -> bool: def stop_preview(self): """Stop the on board GPU camera preview.""" self.camera.stop_preview() self.state['preview_active'] = False Loading Loading @@ -289,8 +293,6 @@ class StreamingCamera(BaseCamera): # If output is a StreamObject if isinstance(output, CaptureObject): # Lock the StreamObject while recording output.lock() # Set target to capture stream output_stream = output.stream else: Loading @@ -303,7 +305,7 @@ class StreamingCamera(BaseCamera): output_stream, format=fmt, splitter_port=2, resize=self.config['stream_resolution'], resize=self.stream_resolution, quality=quality) # Update state dictionary Loading Loading @@ -342,7 +344,7 @@ class StreamingCamera(BaseCamera): with self.lock: # If no resolution is specified, default to image_resolution if not resolution: resolution = self.config['image_resolution'] resolution = self.image_resolution # Stop the camera video recording on port 1 try: Loading @@ -366,7 +368,7 @@ class StreamingCamera(BaseCamera): Args: splitter_port (int): Splitter port to start recording on resolution ((int, int)): Resolution to set the camera to, before starting recording. Defaults to `self.config['stream_resolution']`. Defaults to `self.stream_resolution`. """ with self.lock: # If stream object was destroyed Loading @@ -375,7 +377,7 @@ class StreamingCamera(BaseCamera): # If no explicit resolution is passed if not resolution: resolution = self.config['stream_resolution'] # Default to video recording resolution resolution = self.stream_resolution # Default to video recording resolution # Reduce the resolution for video streaming try: Loading @@ -392,7 +394,7 @@ class StreamingCamera(BaseCamera): self.camera.start_recording( self.stream, format='mjpeg', quality=self.config['jpeg_quality'], quality=self.jpeg_quality, bitrate=-1, # RWB: disable bitrate control # (bitrate control makes JPEG size less good as a focus # metric) Loading Loading @@ -463,9 +465,9 @@ class StreamingCamera(BaseCamera): """ with self.lock: if use_video_port: resolution = self.config['stream_resolution'] resolution = self.stream_resolution else: resolution = self.config['numpy_resolution'] resolution = self.numpy_resolution if resize: size = resize Loading Loading @@ -503,9 +505,9 @@ class StreamingCamera(BaseCamera): """ with self.lock: if use_video_port: resolution = self.config['stream_resolution'] resolution = self.stream_resolution else: resolution = self.config['numpy_resolution'] resolution = self.numpy_resolution if resize: size = resize Loading Loading
openflexure_microscope/camera/pi.py +77 −75 Original line number Diff line number Diff line Loading @@ -41,34 +41,29 @@ from .set_picamera_gain import set_analog_gain, set_digital_gain # Handle config and picamera settings CONFIG_KEYS = { 'stream_resolution': (832, 624), 'image_resolution': (2592, 1944), # Default for PiCamera v1. Overridden in __init__ 'numpy_resolution': (1312, 976), 'jpeg_quality': 75, 'stream_resolution': tuple, 'image_resolution': tuple, 'numpy_resolution': tuple, 'analog_gain': float, 'digital_gain': float, 'jpeg_quality': int, 'picamera_settings': { 'exposure_mode': None, 'awb_mode': None, 'awb_gains': None, 'framerate': None, 'shutter_speed': None, 'saturation': None, 'analog_gain': None, 'digital_gain': None, 'lens_shading_table': None, 'exposure_mode': str, 'awb_mode': str, 'awb_gains': tuple, 'framerate': int, 'shutter_speed': float, 'saturation': float, 'lens_shading_table': np.ndarray, }, } # MAIN CLASS class StreamingCamera(BaseCamera): """Raspberry Pi camera implementation of StreamingCamera. Args: config (dict): Dictionary of config parameters to apply on init. If None, will default to basic config. """ def __init__(self, config: dict = None): global CONFIG_KEYS """Raspberry Pi camera implementation of StreamingCamera.""" def __init__(self): # Run BaseCamera init BaseCamera.__init__(self) # Attach to Pi camera Loading @@ -83,17 +78,11 @@ class StreamingCamera(BaseCamera): # Reset variable states self.set_zoom(1.0) # Populate config and settings with all available keys self.config.update(CONFIG_KEYS) # Update config based on PiCamera parameters self.config.update({ 'image_resolution': tuple(self.camera.MAX_RESOLUTION) }) # Load config dictionary if passed if config: self.apply_config(config) # Update config properties self.image_resolution = tuple(self.camera.MAX_RESOLUTION) self.stream_resolution = (832, 624) self.numpy_resolution = (1312, 976) self.jpeg_quality = 75 # Create an empty stream self.stream = io.BytesIO() Loading Loading @@ -127,26 +116,47 @@ class StreamingCamera(BaseCamera): """ return hasattr(self.camera, "lens_shading_table") @property def digital_gain(self): # TODO: Handle missing picamera? return self.camera.digital_gain @digital_gain.setter def digital_gain(self, value): set_digital_gain(self.camera, value) @property def analog_gain(self): # TODO: Handle missing picamera? return self.camera.analog_gain @analog_gain.setter def analog_gain(self, value): set_analog_gain(self.camera, value) def read_config(self): """ Return config dictionary of the StreamingCamera. """ global CONFIG_KEYS conf_dict = { 'picamera_settings': {}, } # PiCamera parameters (obtained directly from PiCamera object) for key, _ in CONFIG_KEYS['picamera_settings'].items(): # PiCamera parameters for key in CONFIG_KEYS['picamera_settings']: try: value = getattr(self.camera, key) logging.debug("{}: {}".format(key, value)) conf_dict['picamera_settings'][key] = value except AttributeError: logging.debug("Unable to read PiCamera attribute {}".format(key)) # StreamingCamera parameters (obtained from StreamingCamera _config) # StreamingCamera parameters for key in CONFIG_KEYS: if (key != 'picamera_settings') and (key in self.config): conf_dict[key] = self.config[key] if (key != 'picamera_settings') and hasattr(self, key): conf_dict[key] = getattr(self, key) return conf_dict Loading @@ -160,8 +170,12 @@ class StreamingCamera(BaseCamera): Args: config (dict): Dictionary of config parameters. """ global CONFIG_KEYS # TODO: Include timing and batching logic when applying PiCamera settings paused_stream = False logging.debug("Applying config:") logging.debug(config) with self.lock: Loading @@ -177,32 +191,22 @@ class StreamingCamera(BaseCamera): self.stop_stream_recording() # Pause stream paused_stream = True # Remember to unpause stream when done # PiCamera parameters (applied directly to PiCamera object) # PiCamera parameters if 'picamera_settings' in config: # If new settings are given for key, value in config['picamera_settings'].items(): # For each given setting if hasattr(self.camera, key): self.config['picamera_settings'][key] = None # Add the key to the list of returned settings if key not in CONFIG_KEYS['picamera_settings']: CONFIG_KEYS['picamera_settings'][key] = type(value) logging.debug("Setting parameter {}: {}".format(key, config['picamera_settings'][key])) # Handle special attributes: if key == 'digital_gain': set_digital_gain(self.camera, value) elif key == 'analog_gain': set_analog_gain(self.camera, value) elif key == "shutter_speed": # TODO: use types from CONFIG_KEYS? @jtc42 self.camera.shutter_speed = int(value) else: setattr(self.camera, key, value) # Write setting to camera # StreamingCamera parameters (applied via StreamingCamera config) # StreamingCamera parameters for key, value in config.items(): # For each provided setting if key != 'picamera_settings': # We already handled this if key not in CONFIG_KEYS.keys(): logging.warn("{} is not in the streaming camera settings dictionary - adding it.") #continue #TODO: filter settings somehow? if (key != 'picamera_settings') and hasattr(self, key): if key not in CONFIG_KEYS: CONFIG_KEYS[key] = type(value) logging.debug("Setting parameter {}: {}".format(key, value)) self.config[key] = value setattr(self, key, value) # If stream was paused to update config, unpause if paused_stream: Loading Loading @@ -237,7 +241,7 @@ class StreamingCamera(BaseCamera): # LAUNCH ACTIONS def start_preview(self, fullscreen=True, window=None) -> bool: def start_preview(self, fullscreen=True, window=None): """Start the on board GPU camera preview.""" logging.info("Starting the GPU preview") Loading @@ -260,7 +264,7 @@ class StreamingCamera(BaseCamera): except picamera.exc.PiCameraValueError as e: logging.error("Suppressed a ValueError exception in start_preview. Exception: {}".format(e)) def stop_preview(self) -> bool: def stop_preview(self): """Stop the on board GPU camera preview.""" self.camera.stop_preview() self.state['preview_active'] = False Loading Loading @@ -289,8 +293,6 @@ class StreamingCamera(BaseCamera): # If output is a StreamObject if isinstance(output, CaptureObject): # Lock the StreamObject while recording output.lock() # Set target to capture stream output_stream = output.stream else: Loading @@ -303,7 +305,7 @@ class StreamingCamera(BaseCamera): output_stream, format=fmt, splitter_port=2, resize=self.config['stream_resolution'], resize=self.stream_resolution, quality=quality) # Update state dictionary Loading Loading @@ -342,7 +344,7 @@ class StreamingCamera(BaseCamera): with self.lock: # If no resolution is specified, default to image_resolution if not resolution: resolution = self.config['image_resolution'] resolution = self.image_resolution # Stop the camera video recording on port 1 try: Loading @@ -366,7 +368,7 @@ class StreamingCamera(BaseCamera): Args: splitter_port (int): Splitter port to start recording on resolution ((int, int)): Resolution to set the camera to, before starting recording. Defaults to `self.config['stream_resolution']`. Defaults to `self.stream_resolution`. """ with self.lock: # If stream object was destroyed Loading @@ -375,7 +377,7 @@ class StreamingCamera(BaseCamera): # If no explicit resolution is passed if not resolution: resolution = self.config['stream_resolution'] # Default to video recording resolution resolution = self.stream_resolution # Default to video recording resolution # Reduce the resolution for video streaming try: Loading @@ -392,7 +394,7 @@ class StreamingCamera(BaseCamera): self.camera.start_recording( self.stream, format='mjpeg', quality=self.config['jpeg_quality'], quality=self.jpeg_quality, bitrate=-1, # RWB: disable bitrate control # (bitrate control makes JPEG size less good as a focus # metric) Loading Loading @@ -463,9 +465,9 @@ class StreamingCamera(BaseCamera): """ with self.lock: if use_video_port: resolution = self.config['stream_resolution'] resolution = self.stream_resolution else: resolution = self.config['numpy_resolution'] resolution = self.numpy_resolution if resize: size = resize Loading Loading @@ -503,9 +505,9 @@ class StreamingCamera(BaseCamera): """ with self.lock: if use_video_port: resolution = self.config['stream_resolution'] resolution = self.stream_resolution else: resolution = self.config['numpy_resolution'] resolution = self.numpy_resolution if resize: size = resize Loading