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 2: Video capture
|
||||||
Splitter port 3: [Currently unused]
|
Splitter port 3: [Currently unused]
|
||||||
|
|
||||||
StreamingCamera streams at stream_resolution
|
StreamingCamera streams at video_resolution
|
||||||
Camera capture resolution set to stream_resolution in frames()
|
Camera capture resolution set to stream_resolution in frames()
|
||||||
Video port uses that resolution for everything. If a different resolution
|
Video port uses that resolution for everything. If a different resolution
|
||||||
is specified for video capture, this is handled by the resizer.
|
is specified for video capture, this is handled by the resizer.
|
||||||
|
|
@ -39,29 +39,21 @@ from .base import BaseCamera, CaptureObject
|
||||||
# Richard's fix gain
|
# Richard's fix gain
|
||||||
from .set_picamera_gain import set_analog_gain, set_digital_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
|
# MAIN CLASS
|
||||||
class StreamingCamera(BaseCamera):
|
class StreamingCamera(BaseCamera):
|
||||||
"""Raspberry Pi camera implementation of StreamingCamera."""
|
"""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):
|
def __init__(self):
|
||||||
# Run BaseCamera init
|
# Run BaseCamera init
|
||||||
|
|
@ -103,61 +95,28 @@ class StreamingCamera(BaseCamera):
|
||||||
self.camera.close()
|
self.camera.close()
|
||||||
|
|
||||||
# HANDLE SETTINGS
|
# 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):
|
def read_config(self):
|
||||||
"""
|
"""
|
||||||
Return config dictionary of the StreamingCamera.
|
Return config dictionary of the StreamingCamera.
|
||||||
"""
|
"""
|
||||||
global CONFIG_KEYS
|
|
||||||
|
|
||||||
conf_dict = {
|
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_settings': {},
|
||||||
}
|
}
|
||||||
|
|
||||||
# PiCamera parameters
|
# PiCamera parameters
|
||||||
for key in CONFIG_KEYS['picamera_settings']:
|
for key in StreamingCamera.picamera_settings_keys:
|
||||||
try:
|
try:
|
||||||
value = getattr(self.camera, key)
|
value = getattr(self.camera, key)
|
||||||
logging.debug("{}: {}".format(key, value))
|
logging.debug("Reading PiCamera().{}: {}".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
|
|
||||||
for key in CONFIG_KEYS:
|
|
||||||
if (key != 'picamera_settings') and hasattr(self, key):
|
|
||||||
conf_dict[key] = getattr(self, key)
|
|
||||||
|
|
||||||
return conf_dict
|
return conf_dict
|
||||||
|
|
||||||
def apply_config(self, config: dict) -> None:
|
def apply_config(self, config: dict) -> None:
|
||||||
|
|
@ -170,11 +129,10 @@ 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
|
# TODO: Include timing and batching logic when applying PiCamera settings
|
||||||
|
|
||||||
paused_stream = False
|
paused_stream = False
|
||||||
logging.debug("Applying config:")
|
logging.debug("StreamingCamera: Applying config:")
|
||||||
logging.debug(config)
|
logging.debug(config)
|
||||||
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
|
|
@ -182,9 +140,6 @@ class StreamingCamera(BaseCamera):
|
||||||
# Apply valid config params to Picamera object
|
# Apply valid config params to Picamera object
|
||||||
if not self.state['record_active']: # If not recording a video
|
if not self.state['record_active']: # If not recording a video
|
||||||
|
|
||||||
logging.debug("Applying config:")
|
|
||||||
logging.debug(config)
|
|
||||||
|
|
||||||
# Pause stream while changing settings
|
# Pause stream while changing settings
|
||||||
if self.state['stream_active']: # If stream is active
|
if self.state['stream_active']: # If stream is active
|
||||||
logging.info("Pausing stream to update config.")
|
logging.info("Pausing stream to update config.")
|
||||||
|
|
@ -193,19 +148,11 @@ class StreamingCamera(BaseCamera):
|
||||||
|
|
||||||
# PiCamera parameters
|
# 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
|
self.apply_picamera_settings(config['picamera_settings'], pause_for_effect=True)
|
||||||
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
|
|
||||||
|
|
||||||
# StreamingCamera parameters
|
# StreamingCamera parameters
|
||||||
for key, value in config.items(): # For each provided setting
|
for key, value in config.items(): # For each provided setting
|
||||||
if (key != 'picamera_settings') and hasattr(self, key):
|
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)
|
setattr(self, key, value)
|
||||||
|
|
||||||
# If stream was paused to update config, unpause
|
# If stream was paused to update config, unpause
|
||||||
|
|
@ -217,6 +164,53 @@ class StreamingCamera(BaseCamera):
|
||||||
raise Exception(
|
raise Exception(
|
||||||
"Cannot update camera config while recording is active.")
|
"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:
|
def set_zoom(self, zoom_value: float = 1.) -> None:
|
||||||
"""
|
"""
|
||||||
Change the camera zoom, handling re-centering and scaling.
|
Change the camera zoom, handling re-centering and scaling.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue