Regressives JPEG
(maurycyz.com)- Wenn man die Mehrfach-Scan-Struktur eines progressiven JPEGs verändert, kann man erreichen, dass sich die bereits angezeigte Grafik beim weiteren Download nicht einfach in besserer Qualität verfeinert, sondern fortlaufend in andere Bilder verwandelt
- Jeder Scan legt Farbkanäle, DCT-Frequenzbereich und Präzision fest; wenn man bei JPEGs mit derselben Auflösung bestimmte Marker entfernt und sie aneinanderhängt, kann man bereits vorhandene Rendering-Daten überschreiben
- Decoder begrenzen die Zahl der verarbeiteten Scans, um Probleme ähnlich einer Compression Bomb zu vermeiden; Chrome rendert bis zu etwa 90 Frames, Firefox und andere verarbeiten mehr
- Wenn pro Frame nur ein einzelner DC-only-Scan verwendet wird, lässt sich die Frame-Zahl erhöhen und Ghosting vermeiden, allerdings sinkt die resultierende Auflösung aufgrund der DCT-Blockeigenschaften auf 1/16 des Originals
- So lassen sich zwar mehrere Frames wie in einem Video in einer einzelnen JPEG-Datei unterbringen, aber ohne Timing-Informationen hängt die Wiedergabegeschwindigkeit von der Netzwerklatenz ab; praktischer als für Video ist das für Experimente mit partiellem Rendering in HTML und Single-Page-Apps
Wie progressive JPEGs Bilder anzeigen
- JPEG speichert zuerst niederfrequente Anteile, sodass schon bei teilweise heruntergeladener Datei statt eines abgeschnittenen Bildes eine niedrig aufgelöste Vorschau angezeigt werden kann
- Die komprimierten Daten sind in mehrere Scans unterteilt, vor denen jeweils ein Header steht
FF DAist der Startmarker eines Scans- Danach folgen Längenfeld, Anzahl und IDs der enthaltenen Kanäle sowie der Index der Huffman-Tabellen
- Anschließend werden Start- und End-DCT-Bin sowie Präzision festgelegt, dann werden Huffman-kodierte DCT-Koeffizienten gespeichert
- Der erste Scan enthält in allen drei Farbkanälen den niedrigsten DC-Frequenz-Bin
YCbCr und schrittweise Qualitätsverbesserung
- Die drei Farbkanäle eines JPEGs verwenden statt des üblichen RGB YCbCr
Yist die Luminanz und benötigt daher hohe QualitätCbundCrsind Chrominanzanteile, daher fällt geringere Qualität visuell weniger stark auf- Sehr grob dargestellt gilt:
Y = G,Cb = B - G,Cr = R - G
- Das Beispiel-JPEG füllt die Daten schrittweise von Scan 0 bis 9 auf
- Scan 0:
Y Cb Cr, speichert DCT-Bin0–0mit halber Präzision und liefert eine extrem niedrig aufgelöste Vorschau - Scan 1:
Y, ergänzt Bin1–5mit 1/4-Präzision und verbessert damit die Luminanzdetails - Scan 2·3: ergänzen bei
CbundCrdie Bins1–63mit halber Präzision - Scan 4: füllt bei
Ydie Bins6–63mit 1/4-Präzision und ergänzt so den von Scan 1 offen gelassenen Bereich - Scan 5: verbessert bei
Ydie Bins1–63auf halbe Präzision - Scan 6–9: ergänzen für jeden Kanal das letzte Bit und erreichen volle Qualität
- Scan 0:
- Obwohl die Chrominanzdaten vor der Luminanz vollständig werden, werden sie mit halber Auflösung, also nur 1/4 der Pixelzahl, gespeichert; die Gesamtkapazität von
Cr + Cbbeträgt daher nur die Hälfte der Luminanz
Bild während des Downloads austauschen
- Da jeder Scan den anzuwendenden Frequenzbereich angibt, kann man JPEGs erzeugen, bei denen später folgende Scans bereits gerenderte Bilddaten überschreiben
- Umgesetzt wird das, indem mehrere Bilder mit derselben Auflösung aneinandergehängt werden, während folgende Marker herausgefiltert werden
- Bildanfang (start-of-image)
- Frame-Anfang (start-of-frame)
- Bildende (end-of-image)
- Das ist sogar mit einem Hex-Editor möglich, für die tatsächliche Erzeugung wurde aber ein einfaches C-Programm verwendet
- Wird die Datei über ein langsames Netzwerk übertragen, wechseln während des Downloads nacheinander mehrere Bilder durch
Scan-Limits der Decoder
- Die meisten JPEG-Decoder brechen nach einer bestimmten Zahl verarbeiteter Scans ab
- Vermutlich ist das eine Begrenzung, um Probleme ähnlich einer Compression Bomb zu vermeiden; mit dem einfachen Verkettungsansatz sind mehr als 9 Frames schwer umsetzbar
- Für längere Animationen muss daher die Zahl der pro Frame benötigten Scans minimiert werden
Warum Baseline-JPEG nicht verwendet werden kann
- Der Ansatz, mit einem Baseline-JPEG zu beginnen, das nur einen einzigen Scan verwendet, funktioniert nicht
- Im progressiven Modus können DC-Daten aus Bin 0 und AC-Daten aus Bin 1 oder höher nicht gemeinsam in einen Scan gepackt werden
- Im Baseline-Modus gibt es diese Einschränkung nicht, aber Baseline-Decoder beenden die Verarbeitung nach dem ersten Scan
- Da AC-Daten nach den DC-Daten kommen müssen, besteht der kleinste progressive JPEG-Frame aus genau einem DC-only-Scan
Aufbau und Auflösung von DC-only-Frames
- Da DCT in 16×16-Blöcken verarbeitet wird, ergibt selbst ein Bild nur mit DC-Daten keine Volltonfläche, sondern ein Bild mit 1/16 der Originalauflösung
- Der kleinste Frame verwendet genau einen Scan, der in allen drei Kanälen
Y Cb Crden DCT-Bin0–0mit voller Präzision speichert - In dieser Konfiguration rendert Chrome etwa 90 Frames, bevor die Verarbeitung abgebrochen wird
- Andere Browser wie Firefox verarbeiten mehr Scans
- Ein Bild mit 90 Scans funktioniert in fast allen Browsern
Frame-Wechsel ohne Ghosting
- Ghosting beim einfachen Verkettungsansatz entsteht dadurch, dass AC-Scans dafür ausgelegt sind, vorhandene Daten zu verfeinern
- In normalen progressiven JPEGs lassen sich so mehrere Präzisionsstufen speichern, ohne die Dateigröße stark zu erhöhen, für den Bildaustausch eignet sich das aber nicht
- Wenn man ausschließlich DC-only-Scans ohne echte progressive Verbesserung verwendet, findet keine Verfeinerung früherer AC-Daten statt, wodurch sich Ghosting vermeiden lässt
- DC-only-Frames sind standardkonforme JPEG-Bilder, daher ist kein spezieller Encoder nötig
- In der Scan-Spezifikation werden nur die DC-Bins der drei Kanäle angegeben, etwa
0,1,2:0-0,0,0;
- In der Scan-Spezifikation werden nur die DC-Bins der drei Kanäle angegeben, etwa
Grenzen eines einzelnen JPEG-Videos
- Wenn man DC-only-Frames aneinanderhängt, kann eine einzige JPEG-Datei ein vollständiges Video enthalten
- JPEG-Scans bieten jedoch keine Möglichkeit, Frame-Timing hinzuzufügen, daher hängt die Wiedergabegeschwindigkeit vollständig von der Netzwerklatenz ab
- Abgesehen von unkonventionellen Rickrolls oder Streichen gibt es dafür keine praktischen Anwendungsfälle
Experimente mit erweitertem partiellem Rendering
- Mit partiellem Rendering sind auch andere Experimente als der Austausch von JPEG-Frames möglich
- Mit einem reinen HTML-Video nur mit dem
<dialog>-Tag wurde das Bad-Apple-Video umgesetzt - Auch eine interaktive Single-Page-Anwendung ohne CSS oder JavaScript wurde umgesetzt, ohne Daten fest in eine Datei eincodieren zu müssen
- Für die Bilderzeugung wurde der C-Code
merge.cverwendet
1 Kommentare
Hacker-News-Kommentare
Ich habe mit Adam7-Interlaced-PNGs etwas sehr Ähnliches gemacht: https://www.da.vidbuchanan.co.uk/adamation/image.png
Die Wiedergabe hängt am Ende zwar von der Netzwerklatenz ab, aber ich habe den Server so eingerichtet, dass er jedes Frame in festen Abständen separat sendet. Da die Frames klein sind, bestimmt der Server den Abspielzeitpunkt, solange das Netzwerk nicht außergewöhnlich langsam ist
Refreshsetzen, damit der Client periodisch neue Animationsframes abruft[1]. Ein IOCCC-Beitrag von 2013 aktualisiert mit dieser Technik auch ständig eine als PNG gerenderte Uhr[2][1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/...
[2] https://www.ioccc.org/2013/mills/index.html
In der nächsten Zeile ist der
Refresh-Header rückwärts codiert: https://github.com/ioccc-src/winner/blob/619f554bbdb19e5003a...Eine verfluchte Technik, aber sie passt definitiv hierher
Ich frage mich, ob sich diese Technik für Steganografie eignet, um Daten direkt vor den Augen zu verstecken. Die meisten automatischen Bildanalyseprogramme prüfen wahrscheinlich nur das letzte Bild, also könnten Schüler damit vielleicht die Content-Filter ihrer Schule umgehen
Ich frage mich, ob man das als Fortschrittsbalken für parallel geladene Aufgaben im selben Netzwerk nutzen könnte, damit Nutzer abschätzen können, wie viel Verzögerung noch bleibt
Wenn der Webserver on the fly ein „JPEG“ erzeugt und es in zeitlich versetzten Stücken an den Client sendet, kann er den Wiedergabezeitpunkt einigermaßen steuern. Mit einer Webcam als Quelle wäre sogar ein endloses „JPEG“ möglich
multipart/x-mixed-replace. Dabei weist der Server den Client an, die gerade gesendeten Daten durch neue Daten zu ersetzen; das funktioniert mit normalem HTTP und JPEG ganz ohne JavaScriptMit einem Service Worker könnte man eine langsame Verbindung simulieren und so die Wiedergabegeschwindigkeit steuern
Es ist seltsam, dass das je nach Umgebung unterschiedlich funktioniert. Auf Firefox am Desktop wird es korrekt abgespielt, aber auf iOS mobil besteht das „gesamte Video im JPEG“ fast nur aus drei nahezu einfarbigen Frames in Braun → Orange → Rot und einer verschwommenen Katzenkontur
Dass sich die Farbe pro Frame ändert, zeigt zwar, dass es läuft, aber als Video kann man das kaum bezeichnen. Auf dem Desktop wird es überraschend wie ein echtes Video abgespielt, und es scheint einen seltsamen Grenzfall im iOS-Bilddecoder zu treffen
Ich habe kürzlich mit OpenGL und jpeg-turbo daran gearbeitet, Bilder schnell anzuzeigen, und das Aktivieren des progressiven Modus bei JPEG hat das Decoding deutlich verlangsamt. Der alte Rat, progressive JPEGs seien gut, gilt heute vielleicht nicht mehr
Ich habe seit Jahrzehnten kaum noch gesehen, dass Bilder sich stufenweise schärfen, daher scheint auch der praktische Nutzen gering zu sein
Beispiel: https://youtube.com/watch?v=UphN1_7nP8U
Zuerst dachte ich daran, mühsam hochfrequente Koeffizienten aus den „falschen“ Koeffizienten des ersten Bildes zu berechnen, aber das bloße Zusammenfügen zweier Bilder ist clever. Es funktioniert erstaunlich gut, einfach die niederfrequenten Anteile eines Bildes mit den hochfrequenten Anteilen eines anderen aneinanderzuhängen
Ich frage mich, ob sich niederfrequente Anteile auch direkt mit einer Kamera erfassen lassen. Das erinnert mich an MRT-Verfahren, die im k-Raum zuerst niedrige Frequenzen aufnehmen