[WIP] Using new picamera2 module for camera.pi, and moved old picamera integration to camera.pi.legacy
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Fabio Manganiello 2024-02-19 00:07:29 +00:00
parent d0f1ee0582
commit 659c33837e
11 changed files with 551 additions and 186 deletions

View File

@ -19,7 +19,7 @@ from platypush.message.event.camera import (
CameraRecordingStoppedEvent,
CameraVideoRenderedEvent,
)
from platypush.plugins import Plugin, action
from platypush.plugins import RunnablePlugin, action
from platypush.plugins.camera.model.camera import CameraInfo, Camera
from platypush.plugins.camera.model.exceptions import (
CameraException,
@ -45,7 +45,7 @@ __all__ = [
]
class CameraPlugin(Plugin, ABC):
class CameraPlugin(RunnablePlugin, ABC):
"""
Abstract plugin to control camera devices.
@ -176,9 +176,10 @@ class CameraPlugin(Plugin, ABC):
def open_device(
self,
device: Optional[Union[int, str]],
device: Optional[Union[int, str]] = None,
stream: bool = False,
redis_queue: Optional[str] = None,
ctx: Optional[dict] = None,
**params,
) -> Camera:
"""
@ -211,7 +212,7 @@ class CameraPlugin(Plugin, ABC):
camera = self._camera_class(info=info)
camera.info.set(**params)
camera.object = self.prepare_device(camera)
camera.object = self.prepare_device(camera, **(ctx or {}))
if stream and camera.info.stream_format:
writer_class = StreamWriter.get_class_by_name(camera.info.stream_format)
@ -288,7 +289,7 @@ class CameraPlugin(Plugin, ABC):
self.close_device(camera)
@abstractmethod
def prepare_device(self, device: Camera):
def prepare_device(self, device: Camera, **_):
"""
Prepare a device using the plugin-specific logic - to be implemented by the derived classes.
@ -788,7 +789,7 @@ class CameraPlugin(Plugin, ABC):
assert (
not camera.stream_event.is_set() and camera.info.device not in self._streams
), f'A streaming session is already running for device {camera.info.device}'
assert camera.info.device, 'No device name available'
assert camera.info.device is not None, 'No device name available'
self._streams[camera.info.device] = camera
camera.stream_event.set()
@ -949,5 +950,8 @@ class CameraPlugin(Plugin, ABC):
return camera.info.warmup_frames / camera.info.fps
return 0
def main(self):
self.wait_stop()
# vim:sw=4:ts=4:et:

View File

@ -43,7 +43,7 @@ class CameraCvPlugin(CameraPlugin):
if video_writer == 'cv':
self._video_writer_class = CvFileWriter
def prepare_device(self, device: Camera):
def prepare_device(self, device: Camera, **_):
import cv2
cam = cv2.VideoCapture(device.info.device)

View File

@ -34,7 +34,7 @@ class CameraFfmpegPlugin(CameraPlugin):
super().__init__(device=device, input_format=input_format, **opts)
self.camera_info.ffmpeg_args = ffmpeg_args or () # type: ignore
def prepare_device(self, device: Camera) -> subprocess.Popen:
def prepare_device(self, device: Camera, **_) -> subprocess.Popen:
assert isinstance(device, FFmpegCamera)
warmup_seconds = self._get_warmup_seconds(device)
ffmpeg = [

View File

@ -22,7 +22,7 @@ class CameraGstreamerPlugin(CameraPlugin):
"""
super().__init__(device=device, **opts)
def prepare_device(self, camera: GStreamerCamera) -> Pipeline:
def prepare_device(self, camera: GStreamerCamera, **_) -> Pipeline:
pipeline = Pipeline()
src = pipeline.add_source('v4l2src', device=camera.info.device)
convert = pipeline.add('videoconvert')

View File

@ -65,7 +65,7 @@ class CameraIrMlx90640Plugin(CameraPlugin):
def _is_capture_running(self):
return self._capture_proc is not None and self._capture_proc.poll() is None
def prepare_device(self, device: Camera):
def prepare_device(self, device: Camera, **_):
if not self._is_capture_running():
self._capture_proc = subprocess.Popen(
[self.rawrgb_path, '{}'.format(device.info.fps)],

View File

@ -1,208 +1,222 @@
import threading
import os
import time
from typing import Optional, List, Tuple, Union
from typing import Optional, Union
from platypush.plugins import action
from platypush.plugins.camera import CameraPlugin, Camera
from platypush.plugins.camera.pi.model import PiCameraInfo, PiCamera
from .model import PiCameraInfo, PiCamera
class CameraPiPlugin(CameraPlugin):
"""
Plugin to control a Pi camera.
Plugin to interact with a `Pi Camera
<https://www.raspberrypi.com/documentation/accessories/camera.html>`_.
.. warning::
This plugin is **DEPRECATED**, as it relies on the old ``picamera`` module.
On recent systems, it should be possible to access the Pi Camera through
the ffmpeg or gstreamer integrations.
This integration is intended to work with the `picamera2
<https://github.com/raspberrypi/picamera2>`_ module.
If you are running a very old OS that only provides the deprecated
`picamera <https://github.com/waveform80/picamera>`_ module, or you rely on
features that are currently only supported by the old module, you should
use :class:`platypush.plugins.camera.pi_legacy.CameraPiLegacyPlugin`
instead.
"""
_camera_class = PiCamera
_camera_info_class = PiCameraInfo
_awb_modes = [
"Auto",
"Incandescent",
"Tungsten",
"Fluorescent",
"Indoor",
"Daylight",
"Cloudy",
]
def __init__(
self,
device: int = 0,
fps: float = 30.0,
warmup_seconds: float = 2.0,
sharpness: int = 0,
contrast: int = 0,
brightness: int = 50,
video_stabilization: bool = False,
sharpness: float = 1.0,
contrast: float = 1.0,
brightness: float = 0.0,
iso: int = 0,
exposure_compensation: int = 0,
exposure_mode: str = 'auto',
meter_mode: str = 'average',
awb_mode: str = 'auto',
image_effect: str = 'none',
led_pin: Optional[int] = None,
color_effects: Optional[Union[str, List[str]]] = None,
zoom: Tuple[float, float, float, float] = (0.0, 0.0, 1.0, 1.0),
**camera
exposure_compensation: float = 0.0,
awb_mode: str = 'Auto',
**camera,
):
"""
See https://www.raspberrypi.org/documentation/usage/camera/python/README.md
for a detailed reference about the Pi camera options.
:param device: Camera device number (default: 0). Only supported on
devices with multiple camera slots.
:param fps: Frames per second (default: 30.0).
:param warmup_seconds: Seconds to wait for the camera to warm up
before taking a photo (default: 2.0).
:param sharpness: Sharpness level, as a float between 0.0 and 16.0,
where 1.0 is the default value, and higher values are mapped to
higher sharpness levels.
:param contrast: Contrast level, as a float between 0.0 and 32.0, where
1.0 is the default value, and higher values are mapped to higher
contrast levels.
:param brightness: Brightness level, as a float between -1.0 and 1.0.
:param video_stabilization: Enable video stabilization (default: False).
Only available on the old picamera module for now.
:param iso: ISO level (default: 0).
:param exposure_compensation: Exposure compensation level, as a float
between -8.0 and 8.0.
:param awb_mode: Auto white balance mode. Allowed values:
:param camera: Options for the base camera plugin (see :class:`platypush.plugins.camera.CameraPlugin`).
- ``Auto`` (default)
- ``Daylight``
- ``Cloudy``
- ``Indoor``
- ``Fluorescent``
:param camera: Options for the base camera plugin (see
:class:`platypush.plugins.camera.CameraPlugin`).
"""
super().__init__(
device=device, fps=fps, warmup_seconds=warmup_seconds, **camera
)
self.camera_info.sharpness = sharpness
self.camera_info.contrast = contrast
self.camera_info.brightness = brightness
self.camera_info.video_stabilization = video_stabilization
self.camera_info.iso = iso
self.camera_info.exposure_compensation = exposure_compensation
self.camera_info.meter_mode = meter_mode
self.camera_info.exposure_mode = exposure_mode
self.camera_info.awb_mode = awb_mode
self.camera_info.image_effect = image_effect
self.camera_info.color_effects = color_effects
self.camera_info.zoom = zoom
self.camera_info.led_pin = led_pin
self.camera_info.sharpness = sharpness # type: ignore
self.camera_info.contrast = contrast # type: ignore
self.camera_info.brightness = brightness # type: ignore
self.camera_info.iso = iso # type: ignore
self.camera_info.exposure_compensation = exposure_compensation # type: ignore
self.camera_info.awb_mode = awb_mode # type: ignore
# noinspection DuplicatedCode
def prepare_device(self, device: PiCamera):
# noinspection PyUnresolvedReferences
import picamera
def prepare_device(
self, device: Camera, start: bool = True, video: bool = False, **_
):
from libcamera import Transform # type: ignore
from picamera2 import Picamera2 # type: ignore
camera = picamera.PiCamera(
camera_num=device.info.device,
resolution=device.info.resolution,
framerate=device.info.fps,
led_pin=device.info.led_pin,
)
assert isinstance(device, PiCamera), f'Invalid device type: {type(device)}'
camera = Picamera2(camera_num=device.info.device)
cfg_params = {
'main': {
'format': 'XBGR8888' if video else 'BGR888',
**(
{'size': tuple(map(int, device.info.resolution))}
if device.info.resolution
else {}
),
},
**(
{
'transform': Transform(
# It may seem counterintuitive, but the picamera2 library's flip
# definition is the opposite of ours
hflip=device.info.vertical_flip,
vflip=device.info.horizontal_flip,
),
}
if video
# We don't need to flip the image for individual frames, the base camera
# class methods will take care of that
else {}
),
'controls': {
'Brightness': float(device.info.brightness),
'Contrast': float(device.info.contrast),
'Sharpness': float(device.info.sharpness),
'AwbMode': self._awb_modes.index(device.info.awb_mode),
},
}
camera.hflip = device.info.horizontal_flip
camera.vflip = device.info.vertical_flip
camera.sharpness = device.info.sharpness
camera.contrast = device.info.contrast
camera.brightness = device.info.brightness
camera.video_stabilization = device.info.video_stabilization
camera.iso = device.info.iso
camera.exposure_compensation = device.info.exposure_compensation
camera.exposure_mode = device.info.exposure_mode
camera.meter_mode = device.info.meter_mode
camera.awb_mode = device.info.awb_mode
camera.image_effect = device.info.image_effect
camera.color_effects = device.info.color_effects
camera.rotation = device.info.rotate or 0
camera.zoom = device.info.zoom
cfg = (
camera.create_video_configuration
if video
else camera.create_still_configuration
)(**cfg_params)
camera.configure(cfg)
if start:
camera.start()
time.sleep(max(1, device.info.warmup_seconds))
return camera
def release_device(self, device: PiCamera):
# noinspection PyUnresolvedReferences
import picamera
def release_device(self, device: Camera):
if device.object:
try:
device.object.stop_recording()
except (ConnectionError, picamera.PiCameraNotRecording):
pass
device.object.stop()
device.object.close()
if device.object and not device.object.closed:
try:
device.object.close()
except (ConnectionError, picamera.PiCameraClosed):
pass
def capture_frame(self, camera: Camera, *args, **kwargs):
import numpy as np
from PIL import Image
shape = (
camera.info.resolution[1] + (camera.info.resolution[1] % 16),
camera.info.resolution[0] + (camera.info.resolution[0] % 32),
3,
)
frame = np.empty(shape, dtype=np.uint8)
camera.object.capture(frame, 'rgb')
return Image.fromarray(frame)
def start_preview(self, camera: Camera):
"""
Start camera preview.
"""
camera.object.start_preview()
def stop_preview(self, camera: Camera):
"""
Stop camera preview.
"""
try:
camera.object.stop_preview()
except Exception as e:
self.logger.warning(str(e))
def capture_frame(self, device: Camera, *_, **__):
assert device.object, 'Camera not open'
return device.object.capture_image('main')
@action
def capture_preview(
self, duration: Optional[float] = None, n_frames: Optional[int] = None, **camera
) -> dict:
camera = self.open_device(**camera)
self.start_preview(camera)
def capture_video(
self,
device: Optional[int] = None,
duration: Optional[float] = None,
video_file: Optional[str] = None,
preview: bool = False,
**camera,
) -> Optional[Union[str, dict]]:
"""
Capture a video.
if n_frames:
duration = n_frames * (camera.info.fps or 0)
if duration:
threading.Timer(duration, lambda: self.stop_preview(camera))
:param device: 0-based index of the camera to capture from, if the
device supports multiple cameras. Default: use the configured
camera index or the first available camera.
:param duration: Record duration in seconds (default: None, record
until :meth:`.stop_capture``).
:param video_file: If set, the stream will be recorded to the specified
video file (default: None).
:param camera: Camera parameters override - see constructors parameters.
:param preview: Show a preview of the camera frames.
:return: If duration is specified, the method will wait until the
recording is done and return the local path to the recorded
resource. Otherwise, it will return the status of the camera device
after starting it.
"""
from picamera2 import Picamera2 # type: ignore
from picamera2.encoders import H264Encoder # type: ignore
return self.status()
def streaming_thread(
self, camera: PiCamera, stream_format: str, duration: Optional[float] = None
):
server_socket = self._prepare_server_socket(camera)
sock = None
streaming_started_time = time.time()
self.logger.info(
'Starting streaming on port {}'.format(camera.info.listen_port)
assert video_file, 'Video file is required'
camera = self.open_device(
device=device, ctx={'start': False, 'video': True}, **camera
)
try:
while camera.stream_event.is_set():
if duration and time.time() - streaming_started_time >= duration:
break
encoder = H264Encoder()
assert camera.object, 'Camera not open'
assert isinstance(
camera.object, Picamera2
), f'Invalid camera object type: {type(camera.object)}'
sock = self._accept_client(server_socket)
if not sock:
continue
if preview:
camera.object.start_preview()
if camera.object is None or camera.object.closed:
camera = self.open_device(**camera.info.to_dict())
# Only H264 is supported for now
camera.object.start_recording(encoder, os.path.expanduser(video_file))
try:
camera.object.start_recording(sock, format=stream_format)
while camera.stream_event.is_set():
camera.object.wait_recording(1)
except ConnectionError:
self.logger.info('Client closed connection')
finally:
if sock:
try:
sock.close()
except Exception as e:
self.logger.warning(
'Error while closing client socket: {}'.format(str(e))
)
if duration:
self.wait_stop(duration)
try:
if preview:
camera.object.stop_preview()
finally:
if camera.object:
camera.object.stop_recording()
camera.object.close()
self.close_device(camera)
finally:
self._cleanup_stream(camera, server_socket, sock)
self.logger.info('Stopped camera stream')
return video_file
return self.status(camera.info.device).output
@action
def start_streaming(
self, duration: Optional[float] = None, stream_format: str = 'h264', **camera
) -> dict:
camera = self.open_device(stream_format=stream_format, **camera)
return self._start_streaming(camera, duration, stream_format)
return self._start_streaming(camera, duration, stream_format) # type: ignore
# vim:sw=4:ts=4:et:

View File

@ -0,0 +1,284 @@
import threading
import time
from typing import Optional, List, Tuple, Union
from platypush.plugins import action
from platypush.plugins.camera import CameraPlugin, Camera
from .model import PiCameraInfo, PiCamera
class CameraPiLegacyPlugin(CameraPlugin):
"""
Plugin to control a Pi camera over the legacy ``picamera`` module.
.. warning::
This plugin is **DEPRECATED**, as it relies on the old ``picamera`` module.
Recent operating systems should probably use the
:class:`platypush.plugins.camera.pi.CameraPiPlugin` plugin instead, or
the generic v4l2 driver through
:class:`platypush.plugins.camera.ffmpeg.FfmpegCameraPlugin` or
:class:`platypush.plugins.camera.gstreamer.GStreamerCameraPlugin`.
"""
_camera_class = PiCamera
_camera_info_class = PiCameraInfo
def __init__(
self,
device: int = 0,
fps: float = 30.0,
warmup_seconds: float = 2.0,
sharpness: int = 0,
contrast: int = 0,
brightness: int = 50,
video_stabilization: bool = False,
iso: int = 0,
exposure_compensation: int = 0,
exposure_mode: str = 'auto',
meter_mode: str = 'average',
awb_mode: str = 'auto',
image_effect: str = 'none',
led_pin: Optional[int] = None,
color_effects: Optional[Union[str, List[str]]] = None,
zoom: Tuple[float, float, float, float] = (0.0, 0.0, 1.0, 1.0),
**camera
):
"""
:param device: Camera device number (default: 0). Only supported on
devices with multiple camera slots.
:param fps: Frames per second (default: 30.0).
:param warmup_seconds: Seconds to wait for the camera to warm up
before taking a photo (default: 2.0).
:param sharpness: Sharpness level, as an integer between -100 and 100.
:param contrast: Contrast level, as an integer between -100 and 100.
:param brightness: Brightness level, as an integer between 0 and 100.
:param video_stabilization: Enable video stabilization (default: False).
:param iso: ISO level (default: 0).
:param exposure_compensation: Exposure compensation level, as an
integer between -25 and 25.
:param exposure_mode: Exposure mode. Allowed values:
- ``off``
- ``auto`` (default)
- ``night``
- ``nightpreview``
- ``backlight``
- ``spotlight``
- ``sports``
- ``snow``
- ``beach``
- ``verylong``
- ``fixedfps``
- ``antishake``
- ``fireworks``
:param meter_mode: Metering mode used for the exposure. Allowed values:
- ``average`` (default)
- ``spot``
- ``backlit``
- ``matrix``
:param awb_mode: Auto white balance mode. Allowed values:
- ``off``
- ``auto`` (default)
- ``sunlight``
- ``cloudy``
- ``shade``
- ``tungsten``
- ``fluorescent``
- ``incandescent``
- ``flash``
- ``horizon``
:param image_effect: Image effect applied to the camera. Allowed values:
- ``none`` (default)
- ``negative``
- ``solarize``
- ``sketch``
- ``denoise``
- ``emboss``
- ``oilpaint``
- ``hatch``
- ``gpen``
- ``pastel``
- ``watercolor``
- ``film``
- ``blur``
- ``saturation``
- ``colorswap``
- ``washedout``
- ``posterise``
- ``colorpoint``
- ``colorbalance``
- ``cartoon``
- ``deinterlace1``
- ``deinterlace2``
:param led_pin: LED PIN number, if the camera LED is wired to a GPIO
PIN and you want to control it.
:param zoom: Camera zoom, in the format ``(x, y, width, height)``
(default: ``(0.0, 0.0, 1.0, 1.0)``).
:param camera: Options for the base camera plugin (see
:class:`platypush.plugins.camera.CameraPlugin`).
"""
super().__init__(
device=device, fps=fps, warmup_seconds=warmup_seconds, **camera
)
self.camera_info.sharpness = sharpness # type: ignore
self.camera_info.contrast = contrast # type: ignore
self.camera_info.brightness = brightness # type: ignore
self.camera_info.video_stabilization = video_stabilization # type: ignore
self.camera_info.iso = iso # type: ignore
self.camera_info.exposure_compensation = exposure_compensation # type: ignore
self.camera_info.meter_mode = meter_mode # type: ignore
self.camera_info.exposure_mode = exposure_mode # type: ignore
self.camera_info.awb_mode = awb_mode # type: ignore
self.camera_info.image_effect = image_effect # type: ignore
self.camera_info.color_effects = color_effects # type: ignore
self.camera_info.zoom = zoom # type: ignore
self.camera_info.led_pin = led_pin # type: ignore
def prepare_device(self, device: PiCamera):
import picamera
camera = picamera.PiCamera(
camera_num=device.info.device,
resolution=device.info.resolution,
framerate=device.info.fps,
led_pin=device.info.led_pin,
)
camera.hflip = device.info.horizontal_flip
camera.vflip = device.info.vertical_flip
camera.sharpness = device.info.sharpness
camera.contrast = device.info.contrast
camera.brightness = device.info.brightness
camera.video_stabilization = device.info.video_stabilization
camera.iso = device.info.iso
camera.exposure_compensation = device.info.exposure_compensation
camera.exposure_mode = device.info.exposure_mode
camera.meter_mode = device.info.meter_mode
camera.awb_mode = device.info.awb_mode
camera.image_effect = device.info.image_effect
camera.color_effects = device.info.color_effects
camera.rotation = device.info.rotate or 0
camera.zoom = device.info.zoom
return camera
def release_device(self, device: PiCamera):
import picamera
if device.object:
try:
device.object.stop_recording()
except (ConnectionError, picamera.PiCameraNotRecording):
pass
if device.object and not device.object.closed:
try:
device.object.close()
except (ConnectionError, picamera.PiCameraClosed):
pass
def capture_frame(self, camera: Camera, *args, **kwargs):
import numpy as np
from PIL import Image
shape = (
camera.info.resolution[1] + (camera.info.resolution[1] % 16),
camera.info.resolution[0] + (camera.info.resolution[0] % 32),
3,
)
frame = np.empty(shape, dtype=np.uint8)
camera.object.capture(frame, 'rgb')
return Image.fromarray(frame)
def start_preview(self, camera: Camera):
"""
Start camera preview.
"""
camera.object.start_preview()
def stop_preview(self, camera: Camera):
"""
Stop camera preview.
"""
try:
camera.object.stop_preview()
except Exception as e:
self.logger.warning(str(e))
@action
def capture_preview(
self, duration: Optional[float] = None, n_frames: Optional[int] = None, **camera
) -> dict:
camera = self.open_device(**camera)
self.start_preview(camera)
if n_frames:
duration = n_frames * (camera.info.fps or 0)
if duration:
threading.Timer(duration, lambda: self.stop_preview(camera))
return self.status()
def streaming_thread(
self, camera: PiCamera, stream_format: str, duration: Optional[float] = None
):
server_socket = self._prepare_server_socket(camera)
sock = None
streaming_started_time = time.time()
self.logger.info(
'Starting streaming on port {}'.format(camera.info.listen_port)
)
try:
while camera.stream_event.is_set():
if duration and time.time() - streaming_started_time >= duration:
break
sock = self._accept_client(server_socket)
if not sock:
continue
if camera.object is None or camera.object.closed:
camera = self.open_device(**camera.info.to_dict())
try:
camera.object.start_recording(sock, format=stream_format)
while camera.stream_event.is_set():
camera.object.wait_recording(1)
except ConnectionError:
self.logger.info('Client closed connection')
finally:
if sock:
try:
sock.close()
except Exception as e:
self.logger.warning(
'Error while closing client socket: {}'.format(str(e))
)
self.close_device(camera)
finally:
self._cleanup_stream(camera, server_socket, sock)
self.logger.info('Stopped camera stream')
@action
def start_streaming(
self, duration: Optional[float] = None, stream_format: str = 'h264', **camera
) -> dict:
camera = self.open_device(stream_format=stream_format, **camera)
return self._start_streaming(camera, duration, stream_format)
# vim:sw=4:ts=4:et:

View File

@ -0,0 +1,25 @@
manifest:
events: {}
install:
apk:
- ffmpeg
- py3-numpy
- py3-pillow
apt:
- ffmpeg
- python3-numpy
- python3-pillow
dnf:
- ffmpeg
- python-numpy
- python-pillow
pacman:
- ffmpeg
- python-numpy
- python-pillow
pip:
- picamera
- numpy
- Pillow
package: platypush.plugins.camera.pi
type: plugin

View File

@ -0,0 +1,46 @@
from dataclasses import dataclass
from typing import Optional, Union, List, Tuple
from platypush.plugins.camera import CameraInfo, Camera
@dataclass
class PiCameraInfo(CameraInfo):
sharpness: int = 0
contrast: int = 0
brightness: int = 50
video_stabilization: bool = False
iso: int = 0
exposure_compensation: int = 0
exposure_mode: str = 'auto'
meter_mode: str = 'average'
awb_mode: str = 'auto'
image_effect: str = 'none'
color_effects: Optional[Union[str, List[str]]] = None
zoom: Tuple[float, float, float, float] = (0.0, 0.0, 1.0, 1.0)
led_pin: Optional[int] = None
def to_dict(self) -> dict:
return {
'sharpness': self.sharpness,
'contrast': self.contrast,
'brightness': self.brightness,
'video_stabilization': self.video_stabilization,
'iso': self.iso,
'exposure_compensation': self.exposure_compensation,
'exposure_mode': self.exposure_mode,
'meter_mode': self.meter_mode,
'awb_mode': self.awb_mode,
'image_effect': self.image_effect,
'color_effects': self.color_effects,
'zoom': self.zoom,
'led_pin': self.led_pin,
**super().to_dict()
}
class PiCamera(Camera):
info: PiCameraInfo
# vim:sw=4:ts=4:et:

View File

@ -2,19 +2,23 @@ manifest:
events: {}
install:
apk:
- ffmpeg
- py3-numpy
- py3-pillow
apt:
- ffmpeg
- python3-numpy
- python3-pillow
dnf:
- ffmpeg
- python-numpy
- python-pillow
pacman:
- ffmpeg
- python-numpy
- python-pillow
pip:
- picamera
- picamera2
- numpy
- Pillow
package: platypush.plugins.camera.pi

View File

@ -1,46 +1,34 @@
from dataclasses import dataclass
from typing import Optional, Union, List, Tuple
from dataclasses import asdict, dataclass
from platypush.plugins.camera import CameraInfo, Camera
@dataclass
class PiCameraInfo(CameraInfo):
"""
PiCamera info dataclass.
"""
sharpness: int = 0
contrast: int = 0
brightness: int = 50
video_stabilization: bool = False
iso: int = 0
exposure_compensation: int = 0
exposure_mode: str = 'auto'
hdr_mode: str = 'auto'
meter_mode: str = 'average'
awb_mode: str = 'auto'
image_effect: str = 'none'
color_effects: Optional[Union[str, List[str]]] = None
zoom: Tuple[float, float, float, float] = (0.0, 0.0, 1.0, 1.0)
led_pin: Optional[int] = None
def to_dict(self) -> dict:
return {
'sharpness': self.sharpness,
'contrast': self.contrast,
'brightness': self.brightness,
'video_stabilization': self.video_stabilization,
'iso': self.iso,
'exposure_compensation': self.exposure_compensation,
'exposure_mode': self.exposure_mode,
'meter_mode': self.meter_mode,
'awb_mode': self.awb_mode,
'image_effect': self.image_effect,
'color_effects': self.color_effects,
'zoom': self.zoom,
'led_pin': self.led_pin,
**super().to_dict()
}
return asdict(self)
class PiCamera(Camera):
info: PiCameraInfo
"""
PiCamera model.
"""
info: PiCameraInfo # type: ignore
# vim:sw=4:ts=4:et: