bmp-Dateien auf dem Display darstellen

Hier werden einzelne Projekte mit MicroPython vorgestellt
Antworten
Heinrichs
Beiträge: 173
Registriert: Do 21. Okt 2010, 18:31

bmp-Dateien auf dem Display darstellen

Beitrag von Heinrichs » Sa 24. Okt 2020, 18:36

Zur graphischen Darstellung stellt die MikroPython-Firmware von Russ Hughes über die Klasse ST7789 eine Reihe von Methoden zur Verfügung. Leider gibt es in dieser Klasse keine Methode, durch die man Bilder mit dem TTGO T-Display darstellen kann. Trotzdem ist es nicht allzu schwer, Bilder auf einem solchen Display wiederzugeben. Der Weg, den ich hier skizzieren werde, geschieht in drei Schritten:
  • Bild-Datei in eine Binär-Datei umwandeln, welche die Farbwerte der einzelnen Pixel im RGB565-Format (s. u.) durch jeweils zwei Bytes beschreibt
  • Binär-Datei auf den ESP32 laden
  • Mit einem Python-Programm diese Binär-Datei auslesen und damit auf dem Display die Farbwerte der Pixel festlegen

1. Bild-Datei in Binär-Datei konvertieren

Für diesen Schritt habe ich mit Delphi ein kleines Programm geschrieben. Dabei habe ich mich auf die Konvertierung von bmp-Dateien beschränkt. Andere Datei-Typen (wie etwa jpg) kann man ja leicht in dieses bmp-Format umwandeln (z. B. mit dem frei erhältlichen Programm Irfan View).

Mein Konvertierungs-Programm bmp2bytes565.exe finden Sie im Anhang bmp2bytes256.zip zusammen mit einem Testbild. Die Benutzung des Programms ist denkbar einfach: Starten Sie das Programm und öffnen Sie die gewünschte bmp-Datei. Achten Sie dabei darauf, dass das Bild maximal 240 Pixel breit und 135 Pixel hoch ist. Im nächsten Schritt führen Sie die Kodierung durch; falls im Code-Fenster noch Bytes von einer vorangegangenen Kodierung stehen, sollte der Inhalt des Code-Fenster zunächst gelöscht werden. Je nach Größe der Bild-Datei kann die Konvertierung einige Sekunden dauern. Die ersten beiden Bytes im Code-Fenster geben die Breite und Höhe des Bildes an, die restlichen Bytes geben die RGB565-Bytes (s. u.) in dezimaler Darstellung an. Im dritten Schritt muss der RGB565-Code gespeichert werden; die Extension ist 'bin'.

Im Rest dieses Abschnitts möchte ich kurz auf das RGB565-Format und die Konvertierung von RGB nach RGB565 eingehen. Diese Darlegungen sind für das Verständnis der restlichen Abschnitte aber nicht unbedingt erforderlich. Bei der meist verwendeten RGB-Codierung wird der Farbwert eines Pixels durch 3 Bytes beschrieben: ein Byte für den Rot-Anteil, ein Byte für den Grün-Anteil und ein Byte für den Blau-Anteil. Bei der RGB565-Codierung werden für den Rot-Anteil statt 8 Bit nur 5 Bit benutzt, für den Grün-Anteil 6 Bit und für den Blau-Anteil wieder nur 5 Bit; insgesamt werden für den Farbwert eines Pixels also nur 16 Bit, d. h. 2 Bytes benutzt:

rgb565.jpg
Abb. 1: RGB565
rgb565.jpg (21.6 KiB) 5467 mal betrachtet

Bei meinem Konvertierungs-Programm folgt die Umwandlung vom RGB- ins RGB565-Format dem folgenden Schema:

bmp2rgb565_klein.jpg
Abb. 2: Schema für die Konvertierung von RGB nach RGB565
bmp2rgb565_klein.jpg (74.83 KiB) 5466 mal betrachtet

2. Binär-Datei auf den ESP32 laden

Öffnen Sie die Thonny-IDE und suchen Sie im This-Computer-Bereich des Datei-Fensters (s. Abb. 3) nach der Binär-Datei des Bildes (z. B. testbild.bin aus dem Anhang). Klicken Sie mit der rechten Maustaste auf diesen Dateinamen; es öffnet sich ein Kontext-Menü. Hier wählen Sie die Option 'upload to /' aus. Nun wird die Binär-Datei in den ESP32 geladen. Dies dauert einige Sekunden. Danach sehen Sie den Namen dieser Datei auch im MicroPython-device-Teil des Datei-Fensters.

