Evolution Programming Resources |
Auch wenn es inzwischen
zum Standard für PC’s geworden ist hat nicht jeder Computer die maximale Farbtiefe von 24 Bit (also 16 Millionen unterschiedliche Farben) zur Auswahl. In Zeiten, in denen viele Aufgaben immer
mehr auf spezialisierte - billige und damit leistungsschwache - Kleinstrechner übertragen werden kann es auch wieder häufiger passieren, das man mit einer limitierten Auswahl an Farben auskommen
muss.
Wenn man nun Bilder mit sehr vielen Farben (z.B. als Webbrowser oder Multimediaanwendung) auf einem Gerät mit weniger Farben darstellen will hat man wenig Auswahl - besonders wenn man
sehr schnell arbeiten will. Die schnellste Methode ist in der Regel ein “GetNearestColor”-Blit, den jedes bessere Betriebsystem (BeOS, Windows, ...) standardmäßig nutzt und jeden Farbpunkt beim
Kopieren der Grafik in den Bildschirmspeicher durch eine Farbe ersetzt, die in der derzeitigen Palette vorhanden ist und dem Ursprungspunkt am nächsten kommt. Das ist schnell, sieht aber auch oft
nicht so gut aus:
ein Bild mit 16 Millionen Farben... |
...das gleiche Bild mit GetNearestColor in den 16 Standard-Windows Farben |
Auch wenn man zunächst nicht davon ausgehen muss, das der zukünftige Anwender mit weniger als 16 Millionen Farben arbeiten wird ist es
in vielen Fällen professioneller auch bei schlechten Farbtiefen noch einigermassen gut auszusehen. Ein Beispiel von dieser Professionalität ist der Be Taskmanager
an dem wir gearbeitet haben und der das Dithering nur für den Blur und Alpha-Effekt in seiner About-Box eingebaut hat (selbstverständlich war auch die interessante Implementierung des Hilbert-Ditherns ein ausschlaggebender Grund für dieses Feature).
Die Möglichkeit, die Qualität des Bildes unter dieser Farblimitierung zu verbessern ist Dithern. Grundsätzlich versucht ein Ditheralgorithmus den Fehler, der bei jedem Punkt bei
GetNearestColor autritt auf seine umliegenden Punkte weiterzugeben, und damit den Fehler annähernd auszugleichen - wobei dann ein typisches Punktemuster entsteht. Das ist normalerweise nicht
besonders schnell, da ja nicht nur ein Punkt sondern für jeden Punkt gleich ein ganzer Haufen anderer Punkte verarbeitet werden müssen.
Eine schnelle - und deswegen häufig benutzte Methode ist das “ordered Dither”. Für jede der 256 möglichen Werte einer Farbkomponente (die da wären: Rot, Grün und Blau) liegt ein Muster in einer Tabelle vor, das insgesamt diesen Farbwert auf die vorhandene Palette dithert. Da diese Tabelle alle paar Punkte wiederholt wird sind die Muster im Ergebnissbild sehr regelmäßig. |
|
|
Das beste Ergebniss allerdings bekommt man mit “error diffusion”, dessen bekanntester Vertreter der Floyd-Steinberg Algorithmus ist. Dieser - gegenüber den anderen vorgestellten Verfahren - in der Regel langsame Algorithmus verteilt die Fehler der einzelnen Punkte korrekt und liefert ein bestmögliches Bild. |
|
Die Hilbert-Kurve ist ein
flächenfüllendes Fraktal, das in einem quadratischen Bild mit einer Seitenlänge einer Quadratzahl jeden Punkt einmal besucht. Sie ist das Trumm der Wahl, das unseren Dither-Algorithmus durchs
Bild schickt, damit er die Fehler möglichst diffus an die nächsten Punkte weitergibt. Das bedeutet, das eine Hilbert-Kurve nur Flächen der Größe 2x2, 4x4, 8x8 etc ausfüllen kann - was natürlich
zunächst ein Problem aufwirft!
Die Generierung der Hilbert-Kurfe ist ein rekursiver Algorithmus. Das heisst: jede grosse Kurve wird aus vielen kleinen Kurven zusammengebaut, die wiederum
letztendlich aus kleinen Elementar-Kurven bestehen. Die Elementar-Kurven werden von Vektoren “zusammengeklebt” und werden so immer größer. Ein solcher Vektor geht immer nur nach oben, nach unten,
links oder rechts.
|
Eine Elementarkurve füllt einen 2x2 Raum und wird bei größeren Kurven mit Vektoren aneinandergehängt. Die Regeln für das aneinanderhängen sieht man im unteren Bild: |
|
Regeln für das aneinanderhängen von ElementarKurven |
Entsprechende Hilbert Kurven sehen dann so aus:
|
Level 2 Hilbert Kurve |
|
|
Level 3 Hilbert Kurve |
Das Problem ist nun, das unsere Eingangs-Bilder nicht immer quadratisch sind und die Seitenlänge nur selten einer Quadratzahl
entspricht. Um das Problem zu lösen sollte man wissen, das die Hilbertkurve symmetrisch zu ihrer Vertikalen sind, was heisst, das wenn die Kurve oben links anfängt hört sie garantiert rechts oben
auf!
Um eine Kurve zu erzeugen die jeden beliebigen Raum mit Hilbert-Kurven füllt habe ich einen HilbertController geschrieben, der (teilweise um 90 Grad gedrehte) Hilbert-Kurven so
aneinanderhaengt, das der gesamte Raum ausgefüllt wird - zwei Beispiele finden sie hier:
|
Der HilbertController hat 4 wesentliche Funktionen.
|
|
|
Hier ist noch ein Beispiel. Gut zu sehen: die rote Hilbert-Kurve (oben/links - unten/links) wird genutzt den der rechte Rest-Bereich zufällig einer Quadratzahl entspricht. |
Gefunden habe ich die Hilbert-Kurve zum ersten Mal bei
|
|
/* die normale Hilbert-Kurve. |
Das schöne am Dithern mit der Hilbert-Kurve ist das die Kurve schnell aufgebaut werden kann - der eigentliche Dither-Algorithmus jedoch auf eine Dimension runtergebrochen wird. Dadurch wird das Dithern sehr schnell - bei einer Qualität, die dem Floyd-Steinberg Algorithmus ziemlich nahekommt.
|
Ein guter Ditheralgorithmus verteilt nicht nur den Fehler in die umliegenden Punkte,
sondern lässt den Fehler mit dem größerwerdenden Abstand zum Ursprungspixel linear verschwinden. Eine bessere Beschreibung dazu gibt es bei http://ourworld.compuserve.com/homepages/compuphase/riemer.htm Mein Algorithmus arbeitet ein bischen unsauberer. Er überträgt den Fehler immer auf den nächsten Pixel - nur der Über- oder Unterlauf wird halbiert. Das Ergebniss sehen Sie links.
|
OriginalBild in TrueColor |
in 256-Farben gedithered unter der BeOS-StandardPalette |
Riemersma Dither - Artikel, der in der C/C++ User’s Journal stand
The Hilber Curve - mit dem Riemersma Artikel zusammenhängend
Be Taskmanager Seite
- Implementierung des Hilbert-Ditherns in der About-Box
5. Zukunft
Es gibt eine rekursionsfreie Variante des Hilbert - Ditherers, die etwas schneller sein könnte als das hier vorgestellte Verfahren. Zudem habe ich eine Variante unter Windows geschaffen, die auf Paletten eingehen kann, dies alles werde ich mal dokumentieren und auf www.3rd-evolution.de als Artikel und Source bereitstellen wenn ich mal wieder Zeit habe.