Dithering und Bild-Kodierung
Die e-paper Displays zeigen (so weit ich bis jetzt verstanden habe) nur Pixel in Weiss, Schwarz und Rot an. Bilder müssen also entsprechend konvertiert werden.
Siehe auch das Erklärvideo zur dreifarbigen E-Ink Technologie: https://www.youtube.com/watch?v=58ns93IoHpQ
Dithering
Studieren und Implementieren Sie den https://de.wikipedia.org/wiki/Floyd-Steinberg-Algorithmus
Verwenden Sie dazu folgende Vorlage:
- dithering.py
import cv2 import sys import os import numpy as np file = "test.jpg" if len(sys.argv)>1: file = sys.argv[1] if not os.path.exists(file): raise f"Datei {file} nicht gefunden!" def readAndConvert(file): return cv2.cvtColor(cv2.imread(file), cv2.COLOR_BGR2GRAY)/255 # Conversion to float in 0..1 def toBWSimple(img): c = np.copy(img) h,w = c.shape for y in range(h): for x in range(w): if img[y,x]<0.5: c[y,x] = 0 else: c[y,x] = 1.0 return c def floydSteinberg(img): c = np.copy(img) h,w = c.shape # # TODO # return c img = readAndConvert(file) print(img.shape) cv2.imshow('Image', img) cv2.waitKey(1000) c = toBWSimple(img) cv2.imshow('Image', c) cv2.waitKey(1000) c = floydSteinberg(img) cv2.imshow('Image', c) cv2.waitKey(10000)
Codierung als Binärdaten
Die Auflösung unserer Displays ist 800×480, d.h. es werden für ein Schwarz/Weiss Bild (bzw. Rot/Weiss) 48'000 Bytes benötigt.
Die Bits in den einzelnen Bytes werden als Big-Endian interpretiert, die Daten sind Zeilenweise:
D.h. die ersten 8 Pixel sind wie folgt codiert: Bit 7, Bit 6, Bit 5, … , Bit 0
Befüllen Sie folgendes Array mit dem korrekten Werten:
n = 48000 # Oder besser aus der Displaygrösse berechnen data = np.zeros((n,),dtype=np.uint8)
Umrechnung in Bytes
Variante 1: Multiplikation mit 2 (um eine Stelle nach links zu schieben) und Addition von 1 oder 0 (je nach Bit)
Variante 2: Bit Operationen. Um das Bit $n$ einer Binärzahl $b$ zu setzen kann folgendes verwendet werden:
b = b | (1 << n) # Der << Operator ist der Bitshift-Operator, er verschiebt die Bits der Zahl links um n Stellen (d.h. es kann als Multiplikation mit 2^n interpretiert werden) b |= (1 << n) # Kurzform
Variante 3: Kombination der obigen Varianten:
b = 0 for bit in bits: # Annahme: bits ist ein Array von Einsen und Nullen b = (b << 1) | bit
Wandeln Sie dann das Array ins folgende Format um
const unsigned char myimage[48000] = { 18,0,9,74,170,160,0,0,0,0,0,0,0,0,0,4, .... };
Das könnte dann im Code für das Display gebraucht werden.
Später sollen diese Daten natürlich via https direkt vom Server kommen.
Schreiben in eine Datei
Text:
textDaten = "... whatever ..." with open("data.c", "w") as f: f.write(textDaten)
Binär:
binData = np.zeros((100,), dtype=np.uint8) # Array of bytes with open("data.bin", "wb") as f: f.write(binData)
Binäre Übertragung von Daten an den ESP32
- sendBySerial.py
# On Linux: sudo apt install python3-serial # On Windows: pip install serial import serial import serial.tools.list_ports import re import sys import os import time def getPort(): # From https://stackoverflow.com/questions/12090503/listing-available-com-ports-with-python ports = serial.tools.list_ports.comports() for port, desc, hwid in sorted(ports): print("{}: {} [{}]".format(port, desc, hwid)) return [port for port, desc, hwid in ports if re.search(r"USB", desc) or re.search(r"USB", hwid)][0] def getData(): if len(sys.argv)!=2: raise "Bitte Dateinnamen mit binären Daten angeben." fn = sys.argv[1] if not os.path.exists(fn): raise f"Datei {fn} existiert nicht." if not os.path.getsize(fn)==96000: raise f"Die Datei hat nicht die korrekt Grösse von 96000 Bytes" with open(fn, mode='rb') as file: # b is important -> binary data = file.read() return data data = getData() port = getPort() print(f"Sending data to port {port}") with serial.Serial(port, 115200, timeout=0) as ser: ser.write(data) for i in range(3): s = ser.read(1000) if len(s)>0: print(s,end='') time.sleep(0.01) print(f"Done")
Hausaufgaben auf 9. Januar 2023
- Verschlüsselte Zip-Datei mit passwort 'dithering' per e-mail mit:
- Python code, der das Dithering ausführt, nach Binary konvertiert und eine 96kB grosse Binärdatei schreibt.
- Input-Bild
- Output-Datei (binär, 96kB)
Überprüfung der Binärdatei
- readBinary.py
import cv2 import sys import os import numpy as np file = "data.bin" if len(sys.argv)>1: file = sys.argv[1] if not os.path.exists(file): raise f"Datei {file} nicht gefunden!" def readData(file): with open(file, "rb") as f: bindata = f.read() img = np.zeros((480,800,3), dtype=np.uint8) for y in range(480): for x in range(800): bit = 7-x%8 byte = x//8+y*100 bw = (bindata[byte] >> bit) & 1 rw = (bindata[byte+48000] >> bit) & 1 if (bw==1 and rw==1): img[y][x][0] = 255 img[y][x][1] = 255 img[y][x][2] = 255 elif rw==0: img[y][x][2] = 255 return img img = readData(file) cv2.imshow("result", img) cv2.waitKey(5000)