Converted config dict into properties/attributes
This commit is contained in:
parent
5e6372835e
commit
3cf166226b
1 changed files with 77 additions and 75 deletions
|
|
@ -41,34 +41,29 @@ from .set_picamera_gain import set_analog_gain, set_digital_gain
|
||||||
|
|
||||||
# Handle config and picamera settings
|
# Handle config and picamera settings
|
||||||
CONFIG_KEYS = {
|
CONFIG_KEYS = {
|
||||||
'stream_resolution': (832, 624),
|
'stream_resolution': tuple,
|
||||||
'image_resolution': (2592, 1944), # Default for PiCamera v1. Overridden in __init__
|
'image_resolution': tuple,
|
||||||
'numpy_resolution': (1312, 976),
|
'numpy_resolution': tuple,
|
||||||
'jpeg_quality': 75,
|
'analog_gain': float,
|
||||||
|
'digital_gain': float,
|
||||||
|
'jpeg_quality': int,
|
||||||
'picamera_settings': {
|
'picamera_settings': {
|
||||||
'exposure_mode': None,
|
'exposure_mode': str,
|
||||||
'awb_mode': None,
|
'awb_mode': str,
|
||||||
'awb_gains': None,
|
'awb_gains': tuple,
|
||||||
'framerate': None,
|
'framerate': int,
|
||||||
'shutter_speed': None,
|
'shutter_speed': float,
|
||||||
'saturation': None,
|
'saturation': float,
|
||||||
'analog_gain': None,
|
'lens_shading_table': np.ndarray,
|
||||||
'digital_gain': None,
|
|
||||||
'lens_shading_table': None,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# MAIN CLASS
|
# MAIN CLASS
|
||||||
class StreamingCamera(BaseCamera):
|
class StreamingCamera(BaseCamera):
|
||||||
"""Raspberry Pi camera implementation of StreamingCamera.
|
"""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
|
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
# Run BaseCamera init
|
# Run BaseCamera init
|
||||||
BaseCamera.__init__(self)
|
BaseCamera.__init__(self)
|
||||||
# Attach to Pi camera
|
# Attach to Pi camera
|
||||||
|
|
@ -83,17 +78,11 @@ class StreamingCamera(BaseCamera):
|
||||||
# Reset variable states
|
# Reset variable states
|
||||||
self.set_zoom(1.0)
|
self.set_zoom(1.0)
|
||||||
|
|
||||||
# Populate config and settings with all available keys
|
# Update config properties
|
||||||
self.config.update(CONFIG_KEYS)
|
self.image_resolution = tuple(self.camera.MAX_RESOLUTION)
|
||||||
|
self.stream_resolution = (832, 624)
|
||||||
# Update config based on PiCamera parameters
|
self.numpy_resolution = (1312, 976)
|
||||||
self.config.update({
|
self.jpeg_quality = 75
|
||||||
'image_resolution': tuple(self.camera.MAX_RESOLUTION)
|
|
||||||
})
|
|
||||||
|
|
||||||
# Load config dictionary if passed
|
|
||||||
if config:
|
|
||||||
self.apply_config(config)
|
|
||||||
|
|
||||||
# Create an empty stream
|
# Create an empty stream
|
||||||
self.stream = io.BytesIO()
|
self.stream = io.BytesIO()
|
||||||
|
|
@ -127,26 +116,47 @@ class StreamingCamera(BaseCamera):
|
||||||
"""
|
"""
|
||||||
return hasattr(self.camera, "lens_shading_table")
|
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):
|
def read_config(self):
|
||||||
"""
|
"""
|
||||||
Return config dictionary of the StreamingCamera.
|
Return config dictionary of the StreamingCamera.
|
||||||
"""
|
"""
|
||||||
|
global CONFIG_KEYS
|
||||||
|
|
||||||
conf_dict = {
|
conf_dict = {
|
||||||
'picamera_settings': {},
|
'picamera_settings': {},
|
||||||
}
|
}
|
||||||
|
|
||||||
# PiCamera parameters (obtained directly from PiCamera object)
|
# PiCamera parameters
|
||||||
for key, _ in CONFIG_KEYS['picamera_settings'].items():
|
for key in CONFIG_KEYS['picamera_settings']:
|
||||||
try:
|
try:
|
||||||
value = getattr(self.camera, key)
|
value = getattr(self.camera, key)
|
||||||
|
logging.debug("{}: {}".format(key, value))
|
||||||
conf_dict['picamera_settings'][key] = value
|
conf_dict['picamera_settings'][key] = value
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
logging.debug("Unable to read PiCamera attribute {}".format(key))
|
logging.debug("Unable to read PiCamera attribute {}".format(key))
|
||||||
|
|
||||||
# StreamingCamera parameters (obtained from StreamingCamera _config)
|
# StreamingCamera parameters
|
||||||
for key in CONFIG_KEYS:
|
for key in CONFIG_KEYS:
|
||||||
if (key != 'picamera_settings') and (key in self.config):
|
if (key != 'picamera_settings') and hasattr(self, key):
|
||||||
conf_dict[key] = self.config[key]
|
conf_dict[key] = getattr(self, key)
|
||||||
|
|
||||||
return conf_dict
|
return conf_dict
|
||||||
|
|
||||||
|
|
@ -160,8 +170,12 @@ class StreamingCamera(BaseCamera):
|
||||||
Args:
|
Args:
|
||||||
config (dict): Dictionary of config parameters.
|
config (dict): Dictionary of config parameters.
|
||||||
"""
|
"""
|
||||||
|
global CONFIG_KEYS
|
||||||
|
# TODO: Include timing and batching logic when applying PiCamera settings
|
||||||
|
|
||||||
paused_stream = False
|
paused_stream = False
|
||||||
|
logging.debug("Applying config:")
|
||||||
|
logging.debug(config)
|
||||||
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
|
|
||||||
|
|
@ -177,32 +191,22 @@ class StreamingCamera(BaseCamera):
|
||||||
self.stop_stream_recording() # Pause stream
|
self.stop_stream_recording() # Pause stream
|
||||||
paused_stream = True # Remember to unpause stream when done
|
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
|
if 'picamera_settings' in config: # If new settings are given
|
||||||
for key, value in config['picamera_settings'].items(): # For each given setting
|
for key, value in config['picamera_settings'].items(): # For each given setting
|
||||||
if hasattr(self.camera, key):
|
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]))
|
logging.debug("Setting parameter {}: {}".format(key, config['picamera_settings'][key]))
|
||||||
|
setattr(self.camera, key, value) # Write setting to camera
|
||||||
|
|
||||||
# Handle special attributes:
|
# StreamingCamera parameters
|
||||||
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)
|
|
||||||
for key, value in config.items(): # For each provided setting
|
for key, value in config.items(): # For each provided setting
|
||||||
if key != 'picamera_settings': # We already handled this
|
if (key != 'picamera_settings') and hasattr(self, key):
|
||||||
if key not in CONFIG_KEYS.keys():
|
if key not in CONFIG_KEYS:
|
||||||
logging.warn("{} is not in the streaming camera settings dictionary - adding it.")
|
CONFIG_KEYS[key] = type(value)
|
||||||
#continue #TODO: filter settings somehow?
|
|
||||||
logging.debug("Setting parameter {}: {}".format(key, 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 stream was paused to update config, unpause
|
||||||
if paused_stream:
|
if paused_stream:
|
||||||
|
|
@ -223,21 +227,21 @@ class StreamingCamera(BaseCamera):
|
||||||
self.state['zoom_value'] = 1
|
self.state['zoom_value'] = 1
|
||||||
# Richard's code for zooming !
|
# Richard's code for zooming !
|
||||||
fov = self.camera.zoom
|
fov = self.camera.zoom
|
||||||
centre = np.array([fov[0] + fov[2]/2.0, fov[1] + fov[3]/2.0])
|
centre = np.array([fov[0] + fov[2] / 2.0, fov[1] + fov[3] / 2.0])
|
||||||
size = 1.0/self.state['zoom_value']
|
size = 1.0 / self.state['zoom_value']
|
||||||
# If the new zoom value would be invalid, move the centre to
|
# If the new zoom value would be invalid, move the centre to
|
||||||
# keep it within the camera's sensor (this is only relevant
|
# keep it within the camera's sensor (this is only relevant
|
||||||
# when zooming out, if the FoV is not centred on (0.5, 0.5)
|
# when zooming out, if the FoV is not centred on (0.5, 0.5)
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
if np.abs(centre[i] - 0.5) + size/2 > 0.5:
|
if np.abs(centre[i] - 0.5) + size / 2 > 0.5:
|
||||||
centre[i] = 0.5 + (1.0 - size)/2 * np.sign(centre[i]-0.5)
|
centre[i] = 0.5 + (1.0 - size) / 2 * np.sign(centre[i] - 0.5)
|
||||||
logging.info("setting zoom, centre {}, size {}".format(centre, size))
|
logging.info("setting zoom, centre {}, size {}".format(centre, size))
|
||||||
new_fov = (centre[0] - size/2, centre[1] - size/2, size, size)
|
new_fov = (centre[0] - size / 2, centre[1] - size / 2, size, size)
|
||||||
self.camera.zoom = new_fov
|
self.camera.zoom = new_fov
|
||||||
|
|
||||||
# LAUNCH ACTIONS
|
# 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."""
|
"""Start the on board GPU camera preview."""
|
||||||
logging.info("Starting the GPU preview")
|
logging.info("Starting the GPU preview")
|
||||||
|
|
||||||
|
|
@ -260,7 +264,7 @@ class StreamingCamera(BaseCamera):
|
||||||
except picamera.exc.PiCameraValueError as e:
|
except picamera.exc.PiCameraValueError as e:
|
||||||
logging.error("Suppressed a ValueError exception in start_preview. Exception: {}".format(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."""
|
"""Stop the on board GPU camera preview."""
|
||||||
self.camera.stop_preview()
|
self.camera.stop_preview()
|
||||||
self.state['preview_active'] = False
|
self.state['preview_active'] = False
|
||||||
|
|
@ -289,8 +293,6 @@ class StreamingCamera(BaseCamera):
|
||||||
|
|
||||||
# If output is a StreamObject
|
# If output is a StreamObject
|
||||||
if isinstance(output, CaptureObject):
|
if isinstance(output, CaptureObject):
|
||||||
# Lock the StreamObject while recording
|
|
||||||
output.lock()
|
|
||||||
# Set target to capture stream
|
# Set target to capture stream
|
||||||
output_stream = output.stream
|
output_stream = output.stream
|
||||||
else:
|
else:
|
||||||
|
|
@ -303,7 +305,7 @@ class StreamingCamera(BaseCamera):
|
||||||
output_stream,
|
output_stream,
|
||||||
format=fmt,
|
format=fmt,
|
||||||
splitter_port=2,
|
splitter_port=2,
|
||||||
resize=self.config['stream_resolution'],
|
resize=self.stream_resolution,
|
||||||
quality=quality)
|
quality=quality)
|
||||||
|
|
||||||
# Update state dictionary
|
# Update state dictionary
|
||||||
|
|
@ -342,7 +344,7 @@ class StreamingCamera(BaseCamera):
|
||||||
with self.lock:
|
with self.lock:
|
||||||
# If no resolution is specified, default to image_resolution
|
# If no resolution is specified, default to image_resolution
|
||||||
if not resolution:
|
if not resolution:
|
||||||
resolution = self.config['image_resolution']
|
resolution = self.image_resolution
|
||||||
|
|
||||||
# Stop the camera video recording on port 1
|
# Stop the camera video recording on port 1
|
||||||
try:
|
try:
|
||||||
|
|
@ -366,7 +368,7 @@ class StreamingCamera(BaseCamera):
|
||||||
Args:
|
Args:
|
||||||
splitter_port (int): Splitter port to start recording on
|
splitter_port (int): Splitter port to start recording on
|
||||||
resolution ((int, int)): Resolution to set the camera to, before starting recording.
|
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:
|
with self.lock:
|
||||||
# If stream object was destroyed
|
# If stream object was destroyed
|
||||||
|
|
@ -375,7 +377,7 @@ class StreamingCamera(BaseCamera):
|
||||||
|
|
||||||
# If no explicit resolution is passed
|
# If no explicit resolution is passed
|
||||||
if not resolution:
|
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
|
# Reduce the resolution for video streaming
|
||||||
try:
|
try:
|
||||||
|
|
@ -392,7 +394,7 @@ class StreamingCamera(BaseCamera):
|
||||||
self.camera.start_recording(
|
self.camera.start_recording(
|
||||||
self.stream,
|
self.stream,
|
||||||
format='mjpeg',
|
format='mjpeg',
|
||||||
quality=self.config['jpeg_quality'],
|
quality=self.jpeg_quality,
|
||||||
bitrate=-1, # RWB: disable bitrate control
|
bitrate=-1, # RWB: disable bitrate control
|
||||||
# (bitrate control makes JPEG size less good as a focus
|
# (bitrate control makes JPEG size less good as a focus
|
||||||
# metric)
|
# metric)
|
||||||
|
|
@ -463,9 +465,9 @@ class StreamingCamera(BaseCamera):
|
||||||
"""
|
"""
|
||||||
with self.lock:
|
with self.lock:
|
||||||
if use_video_port:
|
if use_video_port:
|
||||||
resolution = self.config['stream_resolution']
|
resolution = self.stream_resolution
|
||||||
else:
|
else:
|
||||||
resolution = self.config['numpy_resolution']
|
resolution = self.numpy_resolution
|
||||||
|
|
||||||
if resize:
|
if resize:
|
||||||
size = resize
|
size = resize
|
||||||
|
|
@ -503,9 +505,9 @@ class StreamingCamera(BaseCamera):
|
||||||
"""
|
"""
|
||||||
with self.lock:
|
with self.lock:
|
||||||
if use_video_port:
|
if use_video_port:
|
||||||
resolution = self.config['stream_resolution']
|
resolution = self.stream_resolution
|
||||||
else:
|
else:
|
||||||
resolution = self.config['numpy_resolution']
|
resolution = self.numpy_resolution
|
||||||
|
|
||||||
if resize:
|
if resize:
|
||||||
size = resize
|
size = resize
|
||||||
|
|
@ -557,7 +559,7 @@ class StreamingCamera(BaseCamera):
|
||||||
self.stream.seek(0)
|
self.stream.seek(0)
|
||||||
self.stream.truncate()
|
self.stream.truncate()
|
||||||
# to stream, read the new frame
|
# to stream, read the new frame
|
||||||
time.sleep(1/self.camera.framerate*0.1)
|
time.sleep(1 / self.camera.framerate * 0.1)
|
||||||
# yield the result to be read
|
# yield the result to be read
|
||||||
frame = self.stream.getvalue()
|
frame = self.stream.getvalue()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue