1 Punkte von GN⁺ 21 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • 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 DA ist 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
    • Y ist die Luminanz und benötigt daher hohe Qualität
    • Cb und Cr sind 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-Bin 0–0 mit halber Präzision und liefert eine extrem niedrig aufgelöste Vorschau
    • Scan 1: Y, ergänzt Bin 1–5 mit 1/4-Präzision und verbessert damit die Luminanzdetails
    • Scan 2·3: ergänzen bei Cb und Cr die Bins 1–63 mit halber Präzision
    • Scan 4: füllt bei Y die Bins 6–63 mit 1/4-Präzision und ergänzt so den von Scan 1 offen gelassenen Bereich
    • Scan 5: verbessert bei Y die Bins 1–63 auf halbe Präzision
    • Scan 6–9: ergänzen für jeden Kanal das letzte Bit und erreichen volle Qualität
  • 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 + Cb beträ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 Cr den DCT-Bin 0–0 mit 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;

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.c verwendet

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

    • Wenn man den HTTP-Server kontrollieren kann, lässt sich im Response-Header auch Refresh setzen, 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...
    • Ich habe auch etwas Ähnliches gemacht, um mit endlosen GIFs Live-Video zu streamen: https://github.com/jbochi/gifstreaming#live-video-streaming-...
    • Während man mit Tricks versucht, PNG zu animieren, scheint APNG daneben zu stehen und zu rufen: „Mich gibt es auch!“
    • Ich habe dasselbe vor 20 Jahren bei einem früheren Arbeitgeber mit animierten GIFs gemacht, um einer Kartenanwendung einen Echtzeit-Luftqualitäts-Layer hinzuzufügen. Die Bilder wurden mit Java2D gerendert, und ich habe auch eine JOGL-Version entwickelt, um statt Punktwolken eher Gradientenkleckse zu erzeugen, konnte sie aber nie fertigstellen, weil der Server-Admin die Installation von OpenGL-Treibern nicht erlaubte
  • Eine verfluchte Technik, aber sie passt definitiv hierher

    • Große Tech-Konzerne überlegen sicher schon, wie sie das für Browser-Fingerprinting nutzen können
  • 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

    • Man könnte es als AI-Jailbreak-Technik verwenden, mit einer Nachricht für Menschen im ersten Frame und einer Nachricht für die KI im letzten Frame. So würde man Skynet besiegen, indem man sich Katzenbilder schickt
    • Es scheint aber keine bessere Methode zu sein als normale Steganografie
  • 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

    • Ich meine mich zu erinnern, dass Seiten wie 4chan dieses Verhalten komplett blockiert haben, weil es missbraucht wurde, um anstößige oder illegale Bilder zu verstecken
  • 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

    • Einige Webcams machen das bereits mit einer MIME-Technik namens 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 JavaScript
    • Viele IP-Kameras machen das bereits mit https://en.wikipedia.org/wiki/Motion_JPEG
    • Mit GIF geht es auch. Ich habe einmal ein Spielzeug-CGI gebaut, das GIFs frameweise verschickte und Klicks per Image-Map behandelte, sodass es sich ohne JavaScript wie ein Remote-Desktop nutzen ließ. Mit JPEG wäre es vielleicht besser gewesen; einen neuen Versuch wäre es wert
  • Mit 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

    • Vielleicht ist dir entgangen, dass ein Bild direkt nach dem Laden der Seite leicht unscharf war und im nächsten Schritt schärfer wurde. Nicht mehr so stark wie früher, als die erste Stufe kaum erkennbar war, aber der Vorteil, mit nur etwa 30 % der Gesamtdateigröße fast kostenlos eine Vorschau in niedriger Auflösung zu bekommen, besteht weiterhin
    • Ich verwende cjpegli als Encoder, und weil die Standardkombination aus progressiv und vollem 4:4:4 die beste Kompressionsrate liefert, ist es nicht nur eine einfache Zusatzfunktion
    • Progressiv gespeicherte JPEG-Fotos sind normalerweise etwa 5 % kleiner, und man kann JPEGs auch verlustfrei in das progressive Format umwandeln. Mit einer verlustfreien Umwandlung nach JPEG XL lässt sich noch mehr Platz sparen
    • Progressives Decoding soll das Decoding selbst nicht beschleunigen, sondern vor allem dafür sorgen, dass große Bilder schneller sichtbar werden, besonders bei langsamen mobilen Verbindungen
      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