Refactored MLX90640 plugin and HTTP route to work with direct BytesIO - it improves performance over using temporary files or base64-encoded responses
This commit is contained in:
parent
ac02becba8
commit
53ddbad7ce
2 changed files with 59 additions and 16 deletions
|
@ -1,11 +1,12 @@
|
|||
import base64
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from flask import Response, request, Blueprint, send_from_directory
|
||||
|
||||
from platypush import Config
|
||||
from platypush.backend.http.app import template_folder
|
||||
from platypush.backend.http.app.utils import authenticate, send_request
|
||||
from platypush.plugins.camera.ir.mlx90640 import CameraIrMlx90640Plugin
|
||||
|
||||
camera_ir_mlx90640 = Blueprint('camera.ir.mlx90640', __name__, template_folder=template_folder)
|
||||
|
||||
|
@ -15,15 +16,21 @@ __routes__ = [
|
|||
]
|
||||
|
||||
|
||||
def get_feed(**args):
|
||||
try:
|
||||
def get_feed(**_):
|
||||
camera_conf = Config.get('camera.mlx90640') or {}
|
||||
camera = CameraIrMlx90640Plugin(**camera_conf)
|
||||
|
||||
with camera:
|
||||
while True:
|
||||
frame = send_request(action='camera.ir.mlx90640.capture', **args).output[0]
|
||||
frame = base64.decodebytes(frame.encode())
|
||||
yield (b'--frame\r\n'
|
||||
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
|
||||
finally:
|
||||
send_request(action='camera.ir.mlx90640.stop')
|
||||
output = camera.get_stream()
|
||||
|
||||
with output.ready:
|
||||
output.ready.wait()
|
||||
frame = output.frame
|
||||
|
||||
if frame and len(frame):
|
||||
yield (b'--frame\r\n'
|
||||
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
|
||||
|
||||
|
||||
@camera_ir_mlx90640.route('/camera/ir/mlx90640/frame', methods=['GET'])
|
||||
|
|
|
@ -2,12 +2,14 @@ import base64
|
|||
import io
|
||||
import os
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
|
||||
from platypush.plugins import Plugin, action
|
||||
from platypush.plugins import action
|
||||
from platypush.plugins.camera import CameraPlugin, StreamingOutput
|
||||
|
||||
|
||||
class CameraIrMlx90640Plugin(Plugin):
|
||||
class CameraIrMlx90640Plugin(CameraPlugin):
|
||||
"""
|
||||
Plugin to interact with a `ML90640 <https://shop.pimoroni.com/products/mlx90640-thermal-camera-breakout>`_
|
||||
infrared thermal camera.
|
||||
|
@ -35,13 +37,15 @@ class CameraIrMlx90640Plugin(Plugin):
|
|||
_img_size = (32, 24)
|
||||
_rotate_values = {}
|
||||
|
||||
def __init__(self, fps=16, skip_frames=2, scale_factor=1, rotate=0, rawrgb_path=None, **kwargs):
|
||||
def __init__(self, fps=16, skip_frames=2, scale_factor=1, rotate=0, grayscale=False, rawrgb_path=None, **kwargs):
|
||||
"""
|
||||
:param fps: Frames per seconds (default: 16)
|
||||
:param skip_frames: Number of frames to be skipped on sensor initialization/warmup (default: 2)
|
||||
:param scale_factor: The camera outputs 24x32 pixels artifacts. Use scale_factor to scale them up to a larger
|
||||
image (default: 1)
|
||||
:param rotate: Rotation angle in degrees (default: 0)
|
||||
:param grayscale: Save the image as grayscale - black pixels will be colder, white pixels warmer
|
||||
(default: False = use false colors)
|
||||
:param rawrgb_path: Specify it if the rawrgb executable compiled from
|
||||
https://github.com/pimoroni/mlx90640-library is in another folder than
|
||||
`<directory of this file>/lib/examples`.
|
||||
|
@ -68,6 +72,7 @@ class CameraIrMlx90640Plugin(Plugin):
|
|||
self.skip_frames = skip_frames
|
||||
self.scale_factor = scale_factor
|
||||
self.rawrgb_path = rawrgb_path
|
||||
self.grayscale = grayscale
|
||||
self._capture_proc = None
|
||||
|
||||
def _is_capture_proc_running(self):
|
||||
|
@ -81,9 +86,40 @@ class CameraIrMlx90640Plugin(Plugin):
|
|||
|
||||
return self._capture_proc
|
||||
|
||||
def _raw_capture(self):
|
||||
from PIL import Image
|
||||
|
||||
camera = self._get_capture_proc(fps=self.fps)
|
||||
size = self._img_size
|
||||
|
||||
while self._is_capture_proc_running():
|
||||
frame = camera.stdout.read(size[0] * size[1] * 3)
|
||||
image = Image.frombytes('RGB', size, frame)
|
||||
self._output.write(frame)
|
||||
|
||||
if self.grayscale:
|
||||
image = self._convert_to_grayscale(image)
|
||||
if self.scale_factor != 1:
|
||||
size = tuple(i * self.scale_factor for i in size)
|
||||
image = image.resize(size, Image.ANTIALIAS)
|
||||
if self.rotate:
|
||||
image = image.transpose(self.rotate)
|
||||
|
||||
temp = io.BytesIO()
|
||||
image.save(temp, format='jpg')
|
||||
self._output.write(temp.getvalue())
|
||||
|
||||
def __enter__(self):
|
||||
self._output = StreamingOutput(raw=False)
|
||||
self._capturing_thread = threading.Thread(target=self._raw_capture)
|
||||
self._capturing_thread.start()
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.stop()
|
||||
|
||||
# noinspection PyShadowingBuiltins
|
||||
@action
|
||||
def capture(self, output_file=None, frames=1, grayscale=False, fps=None, skip_frames=None, scale_factor=None,
|
||||
def capture(self, output_file=None, frames=1, grayscale=None, fps=None, skip_frames=None, scale_factor=None,
|
||||
rotate=None, format='jpeg'):
|
||||
"""
|
||||
Capture one or multiple frames and return them as raw RGB
|
||||
|
@ -97,8 +133,7 @@ class CameraIrMlx90640Plugin(Plugin):
|
|||
`stop` is called.
|
||||
:type frames: int
|
||||
|
||||
:param grayscale: Save the image as grayscale - black pixels will be colder, white pixels warmer
|
||||
(default: False)
|
||||
:param grayscale: Override the default ``grayscale`` parameter.
|
||||
:type grayscale: bool
|
||||
|
||||
:param fps: If set it overrides the fps parameter specified on the object (default: None)
|
||||
|
@ -126,9 +161,10 @@ class CameraIrMlx90640Plugin(Plugin):
|
|||
skip_frames = self.skip_frames if skip_frames is None else skip_frames
|
||||
scale_factor = self.scale_factor if scale_factor is None else scale_factor
|
||||
rotate = self._rotate_values.get(self.rotate if rotate is None else rotate, 0)
|
||||
grayscale = self.grayscale if grayscale is None else grayscale
|
||||
|
||||
size = self._img_size
|
||||
sleep_time = 1.0 / self.fps
|
||||
sleep_time = 1.0 / fps
|
||||
captured_frames = []
|
||||
n_captured_frames = 0
|
||||
files = set()
|
||||
|
|
Loading…
Reference in a new issue