From a130edb74f2ab5e7066d6cbb0f3cd77c446b54da Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Wed, 11 Mar 2020 01:06:07 +0100 Subject: [PATCH] Made QR-code scan work also on picamera --- platypush/plugins/camera/__init__.py | 27 ++++++++-------- platypush/plugins/camera/pi.py | 46 +++++++++++++++++++++++++--- platypush/plugins/qrcode.py | 3 +- setup.py | 2 +- 4 files changed, 60 insertions(+), 18 deletions(-) diff --git a/platypush/plugins/camera/__init__.py b/platypush/plugins/camera/__init__.py index 28575fab2f..dd9152c010 100644 --- a/platypush/plugins/camera/__init__.py +++ b/platypush/plugins/camera/__init__.py @@ -34,19 +34,22 @@ class StreamingOutput: return buf.startswith(b'\xff\xd8') def write(self, buf): - if self.is_new_frame(buf): - if self.raw: - with self.ready: - self.raw_frame = buf - self.ready.notify_all() - else: - # New frame, copy the existing buffer's content and notify all clients that it's available - self.buffer.truncate() - with self.ready: - self.frame = self.buffer.getvalue() - self.ready.notify_all() - self.buffer.seek(0) + if not self.is_new_frame(buf): + return + if self.raw: + with self.ready: + self.raw_frame = buf + self.ready.notify_all() + return + + # New frame, copy the existing buffer's content and notify all clients that it's available + self.buffer.truncate() + with self.ready: + self.frame = self.buffer.getvalue() + self.ready.notify_all() + + self.buffer.seek(0) return self.buffer.write(buf) def close(self): diff --git a/platypush/plugins/camera/pi.py b/platypush/plugins/camera/pi.py index 86353fc64f..28a5dede32 100644 --- a/platypush/plugins/camera/pi.py +++ b/platypush/plugins/camera/pi.py @@ -20,6 +20,8 @@ class CameraPiPlugin(CameraPlugin): Requires: * **picamera** (``pip install picamera``) + * **numpy** (``pip install numpy``) + """ _default_resolution = (800, 600) @@ -64,9 +66,11 @@ class CameraPiPlugin(CameraPlugin): self._time_lapse_thread = None self._recording_thread = None self._streaming_thread = None + self._capturing_thread = None self._time_lapse_stop_condition = threading.Condition() self._recording_stop_condition = threading.Condition() self._can_stream = False + self._can_capture = False # noinspection PyUnresolvedReferences,PyPackageRequirements def _get_camera(self, **opts): @@ -88,11 +92,19 @@ class CameraPiPlugin(CameraPlugin): """ Close an active connection to the camera. """ + import picamera + if self._output and self._camera: - self._camera.stop_recording() + try: + self._camera.stop_recording() + except picamera.PiCameraNotRecording: + pass if self._camera and not self._camera.closed: - self._camera.close() + try: + self._camera.close() + except picamera.PiCameraClosed: + pass self._camera = None @@ -175,12 +187,38 @@ class CameraPiPlugin(CameraPlugin): if camera and close: self.close() + def _raw_capture(self): + import numpy as np + resolution = self.camera_args['resolution'] + camera = self._get_camera() + + while self._can_capture: + shape = (resolution[1] + (resolution[1]%16), + resolution[0] + (resolution[0]%32), + 3) + + frame = np.empty(shape, dtype=np.uint8) + camera.capture(frame, 'bgr') + frame.reshape((shape[0], shape[1], 3)) + self._output.write(frame) + def __enter__(self): camera = self._get_camera() - self._output = StreamingOutput() - camera.start_recording(self._output, format='mjpeg') + self._output = StreamingOutput(raw=self.stream_raw_frames) + self._can_capture = True + + if self.stream_raw_frames: + self._capturing_thread = threading.Thread(target=self._raw_capture) + self._capturing_thread.start() + else: + camera.start_recording(self._output, format='mjpeg') def __exit__(self, exc_type, exc_val, exc_tb): + self._can_capture = False + if self._capturing_thread: + self._capturing_thread.join() + self._capturing_thread = None + self.close() @action diff --git a/platypush/plugins/qrcode.py b/platypush/plugins/qrcode.py index cf11ace324..289c609d49 100644 --- a/platypush/plugins/qrcode.py +++ b/platypush/plugins/qrcode.py @@ -106,7 +106,8 @@ class QrcodePlugin(Plugin): return QrcodeDecodedResponse(results) def _convert_frame(self, frame) -> Image: - assert isinstance(frame, np.ndarray), 'Image conversion only works with numpy arrays for now' + assert isinstance(frame, np.ndarray), \ + 'Image conversion only works with numpy arrays for now (got {})'.format(type(frame)) mode = 'RGB' if len(frame.shape) > 2 and frame.shape[2] == 4: mode = 'RGBA' diff --git a/setup.py b/setup.py index 4fc727f1a0..3f7bbd4a42 100755 --- a/setup.py +++ b/setup.py @@ -182,7 +182,7 @@ setup( # Support for torrents download 'torrent': ['python-libtorrent'], # Support for RaspberryPi camera - 'picamera': ['picamera'], + 'picamera': ['picamera', 'numpy'], # Support for inotify file monitors 'inotify': ['inotify'], # Support for Google Assistant