Refactor
This commit is contained in:
parent
313d3aa471
commit
acafc0fc87
85
filters.py
Normal file
85
filters.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
########################################################################
|
||||||
|
# Filter Api #
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
import string
|
||||||
|
import time
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import scipy
|
||||||
|
|
||||||
|
import config
|
||||||
|
|
||||||
|
|
||||||
|
class MakeBrightnessFilter:
|
||||||
|
def __init__(self, intensity):
|
||||||
|
self.intensity = intensity
|
||||||
|
|
||||||
|
def __call__(self, img):
|
||||||
|
return (img * self.intensity).astype(np.uint8)
|
||||||
|
|
||||||
|
|
||||||
|
def FlipXFilter(intensity):
|
||||||
|
return intensity[:, ::-1, :]
|
||||||
|
|
||||||
|
|
||||||
|
def FlipYFilter(intensity):
|
||||||
|
return intensity[::-1, :, :]
|
||||||
|
|
||||||
|
|
||||||
|
class MakeBrightnessImageFilter:
|
||||||
|
def __init__(self, name):
|
||||||
|
self.filter_img = scipy.misc.imread("filter/" + name + ".png", flatten=True) / 255
|
||||||
|
# img = np.transpose(img)
|
||||||
|
|
||||||
|
def __call__(self, img):
|
||||||
|
img = img.astype(float)
|
||||||
|
for i in range(img.shape[2]):
|
||||||
|
img[:, :, i] *= self.filter_img
|
||||||
|
return img.astype(np.uint8)
|
||||||
|
|
||||||
|
|
||||||
|
def strings(s):
|
||||||
|
allowed_chars = string.ascii_letters + string.digits + "+-*/()."
|
||||||
|
i = 0
|
||||||
|
outlist = []
|
||||||
|
while i < len(s):
|
||||||
|
if s[i] not in allowed_chars:
|
||||||
|
raise Exception("Unexpected char " + s[i])
|
||||||
|
if s[i] not in string.ascii_letters:
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
out = ""
|
||||||
|
while i < len(s) and s[i] in string.ascii_letters + string.digits:
|
||||||
|
out += s[i]
|
||||||
|
i += 1
|
||||||
|
outlist.append(out)
|
||||||
|
return outlist
|
||||||
|
|
||||||
|
|
||||||
|
def eval_safer(expr, x, y, t):
|
||||||
|
symbols = {"x": x, "y": y, "t": t,
|
||||||
|
"sin": np.sin, "cos": np.cos, "exp": np.exp, "tan": np.tan}
|
||||||
|
strs = strings(expr)
|
||||||
|
for s in strs:
|
||||||
|
if s not in symbols.keys():
|
||||||
|
raise Exception(f"Unexpected symbol: {s}")
|
||||||
|
return eval(expr, {}, symbols)
|
||||||
|
|
||||||
|
|
||||||
|
class MakeBrightnessExprFilter:
|
||||||
|
def __init__(self, expr: str):
|
||||||
|
self.expr = expr.replace(" ", "")
|
||||||
|
self.t0 = time.time()
|
||||||
|
self.x, self.y = np.meshgrid(np.arange(config.ScreenX), np.arange(config.ScreenY))
|
||||||
|
|
||||||
|
eval_safer(self.expr, 0, 0, 0) # check expression
|
||||||
|
|
||||||
|
def __call__(self, img):
|
||||||
|
t = time.time() - self.t0
|
||||||
|
img = img.astype(float)
|
||||||
|
filter_ = 0 * self.x + eval_safer(self.expr, self.x, self.y, t)
|
||||||
|
filter_ = np.clip(np.nan_to_num(filter_), 0, 1)
|
||||||
|
for i in range(img.shape[2]):
|
||||||
|
img[:, :, i] *= filter_
|
||||||
|
return img.astype(np.uint8)
|
402
html/index.html
402
html/index.html
@ -1,222 +1,266 @@
|
|||||||
<html>
|
<!DOCTYPE html>
|
||||||
<head>
|
<html lang="de">
|
||||||
<meta charset="utf-8">
|
<head>
|
||||||
<title>Pixelserver Interface</title>
|
<meta charset="utf-8">
|
||||||
|
<title>Pixelserver Interface</title>
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png">
|
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=1, viewport-fit=auto"/>
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png">
|
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/icons/favicon-16x16.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png">
|
||||||
<link rel="manifest" href="/site.webmanifest">
|
<link rel="icon" type="image/png" sizes="16x16" href="/icons/favicon-16x16.png">
|
||||||
</head>
|
<link rel="manifest" href="/site.webmanifest">
|
||||||
|
|
||||||
<body style="display: grid; grid-template-columns: auto auto auto;">
|
<style>
|
||||||
<h1 style="grid-column: 1 / 4; text-align: center;">Andreas <i>production-ready</i> Interface</h1>
|
.main {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 1rem;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
<div>
|
@media only screen {
|
||||||
|
.main {
|
||||||
|
grid-template-columns: repeat(1, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (min-width: 800px) {
|
||||||
|
.main {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (min-width: 1111px) {
|
||||||
|
.main {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.col {
|
||||||
|
min-width: 300px;
|
||||||
|
/*max-width: 350px;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.just, .just-col {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.just > * {
|
||||||
|
flex: 1;
|
||||||
|
margin: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.just-col {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.just-col > * {
|
||||||
|
flex: 1;
|
||||||
|
margin: 2px 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gammaform span {
|
||||||
|
display: inline-block;
|
||||||
|
width: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logging {
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
max-width: 350px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1 style="text-align: center;">Andreas <i>production-ready</i> Interface</h1>
|
||||||
|
<div class="main">
|
||||||
|
<div class="col">
|
||||||
<h2>Kommando:</h2>
|
<h2>Kommando:</h2>
|
||||||
<form id='in' onSubmit="return request()">
|
<form id="in" class="just" onSubmit="return request()">
|
||||||
<select id="list"></select>
|
<select id="list"></select>
|
||||||
<input id="args" />
|
<input id="args"/>
|
||||||
<button id="execute">Ausführen</button>
|
<button id="execute">Ausführen</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<h2>Intensität:</h2>
|
<h2>Intensität:</h2>
|
||||||
<form id='brightnessform' onSubmit="return setbrightness()">
|
<form id="brightnessform" class="just" onSubmit="return setbrightness()">
|
||||||
<input id="brightness" value=1.0 /><br/>
|
<input id="brightness" value="1.0"><br/>
|
||||||
<button id="sendbrightness">Setzen</button>
|
<button id="sendbrightness">Setzen</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div style="border-left: solid 1px; padding-left: 10px;">
|
<div class="options col">
|
||||||
<h2>Gamma:</h2>
|
<h2>Gamma:</h2>
|
||||||
<form id='gammeform' onSubmit="return setgamma()">
|
<form id="gammaform" class="just-col" onSubmit="return setgamma()">
|
||||||
Rot: <input id="gammar" value=2.8 /><br/>
|
<label><span>Rot: </span><input id="gammar" value="2.8"/></label><br/>
|
||||||
Grün: <input id="gammag" value=2.65 /><br/>
|
<label><span>Grün: </span><input id="gammag" value="2.65"/></label><br/>
|
||||||
Blau: <input id="gammab" value=2.65 /><br/>
|
<label><span>Blau: </span><input id="gammab" value="2.65"/></label><br/>
|
||||||
Weiß: <input id="gammaw" value=2.65 /><br/>
|
<label><span>Weiß: </span><input id="gammaw" value="2.65"/></label><br/>
|
||||||
<button id="sendgamma">Setzen</button>
|
<button id="sendgamma">Setzen</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<h2>Flip:</h2>
|
<h2>Flip:</h2>
|
||||||
<form id="flipform" onSubmit="return setFlip()">
|
<form id="flipform" class="just" onSubmit="return setFlip()">
|
||||||
<input id="flipx" type="checkbox" /> Flip X <br/>
|
<label><input id="flipx" type="checkbox"/> Flip X</label>
|
||||||
<input id="flipy" type="checkbox" /> Flip Y <br/>
|
<label><input id="flipy" type="checkbox"/> Flip Y</label>
|
||||||
<button id="sendflip">Setzen</button>
|
<button id="sendflip">Setzen</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<h2>Filterimage:</h2>
|
<h2>Filterimage:</h2>
|
||||||
<form id="filteform" onSubmit="return setFilter()">
|
<form id="filterform" class="just" onSubmit="return setFilter()">
|
||||||
<input id="filtername" value="test"></intput><br/>
|
<input id="filtername" value="test">
|
||||||
<button id="filterflip">Setzten</button>
|
<button id="filterflip">Setzen</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<h2>Filter expression:</h2>
|
<h2>Filter expression:</h2>
|
||||||
<form id="filterexprform" onSubmit="return setFilterExpr()">
|
<form id="filterexprform" class="just" onSubmit="return setFilterExpr()">
|
||||||
<input id="filterexpr" value="0.5+0.25*sin(x/3+t)"></intput><br/>
|
<input id="filterexpr" value="0.5+0.25*sin(x/3+t)">
|
||||||
<button>Setzten</button>
|
<button>Setzen</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div style="border-left: solid 1px; padding-left: 10px; width: 600px;">
|
<div class="logging col">
|
||||||
<h2>Crash Log:</h2>
|
<h2>Crash Log:
|
||||||
<form id="crashlogform" onSubmit="return enableCrashLog()">
|
<button id="crashlogbtn" type="button" onclick="return enableCrashLog()">start</button>
|
||||||
<button>start</button>
|
</h2>
|
||||||
</form>
|
<textarea readonly id="crashlogs" style="width: 500px; height: 300px; display: none;"></textarea>
|
||||||
<textarea readonly id='crashlogs' style="width: 500px; height: 300px; display: none;"></textarea>
|
|
||||||
|
|
||||||
<h2>Log:</h2>
|
<h2>Log:
|
||||||
<form id="logform" onSubmit="return enableLog()">
|
<button id="logbtn" type="button" onclick="return enableLog()">start</button>
|
||||||
<button>start</button>
|
</h2>
|
||||||
</form>
|
<textarea readonly id="logs" style="width: 500px; height: 300px; display: none;"></textarea>
|
||||||
<textarea readonly id='logs' style="width: 500px; height: 300px; display: none;"></textarea>
|
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
function getRaw(from, callback){
|
function getRaw(url, callback) {
|
||||||
var xhttp = new XMLHttpRequest();
|
const xhttp = new XMLHttpRequest();
|
||||||
xhttp.onreadystatechange = function() {
|
xhttp.onreadystatechange = function () {
|
||||||
if (this.readyState == 4 && this.status == 200) {
|
if (this.readyState === 4 && this.status === 200)
|
||||||
callback(xhttp.responseText);
|
if (callback) callback(xhttp.responseText);
|
||||||
}
|
};
|
||||||
};
|
xhttp.open("GET", url, true);
|
||||||
xhttp.open("GET", from, true);
|
xhttp.send();
|
||||||
xhttp.send();
|
}
|
||||||
|
|
||||||
|
function getJSON(url, callback) {
|
||||||
|
getRaw(url, function (text) {
|
||||||
|
callback(JSON.parse(text));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function post(url, data) {
|
||||||
|
const formdata = new FormData();
|
||||||
|
for (const key in data)
|
||||||
|
formdata.append(key, data[key]);
|
||||||
|
|
||||||
|
const xhttp = new XMLHttpRequest();
|
||||||
|
// xhttp.onreadystatechange = function () {
|
||||||
|
// if (this.readyState === 4 && this.status === 200) {}
|
||||||
|
// };
|
||||||
|
xhttp.open("POST", url, true);
|
||||||
|
xhttp.send(formdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
function populateForm(parameters) {
|
||||||
|
const list = document.getElementById("list");
|
||||||
|
|
||||||
|
for (const i in parameters) {
|
||||||
|
const app = parameters[i];
|
||||||
|
const name = app["name"];
|
||||||
|
const guiname = app["guiname"];
|
||||||
|
const persistent = app["persistent"];
|
||||||
|
const element = document.createElement("option");
|
||||||
|
element.value = name;
|
||||||
|
element.innerHTML = guiname;
|
||||||
|
element.dataset.persistent = persistent;
|
||||||
|
list.appendChild(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getJSON(from, callback){
|
list.onchange = function () {
|
||||||
getRaw(from, function(text){
|
const app = document.getElementById('list');
|
||||||
callback(JSON.parse(text));
|
const persistent = app.options[app.selectedIndex].dataset.persistent;
|
||||||
});
|
if (persistent == "true") {
|
||||||
}
|
document.getElementById("args").style.display = "none";
|
||||||
|
} else {
|
||||||
function post(from, data){
|
document.getElementById("args").style.display = "inline";
|
||||||
var formdata = new FormData();
|
|
||||||
for (var key in data) {
|
|
||||||
formdata.append(key, data[key]);
|
|
||||||
}
|
}
|
||||||
let xhttp = new XMLHttpRequest();
|
};
|
||||||
xhttp.onreadystatechange = function() {
|
list.onchange();
|
||||||
if (this.readyState == 4 && this.status == 200) {
|
document.getElementById("container").style.display = "block";
|
||||||
}
|
}
|
||||||
};
|
|
||||||
xhttp.open("POST", from, true);
|
|
||||||
xhttp.send(formdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
function populateForm(parameters){
|
function request() {
|
||||||
let list = document.getElementById("list");
|
let app = document.getElementById('list');
|
||||||
|
let val = app.options[app.selectedIndex].value;
|
||||||
|
let parameter = document.getElementById('args').value;
|
||||||
|
post("/apps/start/" + val, {"param": parameter});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
parameters.map ( e => {
|
function setgamma() {
|
||||||
})
|
const r = document.getElementById('gammar').value;
|
||||||
for (var i in parameters) {
|
const g = document.getElementById('gammag').value;
|
||||||
let app = parameters[i];
|
const b = document.getElementById('gammab').value;
|
||||||
let name = app["name"];
|
const w = document.getElementById('gammaw').value;
|
||||||
let guiname = app["guiname"];
|
getRaw(`/setgamma/${r}/${g}/${b}/${w}`);
|
||||||
let persistent = app["persistent"];
|
return false;
|
||||||
let element = document.createElement("option");
|
}
|
||||||
element.value = name;
|
|
||||||
element.innerHTML = guiname;
|
|
||||||
element.dataset.persistent = persistent;
|
|
||||||
list.appendChild(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
list.onchange = function(){
|
function setbrightness() {
|
||||||
let app = document.getElementById('list');
|
const i = document.getElementById('brightness').value;
|
||||||
let persistent = app.options[app.selectedIndex].dataset.persistent;
|
getRaw("/setbrightness/" + i);
|
||||||
if (persistent == "true"){
|
return false;
|
||||||
document.getElementById("args").style.display = "none";
|
}
|
||||||
}else{
|
|
||||||
document.getElementById("args").style.display = "inline";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
list.onchange();
|
|
||||||
document.getElementById("container").style.display = "block";
|
|
||||||
}
|
|
||||||
|
|
||||||
function request(){
|
function setFlip() {
|
||||||
let app = document.getElementById('list');
|
const x = document.getElementById('flipx').checked;
|
||||||
let val = app.options[app.selectedIndex].value;
|
const y = document.getElementById('flipy').checked;
|
||||||
let parameter = document.getElementById('args').value;
|
getRaw("/filter/flipx/" + x);
|
||||||
let url = "/apps/start/" + val;
|
getRaw("/filter/flipy/" + y);
|
||||||
post(url, {"param": parameter});
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function setgamma(){
|
function setFilter() {
|
||||||
let r = document.getElementById('gammar').value;
|
const i = document.getElementById('filtername').value;
|
||||||
let g = document.getElementById('gammag').value;
|
getRaw("/filter/img/" + i);
|
||||||
let b = document.getElementById('gammab').value;
|
return false;
|
||||||
let w = document.getElementById('gammaw').value;
|
}
|
||||||
let url = "/setgamma/" + r+"/"+g+"/"+b+"/"+w;
|
|
||||||
getRaw(url, function test(){});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setbrightness(){
|
function setFilterExpr() {
|
||||||
let i = document.getElementById('brightness').value;
|
const expr = document.getElementById('filterexpr').value;
|
||||||
let url = "/setbrightness/" + i;
|
post("/filter/expr/", {"expr": expr});
|
||||||
getRaw(url, function test(){});
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function setFlip(){
|
function updateLog() {
|
||||||
let x = document.getElementById('flipx').checked;
|
getRaw("/apps/log", function (text) {
|
||||||
let y = document.getElementById('flipy').checked;
|
document.getElementById('logs').value = text;
|
||||||
console.log(x);
|
});
|
||||||
console.log(y);
|
}
|
||||||
getRaw("/filter/flipx/" + x, function test(){});
|
|
||||||
getRaw("/filter/flipy/" + y, function test(){});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setFilter(){
|
function updateCrashLog() {
|
||||||
let i = document.getElementById('filtername').value;
|
getRaw("/apps/crashlog", function (text) {
|
||||||
let url = "/filter/img/" + i;
|
document.getElementById('crashlogs').value = text;
|
||||||
getRaw(url, function test(){});
|
});
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function setFilterExpr(){
|
|
||||||
let expr = document.getElementById('filterexpr').value;
|
|
||||||
let url = "/filter/expr/";
|
|
||||||
post(url, {"expr": expr});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateLog(){
|
|
||||||
getRaw("/apps/log", function(text){
|
|
||||||
document.getElementById('logs').value = text;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateCrashLog(){
|
|
||||||
getRaw("/apps/crashlog", function(text){
|
|
||||||
document.getElementById('crashlogs').value = text;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function enableCrashLog(){
|
|
||||||
document.getElementById("crashlogform").style.display = "none";
|
|
||||||
document.getElementById("crashlogs").style.display = "block";
|
|
||||||
|
|
||||||
updateCrashLog();
|
|
||||||
setInterval(updateCrashLog, 1000);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function enableLog(){
|
|
||||||
document.getElementById("logform").style.display = "none";
|
|
||||||
document.getElementById("logs").style.display = "block";
|
|
||||||
updateLog();
|
|
||||||
setInterval(updateLog, 1000);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
getJSON("/apps/list", populateForm);
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
|
function enableCrashLog() {
|
||||||
|
document.getElementById("crashlogbtn").remove();
|
||||||
|
document.getElementById("crashlogs").style.display = "block";
|
||||||
|
updateCrashLog();
|
||||||
|
setInterval(updateCrashLog, 1000);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableLog() {
|
||||||
|
document.getElementById("logbtn").remove();
|
||||||
|
document.getElementById("logs").style.display = "block";
|
||||||
|
updateLog();
|
||||||
|
setInterval(updateLog, 1000);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getJSON("/apps/list", populateForm);
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
153
main.py
153
main.py
@ -4,7 +4,6 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
import string
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
@ -12,13 +11,12 @@ from collections import OrderedDict
|
|||||||
|
|
||||||
import bottle
|
import bottle
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import scipy.misc
|
|
||||||
import serial
|
import serial
|
||||||
|
|
||||||
import config
|
import config
|
||||||
|
import filters
|
||||||
|
|
||||||
logging.basicConfig(filename='pixelserver.log', level=config.LogLevel)
|
logging.basicConfig(filename='pixelserver.log', level=config.LogLevel)
|
||||||
running = True
|
|
||||||
|
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
@ -151,7 +149,7 @@ if config.UseGui:
|
|||||||
def join(self, **kwargs):
|
def join(self, **kwargs):
|
||||||
with self.cv:
|
with self.cv:
|
||||||
self.cv.notify_all()
|
self.cv.notify_all()
|
||||||
super().join()
|
super().join(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
@ -162,6 +160,7 @@ class SerialWriter(threading.Thread):
|
|||||||
super().__init__(daemon=True)
|
super().__init__(daemon=True)
|
||||||
self.cv = threading.Condition()
|
self.cv = threading.Condition()
|
||||||
self.datasource = datasource.addListener(self.cv)
|
self.datasource = datasource.addListener(self.cv)
|
||||||
|
self.gamma_rgbw = 0, 0, 0, 0
|
||||||
self.updateGamma = False
|
self.updateGamma = False
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@ -179,13 +178,15 @@ class SerialWriter(threading.Thread):
|
|||||||
frame = self.datasource.getData()
|
frame = self.datasource.getData()
|
||||||
data = frame.buffer.reshape((config.ScreenX * config.ScreenY * frame.channels,)).astype(np.uint8).tobytes()
|
data = frame.buffer.reshape((config.ScreenX * config.ScreenY * frame.channels,)).astype(np.uint8).tobytes()
|
||||||
if self.updateGamma:
|
if self.updateGamma:
|
||||||
buf = bytearray(b"\x00") * 4 * 256
|
r, g, b, w = self.gamma_rgbw
|
||||||
|
apply = lambda x, g: max(0, min(255, int(math.pow(x / 255, g) * 255)))
|
||||||
|
|
||||||
|
buf = bytearray(4 * 256)
|
||||||
for i in range(256):
|
for i in range(256):
|
||||||
apply = lambda x, g: max(0, min(255, int(math.pow(x / 255, g) * 255)))
|
buf[i] = apply(i, r)
|
||||||
buf[i] = apply(i, self.r)
|
buf[i + 256] = apply(i, g)
|
||||||
buf[i + 256] = apply(i, self.g)
|
buf[i + 256 * 2] = apply(i, b)
|
||||||
buf[i + 512] = apply(i, self.b)
|
buf[i + 256 * 3] = apply(i, w)
|
||||||
buf[i + 512 + 256] = apply(i, self.w)
|
|
||||||
ser.write(b"\x02")
|
ser.write(b"\x02")
|
||||||
ser.write(buf)
|
ser.write(buf)
|
||||||
self.updateGamma = False
|
self.updateGamma = False
|
||||||
@ -195,7 +196,7 @@ class SerialWriter(threading.Thread):
|
|||||||
elif frame.channels == 4:
|
elif frame.channels == 4:
|
||||||
ser.write(b"\03")
|
ser.write(b"\03")
|
||||||
ser.write(data)
|
ser.write(data)
|
||||||
logging.debug("Time to gui: " + str(time.time() - frame.created))
|
logging.debug(f"Time to gui: {time.time() - frame.created}")
|
||||||
ser.flush()
|
ser.flush()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if ser is not None:
|
if ser is not None:
|
||||||
@ -209,11 +210,11 @@ class SerialWriter(threading.Thread):
|
|||||||
def join(self, **kwargs):
|
def join(self, **kwargs):
|
||||||
with self.cv:
|
with self.cv:
|
||||||
self.cv.notify_all()
|
self.cv.notify_all()
|
||||||
super().join()
|
super().join(**kwargs)
|
||||||
|
|
||||||
def setGamma(self, r, g, b, w):
|
def setGamma(self, r, g, b, w):
|
||||||
with self.cv:
|
with self.cv:
|
||||||
self.r, self.g, self.b, self.w = r, g, b, w
|
self.gamma_rgbw = r, g, b, w
|
||||||
self.updateGamma = True
|
self.updateGamma = True
|
||||||
self.cv.notify_all()
|
self.cv.notify_all()
|
||||||
|
|
||||||
@ -317,7 +318,7 @@ class AppRunner(threading.Thread):
|
|||||||
self.requestedApp = app
|
self.requestedApp = app
|
||||||
self.param = param
|
self.param = param
|
||||||
self.cv.notify_all()
|
self.cv.notify_all()
|
||||||
logging.info("Requesting app: " + str(app))
|
logging.info(f"Requesting app: {app}")
|
||||||
|
|
||||||
def startApp(self, i, param=""):
|
def startApp(self, i, param=""):
|
||||||
app = config.Apps[i]
|
app = config.Apps[i]
|
||||||
@ -373,90 +374,14 @@ class AppRunner(threading.Thread):
|
|||||||
def setGamma(self, r, g, b, w):
|
def setGamma(self, r, g, b, w):
|
||||||
self.serial.setGamma(r, g, b, w)
|
self.serial.setGamma(r, g, b, w)
|
||||||
|
|
||||||
def setFilter(self, name, filter):
|
def setFilter(self, name, filter_):
|
||||||
self.filters[name] = filter
|
self.filters[name] = filter_
|
||||||
|
|
||||||
def removeFilter(self, name):
|
def removeFilter(self, name):
|
||||||
if name in self.filters.keys():
|
if name in self.filters.keys():
|
||||||
del self.filters[name]
|
del self.filters[name]
|
||||||
|
|
||||||
|
|
||||||
########################################################################
|
|
||||||
# Filter Api #
|
|
||||||
########################################################################
|
|
||||||
def MakeBrightnessFilter(intensity):
|
|
||||||
def filter(img):
|
|
||||||
return (img * intensity).astype(np.uint8)
|
|
||||||
|
|
||||||
return filter
|
|
||||||
|
|
||||||
|
|
||||||
def FlipXFilter(intensity):
|
|
||||||
return intensity[:, ::-1, :]
|
|
||||||
|
|
||||||
|
|
||||||
def FlipYFilter(intensity):
|
|
||||||
return intensity[::-1, :, :]
|
|
||||||
|
|
||||||
|
|
||||||
def MakeBrightnessImageFilter(name):
|
|
||||||
img = scipy.misc.imread("filter/" + name + ".png", flatten=True) / 255
|
|
||||||
|
|
||||||
# img = np.transpose(img)
|
|
||||||
def filter(intensity):
|
|
||||||
intensity = intensity.astype(float)
|
|
||||||
for i in range(intensity.shape[2]):
|
|
||||||
intensity[:, :, i] *= img
|
|
||||||
return intensity.astype(np.uint8)
|
|
||||||
|
|
||||||
return filter
|
|
||||||
|
|
||||||
|
|
||||||
def strings(s):
|
|
||||||
allowed_chars = string.ascii_letters + string.digits + "+-*/()."
|
|
||||||
i = 0
|
|
||||||
outlist = []
|
|
||||||
while i != len(s):
|
|
||||||
if s[i] not in allowed_chars:
|
|
||||||
raise Exception("Unexpected char " + s[i])
|
|
||||||
if s[i] not in string.ascii_letters:
|
|
||||||
i += 1
|
|
||||||
continue
|
|
||||||
out = ""
|
|
||||||
while i != len(s) and s[i] in string.ascii_letters + string.digits:
|
|
||||||
out += s[i]
|
|
||||||
i += 1
|
|
||||||
outlist.append(out)
|
|
||||||
return outlist
|
|
||||||
|
|
||||||
|
|
||||||
def eval_safer(expr, x, y, t):
|
|
||||||
symbols = {"x": x, "y": y, "t": t,
|
|
||||||
"sin": np.sin, "cos": np.cos, "exp": np.exp, "tan": np.tan}
|
|
||||||
strs = strings(expr)
|
|
||||||
for s in strs:
|
|
||||||
if s not in symbols.keys():
|
|
||||||
raise Exception("unexpected symbol: " + s)
|
|
||||||
return eval(expr, {}, symbols)
|
|
||||||
|
|
||||||
|
|
||||||
def MakeBrightnessExprFilter(expr):
|
|
||||||
t0 = time.time()
|
|
||||||
x, y = np.meshgrid(np.arange(config.ScreenX), np.arange(config.ScreenY))
|
|
||||||
eval_safer(expr, 0, 0, 0)
|
|
||||||
|
|
||||||
def filter(intensity):
|
|
||||||
t = time.time() - t0
|
|
||||||
intensity = intensity.astype(float)
|
|
||||||
filter = 0 * x + eval_safer(expr, x, y, t)
|
|
||||||
filter = np.clip(np.nan_to_num(filter), 0, 1)
|
|
||||||
for i in range(intensity.shape[2]):
|
|
||||||
intensity[:, :, i] *= filter
|
|
||||||
return intensity.astype(np.uint8)
|
|
||||||
|
|
||||||
return filter
|
|
||||||
|
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
# Web Api #
|
# Web Api #
|
||||||
########################################################################
|
########################################################################
|
||||||
@ -530,17 +455,15 @@ def apps_running():
|
|||||||
def index():
|
def index():
|
||||||
return bottle.static_file("index.html", root='html')
|
return bottle.static_file("index.html", root='html')
|
||||||
|
|
||||||
|
|
||||||
@bottle.route("/<filepath:path>")
|
@bottle.route("/<filepath:path>")
|
||||||
def serve_static(filepath):
|
def serve_static(filepath):
|
||||||
return bottle.static_file(filepath, root='html')
|
return bottle.static_file(filepath, root='html')
|
||||||
|
|
||||||
|
|
||||||
@bottle.route("/setgamma/<r>/<g>/<b>/<w>")
|
@bottle.route("/setgamma/<r>/<g>/<b>/<w>")
|
||||||
def setGamma(r, g, b, w):
|
def setGamma(r, g, b, w):
|
||||||
r = float(r)
|
runner.setGamma(float(r), float(g), float(b), float(w))
|
||||||
g = float(g)
|
|
||||||
b = float(b)
|
|
||||||
w = float(w)
|
|
||||||
runner.setGamma(r, g, b, w)
|
|
||||||
return "ok"
|
return "ok"
|
||||||
|
|
||||||
|
|
||||||
@ -549,14 +472,14 @@ def setIntensity(i):
|
|||||||
i = float(i)
|
i = float(i)
|
||||||
if not 0 <= i <= 1:
|
if not 0 <= i <= 1:
|
||||||
return "bad_value"
|
return "bad_value"
|
||||||
runner.setFilter("0_intensity", MakeBrightnessFilter(i))
|
runner.setFilter("0_intensity", filters.MakeBrightnessFilter(i))
|
||||||
return "ok"
|
return "ok"
|
||||||
|
|
||||||
|
|
||||||
@bottle.route("/filter/flipx/<do>")
|
@bottle.route("/filter/flipx/<do>")
|
||||||
def flipx(do):
|
def flipx(do):
|
||||||
if do == "true":
|
if do == "true":
|
||||||
runner.setFilter("1_flipx", FlipXFilter)
|
runner.setFilter("1_flipx", filters.FlipXFilter)
|
||||||
else:
|
else:
|
||||||
runner.removeFilter("1_flipx")
|
runner.removeFilter("1_flipx")
|
||||||
return "ok"
|
return "ok"
|
||||||
@ -565,7 +488,7 @@ def flipx(do):
|
|||||||
@bottle.route("/filter/flipy/<do>")
|
@bottle.route("/filter/flipy/<do>")
|
||||||
def flipy(do):
|
def flipy(do):
|
||||||
if do == "true":
|
if do == "true":
|
||||||
runner.setFilter("1_flipy", FlipYFilter)
|
runner.setFilter("1_flipy", filters.FlipYFilter)
|
||||||
else:
|
else:
|
||||||
runner.removeFilter("1_flipy")
|
runner.removeFilter("1_flipy")
|
||||||
return "ok"
|
return "ok"
|
||||||
@ -576,7 +499,7 @@ def setfilter(name):
|
|||||||
if name == "none":
|
if name == "none":
|
||||||
runner.removeFilter("3_imgfilter")
|
runner.removeFilter("3_imgfilter")
|
||||||
else:
|
else:
|
||||||
runner.setFilter("3_imgfilter", MakeBrightnessImageFilter(name))
|
runner.setFilter("3_imgfilter", filters.MakeBrightnessImageFilter(name))
|
||||||
return "ok"
|
return "ok"
|
||||||
|
|
||||||
|
|
||||||
@ -586,21 +509,23 @@ def filter_expr():
|
|||||||
if expr == "" or expr == "none":
|
if expr == "" or expr == "none":
|
||||||
runner.removeFilter("5_brightnessfunction")
|
runner.removeFilter("5_brightnessfunction")
|
||||||
else:
|
else:
|
||||||
runner.setFilter("5_brightnessfunction", MakeBrightnessExprFilter(expr))
|
runner.setFilter("5_brightnessfunction", filters.MakeBrightnessExprFilter(expr))
|
||||||
return "ok"
|
return "ok"
|
||||||
|
|
||||||
|
|
||||||
########################################################################
|
if __name__ == '__main__':
|
||||||
# Startup #
|
########################################################################
|
||||||
########################################################################
|
# Startup #
|
||||||
runner = AppRunner()
|
########################################################################
|
||||||
runner.start()
|
running = True
|
||||||
|
runner = AppRunner()
|
||||||
|
runner.start()
|
||||||
|
|
||||||
# runner.setFilter("5_crazy", MakeBrightnessExprFilter("0.5+0.25*sin(x/3)/x"))
|
# runner.setFilter("5_crazy", MakeBrightnessExprFilter("0.5+0.25*sin(x/3)/x"))
|
||||||
bottle.run(host=config.WebHost, port=config.WebPort)
|
bottle.run(host=config.WebHost, port=config.WebPort)
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
# Shutdown #
|
# Shutdown #
|
||||||
########################################################################
|
########################################################################
|
||||||
running = False
|
running = False
|
||||||
runner.join()
|
runner.join()
|
||||||
|
Loading…
Reference in New Issue
Block a user