Grayscale - Methoden der Graustufenerzeugung
Graustufen auf einem monochromen (einfarbigen) Display erzeugen? Zur Lösung dieses Problems gibt es generell
zwei Lösungsansätze.
Graustufen per Rasterung
Der erste Ansatz nutzt das begrenzte Auflösungsvermögen des menschlichen Auges aus und erzeugt die
unterschiedlichen Graustufen mittels Rasterung. Ähnlich der Graustufenrasterung im Druckbereich
werden mit der Anzahl gesetzter Pixel verschiedene Helligkeitswerte erreicht. Das wohl
bekannteste Muster zur Graustufenerzeugung ist das Schachbrettmuster das den Mittelwert
zwischen Schwarz und Weiß markiert. Die meisten Grafikanwendungen bieten die Möglichkeit
verschiedene Grautöne mittels Stufenrasterung zu erzeugen. Leider geht das je nach Abstufung
(Anzahl der Grautöne) mit einem massiven Verlust an Auflösung einher. Die nachfolgende Abbildung
zeigt eine einfache Testanwendung zur Demonstration. Die Rasterung im unteren
Farbverlauf ist deutlich zu erkennen. Erst mit genügend Abstand zum Display werden die verschiedenen
Pixeldichten als unterschiedliche Grautöne erkannt.
Flackern erzeugt Graustufen (experimentell)
Die zweite Methode ist etwas aufwendiger und ist vergleichbar mit dem Verfahren zur Helligkeitssteuerung
von LEDs mittels Pulsweitenmodulation.
Einzelne Pixel wirken bei hohen Frequenzen abhängig von ihrer Anzeigedauer dunkler oder heller.
In der Demoanwendung wird dies mit einem einfachen Binärzähler (Variable "pattern") erreicht.
Im oberen Teil der Anzeige werden so 8 verschiedene Graustufen erzeugt. Das Experiment zeigt, dass manche Grautöne
flackern und andere stabil erscheinen. Dies ist auf den Alias-Effekt
zurückzuführen. Der Displaycontroller zeigt den Displayinhalt mit einer bestimmten Bildwiederholfrequenz an und es kommt zum
Stroboskopeffekt.
Die Demoanwendung kommt mit dem Standardbefehlssatz der Display-API aus.
Hier geht's zum Download.
#include "Display.h" #include "SPI.h" #include "Bitmap.h" Display lcd = Display(); byte pattern = 0; void setup() { lcd.init(20); lcd.drawBitmap(0, 0, 128, 62, bitmap); lcd.show(); } void loop() { lcd.setPageAddress(0); lcd.setColumnAddress(0); drawWhiteRect(); for (int i=0; i<8; i++) { if(pattern & (i+1) * (i+1)) { drawBlackRect(); } else { drawWhiteRect(); } } drawBlackRect(); pattern++; } void drawBlackRect() { for (int i=0; i<8; i++) { lcd.writeData(0xFF); } } void drawWhiteRect() { for (int i=0; i<8; i++) { lcd.writeData(0x00); } }
Links und Referenzen: