====== 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:
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 800x480, 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 =====
# 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 ====
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)