diff --git a/filters.py b/filters.py new file mode 100644 index 0000000..a1a15ad --- /dev/null +++ b/filters.py @@ -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) diff --git a/html/index.html b/html/index.html index b12ad28..7de147b 100644 --- a/html/index.html +++ b/html/index.html @@ -1,222 +1,266 @@ - - - - Pixelserver Interface + + + + + Pixelserver Interface - - - - - + + + + + - -

Andreas production-ready Interface

+ + + +

Andreas production-ready Interface

+
+

Kommando:

-
+ - +

Intensität:

-
-
+ +
-
+

Gamma:

-
- Rot:
- Grün:
- Blau:
- Weiß:
+ +
+
+
+

Flip:

-
- Flip X
- Flip Y
+ + +

Filterimage:

-
-
- + + +

Filter expression:

-
-
- + + +
-
-

Crash Log:

-
- -
- +
+

Crash Log: + +

+ -

Log:

-
- -
- +

Log: + +

+
- - + function updateCrashLog() { + getRaw("/apps/crashlog", function (text) { + document.getElementById('crashlogs').value = text; + }); + } + 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); + +
+ diff --git a/main.py b/main.py index 9956f88..0a20679 100755 --- a/main.py +++ b/main.py @@ -4,7 +4,6 @@ import json import logging import math import os -import string import subprocess import threading import time @@ -12,13 +11,12 @@ from collections import OrderedDict import bottle import numpy as np -import scipy.misc import serial import config +import filters logging.basicConfig(filename='pixelserver.log', level=config.LogLevel) -running = True ######################################################################## @@ -151,7 +149,7 @@ if config.UseGui: def join(self, **kwargs): with self.cv: self.cv.notify_all() - super().join() + super().join(**kwargs) ######################################################################## @@ -162,6 +160,7 @@ class SerialWriter(threading.Thread): super().__init__(daemon=True) self.cv = threading.Condition() self.datasource = datasource.addListener(self.cv) + self.gamma_rgbw = 0, 0, 0, 0 self.updateGamma = False def run(self): @@ -179,13 +178,15 @@ class SerialWriter(threading.Thread): frame = self.datasource.getData() data = frame.buffer.reshape((config.ScreenX * config.ScreenY * frame.channels,)).astype(np.uint8).tobytes() 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): - apply = lambda x, g: max(0, min(255, int(math.pow(x / 255, g) * 255))) - buf[i] = apply(i, self.r) - buf[i + 256] = apply(i, self.g) - buf[i + 512] = apply(i, self.b) - buf[i + 512 + 256] = apply(i, self.w) + buf[i] = apply(i, r) + buf[i + 256] = apply(i, g) + buf[i + 256 * 2] = apply(i, b) + buf[i + 256 * 3] = apply(i, w) ser.write(b"\x02") ser.write(buf) self.updateGamma = False @@ -195,7 +196,7 @@ class SerialWriter(threading.Thread): elif frame.channels == 4: ser.write(b"\03") 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() except Exception as e: if ser is not None: @@ -209,11 +210,11 @@ class SerialWriter(threading.Thread): def join(self, **kwargs): with self.cv: self.cv.notify_all() - super().join() + super().join(**kwargs) def setGamma(self, r, g, b, w): 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.cv.notify_all() @@ -317,7 +318,7 @@ class AppRunner(threading.Thread): self.requestedApp = app self.param = param self.cv.notify_all() - logging.info("Requesting app: " + str(app)) + logging.info(f"Requesting app: {app}") def startApp(self, i, param=""): app = config.Apps[i] @@ -373,90 +374,14 @@ class AppRunner(threading.Thread): def setGamma(self, r, g, b, w): self.serial.setGamma(r, g, b, w) - def setFilter(self, name, filter): - self.filters[name] = filter + def setFilter(self, name, filter_): + self.filters[name] = filter_ def removeFilter(self, name): if name in self.filters.keys(): 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 # ######################################################################## @@ -530,17 +455,15 @@ def apps_running(): def index(): return bottle.static_file("index.html", root='html') + @bottle.route("/") def serve_static(filepath): return bottle.static_file(filepath, root='html') + @bottle.route("/setgamma////") def setGamma(r, g, b, w): - r = float(r) - g = float(g) - b = float(b) - w = float(w) - runner.setGamma(r, g, b, w) + runner.setGamma(float(r), float(g), float(b), float(w)) return "ok" @@ -549,14 +472,14 @@ def setIntensity(i): i = float(i) if not 0 <= i <= 1: return "bad_value" - runner.setFilter("0_intensity", MakeBrightnessFilter(i)) + runner.setFilter("0_intensity", filters.MakeBrightnessFilter(i)) return "ok" @bottle.route("/filter/flipx/") def flipx(do): if do == "true": - runner.setFilter("1_flipx", FlipXFilter) + runner.setFilter("1_flipx", filters.FlipXFilter) else: runner.removeFilter("1_flipx") return "ok" @@ -565,7 +488,7 @@ def flipx(do): @bottle.route("/filter/flipy/") def flipy(do): if do == "true": - runner.setFilter("1_flipy", FlipYFilter) + runner.setFilter("1_flipy", filters.FlipYFilter) else: runner.removeFilter("1_flipy") return "ok" @@ -576,7 +499,7 @@ def setfilter(name): if name == "none": runner.removeFilter("3_imgfilter") else: - runner.setFilter("3_imgfilter", MakeBrightnessImageFilter(name)) + runner.setFilter("3_imgfilter", filters.MakeBrightnessImageFilter(name)) return "ok" @@ -586,21 +509,23 @@ def filter_expr(): if expr == "" or expr == "none": runner.removeFilter("5_brightnessfunction") else: - runner.setFilter("5_brightnessfunction", MakeBrightnessExprFilter(expr)) + runner.setFilter("5_brightnessfunction", filters.MakeBrightnessExprFilter(expr)) return "ok" -######################################################################## -# Startup # -######################################################################## -runner = AppRunner() -runner.start() +if __name__ == '__main__': + ######################################################################## + # Startup # + ######################################################################## + running = True + runner = AppRunner() + runner.start() -# runner.setFilter("5_crazy", MakeBrightnessExprFilter("0.5+0.25*sin(x/3)/x")) -bottle.run(host=config.WebHost, port=config.WebPort) + # runner.setFilter("5_crazy", MakeBrightnessExprFilter("0.5+0.25*sin(x/3)/x")) + bottle.run(host=config.WebHost, port=config.WebPort) -######################################################################## -# Shutdown # -######################################################################## -running = False -runner.join() + ######################################################################## + # Shutdown # + ######################################################################## + running = False + runner.join()