forked from platypush/platypush
Finalized camera.ir.mlx90640 web interface
This commit is contained in:
parent
d7dc74beed
commit
168b1b0e5a
6 changed files with 109 additions and 28 deletions
|
@ -0,0 +1,60 @@
|
|||
import base64
|
||||
import os
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
from flask import Response, request, Blueprint, send_from_directory
|
||||
|
||||
from platypush.backend.http.app import template_folder
|
||||
from platypush.backend.http.app.utils import authenticate, send_request
|
||||
|
||||
camera_ir_mlx90640 = Blueprint('camera.ir.mlx90640', __name__, template_folder=template_folder)
|
||||
|
||||
# Declare routes list
|
||||
__routes__ = [
|
||||
camera_ir_mlx90640,
|
||||
]
|
||||
|
||||
|
||||
def get_feed(**args):
|
||||
try:
|
||||
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')
|
||||
|
||||
|
||||
@camera_ir_mlx90640.route('/camera/ir/mlx90640/frame', methods=['GET'])
|
||||
@authenticate()
|
||||
def get_frame_route():
|
||||
f = tempfile.NamedTemporaryFile(prefix='ir_camera_frame_', suffix='.jpg', delete=False)
|
||||
args = {
|
||||
'grayscale': bool(int(request.args.get('grayscale', 0))),
|
||||
'scale_factor': int(request.args.get('scale_factor', 1)),
|
||||
'rotate': int(request.args.get('rotate', 0)),
|
||||
'output_file': f.name,
|
||||
}
|
||||
|
||||
send_request(action='camera.ir.mlx90640.capture', **args)
|
||||
return send_from_directory(os.path.dirname(f.name),
|
||||
os.path.basename(f.name))
|
||||
|
||||
|
||||
@camera_ir_mlx90640.route('/camera/ir/mlx90640/stream', methods=['GET'])
|
||||
@authenticate()
|
||||
def get_feed_route():
|
||||
args = {
|
||||
'grayscale': bool(int(request.args.get('grayscale', 0))),
|
||||
'scale_factor': int(request.args.get('scale_factor', 1)),
|
||||
'rotate': int(request.args.get('rotate', 0)),
|
||||
'format': 'jpeg',
|
||||
}
|
||||
|
||||
return Response(get_feed(**args),
|
||||
mimetype='multipart/x-mixed-replace; boundary=frame')
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
|
@ -1,9 +1,9 @@
|
|||
@import 'common/vars';
|
||||
|
||||
.camera {
|
||||
height: 90%;
|
||||
margin-top: 7%;
|
||||
overflow: hidden;
|
||||
min-height: 90%;
|
||||
margin-top: 4%;
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
|
|
@ -17,22 +17,43 @@ Vue.component('camera-ir-mlx90640', {
|
|||
return;
|
||||
|
||||
this.capturing = true;
|
||||
|
||||
while (this.capturing) {
|
||||
const img = await request('camera.ir.mlx90640.capture', {
|
||||
format: 'png',
|
||||
rotate: this.rotate,
|
||||
grayscale: this.grayscale,
|
||||
});
|
||||
|
||||
this.$refs.frame.setAttribute('src', 'data:image/png;base64,' + img);
|
||||
}
|
||||
this.$refs.frame.setAttribute('src', '/camera/ir/mlx90640/stream?rotate='
|
||||
+ this.rotate + '&grayscale=' + (this.grayscale ? 1 : 0) + '&t='
|
||||
+ (new Date()).getTime());
|
||||
},
|
||||
|
||||
stopStreaming: async function() {
|
||||
await request('camera.ir.mlx90640.stop');
|
||||
this.$refs.frame.removeAttribute('src');
|
||||
this.capturing = false;
|
||||
},
|
||||
|
||||
onRotationChange: function() {
|
||||
this.rotate = parseInt(this.$refs.rotate.value);
|
||||
const cameraContainer = this.$el.querySelector('.camera-container');
|
||||
|
||||
switch (this.rotate) {
|
||||
case 0:
|
||||
case 180:
|
||||
cameraContainer.style.width = '640px';
|
||||
cameraContainer.style.minWidth = '640px';
|
||||
cameraContainer.style.height = '480px';
|
||||
cameraContainer.style.minHeight = '480px';
|
||||
break;
|
||||
|
||||
case 90:
|
||||
case 270:
|
||||
cameraContainer.style.width = '480px';
|
||||
cameraContainer.style.minWidth = '480px';
|
||||
cameraContainer.style.height = '640px';
|
||||
cameraContainer.style.minHeight = '640px';
|
||||
break;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
mounted: function() {
|
||||
this.onRotationChange();
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<i class="fa fa-stop"></i> Stop streaming
|
||||
</button>
|
||||
|
||||
<select ref="rotate" @change="rotate = $event.target.value" :disabled="capturing">
|
||||
<select ref="rotate" @change="onRotationChange" :disabled="capturing">
|
||||
<option value="0" :selected="rotate == 0">0 degrees</option>
|
||||
<option value="90" :selected="rotate == 90">90 degrees</option>
|
||||
<option value="180" :selected="rotate == 180">180 degrees</option>
|
||||
|
|
|
@ -36,6 +36,11 @@ class CameraIrMlx90640Plugin(Plugin):
|
|||
"""
|
||||
|
||||
_img_size = (32, 24)
|
||||
_rotate_values = {
|
||||
90: Image.ROTATE_90,
|
||||
180: Image.ROTATE_180,
|
||||
270: Image.ROTATE_270,
|
||||
}
|
||||
|
||||
def __init__(self, fps=16, skip_frames=2, scale_factor=1, rotate=0, rawrgb_path=None, **kwargs):
|
||||
"""
|
||||
|
@ -74,7 +79,7 @@ class CameraIrMlx90640Plugin(Plugin):
|
|||
return self._capture_proc
|
||||
|
||||
@action
|
||||
def capture(self, output_file=None, frames=1, grayscale=False, fps=None, skip_frames=None, scale_factor=None, rotate=None, format=None):
|
||||
def capture(self, output_file=None, frames=1, grayscale=False, fps=None, skip_frames=None, scale_factor=None, rotate=None, format='jpeg'):
|
||||
"""
|
||||
Capture one or multiple frames and return them as raw RGB
|
||||
|
||||
|
@ -100,18 +105,18 @@ class CameraIrMlx90640Plugin(Plugin):
|
|||
:param rotate: If set it overrides the rotate parameter specified on the object (default: None)
|
||||
:type rotate: int
|
||||
|
||||
:param format: Output image format if output_file is not specified(default: None, raw RGB).
|
||||
:param format: Output image format if output_file is not specified (default: jpeg).
|
||||
It can be jpg, png, gif or any format supported by PIL
|
||||
:type format: str
|
||||
|
||||
:returns: list[str]. Each item is a base64 encoded raw RGB representation of a frame if output_file is not set, otherwise a list with
|
||||
:returns: list[str]. Each item is a base64 encoded representation of a frame in the specified format if output_file is not set, otherwise a list with
|
||||
the captured image files will be returned.
|
||||
"""
|
||||
|
||||
fps = self.fps if fps is None else fps
|
||||
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 if rotate is None else rotate
|
||||
rotate = self._rotate_values.get(self.rotate if rotate is None else rotate, 0)
|
||||
|
||||
size = self._img_size
|
||||
sleep_time = 1.0 / self.fps
|
||||
|
@ -132,21 +137,16 @@ class CameraIrMlx90640Plugin(Plugin):
|
|||
|
||||
if grayscale:
|
||||
image = self._convert_to_grayscale(image)
|
||||
if rotate:
|
||||
image = image.rotate(rotate)
|
||||
if scale_factor != 1:
|
||||
size = tuple(i*scale_factor for i in size)
|
||||
image = image.resize(size, Image.ANTIALIAS)
|
||||
|
||||
frame = image.getdata()
|
||||
if rotate:
|
||||
image = image.transpose(rotate)
|
||||
|
||||
if not output_file:
|
||||
if format:
|
||||
temp = io.BytesIO()
|
||||
image.save(temp, format=format)
|
||||
frame = temp.getvalue()
|
||||
|
||||
frame = base64.encodebytes(frame).decode()
|
||||
frame = base64.encodebytes(temp.getvalue()).decode()
|
||||
captured_frames.append(frame)
|
||||
else:
|
||||
image_file = os.path.abspath(os.path.expanduser(output_file.format(n_captured_frames)))
|
||||
|
|
Loading…
Reference in a new issue