Blackened everything
This commit is contained in:
parent
e213647217
commit
5966ce29be
57 changed files with 1938 additions and 1414 deletions
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue