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';
|
@import 'common/vars';
|
||||||
|
|
||||||
.camera {
|
.camera {
|
||||||
height: 90%;
|
min-height: 90%;
|
||||||
margin-top: 7%;
|
margin-top: 4%;
|
||||||
overflow: hidden;
|
overflow: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -17,22 +17,43 @@ Vue.component('camera-ir-mlx90640', {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.capturing = true;
|
this.capturing = true;
|
||||||
|
this.$refs.frame.setAttribute('src', '/camera/ir/mlx90640/stream?rotate='
|
||||||
while (this.capturing) {
|
+ this.rotate + '&grayscale=' + (this.grayscale ? 1 : 0) + '&t='
|
||||||
const img = await request('camera.ir.mlx90640.capture', {
|
+ (new Date()).getTime());
|
||||||
format: 'png',
|
|
||||||
rotate: this.rotate,
|
|
||||||
grayscale: this.grayscale,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$refs.frame.setAttribute('src', 'data:image/png;base64,' + img);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
stopStreaming: async function() {
|
stopStreaming: async function() {
|
||||||
await request('camera.ir.mlx90640.stop');
|
await request('camera.ir.mlx90640.stop');
|
||||||
|
this.$refs.frame.removeAttribute('src');
|
||||||
this.capturing = false;
|
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
|
<i class="fa fa-stop"></i> Stop streaming
|
||||||
</button>
|
</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="0" :selected="rotate == 0">0 degrees</option>
|
||||||
<option value="90" :selected="rotate == 90">90 degrees</option>
|
<option value="90" :selected="rotate == 90">90 degrees</option>
|
||||||
<option value="180" :selected="rotate == 180">180 degrees</option>
|
<option value="180" :selected="rotate == 180">180 degrees</option>
|
||||||
|
|
|
@ -36,6 +36,11 @@ class CameraIrMlx90640Plugin(Plugin):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_img_size = (32, 24)
|
_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):
|
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
|
return self._capture_proc
|
||||||
|
|
||||||
@action
|
@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
|
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)
|
:param rotate: If set it overrides the rotate parameter specified on the object (default: None)
|
||||||
:type rotate: int
|
: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
|
It can be jpg, png, gif or any format supported by PIL
|
||||||
:type format: str
|
: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.
|
the captured image files will be returned.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fps = self.fps if fps is None else fps
|
fps = self.fps if fps is None else fps
|
||||||
skip_frames = self.skip_frames if skip_frames is None else skip_frames
|
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
|
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
|
size = self._img_size
|
||||||
sleep_time = 1.0 / self.fps
|
sleep_time = 1.0 / self.fps
|
||||||
|
@ -132,21 +137,16 @@ class CameraIrMlx90640Plugin(Plugin):
|
||||||
|
|
||||||
if grayscale:
|
if grayscale:
|
||||||
image = self._convert_to_grayscale(image)
|
image = self._convert_to_grayscale(image)
|
||||||
if rotate:
|
|
||||||
image = image.rotate(rotate)
|
|
||||||
if scale_factor != 1:
|
if scale_factor != 1:
|
||||||
size = tuple(i*scale_factor for i in size)
|
size = tuple(i*scale_factor for i in size)
|
||||||
image = image.resize(size, Image.ANTIALIAS)
|
image = image.resize(size, Image.ANTIALIAS)
|
||||||
|
if rotate:
|
||||||
frame = image.getdata()
|
image = image.transpose(rotate)
|
||||||
|
|
||||||
if not output_file:
|
if not output_file:
|
||||||
if format:
|
|
||||||
temp = io.BytesIO()
|
temp = io.BytesIO()
|
||||||
image.save(temp, format=format)
|
image.save(temp, format=format)
|
||||||
frame = temp.getvalue()
|
frame = base64.encodebytes(temp.getvalue()).decode()
|
||||||
|
|
||||||
frame = base64.encodebytes(frame).decode()
|
|
||||||
captured_frames.append(frame)
|
captured_frames.append(frame)
|
||||||
else:
|
else:
|
||||||
image_file = os.path.abspath(os.path.expanduser(output_file.format(n_captured_frames)))
|
image_file = os.path.abspath(os.path.expanduser(output_file.format(n_captured_frames)))
|
||||||
|
|
Loading…
Reference in a new issue