thonny_dateien.jpg
Abb. 3: Fenster der Thonny-IDE
thonny_dateien.jpg (30.11 KiB) 5458 mal betrachtet

3. Erstes MicroPython-Programm

In unserem ersten Programm werden die Farbwerte Pixel für Pixel zeilenweise aus der Bin-Datei gelesen und mit der Methode display.pixel ausgegeben; Details können den Programm-Kommentaren entnommen werden:

Code: Alles auswählen


# Bilder mit Hilfe des Delphi-Programms bmp2bytes565.exe in Binär-Format umwandeln:
# Die ersten beiden Bytes geben dabei die Breite und Höhe des Bildes an;
# die einzelnen Pixel werden zeilenweise mit ihren 2 Farbwerten rg und gb im Display-RAM gespeichert (RGB565).

from machine import Pin, SPI
import st7789
from time import sleep

spi = SPI(2, baudrate=20000000, polarity=1, sck=Pin(18), mosi=Pin(19))
display = st7789.ST7789(spi, 135, 240,  reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=3)
# Landscape

display.init()
display.fill(0)

# Zeilenweise lesen

f = open('testbild.bin', 'rb') # öffnen mit "[r]eading as [b]inary"

# Breite und Höhe des Bildes...
data = f.read(2) # Breite und Höhe
data = list(data) # bytearray -> Liste von Zahlen
x_max = data[0]
y_max = data[1]

# Pixel-Daten holen und in Display-Speicher übertragen...
for y in range(0,y_max):
  data = f.read(x_max*2) # 1 Zeile
  data = list(data) # bytearray -> Liste von Zahlen
  # Index des rg- und des gb-Werts vom 1. Pixels in der Zeile y:
  rg = 0
  gb = 1
  for x in range(0,x_max):
    c = (data[rg] << 8) + data[gb]
    display.pixel(x,y,c)
    # nächstes Pixel:
    rg = rg + 2
    gb = gb + 2
    
f.close()    



4. Zweites Micropython-Programm

Unser zweites Programm ist kürzer und arbeitet deutlich schneller. Hier werden die Farbwerte mit der Methode display.blit_buffer übertragen; Details können den Programm-Kommentaren entnommen werden:

Code: Alles auswählen


# Bilder mit Hilfe des Delphi-Programms bmp2bytes565.exe in Binär-Format umwandeln:
# Die ersten beiden Bytes geben dabei die Breite und Höhe des Bildes an, die restlichen Bytes
# stellen für jedes Pixel jeweils das rg- und das gb-Byte dar.
# Die Pixel-Daten werden mit blit_buffer direkt in das Display-RAM geschrieben; sehr einfach und enorm schnell!!!

from machine import Pin, SPI
import st7789

spi = SPI(2, baudrate=20000000, polarity=1, sck=Pin(18), mosi=Pin(19))
display = st7789.ST7789(spi, 135, 240,  reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=3)
# Landscape

display.init()
display.fill(st7789.YELLOW) # Hintergrund

# Breite und Höhe des Bildes...
f = open('testbild.bin', 'rb') # öffnen mit "[r]eading as [b]inary"

data = f.read(2) # Breite und Höhe
data = list(data) # bytearray -> Liste von Zahlen
x_max = data[0]
y_max = data[1]

# Pixel-Daten holen und in Display-Speicher übertragen...
data = f.read(x_max*y_max*2) # alle Pixel-Bytes
display.blit_buffer(data, 50, 10, x_max, y_max)

data = 0 # vermeidet "MemoryError: memory allocation failed, allocating ..." beim Beenden des Programms
    
f.close()    
  

In der folgenden Abb. 4 ist das Testbild (aus dem Anhang) auf dem Display des TTGO zu sehen:

ttgo_mit_testbild.jpg
Abb. 4: TTGO T-Display mit dem Testbild
ttgo_mit_testbild.jpg (18.68 KiB) 5434 mal betrachtet

Zum Zeitbedarf:

Messungen zeigen, dass (bei einer SPI-Baudrate von 33 MHz) zum Löschen des Displays ca. 34 ms benötigt werden. Um unser Testbild aus der Datei zu lesen, braucht das Programm ca. 13 ms; um es mit blit_buffer auf dem Display darzustellen, sind ca. 18 ms nötig.

...
Dateianhänge
bmp2bytes565.zip
Programme und Testbilder
(344.39 KiB) 781-mal heruntergeladen

Antworten