- Entwicklung einer ASCII-Rendering-Technik, die Konturen und Formen von Bildern bewahrt und damit das Problem unscharfer Kanten bisheriger Verfahren löst
- Statt einfacher Helligkeitszuordnung auf Pixelebene wird ein hochmodimensionaler vektorbasierter Ansatz verwendet, der die visuelle Form (shape) jedes Zeichens numerisch erfasst und abgleicht
- Für jedes Zeichen wird die Dichte in oberen, unteren, linken und rechten Bereichen gemessen, um einen von 2D auf 6D erweiterten Shape-Vektor zu erzeugen und so eine präzisere Zeichenauswahl zu ermöglichen
- Um die Schärfe von Kanten zu erhöhen, werden Algorithmen zur globalen und gerichteten Kontrastverstärkung (contrast enhancement) angewendet
- Durch GPU-Beschleunigung, Caching und k-d-Baum-Suche wird Echtzeit-Performance für ASCII-Rendering erreicht und ein hochwertiger visueller Effekt umgesetzt
Umwandlung von Bildern in ASCII
- ASCII umfasst 95 druckbare Zeichen, und mit einer Monospace-Schriftart wird das Bild in ein Raster unterteilt
- Die Helligkeit jeder Zelle wird berechnet und entsprechend der Zeichendichte zugeordnet
- Einfache Nearest-Neighbor-Interpolation verursacht den Jaggies-Effekt mit unruhigen Kanten
- Durch Supersampling werden mehrere Samples innerhalb einer Zelle genommen und ihre mittlere Helligkeit berechnet, was das Ergebnis glättet, aber weiterhin unscharfe Kanten erzeugt
- Das Kernproblem besteht darin, Zeichen wie Pixel zu behandeln und ihre eigentliche Form nicht zu berücksichtigen
Nutzung der Zeichenform (Shape)
- Jedes Zeichen hat innerhalb einer Zelle eine andere Verteilung der visuellen Dichte
- Beispiel:
Tist oben schwerer,Lunten schwerer
- Beispiel:
- Um dies zu quantifizieren, werden Sampling-Kreise innerhalb der Zelle platziert und der Anteil der vom Zeichen belegten Fläche in jedem Bereich berechnet
- Die Belegungsraten der oberen und unteren Bereiche werden als Vektor dargestellt und ergeben einen zweidimensionalen Shape-Vektor
- Die Shape-Vektoren aller Zeichen werden vorab berechnet, und für den Sampling-Vektor des Bildes wird über die euklidische Distanz (Euclidean distance) das nächstliegende Zeichen gewählt
Erweiterung auf einen 6D-Formvektor
- Nur mit einer oberen und unteren 2D-Aufteilung lassen sich Zeichen wie
-,p,qund andere mit mittigem oder seitlichem Schwerpunkt nur schwer darstellen - Die Zelle wird auf sechs Sampling-Kreise erweitert, um oben, Mitte, unten sowie links-rechts-Unterschiede vollständig zu erfassen
- Der 6D-Shape-Vektor bildet Zeichenformen deutlich präziser ab und stellt auch runde und diagonale Zeichen gut dar
- Beim Rendern von 3D-Szenen bleiben zwar Außenkonturen scharf, doch die Grenzen zwischen Flächen wirken weiterhin unscharf
Kontrastverstärkung (Contrast Enhancement)
- Jedes Element des Sampling-Vektors wird mit einem Exponenten angepasst, sodass dunkle Werte dunkler werden, während helle Werte erhalten bleiben
- Der Vektor wird normalisiert, der Exponent angewendet und anschließend auf den ursprünglichen Bereich zurückgeführt
- Dadurch wird die visuelle Trennung von Kanten verstärkt, und die Zeichenauswahl wird klarer
- In Bereichen mit gleichmäßiger Helligkeit gibt es kaum Änderungen, sodass weiche Verläufe erhalten bleiben
- An manchen Kanten entsteht jedoch ein Treppeneffekt (staircasing)
Gerichtete Kontrastverstärkung (Directional Contrast Enhancement)
- Auch außerhalb jeder Zelle werden äußere Sampling-Kreise platziert, um Helligkeitsinformationen aus der Umgebung zu sammeln
- Helle Werte des äußeren Sampling-Vektors dunkeln die entsprechenden Elemente des inneren Vektors ab und verstärken so den Kontrast in Kantenrichtung
- Wird das äußere Sampling so erweitert, dass Einflüsse zwischen oberem, mittlerem und unterem Bereich breiter wirken, lassen sich weiche und zugleich scharfe Kanten darstellen
- In Kombination mit globaler Kontrastverstärkung entsteht ein ASCII-Rendering mit klaren Konturen und hoher Lesbarkeit in 3D-Szenen
Performance-Optimierung
- Da eine einfache wiederholte Nächste-Nachbarn-Suche bei der Zeichenauswahl langsam ist, wird ein k-d-Baum für schnelle Suche im mehrdimensionalen Raum eingesetzt
- Durch Caching werden Ergebnisse identischer Sampling-Vektoren wiederverwendet
- Jeder Vektor wird in 5-Bit-Schritten quantisiert, um einen speichereffizienten Cache-Schlüssel zu erzeugen
- Mit einem auf 8 gesetzten Bereich wird ein Gleichgewicht zwischen Qualität und Speicherverbrauch erreicht
- Die gecachte Suche ist sehr schnell, sodass auch Tausende Zeichen in Echtzeit verarbeitet werden können
- Die Berechnung der Sampling-Vektoren wird auf die GPU verlagert, sodass interne und externe Sampling-, Kontrastverstärkungs- und weitere Operationen in der Shader-Pipeline ausgeführt werden
- Mehrfache Performance-Steigerung gegenüber der CPU
Fazit
- Der Ansatz, Zeichenformen als Vektoren numerisch zu erfassen und zu nutzen, verbessert Auflösung und Schärfe des ASCII-Renderings deutlich
- Das Verfahren ähnelt konzeptionell Word Embeddings und könnte auch auf andere visuelle Probleme angewendet werden
- Die erste Implementierung war langsam, doch mit GPU-Beschleunigung, Caching und k-d-Baum-Suche werden selbst auf Mobilgeräten flüssige FPS erreicht
- Farbasiertes ASCII wurde nicht behandelt; stattdessen wird auf die Möglichkeit künftiger Experimente mit vielfältigeren Form- und Kontrastkombinationen verwiesen
- ASCII-Rendering ist mehr als nur ein visueller Effekt und zeigt die Erweiterbarkeit von Formerkennung und Vektorrepräsentation
1 Kommentare
Hacker-News-Kommentare
Wenn man die Vektoren normalisiert und dann die euklidische Distanz berechnet, kann man mit einer einfachen Matrixmultiplikation (matmul) zum gleichen Ergebnis kommen
Denn bei normalisierten Vektoren ist die euklidische Distanz eine lineare Transformation der Kosinusdistanz
Wenn nicht der eigentliche Distanzwert, sondern nur das Ranking wichtig ist, kann man die sqrt-Operation weglassen, erhält dasselbe Ergebnis und rechnet etwas schneller
Ich liebe genau diese Art von Artikeln. Auf den ersten Blick wirkt es einfach, aber um es wirklich gut zu machen, braucht es gründliche, tiefgehende Untersuchung
Empfehlenswert ist auch Lucas Popes Text über die Entwicklung des Dithering-Systems für Return of The Obra Dinn
Lucas Popes Entwicklungsbericht
Beim Satz „Ich habe ein Saturnbild mit ChatGPT erzeugt“ war ich baff
Echte Saturnfotos liegen gemeinfrei überall herum, deshalb frage ich mich, warum man unbedingt ein gefälschtes Bild erzeugen und damit das Internet verschmutzen muss
Irgendwann schreiben wir vielleicht nicht einmal mehr direkt in Wikis, Websites oder Foren
Wenn man sofort ein kontrastreiches Saturnbild in Größe X×Y erzeugen kann, wäre das eine geradezu magische Veränderung
So wie Taschenrechner oder das Internet die Kreativität nicht zerstört haben, werden Menschen immer das Werkzeug mit der geringsten Reibung wählen und weiter nach den Sternen greifen
Bei jedem Beispiel dachte ich: „Gut, aber das könnte man noch verbessern.“ Umso beeindruckender war es, dass der Autor genau das tatsächlich gelöst hat
Ein wirklich schöner Artikel, und der ganze Blog ist auf diesem tiefgehenden Niveau, sodass sich ein Abo lohnt
alexharri.com/blog
Als ich das Projekt ascii-side-of-the-moon gebaut habe, habe ich überlegt, den ASCII-Renderer selbst zu implementieren
Am Ende habe ich chafa verwendet, aber ich möchte es irgendwann noch einmal selbst versuchen
Ich frage mich, ob es Pläne gibt, das als Bibliothek zu veröffentlichen, oder ob man sich einfach am Website-Code orientieren kann
Aktuell gibt es keine Pläne für eine Bibliothek, aber wenn du willst, kannst du den Website-Code gern frei übernehmen
Wenn ich so etwas bauen würde, würde ich die Konvertierung von WebGL 2 nach WebGL 1 für bessere Kompatibilität einbauen und außerdem ein Tool brauchen, das Shape-Vektoren für jede Schriftart im Voraus berechnet
Zu der Aussage „Ich habe noch nie gesehen, dass in ASCII-Art shape verwendet wird“: Es gibt tatsächlich Generatoren, die shape einsetzen
Ein Beispiel ist ascii-silhouettify, ein Projekt mit einem Algorithmus, der den größten passenden Buchstaben entlang der Konturen von Farbbereichen auswählt
Acerola hat 2024 ASCII-Rendering auf Basis von Kantenerkennung ausprobiert
Dabei wurden gerichtete Symbole (| / - \) über einen helligkeitsbasierten Pass gelegt
Siehe dieses Video
Man könnte zum Beispiel wie in klassischer 2D-Kunst dicke Konturlinien verwenden oder wie beim Chiaroscuro weiche Hell-Dunkel-Kontraste darstellen
Die meisten ASCII-Filter berücksichtigen die Form der Glyphen nicht
chafa behandelt jede Glyphe als 8×8-Bitmap, und dieser Ansatz hat mich beeindruckt
Wenn man sich den chafa-Quellcode und die Galerie ansieht, merkt man, wie ausgefeilt das ist
Ich frage mich, ob ein stärker richtungsorientierter Ansatz größere Formen besser darstellen könnte
Wenn man sich oldschool PC fonts ansieht, ist das wirklich ein endloser Kaninchenbau
In meiner Freizeit experimentiere ich mit farbiger Grafik auf Basis von Braille-Zeichen
Die Auflösung reicht aus, aber bei der Farbdarstellung fehlt es an Präzision, weshalb nach dem Sampling ein Kontrast-Fixup nötig ist
Es wäre interessant, die Sampling-Technik des Autors zur Verstärkung von Farbkontrasten einzusetzen
Zuvor hatte ich versucht, den Kontrast mit einem Sobel-Filter zu erhöhen, aber das hat nicht funktioniert, weil die Ausrichtung nicht zum Zeichenraster passte
Ein wirklich hervorragender technischer Ansatz und eine tiefgehende Analyse
Ich hatte am Ende gehofft, eine verbesserte Version des Cognition cube array zu sehen, und war enttäuscht, dass sie nicht kam
Das erinnerte mich an einen Designer auf YouTube, der mit subpixelgenauem Farbkontrast ein besseres Favicon umgesetzt hat
Zugehöriger Artikel (Web Archive)
Trotzdem war der Artikel selbst großartig, und die dynamischen Beispiele waren wirklich beeindruckend