Smarter application of picamera settings
This commit is contained in:
parent
9b30d1d38f
commit
ea253f8085
1 changed files with 67 additions and 73 deletions
|
|
@ -12,7 +12,7 @@ Video port:
|
|||
Splitter port 2: Video capture
|
||||
Splitter port 3: [Currently unused]
|
||||
|
||||
StreamingCamera streams at stream_resolution
|
||||
StreamingCamera streams at video_resolution
|
||||
Camera capture resolution set to stream_resolution in frames()
|
||||
Video port uses that resolution for everything. If a different resolution
|
||||
is specified for video capture, this is handled by the resizer.
|
||||
|
|
@ -39,29 +39,21 @@ from .base import BaseCamera, CaptureObject
|
|||
# Richard's fix gain
|
||||
from .set_picamera_gain import set_analog_gain, set_digital_gain
|
||||
|
||||
# Handle config and picamera settings
|
||||
CONFIG_KEYS = {
|
||||
'stream_resolution': tuple,
|
||||
'image_resolution': tuple,
|
||||
'numpy_resolution': tuple,
|
||||
'analog_gain': float,
|
||||
'digital_gain': float,
|
||||
'jpeg_quality': int,
|
||||
'picamera_settings': {
|
||||
'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."""
|
||||
picamera_settings_keys = [
|
||||
'exposure_mode',
|
||||
'analog_gain',
|
||||
'digital_gain',
|
||||
'shutter_speed',
|
||||
'awb_gains',
|
||||
'awb_mode',
|
||||
'framerate',
|
||||
'saturation',
|
||||
'lens_shading_table'
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
# Run BaseCamera init
|
||||
|
|
@ -103,61 +95,28 @@ class StreamingCamera(BaseCamera):
|
|||
self.camera.close()
|
||||
|
||||
# HANDLE SETTINGS
|
||||
@property
|
||||
def supports_lens_shading(self):
|
||||
"""Determine whether the picamera module supports lens shading.
|
||||
|
||||
As of March 2018, picamera did not wrap the necessary MMAL commands to
|
||||
set the lens shading table, or to write the value of analog or digital
|
||||
gain. I have a forked version of the library that does support these.
|
||||
For ease of use by people who don't want those features, this library
|
||||
does not have a hard dependency on lens shading. However, we need to
|
||||
check in some places whether it's available.
|
||||
"""
|
||||
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 = {
|
||||
'stream_resolution': self.stream_resolution,
|
||||
'image_resolution': self.image_resolution,
|
||||
'numpy_resolution': self.numpy_resolution,
|
||||
'jpeg_quality': self.jpeg_quality,
|
||||
'picamera_settings': {},
|
||||
}
|
||||
|
||||
# PiCamera parameters
|
||||
for key in CONFIG_KEYS['picamera_settings']:
|
||||
for key in StreamingCamera.picamera_settings_keys:
|
||||
try:
|
||||
value = getattr(self.camera, key)
|
||||
logging.debug("{}: {}".format(key, value))
|
||||
logging.debug("Reading PiCamera().{}: {}".format(key, value))
|
||||
conf_dict['picamera_settings'][key] = value
|
||||
except AttributeError:
|
||||
logging.debug("Unable to read PiCamera attribute {}".format(key))
|
||||
|
||||
# StreamingCamera parameters
|
||||
for key in CONFIG_KEYS:
|
||||
if (key != 'picamera_settings') and hasattr(self, key):
|
||||
conf_dict[key] = getattr(self, key)
|
||||
|
||||
return conf_dict
|
||||
|
||||
def apply_config(self, config: dict) -> None:
|
||||
|
|
@ -170,11 +129,10 @@ 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("StreamingCamera: Applying config:")
|
||||
logging.debug(config)
|
||||
|
||||
with self.lock:
|
||||
|
|
@ -182,9 +140,6 @@ class StreamingCamera(BaseCamera):
|
|||
# Apply valid config params to Picamera object
|
||||
if not self.state['record_active']: # If not recording a video
|
||||
|
||||
logging.debug("Applying config:")
|
||||
logging.debug(config)
|
||||
|
||||
# Pause stream while changing settings
|
||||
if self.state['stream_active']: # If stream is active
|
||||
logging.info("Pausing stream to update config.")
|
||||
|
|
@ -193,19 +148,11 @@ class StreamingCamera(BaseCamera):
|
|||
|
||||
# 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):
|
||||
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]))
|
||||
setattr(self.camera, key, value) # Write setting to camera
|
||||
self.apply_picamera_settings(config['picamera_settings'], pause_for_effect=True)
|
||||
|
||||
# StreamingCamera parameters
|
||||
for key, value in config.items(): # For each provided setting
|
||||
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))
|
||||
setattr(self, key, value)
|
||||
|
||||
# If stream was paused to update config, unpause
|
||||
|
|
@ -217,6 +164,53 @@ class StreamingCamera(BaseCamera):
|
|||
raise Exception(
|
||||
"Cannot update camera config while recording is active.")
|
||||
|
||||
def apply_picamera_settings(self, settings_dict: dict, pause_for_effect: bool=True):
|
||||
# Set exposure mode
|
||||
if 'exposure_mode' in settings_dict:
|
||||
logging.debug("Applying exposure_mode: {}".format(settings_dict['exposure_mode']))
|
||||
self.camera.exposure_mode = settings_dict['exposure_mode']
|
||||
|
||||
# Apply gains and let them settle
|
||||
if 'analog_gain' in settings_dict:
|
||||
logging.debug("Applying analog_gain: {}".format(settings_dict['analog_gain']))
|
||||
set_analog_gain(self.camera, settings_dict['analog_gain'])
|
||||
if 'digital_gain' in settings_dict:
|
||||
logging.debug("Applying digital_gain: {}".format(settings_dict['digital_gain']))
|
||||
set_digital_gain(self.camera, settings_dict['digital_gain'])
|
||||
|
||||
# Apply shutter speed
|
||||
if 'shutter_speed' in settings_dict:
|
||||
logging.debug("Applying shutter_speed: {}".format(settings_dict['shutter_speed']))
|
||||
self.camera.shutter_speed = settings_dict['shutter_speed']
|
||||
|
||||
time.sleep(0.2) # Let gains settle
|
||||
|
||||
# Handle AWB in a half-smart way
|
||||
if 'awb_gains' in settings_dict:
|
||||
logging.debug("Applying awb_mode: off")
|
||||
self.camera.awb_mode = 'off'
|
||||
logging.debug("Applying awb_gains: {}".format(settings_dict['awb_gains']))
|
||||
self.camera.awb_gains = settings_dict['awb_gains']
|
||||
elif 'awb_mode' in settings_dict:
|
||||
logging.debug("Applying awb_mode: {}".format(settings_dict['awb_mode']))
|
||||
self.camera.awb_mode = settings_dict['awb_mode']
|
||||
|
||||
# Handle some properties that can be quickly applied
|
||||
batched_keys = ['framerate', 'saturation']
|
||||
for key in batched_keys:
|
||||
if (key in settings_dict) and hasattr(self.camera, key):
|
||||
logging.debug("Applying {}: {}".format(key, settings_dict[key]))
|
||||
setattr(self.camera, key, settings_dict[key])
|
||||
|
||||
# Handle lens shading if camera supports it
|
||||
if ('lens_shading_table' in settings_dict) and hasattr(self.camera, 'lens_shading_table'):
|
||||
logging.debug("Applying lens_shading_table: {}".format(settings_dict['lens_shading_table']))
|
||||
self.camera.lens_shading_table = settings_dict['lens_shading_table']
|
||||
|
||||
# Final optional pause to settle
|
||||
if pause_for_effect:
|
||||
time.sleep(0.2)
|
||||
|
||||
def set_zoom(self, zoom_value: float = 1.) -> None:
|
||||
"""
|
||||
Change the camera zoom, handling re-centering and scaling.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue