Blackened everything

This commit is contained in:
Joel Collins 2019-09-15 14:17:52 +01:00
parent e213647217
commit 5966ce29be
57 changed files with 1938 additions and 1414 deletions

View file

@ -36,6 +36,7 @@ import picamera.array
from typing import Tuple
from .base import BaseCamera, CaptureObject
# Richard's fix gain
from .set_picamera_gain import set_analog_gain, set_digital_gain
@ -43,30 +44,31 @@ from .set_picamera_gain import set_analog_gain, set_digital_gain
# MAIN CLASS
class PiCameraStreamer(BaseCamera):
"""Raspberry Pi camera implementation of PiCameraStreamer."""
picamera_settings_keys = [
'exposure_mode',
'analog_gain',
'digital_gain',
'shutter_speed',
'awb_gains',
'awb_mode',
'framerate',
'saturation',
'lens_shading_table'
"exposure_mode",
"analog_gain",
"digital_gain",
"shutter_speed",
"awb_gains",
"awb_mode",
"framerate",
"saturation",
"lens_shading_table",
]
def __init__(self):
# Run BaseCamera init
BaseCamera.__init__(self)
# Attach to Pi camera
self.camera = picamera.PiCamera() #: :py:class:`picamera.PiCamera`: Picamera object
self.camera = (
picamera.PiCamera()
) #: :py:class:`picamera.PiCamera`: Picamera object
# Store state of PiCameraStreamer
self.state.update({
'stream_active': False,
'record_active': False,
'preview_active': False,
})
self.state.update(
{"stream_active": False, "record_active": False, "preview_active": False}
)
# Reset variable states
self.set_zoom(1.0)
@ -101,11 +103,11 @@ class PiCameraStreamer(BaseCamera):
"""
conf_dict = {
'stream_resolution': self.stream_resolution,
'image_resolution': self.image_resolution,
'numpy_resolution': self.numpy_resolution,
'jpeg_quality': self.jpeg_quality,
'picamera_settings': {},
"stream_resolution": self.stream_resolution,
"image_resolution": self.image_resolution,
"numpy_resolution": self.numpy_resolution,
"jpeg_quality": self.jpeg_quality,
"picamera_settings": {},
}
# PiCamera parameters
@ -113,7 +115,7 @@ class PiCameraStreamer(BaseCamera):
try:
value = getattr(self.camera, key)
logging.debug("Reading PiCamera().{}: {}".format(key, value))
conf_dict['picamera_settings'][key] = value
conf_dict["picamera_settings"][key] = value
except AttributeError:
logging.debug("Unable to read PiCamera attribute {}".format(key))
@ -138,21 +140,23 @@ class PiCameraStreamer(BaseCamera):
with self.lock:
# 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
# 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.")
self.stop_stream_recording() # Pause stream
paused_stream = True # Remember to unpause stream when done
# PiCamera parameters
if 'picamera_settings' in config: # If new settings are given
self.apply_picamera_settings(config['picamera_settings'], pause_for_effect=True)
if "picamera_settings" in config: # If new settings are given
self.apply_picamera_settings(
config["picamera_settings"], pause_for_effect=True
)
# PiCameraStreamer parameters
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):
setattr(self, key, value)
# If stream was paused to update config, unpause
@ -162,67 +166,84 @@ class PiCameraStreamer(BaseCamera):
else:
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):
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']
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, float(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, float(settings_dict['digital_gain']))
if "analog_gain" in settings_dict:
logging.debug(
"Applying analog_gain: {}".format(settings_dict["analog_gain"])
)
set_analog_gain(self.camera, float(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, float(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 = int(settings_dict['shutter_speed'])
if "shutter_speed" in settings_dict:
logging.debug(
"Applying shutter_speed: {}".format(settings_dict["shutter_speed"])
)
self.camera.shutter_speed = int(settings_dict["shutter_speed"])
time.sleep(0.2) # Let gains settle
# Handle AWB in a half-smart way
if 'awb_gains' in settings_dict:
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']
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']
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']
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.0) -> None:
"""
Change the camera zoom, handling re-centering and scaling.
"""
with self.lock:
self.state['zoom_value'] = float(zoom_value)
if self.state['zoom_value'] < 1:
self.state['zoom_value'] = 1
self.state["zoom_value"] = float(zoom_value)
if self.state["zoom_value"] < 1:
self.state["zoom_value"] = 1
# Richard's code for zooming !
fov = self.camera.zoom
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
# 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)
@ -252,22 +273,24 @@ class PiCameraStreamer(BaseCamera):
self.camera.preview.window = window
if fullscreen:
self.camera.preview.fullscreen = fullscreen
self.state['preview_active'] = True
self.state["preview_active"] = True
except picamera.exc.PiCameraMMALError as e:
logging.error("Suppressed a MMALError in start_preview. Exception: {}".format(e))
logging.error(
"Suppressed a MMALError in start_preview. Exception: {}".format(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):
"""Stop the on board GPU camera preview."""
self.camera.stop_preview()
self.state['preview_active'] = False
self.state["preview_active"] = False
def start_recording(
self,
output,
fmt: str = 'h264',
quality: int = 15):
def start_recording(self, output, fmt: str = "h264", quality: int = 15):
"""Start recording.
Start a new video recording, writing to a output object.
@ -283,7 +306,7 @@ class PiCameraStreamer(BaseCamera):
"""
with self.lock:
# Start recording method only if a current recording is not running
if not self.state['record_active']:
if not self.state["record_active"]:
# If output is a StreamObject
if isinstance(output, CaptureObject):
@ -300,17 +323,19 @@ class PiCameraStreamer(BaseCamera):
format=fmt,
splitter_port=2,
resize=self.stream_resolution,
quality=quality)
quality=quality,
)
# Update state dictionary
self.state['record_active'] = True
self.state["record_active"] = True
return output
else:
logging.error(
"Cannot start a new recording\
until the current recording has stopped.")
until the current recording has stopped."
)
return None
def stop_recording(self):
@ -322,12 +347,11 @@ class PiCameraStreamer(BaseCamera):
logging.info("Recording stopped")
# Update state dictionary
self.state['record_active'] = False
self.state["record_active"] = False
def stop_stream_recording(
self,
splitter_port: int = 1,
resolution: Tuple[int, int] = None) -> None:
self, splitter_port: int = 1, resolution: Tuple[int, int] = None
) -> None:
"""
Sets the camera resolution to the still-image resolution, and stops recording if the stream is active.
@ -346,16 +370,21 @@ class PiCameraStreamer(BaseCamera):
except picamera.exc.PiCameraNotRecording:
logging.info("Not recording on splitter_port {}".format(splitter_port))
else:
logging.info("Stopped MJPEG stream on port {1}. Switching to {0}.".format(resolution, splitter_port))
logging.info(
"Stopped MJPEG stream on port {1}. Switching to {0}.".format(
resolution, splitter_port
)
)
# Increase the resolution for taking an image
time.sleep(0.2) # Sprinkled a sleep to prevent camera getting confused by rapid commands
time.sleep(
0.2
) # Sprinkled a sleep to prevent camera getting confused by rapid commands
self.camera.resolution = resolution
def start_stream_recording(
self,
splitter_port: int = 1,
resolution: Tuple[int, int] = None) -> None:
self, splitter_port: int = 1, resolution: Tuple[int, int] = None
) -> None:
"""
Sets the camera resolution to the video/stream resolution, and starts recording if the stream should be active.
@ -366,45 +395,57 @@ class PiCameraStreamer(BaseCamera):
"""
with self.lock:
# If stream object was destroyed
if not hasattr(self, 'stream'):
if not hasattr(self, "stream"):
self.stream = io.BytesIO() # Create a stream object
# If no explicit resolution is passed
if not resolution:
resolution = self.stream_resolution # Default to video recording resolution
resolution = (
self.stream_resolution
) # Default to video recording resolution
# Reduce the resolution for video streaming
try:
self.camera._check_recording_stopped()
except picamera.exc.PiCameraRuntimeError:
logging.info("Error while changing resolution: Recording already running.")
logging.info(
"Error while changing resolution: Recording already running."
)
else:
self.camera.resolution = resolution
# If the stream should be active
if self.state['stream_active']:
if self.state["stream_active"]:
try:
# Start recording on stream port
self.camera.start_recording(
self.stream,
format='mjpeg',
format="mjpeg",
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
# metric)
splitter_port=splitter_port)
splitter_port=splitter_port,
)
except picamera.exc.PiCameraAlreadyRecording:
logging.info("Error while starting preview: Recording already running.")
logging.info(
"Error while starting preview: Recording already running."
)
else:
logging.debug("Started MJPEG stream at {} on port {}".format(resolution, splitter_port))
logging.debug(
"Started MJPEG stream at {} on port {}".format(
resolution, splitter_port
)
)
def capture(
self,
output,
fmt: str = 'jpeg',
use_video_port: bool = False,
resize: Tuple[int, int] = None,
bayer: bool = True):
self,
output,
fmt: str = "jpeg",
use_video_port: bool = False,
resize: Tuple[int, int] = None,
bayer: bool = True,
):
"""
Capture a still image to a StreamObject.
@ -439,7 +480,8 @@ class PiCameraStreamer(BaseCamera):
quality=100,
resize=resize,
bayer=(not use_video_port) and bayer,
use_video_port=use_video_port)
use_video_port=use_video_port,
)
# Set resolution and start stream recording if necessary
if not use_video_port:
@ -448,9 +490,8 @@ class PiCameraStreamer(BaseCamera):
return output
def yuv(
self,
use_video_port: bool = True,
resize: Tuple[int, int] = None) -> np.ndarray:
self, use_video_port: bool = True, resize: Tuple[int, int] = None
) -> np.ndarray:
"""Capture an uncompressed still YUV image to a Numpy array.
Args:
@ -477,10 +518,8 @@ class PiCameraStreamer(BaseCamera):
logging.info("Capturing to {}".format(output))
self.camera.capture(
output,
resize=size,
format='yuv',
use_video_port=use_video_port)
output, resize=size, format="yuv", use_video_port=use_video_port
)
if not use_video_port:
self.start_stream_recording()
@ -488,9 +527,8 @@ class PiCameraStreamer(BaseCamera):
return output.array
def array(
self,
use_video_port: bool = True,
resize: Tuple[int, int] = None) -> np.ndarray:
self, use_video_port: bool = True, resize: Tuple[int, int] = None
) -> np.ndarray:
"""Capture an uncompressed still RGB image to a Numpy array.
Args:
@ -517,10 +555,8 @@ class PiCameraStreamer(BaseCamera):
logging.info("Capturing to {}".format(output))
self.camera.capture(
output,
resize=size,
format='rgb',
use_video_port=use_video_port)
output, resize=size, format="rgb", use_video_port=use_video_port
)
# Resume stream
self.start_stream_recording()