Refactor
This commit is contained in:
		
							
								
								
									
										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> | ||||
|     <head> | ||||
|         <meta charset="utf-8"> | ||||
|         <title>Pixelserver Interface</title> | ||||
| <!DOCTYPE html> | ||||
| <html lang="de"> | ||||
| <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <title>Pixelserver Interface</title> | ||||
|  | ||||
|         <link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png"> | ||||
|         <link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png"> | ||||
|         <link rel="icon" type="image/png" sizes="16x16" href="/icons/favicon-16x16.png"> | ||||
|         <link rel="manifest" href="/site.webmanifest"> | ||||
|     </head> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=1, viewport-fit=auto"/> | ||||
|     <link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png"> | ||||
|     <link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png"> | ||||
|     <link rel="icon" type="image/png" sizes="16x16" href="/icons/favicon-16x16.png"> | ||||
|     <link rel="manifest" href="/site.webmanifest"> | ||||
|  | ||||
|     <body style="display: grid; grid-template-columns: auto auto auto;"> | ||||
|         <h1 style="grid-column: 1 / 4; text-align: center;">Andreas <i>production-ready</i> Interface</h1> | ||||
|     <style> | ||||
|         .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> | ||||
|         <form id='in' onSubmit="return request()"> | ||||
|         <form id="in" class="just" onSubmit="return request()"> | ||||
|             <select id="list"></select> | ||||
|             <input id="args" /> | ||||
|             <input id="args"/> | ||||
|             <button id="execute">Ausführen</button> | ||||
|         </form> | ||||
|  | ||||
|         <h2>Intensität:</h2> | ||||
|         <form id='brightnessform' onSubmit="return setbrightness()"> | ||||
|             <input id="brightness" value=1.0 /><br/> | ||||
|         <form id="brightnessform" class="just" onSubmit="return setbrightness()"> | ||||
|             <input id="brightness" value="1.0"><br/> | ||||
|             <button id="sendbrightness">Setzen</button> | ||||
|         </form> | ||||
|     </div> | ||||
|     <div style="border-left: solid 1px; padding-left: 10px;"> | ||||
|     <div class="options col"> | ||||
|         <h2>Gamma:</h2> | ||||
|         <form id='gammeform' onSubmit="return setgamma()"> | ||||
|             Rot: <input id="gammar" value=2.8 /><br/> | ||||
|             Grün: <input id="gammag" value=2.65 /><br/> | ||||
|             Blau: <input id="gammab" value=2.65 /><br/> | ||||
|             Weiß: <input id="gammaw" value=2.65 /><br/> | ||||
|         <form id="gammaform" class="just-col" onSubmit="return setgamma()"> | ||||
|             <label><span>Rot: </span><input id="gammar" value="2.8"/></label><br/> | ||||
|             <label><span>Grün: </span><input id="gammag" value="2.65"/></label><br/> | ||||
|             <label><span>Blau: </span><input id="gammab" value="2.65"/></label><br/> | ||||
|             <label><span>Weiß: </span><input id="gammaw" value="2.65"/></label><br/> | ||||
|             <button id="sendgamma">Setzen</button> | ||||
|         </form> | ||||
|  | ||||
|         <h2>Flip:</h2> | ||||
|         <form id="flipform" onSubmit="return setFlip()"> | ||||
|             <input id="flipx" type="checkbox" /> Flip X <br/> | ||||
|             <input id="flipy" type="checkbox" /> Flip Y <br/> | ||||
|         <form id="flipform" class="just" onSubmit="return setFlip()"> | ||||
|             <label><input id="flipx" type="checkbox"/> Flip X</label> | ||||
|             <label><input id="flipy" type="checkbox"/> Flip Y</label> | ||||
|             <button id="sendflip">Setzen</button> | ||||
|         </form> | ||||
|  | ||||
|         <h2>Filterimage:</h2> | ||||
|         <form id="filteform" onSubmit="return setFilter()"> | ||||
|             <input id="filtername" value="test"></intput><br/> | ||||
|             <button id="filterflip">Setzten</button> | ||||
|         <form id="filterform" class="just" onSubmit="return setFilter()"> | ||||
|             <input id="filtername" value="test"> | ||||
|             <button id="filterflip">Setzen</button> | ||||
|         </form> | ||||
|  | ||||
|         <h2>Filter expression:</h2> | ||||
|         <form id="filterexprform" onSubmit="return setFilterExpr()"> | ||||
|             <input id="filterexpr" value="0.5+0.25*sin(x/3+t)"></intput><br/> | ||||
|             <button>Setzten</button> | ||||
|         <form id="filterexprform" class="just" onSubmit="return setFilterExpr()"> | ||||
|             <input id="filterexpr" value="0.5+0.25*sin(x/3+t)"> | ||||
|             <button>Setzen</button> | ||||
|         </form> | ||||
|     </div> | ||||
|     <div  style="border-left: solid 1px; padding-left: 10px; width: 600px;"> | ||||
|         <h2>Crash Log:</h2> | ||||
|         <form id="crashlogform" onSubmit="return enableCrashLog()"> | ||||
|             <button>start</button> | ||||
|         </form> | ||||
|         <textarea readonly id='crashlogs' style="width: 500px; height: 300px; display: none;"></textarea> | ||||
|     <div class="logging col"> | ||||
|         <h2>Crash Log: | ||||
|             <button id="crashlogbtn" type="button" onclick="return enableCrashLog()">start</button> | ||||
|         </h2> | ||||
|         <textarea readonly id="crashlogs" style="width: 500px; height: 300px; display: none;"></textarea> | ||||
|  | ||||
|         <h2>Log:</h2> | ||||
|         <form id="logform" onSubmit="return enableLog()"> | ||||
|             <button>start</button> | ||||
|         </form> | ||||
|         <textarea readonly id='logs' style="width: 500px; height: 300px; display: none;"></textarea> | ||||
|         <h2>Log: | ||||
|             <button id="logbtn" type="button" onclick="return enableLog()">start</button> | ||||
|         </h2> | ||||
|         <textarea readonly id="logs" style="width: 500px; height: 300px; display: none;"></textarea> | ||||
|     </div> | ||||
|         <script> | ||||
|             function getRaw(from, callback){ | ||||
|                 var xhttp = new XMLHttpRequest(); | ||||
|                 xhttp.onreadystatechange = function() { | ||||
|                     if (this.readyState == 4 && this.status == 200) { | ||||
|                         callback(xhttp.responseText); | ||||
|                     } | ||||
|                 }; | ||||
|                 xhttp.open("GET", from, true); | ||||
|                 xhttp.send(); | ||||
|     <script> | ||||
|         function getRaw(url, callback) { | ||||
|             const xhttp = new XMLHttpRequest(); | ||||
|             xhttp.onreadystatechange = function () { | ||||
|                 if (this.readyState === 4 && this.status === 200) | ||||
|                     if (callback) callback(xhttp.responseText); | ||||
|             }; | ||||
|             xhttp.open("GET", url, true); | ||||
|             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){ | ||||
|                 getRaw(from, function(text){ | ||||
|                     callback(JSON.parse(text)); | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             function post(from, data){ | ||||
|                 var formdata = new FormData(); | ||||
|                 for (var key in data) { | ||||
|                     formdata.append(key, data[key]); | ||||
|             list.onchange = function () { | ||||
|                 const app = document.getElementById('list'); | ||||
|                 const persistent = app.options[app.selectedIndex].dataset.persistent; | ||||
|                 if (persistent == "true") { | ||||
|                     document.getElementById("args").style.display = "none"; | ||||
|                 } else { | ||||
|                     document.getElementById("args").style.display = "inline"; | ||||
|                 } | ||||
|                 let xhttp = new XMLHttpRequest(); | ||||
|                 xhttp.onreadystatechange = function() { | ||||
|                     if (this.readyState == 4 && this.status == 200) { | ||||
|                     } | ||||
|                 }; | ||||
|                 xhttp.open("POST", from, true); | ||||
|                 xhttp.send(formdata); | ||||
|             } | ||||
|             }; | ||||
|             list.onchange(); | ||||
|             document.getElementById("container").style.display = "block"; | ||||
|         } | ||||
|  | ||||
|             function populateForm(parameters){ | ||||
|                 let list = document.getElementById("list"); | ||||
|         function request() { | ||||
|             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 => { | ||||
|                 }) | ||||
|                 for (var i in parameters) { | ||||
|                     let app = parameters[i]; | ||||
|                     let name = app["name"]; | ||||
|                     let guiname = app["guiname"]; | ||||
|                     let persistent = app["persistent"]; | ||||
|                     let element = document.createElement("option"); | ||||
|                     element.value = name; | ||||
|                     element.innerHTML = guiname; | ||||
|                     element.dataset.persistent = persistent; | ||||
|                     list.appendChild(element); | ||||
|                 } | ||||
|         function setgamma() { | ||||
|             const r = document.getElementById('gammar').value; | ||||
|             const g = document.getElementById('gammag').value; | ||||
|             const b = document.getElementById('gammab').value; | ||||
|             const w = document.getElementById('gammaw').value; | ||||
|             getRaw(`/setgamma/${r}/${g}/${b}/${w}`); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|                 list.onchange = function(){ | ||||
|                     let app = document.getElementById('list'); | ||||
|                     let persistent = app.options[app.selectedIndex].dataset.persistent; | ||||
|                     if (persistent == "true"){ | ||||
|                         document.getElementById("args").style.display = "none"; | ||||
|                     }else{ | ||||
|                         document.getElementById("args").style.display = "inline"; | ||||
|                     } | ||||
|                 } | ||||
|                 list.onchange(); | ||||
|                 document.getElementById("container").style.display = "block"; | ||||
|             } | ||||
|         function setbrightness() { | ||||
|             const i = document.getElementById('brightness').value; | ||||
|             getRaw("/setbrightness/" + i); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|             function request(){ | ||||
|                 let app = document.getElementById('list'); | ||||
|                 let val = app.options[app.selectedIndex].value; | ||||
|                 let parameter = document.getElementById('args').value; | ||||
|                 let url = "/apps/start/" + val; | ||||
|                 post(url, {"param": parameter}); | ||||
|                 return false; | ||||
|             } | ||||
|         function setFlip() { | ||||
|             const x = document.getElementById('flipx').checked; | ||||
|             const y = document.getElementById('flipy').checked; | ||||
|             getRaw("/filter/flipx/" + x); | ||||
|             getRaw("/filter/flipy/" + y); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|             function setgamma(){ | ||||
|                 let r = document.getElementById('gammar').value; | ||||
|                 let g = document.getElementById('gammag').value; | ||||
|                 let b = document.getElementById('gammab').value; | ||||
|                 let w = document.getElementById('gammaw').value; | ||||
|                 let url = "/setgamma/" + r+"/"+g+"/"+b+"/"+w; | ||||
|                 getRaw(url, function test(){}); | ||||
|                 return false; | ||||
|             } | ||||
|         function setFilter() { | ||||
|             const i = document.getElementById('filtername').value; | ||||
|             getRaw("/filter/img/" + i); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|             function setbrightness(){ | ||||
|                 let i = document.getElementById('brightness').value; | ||||
|                 let url = "/setbrightness/" + i; | ||||
|                 getRaw(url, function test(){}); | ||||
|                 return false; | ||||
|             } | ||||
|         function setFilterExpr() { | ||||
|             const expr = document.getElementById('filterexpr').value; | ||||
|             post("/filter/expr/", {"expr": expr}); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|             function setFlip(){ | ||||
|                 let x = document.getElementById('flipx').checked; | ||||
|                 let y = document.getElementById('flipy').checked; | ||||
|                 console.log(x); | ||||
|                 console.log(y); | ||||
|                 getRaw("/filter/flipx/" + x, function test(){}); | ||||
|                 getRaw("/filter/flipy/" + y, function test(){}); | ||||
|                 return false; | ||||
|             } | ||||
|         function updateLog() { | ||||
|             getRaw("/apps/log", function (text) { | ||||
|                 document.getElementById('logs').value = text; | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|             function setFilter(){ | ||||
|                 let i = document.getElementById('filtername').value; | ||||
|                 let url = "/filter/img/" + i; | ||||
|                 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 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); | ||||
|     </script> | ||||
| </div> | ||||
| </body> | ||||
| </html> | ||||
|   | ||||
							
								
								
									
										153
									
								
								main.py
									
									
									
									
									
								
							
							
						
						
									
										153
									
								
								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("/<filepath:path>") | ||||
| def serve_static(filepath): | ||||
|     return bottle.static_file(filepath, root='html') | ||||
|  | ||||
|  | ||||
| @bottle.route("/setgamma/<r>/<g>/<b>/<w>") | ||||
| 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/<do>") | ||||
| 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/<do>") | ||||
| 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() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 T
					T