Format Quite OK Image (QOI) : simple, rapide, sans perte, mais pas sans défauts
Basique, simple
Le 09 décembre 2021 à 13h55
4 min
Logiciel
Logiciel
Depuis quelques jours, un projet open source fait parler de lui dans le domaine de la (dé)compression d'images : le format QOI, présenté comme très rapide et sans perte. De quoi inciter certains développeurs à l'implémenter dans différents langages. Mais en pratique, il vaut quoi ?
Fin novembre, Dominic Szablewski présentait un projet de format de (dé)compression d'images sans perte aux objectifs simples : un code léger (300 lignes de C), efficace, n'exploitant qu'un thread, n'effectuant qu'une seule passe, sans SIMD. « QOI ne touche chaque pixel qu'une seule fois, ils sont encodés d'une parmi quatre manières ».
Le résultat est présenté comme 20x à 50x plus rapide que des bibliothèques comme libpng ou stb pour la compression, 3x à 4x en décompression. Le tout avec une taille de fichier similaire. Le code source est disponible sous licence MIT, le développeur précisant que le projet n'est pas encore finalisé, bien que déjà fonctionnel.
D'autres sont d'ailleurs déjà nés de cette initiative, comme qoiview pour la visualisation d'images mais aussi des implémentations en C#, Elixir, Go, Python, Rust (1, 2, 3), Swift et même le nouveau langage à la mode : zig.
Téléchargement, compilation
Pour vérifier les chiffres annoncés par le développeur et le fonctionnement de QOI, nous avons lancé une machine virtuelle sous Debian (via Proxmox VE 7.1) avec 1 cœur de notre Xeon E-2388G et 1 Go de mémoire. L'installation était minimale, nous avons donc dû récupérer plusieurs applications pour télécharger et compiler le code source :
sudo apt update && sudo apt upgrade
sudo apt install build-essential curl git libpng++-dev
On récupère le code source de qoi et les images de test :
git clone https://github.com/phoboslab/qoi.git
curl -O https://phoboslab.org/files/qoibench/images.tar
On décompresse l'archive TAR contenant les images :
tar xf images.tar
On récupère les headers nécessaires pour l'utilisation de stb :
cd qoi
curl -LO https://github.com/nothings/stb/raw/master/stb_image.h
curl -LO https://github.com/nothings/stb/raw/master/stb_image_write.h
On compile les outils de mesure de performances et de conversion selon les recommandations du développeur :
gcc qoibench.c -std=gnu99 -lpng -O3 -o qoibench
gcc qoiconv.c -std=c99 -O3 -o qoiconv
Quels résultats ?
On peut alors lancer les tests avec 10 itérations sur chaque image que contient le dossier :
./qoibench 10 ../images/wallpaper/
Cela nous a donné les performances suivantes en moyenne :
decode ms encode ms decode mpps encode mpps size kb
libpng: 115.5 1619.0 81.15 5.79 9224
stbi: 122.7 1041.6 76.40 9.00 13299
qoi: 47.2 53.7 198.61 174.46 10640
On relève bien des écarts de 20x à 30x pour la compression et une division par un peu plus de deux pour la décompression. Le tout avec des fichiers légèrement plus gros par rapport à libpng, plus légers que via stbi. Ce qui est annoncé se vérifie donc bien en pratique. Mais comment vérifier que l'opération est bien sans perte ?
Taille des fichiers, le compte n'y est pas toujours
Pour le savoir, nous avons pris l'un des fonds d'écran (PNG) pour le convertir via qoiconv au format QOI avant de faire le chemin inverse et de retrouver une image au format PNG :
./qoiconv ../images/wallpaper/Hy23XKX.png Hy23XKX.qoi
./qoiconv Hy23XKX.qoi Hy23XKX_new.png
Si tout s'est bien passé, les deux fichiers PNG doivent être identiques, au pixel près. Et c'est effectivement le cas, ce qui est plutôt une bonne nouvelle.
Il y a néanmoins un bémol : le PNG calculé depuis le QOI pèse 9,58 Mo contre 6,38 Mo pour le fichier originel. Si le QOI pèse 7,47 Mo, la conversion inverse se fait avec une inflation importante. Un point à perfectionner.
Mais l'essentiel semble là puisque l'on a bien un format sans perte, plus rapide à (dé)compresser que les solutions concurrentes. Il sera donc intéressant de voir s'il est utilisé pour des usages particuliers où la performance est importante, malgré le besoin d'une qualité d'image non dégradée. L'avenir nous le dira.
Format Quite OK Image (QOI) : simple, rapide, sans perte, mais pas sans défauts
-
Téléchargement, compilation
-
Quels résultats ?
-
Taille des fichiers, le compte n'y est pas toujours
Commentaires (24)
Vous devez être abonné pour pouvoir commenter.
Déjà abonné ? Se connecter
Abonnez-vousLe 09/12/2021 à 14h13
Il ne vaut donc mieux pas faire des cycles de décompression / compressions au risque de se trouver avec un fichier qui passe de qq Mo a qq Go?
Le 09/12/2021 à 14h16
Non, tu restes à la même taille du PNG si tu refais la conversion. Je pense que c’est juste la conversion >PNG qui se fait de manière “inefficace” dans le script de conversion mais je n’ai pas regardé où ça pouvait être et si on peut résoudre le problème simplement.
Mais sur le fond, ça ne change rien à l’efficacité de la (dé)compression QOI
Attention à un point : le web et les navigateurs ne sont pas l’alpha et l’oméga. Le RAW n’est pas adapté au web, il existe très bien et est largement utilisé néanmoins ;)
Ici, ça peut être une solution très utile quand tu as un besoin d’efficacité dans le processus de (dé)compression sans perte. Par exemple pour GIMP qui enregistre un fichier on s’en fout. Par contre pour un script qui traite des millions d’images à la minute, un délai divisé par 20 ça peut faire une sacré différence.
Le 09/12/2021 à 14h15
Un format c’est comme un réseau social, ce qui compte c’est son adoption.
Pour les images la référence c’est un peu le web, si les navigateurs s’y mettent le format se démocratisera, sinon, non.
Le 09/12/2021 à 14h33
Donc a priori beaucoup moins de CPU mais un peu plus de mémoire nécessaires ?
Le 09/12/2021 à 14h38
J’ai pas relevé de gourmandise particulière là-dessus, mais il faudrait faire un benchmark spécifique pour le vérifier avec précision.
Le 09/12/2021 à 14h48
Côté mémoire, je parlais du côté du stockage, pas du calcul (le fichier décompressé étant plus gros que l’original).
Le 09/12/2021 à 14h54
Oui après quand tu fais le ratio entre la différence de taille et le temps CPU gagné…
Le 14/12/2021 à 14h55
Et dans les usages où il serait diablement pratique, il y a tout ce qui est client léger, jeu vidéo, cartographie embarquée (où les supports de masse ne valent plus rien, mais les procs peuvent encore peiner….)
Dans les tests, je serais curieux de savoir ce que donne une tentative de compression d’une grosse image en bruit blanc, le truc par définition le plus casse-burne à compresser voire, le plus mystique)
Le 09/12/2021 à 15h18
Pour le fait que le PNG ait “enflé” après reconversion, c’est peut-être du fait le la lib stb. La fonction utilisée pour l’écriture de PNG (stb_image_write) n’a pas l’air d’être optimisée (ce qu’on peut lire dans son header https://github.com/nothings/stb/blob/master/stb_image_write.h)
Le 09/12/2021 à 16h24
Je me suis posé la même question.
Le plus simple pour vérifier serait de reprendre le fichier “décompressé” (aka enflé), et refaire :
compression QOI -> décompression QOI
Et voir si il a re-enflé.
Le 09/12/2021 à 16h05
Notons tout de même que RAW n’est pas un format à proprement parler. Chaque constructeur d’appareils photos a le sien, et même parfois différents suivant les modèles. Adobe a bien tenté d’harmoniser ça avec le format ouvert DNG, mais ça n’a pas pris tant que ça…
Le 09/12/2021 à 19h15
Faire une passe de
optipng
pourrait valoir le coup pour savoir si c’est “juste” ça.Sachant que le RAW est sensé représenter la mosaïque du capteur directement (avec d’autres indications), c’est normal que ça change d’une marque à l’autre voir même d’un modèle à l’autre. Ca dépend juste du capteur :)
Le 09/12/2021 à 20h21
Je vois bien l’utilité pour de l’embarqué.
Par exemle j’ai fais un cadre photo avec epaper et un esp8266. J’y transfère les images en png via bluetooth. Si ce format permet un tel gain de place, ca veux dire transfert bien plus rapide ! Reste à voir si l’utilisation cpu est raisonable…
Le 10/12/2021 à 01h09
Je comprends l’intéret d’une unique passe par pixel pour être le plus léger/rapide possible. Par contre, quel est l’intéret de ne pas avoir de SIMD?
Le 10/12/2021 à 01h42
Sans doute montrer que la performance ne vient pas d’une optimisation particulière dépendante de telle ou telle architecture
Le 10/12/2021 à 02h04
Ah, je comprends le point de vue. Dans ce cas, il faudrait enlever le -O3 lors de la compilation, gcc va essayer d’auto-vectoriser le code.
Le 10/12/2021 à 04h24
Comme dit dans l’article ces lignes de commande ne sont pas mon choix mais ceux recommandés dans les fichiers, l’idée étant simplement de reproduire les différentes étapes et de voir si on arrive au même résultat. Après il serait intéressant de regarder ce que l’on obtient avec plus ou moins d’optimisation du compilateur & co, mais ça peut se faire dans un second temps (ou quand le projet aura un peu plus avancé).
Le 10/12/2021 à 05h40
Effectivement, la documentation du code préconise -O3. J’ai compilé le code pour voir si certaines boucles étaient vectorisées. Aucune! Donc ça reste bien 1 thread, une seule passe et sans SIMD.
En bonus, j’ai le benchmark pour un Xeon CPU E5-2698 v3 :
Le 10/12/2021 à 07h36
Le gain est sur la compression, donc ça ne servira pas à grand chose dans ce cas. Attention également compression 20x plus rapide ne veut pas dire que ça consomme 20x moins : Le CPU est peut-être simplement mieux gavé (donc il consomme plus mais moins longtemps)
Le 10/12/2021 à 08h00
Un peu quand même, au hasard jpeg2000. Format ouvert largement supérieur au jpeg classique. L’absence de support des navigateurs a tué ce format qui avait pourtant un intérêt de dingue en 2000 où les JPEG et GIF faisaient bien ramer nos 56k avec leur embonpoint :)
Le 10/12/2021 à 08h38
Je suis agréablement surpris de voir un article sur le sujet. C’est un format qui semble extrèmement intéressant pour les bidouilleurs avec des petits microcontrôleurs qui veulent une compression d’image sans devoir se taper un format imbuvable et incompréhensible.
Car c’est un peu ce qui est reproché aux autres formats : même ceux où la spécification est ouverte sont complexes à mettre en œuvre sur un microcontroleur.
Quand il faut inclure une librairie de 2Mo sur 256ko de mémoire dispo, ça pose vite des petits problèmes !
Le 10/12/2021 à 13h36
Je me fais un peu de pub, mais si vous compressez le fichier QOI avec XZ, vous risquez de gagner beaucoup d’octets par rapport à un PNG, même optimisé avec zopflipng : GitHub
Alors évidemment, ce ne sont plus les mêmes performances… Mais ça fait assez longtemps que je compresse des images pour être surpris de ce résultat.
Le 10/12/2021 à 15h31
Normalement une compression de qualité est optimale et rend les données pseudo-aléatoires (entropie élevée, si je ne dis pas de bêtise), et une recompression (quel que soit l’algorithme) ne donne pas grand chose voire augmente légèrement la taille.
Le 10/12/2021 à 18h46
Mais QOI ne prétend pas être optimal, mais vise la rapidité. Et il se trouve que dans beaucoup de cas, les données du format QOI sont bien alignés pour du LZMA, mieux qu’un PNG en tout cas.