Static type analysis
This commit is contained in:
parent
3aebb8bead
commit
7866ec0f47
63 changed files with 1825 additions and 2722 deletions
|
|
@ -4,6 +4,8 @@ import logging
|
|||
import time
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from collections import namedtuple
|
||||
from types import TracebackType
|
||||
from typing import BinaryIO, List, Optional, Tuple, Type, Union
|
||||
|
||||
from labthings import ClientEvent, StrictLock
|
||||
|
||||
|
|
@ -27,16 +29,29 @@ class FrameStream(io.BytesIO):
|
|||
# Array of TrackerFrame objects
|
||||
io.BytesIO.__init__(self, *args, **kwargs)
|
||||
# Array of TrackerFramer objects
|
||||
self.frames = []
|
||||
self.frames: List[TrackerFrame] = []
|
||||
# Last acquired TrackerFramer object
|
||||
self.last = None
|
||||
self.last: Optional[TrackerFrame] = None
|
||||
|
||||
# Are we currently tracking frame sizes?
|
||||
self.tracking = False
|
||||
self.tracking: bool = False
|
||||
|
||||
# Event to track if a new frame is available since the last getvalue() call
|
||||
# We use a ClientEvent so that each thread can call getvalue() independantly
|
||||
self.new_frame = ClientEvent()
|
||||
self.new_frame: ClientEvent = ClientEvent()
|
||||
|
||||
def __enter__(self):
|
||||
self.start_tracking()
|
||||
return super().__enter__()
|
||||
|
||||
def __exit__(
|
||||
self,
|
||||
t: Optional[Type[BaseException]],
|
||||
value: Optional[BaseException],
|
||||
traceback: Optional[TracebackType],
|
||||
) -> Optional[bool]:
|
||||
self.stop_tracking()
|
||||
return super().__exit__(t, value, traceback)
|
||||
|
||||
def start_tracking(self):
|
||||
"""Start tracking frame sizes"""
|
||||
|
|
@ -92,13 +107,16 @@ class BaseCamera(metaclass=ABCMeta):
|
|||
|
||||
def __init__(self):
|
||||
#: :py:class:`labthings.StrictLock`: Access lock for the camera
|
||||
self.lock = StrictLock(name="Camera", timeout=None)
|
||||
self.lock: StrictLock = StrictLock(name="Camera", timeout=None)
|
||||
#: :py:class:`FrameStream`: Streaming and analysis frame buffer
|
||||
self.stream = FrameStream()
|
||||
self.stream: FrameStream = FrameStream()
|
||||
|
||||
self.stream_active = False
|
||||
self.record_active = False
|
||||
self.preview_active = False
|
||||
self.stream_active: bool = False
|
||||
self.record_active: bool = False
|
||||
self.preview_active: bool = False
|
||||
|
||||
self.image_resolution: Tuple[int, int] = (1312, 976)
|
||||
self.stream_resolution: Tuple[int, int] = (640, 480)
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
|
|
@ -114,6 +132,14 @@ class BaseCamera(metaclass=ABCMeta):
|
|||
def settings(self):
|
||||
return self.read_settings()
|
||||
|
||||
@abstractmethod
|
||||
def start_stream(self):
|
||||
"""Ensure the frame stream is actively running"""
|
||||
|
||||
@abstractmethod
|
||||
def stop_stream(self):
|
||||
"""Stop the active stream, if possible"""
|
||||
|
||||
@abstractmethod
|
||||
def update_settings(self, config: dict):
|
||||
"""Update settings from a config dictionary"""
|
||||
|
|
@ -122,6 +148,28 @@ class BaseCamera(metaclass=ABCMeta):
|
|||
def read_settings(self) -> dict:
|
||||
"""Return the current settings as a dictionary"""
|
||||
|
||||
@abstractmethod
|
||||
def capture(
|
||||
self,
|
||||
output: Union[str, BinaryIO],
|
||||
fmt: str = "jpeg",
|
||||
use_video_port: bool = False,
|
||||
resize: Optional[Tuple[int, int]] = None,
|
||||
bayer: bool = True,
|
||||
thumbnail: Optional[Tuple[int, int, int]] = None,
|
||||
):
|
||||
"""
|
||||
Perform a basic capture to output
|
||||
|
||||
Args:
|
||||
output: String or file-like object to write capture data to
|
||||
fmt: Format of the capture.
|
||||
use_video_port: Capture from the video port used for streaming. Lower resolution, faster.
|
||||
resize: Resize the captured image.
|
||||
bayer: Store raw bayer data in capture
|
||||
thumbnail: Dimensions and quality (x, y, quality) of a thumbnail to generate, if supported
|
||||
"""
|
||||
|
||||
def __enter__(self):
|
||||
"""Create camera on context enter."""
|
||||
return self
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import time
|
|||
from datetime import datetime
|
||||
|
||||
# Type hinting
|
||||
from typing import BinaryIO, Optional, Tuple, Union
|
||||
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
from openflexure_microscope.camera.base import BaseCamera
|
||||
|
|
@ -26,17 +28,17 @@ class MissingCamera(BaseCamera):
|
|||
BaseCamera.__init__(self)
|
||||
|
||||
# Update config properties
|
||||
self.image_resolution = (1312, 976)
|
||||
self.stream_resolution = (640, 480)
|
||||
self.numpy_resolution = (1312, 976)
|
||||
self.jpeg_quality = 75
|
||||
self.framerate = 10
|
||||
self.image_resolution: Tuple[int, int] = (1312, 976)
|
||||
self.stream_resolution: Tuple[int, int] = (640, 480)
|
||||
self.numpy_resolution: Tuple[int, int] = (1312, 976)
|
||||
self.jpeg_quality: int = 75
|
||||
self.framerate: int = 10
|
||||
|
||||
# Generate an initial dummy image
|
||||
self.generate_new_dummy_image()
|
||||
|
||||
# Start streaming
|
||||
self.stop = False # Used to indicate that the stream loop should break
|
||||
self.stop: bool = False # Used to indicate that the stream loop should break
|
||||
self.start_worker()
|
||||
# Wait until frames are available
|
||||
logging.info("Waiting for frames")
|
||||
|
|
@ -178,7 +180,11 @@ class MissingCamera(BaseCamera):
|
|||
"""
|
||||
logging.warning("Zoom not implemented in mock camera")
|
||||
|
||||
# LAUNCH ACTIONS
|
||||
def start_stream(self):
|
||||
pass
|
||||
|
||||
def stop_stream(self):
|
||||
pass
|
||||
|
||||
def start_preview(self, *_, **__):
|
||||
"""Start the on board GPU camera preview."""
|
||||
|
|
@ -211,7 +217,15 @@ class MissingCamera(BaseCamera):
|
|||
with self.lock:
|
||||
logging.warning("Recording not implemented in mock camera")
|
||||
|
||||
def capture(self, output, *_, **__):
|
||||
def capture(
|
||||
self,
|
||||
output: Union[str, BinaryIO],
|
||||
fmt: str = "jpeg",
|
||||
use_video_port: bool = False,
|
||||
resize: Optional[Tuple[int, int]] = None,
|
||||
bayer: bool = True,
|
||||
thumbnail: Optional[Tuple[int, int, int]] = None,
|
||||
):
|
||||
"""
|
||||
Capture a still image to a StreamObject.
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import logging
|
|||
import time
|
||||
|
||||
# Type hinting
|
||||
from typing import Tuple
|
||||
from typing import BinaryIO, Tuple, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
|
@ -77,57 +77,64 @@ class PiCameraStreamer(BaseCamera):
|
|||
BaseCamera.__init__(self)
|
||||
|
||||
#: :py:class:`picamerax.PiCamera`: Attached Picamera object
|
||||
self.camera = picamerax.PiCamera()
|
||||
self.picamera: picamerax.PiCamera = picamerax.PiCamera()
|
||||
|
||||
# Store state of PiCameraStreamer
|
||||
self.preview_active = False
|
||||
self.preview_active: bool = False
|
||||
|
||||
# Reset variable states
|
||||
self.set_zoom(1.0)
|
||||
|
||||
#: tuple: Resolution for image captures
|
||||
self.image_resolution = tuple(self.camera.MAX_RESOLUTION)
|
||||
self.image_resolution: Tuple[int, int] = tuple(self.picamera.MAX_RESOLUTION)
|
||||
#: tuple: Resolution for stream and video captures
|
||||
self.stream_resolution = (832, 624)
|
||||
self.stream_resolution: Tuple[int, int] = (832, 624)
|
||||
#: tuple: Resolution for numpy array captures
|
||||
self.numpy_resolution = (1312, 976)
|
||||
self.numpy_resolution: Tuple[int, int] = (1312, 976)
|
||||
|
||||
self.jpeg_quality = 100 #: int: JPEG quality
|
||||
self.mjpeg_quality = 75 #: int: MJPEG quality
|
||||
self.jpeg_quality: int = 100 #: int: JPEG quality
|
||||
self.mjpeg_quality: int = 75 #: int: MJPEG quality
|
||||
|
||||
# Start stream recording (and set resolution)
|
||||
self.start_stream_recording()
|
||||
self.start_stream()
|
||||
# Wait until frames are available
|
||||
logging.debug("Waiting for frames...")
|
||||
self.stream.new_frame.wait()
|
||||
logging.debug("Camera initialised")
|
||||
|
||||
@property
|
||||
def configuration(self):
|
||||
"""The current camera configuration."""
|
||||
return {"board": self.camera.revision}
|
||||
def camera(self):
|
||||
logging.warning(
|
||||
"PiCameraStreamer.camera is deprecated. Replace with PiCameraStreamer.picamera"
|
||||
)
|
||||
return self.picamera
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
def configuration(self) -> dict:
|
||||
"""The current camera configuration."""
|
||||
return {"board": self.picamera.revision}
|
||||
|
||||
@property
|
||||
def state(self) -> dict:
|
||||
"""The current read-only camera state."""
|
||||
return {}
|
||||
|
||||
def close(self):
|
||||
"""Close the Raspberry Pi PiCameraStreamer."""
|
||||
# Stop stream recording
|
||||
self.stop_stream_recording()
|
||||
self.stop_stream()
|
||||
# Run BaseCamera close method
|
||||
super().close()
|
||||
# Detach Pi camera
|
||||
if self.camera:
|
||||
self.camera.close()
|
||||
if self.picamera:
|
||||
self.picamera.close()
|
||||
|
||||
# HANDLE SETTINGS
|
||||
def read_settings(self) -> dict:
|
||||
"""
|
||||
Return config dictionary of the PiCameraStreamer.
|
||||
"""
|
||||
conf_dict = {
|
||||
conf_dict: dict = {
|
||||
"stream_resolution": self.stream_resolution,
|
||||
"image_resolution": self.image_resolution,
|
||||
"numpy_resolution": self.numpy_resolution,
|
||||
|
|
@ -139,7 +146,7 @@ class PiCameraStreamer(BaseCamera):
|
|||
# Include a subset of picamera properties. Excludes lens shading table
|
||||
for key in PiCameraStreamer.picamera_settings_keys:
|
||||
try:
|
||||
value = getattr(self.camera, key)
|
||||
value = getattr(self.picamera, key)
|
||||
logging.debug("Reading PiCamera().%s: %s", key, value)
|
||||
conf_dict["picamera"][key] = value
|
||||
except AttributeError:
|
||||
|
|
@ -147,11 +154,11 @@ class PiCameraStreamer(BaseCamera):
|
|||
|
||||
# Include a serialised lens shading table
|
||||
if (
|
||||
hasattr(self.camera, "lens_shading_table")
|
||||
and getattr(self.camera, "lens_shading_table") is not None
|
||||
hasattr(self.picamera, "lens_shading_table")
|
||||
and getattr(self.picamera, "lens_shading_table") is not None
|
||||
):
|
||||
conf_dict["picamera"]["lens_shading_table"] = ndarray_to_json(
|
||||
getattr(self.camera, "lens_shading_table")
|
||||
getattr(self.picamera, "lens_shading_table")
|
||||
)
|
||||
|
||||
return conf_dict
|
||||
|
|
@ -179,7 +186,7 @@ class PiCameraStreamer(BaseCamera):
|
|||
# Pause stream while changing settings
|
||||
if self.stream_active: # If stream is active
|
||||
logging.info("Pausing stream to update config.")
|
||||
self.stop_stream_recording() # Pause stream
|
||||
self.stop_stream() # Pause stream
|
||||
paused_stream = True # Remember to unpause stream when done
|
||||
|
||||
# PiCamera parameters
|
||||
|
|
@ -190,11 +197,11 @@ class PiCameraStreamer(BaseCamera):
|
|||
|
||||
# Handle lens shading if camera supports it
|
||||
if (
|
||||
hasattr(self.camera, "lens_shading_table")
|
||||
hasattr(self.picamera, "lens_shading_table")
|
||||
and "lens_shading_table" in config["picamera"]
|
||||
):
|
||||
try:
|
||||
self.camera.lens_shading_table = json_to_ndarray(
|
||||
self.picamera.lens_shading_table = json_to_ndarray(
|
||||
config["picamera"].get("lens_shading_table")
|
||||
)
|
||||
except KeyError as e:
|
||||
|
|
@ -208,7 +215,7 @@ class PiCameraStreamer(BaseCamera):
|
|||
# If stream was paused to update config, unpause
|
||||
if paused_stream:
|
||||
logging.info("Resuming stream.")
|
||||
self.start_stream_recording()
|
||||
self.start_stream()
|
||||
|
||||
else:
|
||||
raise Exception(
|
||||
|
|
@ -229,47 +236,47 @@ class PiCameraStreamer(BaseCamera):
|
|||
logging.debug(
|
||||
"Applying exposure_mode: %s", (settings_dict["exposure_mode"])
|
||||
)
|
||||
self.camera.exposure_mode = settings_dict["exposure_mode"]
|
||||
self.picamera.exposure_mode = settings_dict["exposure_mode"]
|
||||
|
||||
# Apply gains and let them settle
|
||||
if "analog_gain" in settings_dict:
|
||||
logging.debug("Applying analog_gain: %s", (settings_dict["analog_gain"]))
|
||||
set_analog_gain(self.camera, float(settings_dict["analog_gain"]))
|
||||
set_analog_gain(self.picamera, float(settings_dict["analog_gain"]))
|
||||
if "digital_gain" in settings_dict:
|
||||
logging.debug("Applying digital_gain: %s", (settings_dict["digital_gain"]))
|
||||
set_digital_gain(self.camera, float(settings_dict["digital_gain"]))
|
||||
set_digital_gain(self.picamera, float(settings_dict["digital_gain"]))
|
||||
|
||||
# Apply shutter speed
|
||||
if "shutter_speed" in settings_dict:
|
||||
logging.debug(
|
||||
"Applying shutter_speed: %s", (settings_dict["shutter_speed"])
|
||||
)
|
||||
self.camera.shutter_speed = int(settings_dict["shutter_speed"])
|
||||
self.picamera.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:
|
||||
logging.debug("Applying awb_mode: off")
|
||||
self.camera.awb_mode = "off"
|
||||
self.picamera.awb_mode = "off"
|
||||
logging.debug("Applying awb_gains: %s", (settings_dict["awb_gains"]))
|
||||
self.camera.awb_gains = settings_dict["awb_gains"]
|
||||
self.picamera.awb_gains = settings_dict["awb_gains"]
|
||||
elif "awb_mode" in settings_dict:
|
||||
logging.debug("Applying awb_mode: %s", (settings_dict["awb_mode"]))
|
||||
self.camera.awb_mode = settings_dict["awb_mode"]
|
||||
self.picamera.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):
|
||||
if (key in settings_dict) and hasattr(self.picamera, key):
|
||||
logging.debug("Applying %s: %s", key, settings_dict[key])
|
||||
setattr(self.camera, key, settings_dict[key])
|
||||
setattr(self.picamera, key, settings_dict[key])
|
||||
|
||||
# Final optional pause to settle
|
||||
if pause_for_effect:
|
||||
time.sleep(0.2)
|
||||
|
||||
def set_zoom(self, zoom_value: float = 1.0) -> None:
|
||||
def set_zoom(self, zoom_value: Union[float, int] = 1.0) -> None:
|
||||
"""
|
||||
Change the camera zoom, handling re-centering and scaling.
|
||||
"""
|
||||
|
|
@ -278,7 +285,7 @@ class PiCameraStreamer(BaseCamera):
|
|||
if self.zoom_value < 1:
|
||||
self.zoom_value = 1
|
||||
# Richard's code for zooming !
|
||||
fov = self.camera.zoom
|
||||
fov = self.picamera.zoom
|
||||
centre = np.array([fov[0] + fov[2] / 2.0, fov[1] + fov[3] / 2.0])
|
||||
size = 1.0 / self.zoom_value
|
||||
# If the new zoom value would be invalid, move the centre to
|
||||
|
|
@ -289,23 +296,23 @@ class PiCameraStreamer(BaseCamera):
|
|||
centre[i] = 0.5 + (1.0 - size) / 2 * np.sign(centre[i] - 0.5)
|
||||
logging.info("setting zoom, centre %s, size %s", centre, size)
|
||||
new_fov = (centre[0] - size / 2, centre[1] - size / 2, size, size)
|
||||
self.camera.zoom = new_fov
|
||||
self.picamera.zoom = new_fov
|
||||
|
||||
# LAUNCH ACTIONS
|
||||
|
||||
def start_preview(self, fullscreen=True, window=None):
|
||||
def start_preview(
|
||||
self, fullscreen: bool = True, window: Tuple[int, int, int, int] = None
|
||||
):
|
||||
"""Start the on board GPU camera preview."""
|
||||
with self.lock(timeout=1):
|
||||
try:
|
||||
if not self.camera.preview:
|
||||
if not self.picamera.preview:
|
||||
logging.debug("Starting preview")
|
||||
self.camera.start_preview(fullscreen=fullscreen, window=window)
|
||||
self.picamera.start_preview(fullscreen=fullscreen, window=window)
|
||||
else:
|
||||
logging.debug("Resizing preview")
|
||||
if window:
|
||||
self.camera.preview.window = window
|
||||
self.picamera.preview.window = window
|
||||
if fullscreen:
|
||||
self.camera.preview.fullscreen = fullscreen
|
||||
self.picamera.preview.fullscreen = fullscreen
|
||||
self.preview_active = True
|
||||
except picamerax.exc.PiCameraMMALError as e:
|
||||
logging.error(
|
||||
|
|
@ -320,11 +327,13 @@ class PiCameraStreamer(BaseCamera):
|
|||
def stop_preview(self):
|
||||
"""Stop the on board GPU camera preview."""
|
||||
with self.lock(timeout=1):
|
||||
if self.camera.preview:
|
||||
self.camera.stop_preview()
|
||||
if self.picamera.preview:
|
||||
self.picamera.stop_preview()
|
||||
self.preview_active = False
|
||||
|
||||
def start_recording(self, output, fmt: str = "h264", quality: int = 15):
|
||||
def start_recording(
|
||||
self, output: Union[str, BinaryIO], fmt: str = "h264", quality: int = 15
|
||||
):
|
||||
"""Start recording.
|
||||
|
||||
Start a new video recording, writing to a output object.
|
||||
|
|
@ -345,7 +354,7 @@ class PiCameraStreamer(BaseCamera):
|
|||
# Start the camera video recording on port 2
|
||||
logging.info("Recording to %s", (output))
|
||||
|
||||
self.camera.start_recording(
|
||||
self.picamera.start_recording(
|
||||
output,
|
||||
format=fmt,
|
||||
splitter_port=2,
|
||||
|
|
@ -370,80 +379,67 @@ class PiCameraStreamer(BaseCamera):
|
|||
with self.lock(timeout=5):
|
||||
# Stop the camera video recording on port 2
|
||||
logging.info("Stopping recording")
|
||||
self.camera.stop_recording(splitter_port=2)
|
||||
self.picamera.stop_recording(splitter_port=2)
|
||||
logging.info("Recording stopped")
|
||||
|
||||
# Update state
|
||||
self.record_active = False
|
||||
|
||||
def start_stream_recording(self, splitter_port: int = 1, **kwargs) -> None:
|
||||
def start_stream(self) -> None:
|
||||
"""
|
||||
Sets the camera resolution to the video/stream resolution, and starts recording if the stream should be active.
|
||||
|
||||
Args:
|
||||
splitter_port (int): Splitter port to start recording on
|
||||
"""
|
||||
for k in kwargs.keys():
|
||||
logging.warning(
|
||||
"Warning, kwarg %s is invalid for stop_stream_recording.", k
|
||||
)
|
||||
with self.lock(timeout=None):
|
||||
# Reduce the resolution for video streaming
|
||||
try:
|
||||
self.camera._check_recording_stopped() # pylint: disable=W0212
|
||||
self.picamera._check_recording_stopped() # pylint: disable=W0212
|
||||
except picamerax.exc.PiCameraRuntimeError:
|
||||
logging.info(
|
||||
"Error while changing resolution: Recording already running."
|
||||
)
|
||||
else:
|
||||
self.camera.resolution = self.stream_resolution
|
||||
self.picamera.resolution = self.stream_resolution
|
||||
# Sprinkled a sleep to prevent camera getting confused by rapid commands
|
||||
time.sleep(0.2)
|
||||
|
||||
# If the stream should be active
|
||||
try:
|
||||
# Start recording on stream port
|
||||
self.camera.start_recording(
|
||||
self.picamera.start_recording(
|
||||
self.stream,
|
||||
format="mjpeg",
|
||||
quality=self.mjpeg_quality,
|
||||
bitrate=-1, # RWB: disable bitrate control
|
||||
# (bitrate control makes JPEG size less good as a focus
|
||||
# metric)
|
||||
splitter_port=splitter_port,
|
||||
splitter_port=1,
|
||||
)
|
||||
except picamerax.exc.PiCameraAlreadyRecording:
|
||||
logging.info("Error while starting preview: Recording already running.")
|
||||
else:
|
||||
self.stream_active = True
|
||||
logging.debug(
|
||||
"Started MJPEG stream at %s on port %s",
|
||||
self.stream_resolution,
|
||||
splitter_port,
|
||||
"Started MJPEG stream at %s on port %s", self.stream_resolution, 1
|
||||
)
|
||||
|
||||
def stop_stream_recording(self, splitter_port: int = 1, **kwargs) -> None:
|
||||
def stop_stream(self) -> None:
|
||||
"""
|
||||
Sets the camera resolution to the still-image resolution, and stops recording if the stream is active.
|
||||
|
||||
Args:
|
||||
splitter_port (int): Splitter port to stop recording on
|
||||
"""
|
||||
for k in kwargs.keys():
|
||||
logging.warning(
|
||||
"Warning, kwarg %s is invalid for stop_stream_recording.", k
|
||||
)
|
||||
with self.lock:
|
||||
# Stop the camera video recording on port 1
|
||||
try:
|
||||
self.camera.stop_recording(splitter_port=splitter_port)
|
||||
self.picamera.stop_recording(splitter_port=1)
|
||||
except picamerax.exc.PiCameraNotRecording:
|
||||
logging.info("Not recording on splitter_port %s", (splitter_port))
|
||||
logging.info("Not recording on splitter_port %s", (1))
|
||||
else:
|
||||
self.stream_active = False
|
||||
logging.info(
|
||||
"Stopped MJPEG stream on port %s. Switching to %s.",
|
||||
splitter_port,
|
||||
1,
|
||||
self.image_resolution,
|
||||
)
|
||||
|
||||
|
|
@ -451,16 +447,16 @@ class PiCameraStreamer(BaseCamera):
|
|||
time.sleep(
|
||||
0.2
|
||||
) # Sprinkled a sleep to prevent camera getting confused by rapid commands
|
||||
self.camera.resolution = self.image_resolution
|
||||
self.picamera.resolution = self.image_resolution
|
||||
|
||||
def capture(
|
||||
self,
|
||||
output,
|
||||
output: Union[str, BinaryIO],
|
||||
fmt: str = "jpeg",
|
||||
use_video_port: bool = False,
|
||||
resize: Tuple[int, int] = None,
|
||||
bayer: bool = True,
|
||||
thumbnail: tuple = None,
|
||||
thumbnail: Tuple[int, int, int] = None,
|
||||
):
|
||||
"""
|
||||
Capture a still image to a StreamObject.
|
||||
|
|
@ -470,10 +466,11 @@ class PiCameraStreamer(BaseCamera):
|
|||
|
||||
Args:
|
||||
output: String or file-like object to write capture data to
|
||||
fmt (str): Format of the capture.
|
||||
use_video_port (bool): Capture from the video port used for streaming. Lower resolution, faster.
|
||||
resize ((int, int)): Resize the captured image.
|
||||
bayer (bool): Store raw bayer data in capture
|
||||
fmt: Format of the capture.
|
||||
use_video_port: Capture from the video port used for streaming. Lower resolution, faster.
|
||||
resize: Resize the captured image.
|
||||
bayer: Store raw bayer data in capture
|
||||
thumbnail: Dimensions and quality (x, y, quality) of a thumbnail to generate, if supported
|
||||
|
||||
Returns:
|
||||
output_object (str/BytesIO): Target object.
|
||||
|
|
@ -483,9 +480,9 @@ class PiCameraStreamer(BaseCamera):
|
|||
|
||||
# Set resolution and stop stream recording if necessary
|
||||
if not use_video_port:
|
||||
self.stop_stream_recording()
|
||||
self.stop_stream()
|
||||
|
||||
self.camera.capture(
|
||||
self.picamera.capture(
|
||||
output,
|
||||
format=fmt,
|
||||
quality=self.jpeg_quality,
|
||||
|
|
@ -497,11 +494,11 @@ class PiCameraStreamer(BaseCamera):
|
|||
|
||||
# Set resolution and start stream recording if necessary
|
||||
if not use_video_port:
|
||||
self.start_stream_recording()
|
||||
self.start_stream()
|
||||
|
||||
return output
|
||||
|
||||
def array(self, use_video_port=True) -> np.ndarray:
|
||||
def array(self, use_video_port: bool = True) -> np.ndarray:
|
||||
"""Capture an uncompressed still RGB image to a Numpy array.
|
||||
|
||||
Args:
|
||||
|
|
@ -513,7 +510,9 @@ class PiCameraStreamer(BaseCamera):
|
|||
"""
|
||||
with self.lock:
|
||||
logging.debug("Creating PiRGBArray")
|
||||
with picamerax.array.PiRGBArray(self.camera) as output:
|
||||
with picamerax.array.PiRGBArray(self.picamera) as output:
|
||||
logging.info("Capturing to %s", (output))
|
||||
self.camera.capture(output, format="rgb", use_video_port=use_video_port)
|
||||
self.picamera.capture(
|
||||
output, format="rgb", use_video_port=use_video_port
|
||||
)
|
||||
return output.array
|
||||
|
|
|
|||
|
|
@ -2,16 +2,17 @@ from __future__ import print_function
|
|||
|
||||
import logging
|
||||
import time
|
||||
from typing import Union
|
||||
|
||||
import picamerax
|
||||
from picamerax import exc, mmal
|
||||
from picamerax.mmalobj import to_rational
|
||||
|
||||
MMAL_PARAMETER_ANALOG_GAIN = mmal.MMAL_PARAMETER_GROUP_CAMERA + 0x59
|
||||
MMAL_PARAMETER_DIGITAL_GAIN = mmal.MMAL_PARAMETER_GROUP_CAMERA + 0x5A
|
||||
MMAL_PARAMETER_ANALOG_GAIN: int = mmal.MMAL_PARAMETER_GROUP_CAMERA + 0x59
|
||||
MMAL_PARAMETER_DIGITAL_GAIN: int = mmal.MMAL_PARAMETER_GROUP_CAMERA + 0x5A
|
||||
|
||||
|
||||
def set_gain(camera, gain, value):
|
||||
def set_gain(camera: picamerax.PiCamera, gain: int, value: Union[int, float]):
|
||||
"""Set the analog gain of a PiCamera.
|
||||
|
||||
camera: the picamerax.PiCamera() instance you are configuring
|
||||
|
|
@ -32,12 +33,12 @@ def set_gain(camera, gain, value):
|
|||
raise exc.PiCameraMMALError(ret)
|
||||
|
||||
|
||||
def set_analog_gain(camera, value):
|
||||
def set_analog_gain(camera: picamerax.PiCamera, value: Union[int, float]):
|
||||
"""Set the gain of a PiCamera object to a given value."""
|
||||
set_gain(camera, MMAL_PARAMETER_ANALOG_GAIN, value)
|
||||
|
||||
|
||||
def set_digital_gain(camera, value):
|
||||
def set_digital_gain(camera: picamerax.PiCamera, value: Union[int, float]):
|
||||
"""Set the digital gain of a PiCamera object to a given value."""
|
||||
set_gain(camera, MMAL_PARAMETER_DIGITAL_GAIN, value)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue