Commit 3cf16622 authored by Joel Collins's avatar Joel Collins
Browse files

Converted config dict into properties/attributes

parent 5e637283
Loading
Loading
Loading
Loading
+77 −75
Original line number Diff line number Diff line
@@ -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
@@ -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()
@@ -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

@@ -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:

@@ -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:
@@ -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")

@@ -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
@@ -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:
@@ -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
@@ -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:
@@ -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
@@ -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:
@@ -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)
@@ -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
@@ -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