1 points par GN⁺ 21 시간 전 | 1 commentaires | Partager sur WhatsApp
  • 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 DA est 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
    • Y correspond à la luminance et demande donc une qualité élevée
    • Cb et Cr sont 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 DCT 0–0 à demi-précision pour fournir un aperçu en très basse résolution
    • scan 1 : Y, ajoute les bins 1–5 à 1/4 de précision pour renforcer les détails de luminance
    • scans 2 et 3 : ajoutent les bins 1–63 de Cb et Cr à demi-précision
    • scan 4 : remplit les bins 6–63 de Y à 1/4 de précision pour compléter la plage laissée par le scan 1
    • scan 5 : améliore les bins 1–63 de Y à demi-précision
    • scans 6–9 : ajoutent le dernier bit à chaque canal pour atteindre la qualité complète
  • 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 + Cb ne 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–0 des trois canaux Y Cb Cr avec 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;

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

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.

    • Si l’on contrôle le serveur HTTP, on peut aussi définir Refresh dans 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 Refresh est encodé à l’envers : https://github.com/ioccc-src/winner/blob/619f554bbdb19e5003a...
    • J’ai aussi fait quelque chose de similaire en streamant de la vidéo en temps réel avec un GIF infini : https://github.com/jbochi/gifstreaming#live-video-streaming-...
    • Pendant qu’on emploie des bidouilles pour animer du PNG, APNG semble crier depuis le côté : « moi aussi, j’existe ! »
    • Il y a 20 ans, dans l’application cartographique de mon travail, j’avais fait la même chose avec un GIF animé pour ajouter une couche de qualité de l’air en temps réel. Les images étaient rendues avec Java2D, et j’avais aussi développé une version JOGL pour produire des masses en dégradé plutôt qu’un nuage de points, mais l’administrateur du serveur n’a pas autorisé l’installation des pilotes OpenGL, donc je n’ai pas pu la terminer.
  • C’est une technique maudite, mais elle a clairement sa place ici.

    • Les grandes entreprises tech doivent sûrement réfléchir à la manière d’utiliser ça pour le fingerprinting de navigateur.
  • 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 peut s’en servir comme technique de brouillage d’IA : mettre le message destiné aux humains dans la première frame, et celui destiné à l’IA dans la dernière. En gros, on s’envoie des photos de chats pour vaincre Skynet.
    • Je ne vois pas en quoi ce serait mieux que de la stéganographie classique.
  • 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 je me souviens bien, des sites comme 4chan ont fini par bloquer complètement ce comportement, car il était détourné pour cacher des images choquantes ou illégales.
  • 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.

    • Certaines webcams le font déjà avec une technique MIME appelée 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.
    • Beaucoup de caméras IP le font déjà avec https://en.wikipedia.org/wiki/Motion_JPEG.
    • C’est aussi possible avec du GIF. Une fois, j’ai créé un petit CGI jouet qui envoyait un GIF frame par frame et gérait les clics avec une image map, ce qui permettait de l’utiliser comme un bureau à distance sans JavaScript. Le JPEG aurait peut-être mieux convenu, ça vaut le coup de réessayer.
  • 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.

    • Il se peut que vous ayez manqué le moment où, juste après le chargement de la page, l’image est légèrement floue puis devient nette à l’étape suivante. Ce n’est plus aussi difficile à reconnaître qu’avant à la première étape, mais fournir quasiment gratuitement un aperçu basse résolution avec seulement environ 30 % de la taille totale du fichier garde de la valeur.
    • J’utilise cjpegli comme encodeur, et la combinaison par défaut progressif + 4:4:4 complet donne le meilleur taux de compression, donc ce n’est pas seulement une fonctionnalité accessoire.
    • Les photos JPEG enregistrées en progressif sont généralement environ 5 % plus petites, et il est aussi possible de convertir sans perte un JPEG au format progressif. Une conversion sans perte vers JPEG XL permet d’économiser encore plus d’espace.
    • Le décodage progressif n’a pas pour but d’accélérer le décodage lui-même, mais de faire en sorte que les grandes images s’affichent plus vite, surtout sur des connexions mobiles lentes.
      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.