lehrkraefte:blc:informatik:efi-2023:images

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

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)

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)

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.

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)
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")
  • 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)
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)
  • lehrkraefte/blc/informatik/efi-2023/images.txt
  • Last modified: 2024/01/09 15:13
  • by Ivo Blöchliger