JPEG régressif
(maurycyz.com)- En modifiant la structure multi-scan d’un JPEG progressif, on peut faire en sorte qu’au lieu de gagner en qualité au fil du téléchargement, l’image déjà affichée soit continuellement remplacée par une autre
- Chaque scan spécifie les canaux de couleur, la plage de fréquences DCT et la précision ; en supprimant certains marqueurs puis en concaténant plusieurs JPEG de même résolution, on peut écraser les données de rendu existantes
- Pour éviter des problèmes comparables aux bombes de compression, les décodeurs limitent le nombre de scans traités ; Chrome rend environ 90 images, tandis que Firefox et d’autres en traitent davantage
- En n’utilisant qu’un seul scan DC par image, on peut augmenter le nombre d’images tout en évitant les rémanences, mais à cause des propriétés des blocs DCT, la résolution obtenue tombe à 1/16 de l’original
- Il est possible d’intégrer plusieurs images dans un seul JPEG comme une vidéo, mais faute d’informations de timing, la vitesse de lecture dépend de la latence réseau ; cela se prête moins à une vidéo pratique qu’à des expériences HTML ou d’applications monopage exploitant le rendu partiel
Comment le JPEG progressif affiche une image
- Le JPEG stocke d’abord les composantes basse fréquence, ce qui permet d’afficher un aperçu basse résolution plutôt qu’une image tronquée même quand seul une partie du fichier a été téléchargée
- Les données compressées sont divisées en plusieurs scans auxquels un en-tête est ajouté
FF DAest le marqueur de début de scan- viennent ensuite un champ de longueur, le nombre de canaux inclus et leur ID, ainsi que l’index de la table de Huffman
- après avoir indiqué les indices DCT de début et de fin ainsi que la précision, les coefficients DCT encodés en Huffman sont stockés
- Le premier scan contient le bin de fréquence DC le plus bas des trois canaux de couleur
YCbCr et amélioration progressive de la qualité
- Les trois canaux de couleur d’un JPEG utilisent YCbCr plutôt que le RGB habituel
Ycorrespond à la luminance et demande donc une qualité élevéeCbetCrsont les composantes de chrominance, dont une qualité plus faible a un impact visuel limité- de façon très grossière, on peut écrire
Y = G,Cb = B - G,Cr = R - G
- Le JPEG d’exemple remplit progressivement les données du scan 0 au scan 9
- scan 0 :
Y Cb Cr, stocke le bin DCT0–0à demi-précision pour fournir un aperçu en très basse résolution - scan 1 :
Y, ajoute les bins1–5à 1/4 de précision pour renforcer les détails de luminance - scans 2 et 3 : ajoutent les bins
1–63deCbetCrà demi-précision - scan 4 : remplit les bins
6–63deYà 1/4 de précision pour compléter la plage laissée par le scan 1 - scan 5 : améliore les bins
1–63deYà demi-précision - scans 6–9 : ajoutent le dernier bit à chaque canal pour atteindre la qualité complète
- scan 0 :
- Les données de chrominance sont finalisées avant la luminance, mais comme elles sont stockées à demi-résolution, soit avec 1/4 du nombre de pixels, la taille totale de
Cr + Cbne représente que la moitié de celle de la luminance
Changer l’image pendant le téléchargement
- Comme chaque scan précise la plage de fréquences à appliquer, on peut créer un JPEG dans lequel des scans ultérieurs écrasent les données d’image déjà rendues
- Cela se met en œuvre en concaténant plusieurs images de même résolution tout en filtrant les marqueurs suivants
- début d’image (start-of-image)
- début de frame (start-of-frame)
- fin d’image (end-of-image)
- C’est faisable avec un éditeur hexadécimal, mais la génération réelle utilise un simple programme en C
- Si le fichier est transmis sur un réseau lent, plusieurs images s’enchaînent à mesure que le téléchargement progresse
Limites de scans des décodeurs
- La plupart des décodeurs JPEG s’arrêtent après avoir traité un certain nombre de scans
- On suppose qu’il s’agit d’une limite destinée à éviter des problèmes similaires aux bombes de compression, ce qui rend difficile de dépasser 9 images avec une simple concaténation
- Pour créer une animation plus longue, il faut minimiser le nombre de scans nécessaires par image
Pourquoi on ne peut pas utiliser le JPEG Baseline
- Une approche consistant à partir d’un JPEG Baseline, qui n’utilise qu’un seul scan, ne fonctionne pas
- En mode progressif, un même scan ne peut pas contenir à la fois le bin 0 des données DC et les bins AC à partir de 1
- Le mode Baseline n’a pas cette contrainte, mais les décodeurs Baseline arrêtent le traitement après le premier scan
- Comme les données AC doivent venir après les données DC, la plus petite image JPEG progressive possible se compose d’un seul scan DC
Composition et résolution des images DC seules
- Comme la DCT est traitée par blocs de 16×16, même avec uniquement des données DC on obtient non pas une couleur unie, mais une image à 1/16 de la résolution originale
- L’image minimale n’utilise qu’un seul scan stockant le bin DCT
0–0des trois canauxY Cb Cravec précision complète - Dans cette configuration, Chrome rend environ 90 images avant d’arrêter le traitement
- d’autres navigateurs comme Firefox traitent davantage de scans
- une image composée de 90 scans fonctionne dans presque tous les navigateurs
Changement d’image sans rémanence
- La rémanence de la méthode par simple concaténation vient du fait que les scans AC sont conçus pour affiner les données existantes
- Dans un JPEG progressif classique, on peut inclure plusieurs niveaux de précision sans augmenter fortement la taille du fichier, mais cela ne convient pas à une approche par remplacement d’image
- En n’utilisant que des scans DC, sans véritable amélioration progressive, on évite la rémanence car les anciennes données AC ne sont jamais affinées
- Les images DC seules sont des images JPEG conformes au standard et ne nécessitent donc pas d’encodeur spécial
- la spécification du scan n’indique que les bins DC des trois canaux, comme
0,1,2:0-0,0,0;
- la spécification du scan n’indique que les bins DC des trois canaux, comme
Limites d’une vidéo JPEG unique
- En concaténant des images DC seules, on peut mettre une vidéo entière dans un seul fichier JPEG
- Les scans JPEG ne permettent pas d’ajouter un timing par image, donc la vitesse de lecture dépend entièrement de la latence réseau
- En dehors d’un Rickroll non conventionnel ou d’une blague, il n’existe pas vraiment d’application pratique
Expériences prolongeant le rendu partiel
- Le rendu partiel permet aussi d’autres expérimentations que le simple remplacement d’images JPEG
- Bad Apple a été implémenté comme une vidéo HTML pure n’utilisant que la balise
<dialog> - Il est aussi possible de réaliser une application monopage interactive sans CSS ni JavaScript, sans avoir à coder les données en dur dans le fichier
- La génération des images utilise le code C
merge.c
1 commentaires
Avis sur Hacker News
J’ai fait quelque chose de très similaire avec le PNG entrelacé Adam7 : https://www.da.vidbuchanan.co.uk/adamation/image.png
La lecture finit certes par dépendre de la latence réseau, mais j’ai configuré le serveur pour envoyer chaque frame séparément à intervalle régulier. Comme les frames sont petites, tant que le réseau n’est pas particulièrement lent, c’est le serveur qui décide du moment de lecture.
Refreshdans les en-têtes de réponse pour que le client récupère périodiquement une nouvelle frame d’animation[1]. Une soumission à l’IOCCC 2013 utilise aussi cette technique pour mettre à jour en continu une horloge rendue en PNG[2][1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/...
[2] https://www.ioccc.org/2013/mills/index.html
À la ligne suivante, l’en-tête
Refreshest encodé à l’envers : https://github.com/ioccc-src/winner/blob/619f554bbdb19e5003a...C’est une technique maudite, mais elle a clairement sa place ici.
Je me demande si cette technique permettrait de faire de la stéganographie en cachant des données à la vue de tous. La plupart des programmes d’analyse automatique d’images ne vérifieraient probablement que la dernière image, donc des élèves pourraient s’en servir pour contourner les filtres de contenu de leur école.
On pourrait peut-être s’en servir comme barre de progression pour des chargements parallèles sur le même réseau, afin d’aider l’utilisateur à estimer le délai restant.
Si un serveur web génère un « JPEG » à la volée et l’envoie au client en morceaux espacés dans le temps, il peut contrôler dans une certaine mesure le moment de lecture. Avec une webcam comme source, on peut même avoir un « JPEG » sans fin.
multipart/x-mixed-replace. Le serveur indique au client de remplacer les données qu’il vient d’envoyer par de nouvelles ; cela fonctionne avec du HTTP et du JPEG ordinaires, sans JavaScript.Avec un Service Worker, on peut simuler une connexion lente et contrôler la vitesse de lecture.
C’est étrange que le comportement varie selon l’environnement. Sur Firefox desktop, la lecture fonctionne correctement, mais sur mobile iOS, la « vidéo complète dans le JPEG » n’affiche presque que trois frames unies marron → orange → rouge, avec un vague contour de chat.
Comme la couleur change à chaque frame, on voit bien que ça fonctionne, mais c’est difficile d’appeler ça une vidéo. Sur desktop, ça se lit comme une vraie vidéo, ce qui m’a surpris ; on dirait que ça touche un cas limite particulier du décodeur d’images d’iOS.
J’ai récemment travaillé sur l’affichage rapide d’images avec OpenGL et jpeg-turbo, et activer le mode progressif de JPEG ralentissait fortement le décodage. Le vieux conseil selon lequel les JPEG progressifs seraient préférables n’est probablement plus valable aujourd’hui.
Cela fait des décennies que je ne vois presque jamais d’images devenir progressivement plus nettes, donc la valeur pratique semble aussi limitée.
Exemple : https://youtube.com/watch?v=UphN1_7nP8U
J’ai d’abord pensé à une méthode consistant à calculer difficilement les coefficients haute fréquence à partir des « mauvais » coefficients de la première image, mais la solution qui consiste simplement à fusionner deux images est astucieuse. Le simple fait de coller les composantes basse fréquence d’une image à la suite des composantes haute fréquence d’une autre fonctionne étonnamment bien.
Je me demande s’il serait possible d’acquérir directement les composantes basse fréquence avec une caméra. Cela me fait aussi penser à l’IRM, qui acquiert d’abord les basses fréquences dans l’espace k.