QOI (format d'image)

Un article de Wikipédia, l'encyclopédie libre.
QOI
Quite OK Image
Caractéristiques
Extension
.qoiVoir et modifier les données sur Wikidata
Signature
71 6F 69 66 (hexa)Voir et modifier les données sur Wikidata
Développé par
Dominic Szablewski
Type de format
Site web

Le QOI (Quite OK Image) est un format de compression d'images sans perte de 24 bits (8 bits par couleur RVB) ou 32 bits (8 bits par couleur avec le canal alpha de 8 bits RGBA), inventé par Dominic Szablewski et annoncé pour la première fois le 24 novembre 2021.

Description[modifier | modifier le code]

L'objectif visé était de créer une méthode de compression d'image sans perte open source, plus rapide et plus facile à mettre en œuvre que PNG. Une vidéo YouTube tierce en anglais explique comment la compression PNG et QOI est effectuée[1]. Les chiffres spécifiés dans l'annonce et la vidéo revendiquent un encodage 20 à 50 fois plus rapide et une vitesse de décodage 3 à 4 fois plus rapide par rapport au PNG, avec des tailles compressées à peine plus lourdes que celles de PNG[2]. Le format appartient au domaine public (CC0).

Support logiciel et langage[modifier | modifier le code]

QOI est pris en charge nativement par ImageMagick[3], IrfanView (à partir de la version 4.60)[4], et FFmpeg (à partir de la version 5.1)[5].

Des plugins créés par la communauté sont disponibles dans GIMP, Paint. NET et XnViewMP[6].

Il existe également des implémentations pour divers langages tels que Rust, Python, Java, C++, C# et bien d'autres[7]. Une liste complète peut être trouvée sur le référentiel GitHub du projet.

Format de fichier[modifier | modifier le code]

Entête[modifier | modifier le code]

Un fichier QOI se compose d'un en-tête de 14 octets, suivi d'un nombre quelconque de "morceaux" de données et d'un marqueur de fin de 8 octets.

qoi_header {
    char magic[4] ; // octets de magie "qoif".
    uint32_t width ; // largeur de l'image en pixels (BE)
    uint32_t height ; // hauteur de l'image en pixels (BE)
    uint8_t channels ; // 3 = RGB, 4 = RGBA
    uint8_t colorspace ; // 0 = sRGB avec alpha linéaire
// 1 = tous les canaux sont linéaires
} ;

Les champs espace colorimétrique et canal sont purement informatifs. Ils ne changent pas la façon dont les blocs de données sont encodés.

Encodage[modifier | modifier le code]

Les images sont encodées ligne par ligne, de gauche à droite, de haut en bas. Le décodeur et l'encodeur commencent par {r: 0, g: 0, b: 0, a: 255} comme valeur de pixel précédente. Une image est complète lorsque tous les pixels spécifiés par width * height ont été couverts. Les pixels sont encodés comme :

  • Codage de longueur d'exécution du pixel précédent ( QOI_OP_RUN )
  • un index dans le tableau des pixels vus précédemment ( QOI_OP_INDEX )
  • une différence par rapport à la valeur de pixel précédente dans r,g,b ( QOI_OP_DIFF ou QOI_OP_LUMA )
  • Valeurs complètes r,g,b ou r,g,b,a ( QOI_OP_RGB ou QOI_OP_RGBA )

Les canaux de couleur sont supposés ne pas être prémultipliés avec le canal alpha (« alpha non prémultiplié »). Un array[64] (initialisé à zéro) de valeurs de pixel vues précédemment est maintenu par l'encodeur et le décodeur. Chaque pixel vu par l'encodeur et le décodeur est placé dans ce tableau à la position formée par une fonction de hachage de la valeur de couleur.

Dans l'encodeur, si la valeur de pixel à l'index correspond au pixel actuel, cette position d'index est écrite dans le flux sous la forme QOI_OP_INDEX . La fonction de hachage de l'index est :

index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64

Chaque bloc commence par une balise de 2 ou 8 bits, suivie d'un certain nombre de bits de données. La longueur en bits des morceaux est divisible par 8 - c'est-à-dire que tous les morceaux sont alignés sur les octets. Toutes les valeurs codées dans ces bits de données ont le bit le plus significatif à gauche. Les balises 8 bits ont priorité sur les balises 2 bits. Un décodeur doit d'abord vérifier la présence d'une étiquette de 8 bits. La fin du flux d'octets est marquée par 7 octets 0x00 suivis d'un seul octet 0x01 . Les morceaux possibles sont :

┌─ QOI_OP_RGB ────┬─────────┬─────────┬─────────┐
     Byte[0]      Byte[1]  Byte[2]  Byte[3] 
 7 6 5 4 3 2 1 0  7 .. 0   7 .. 0   7 .. 0  
│─────────────────┼─────────┼─────────┼─────────│
 1 1 1 1 1 1 1 0    red     green    blue   
└─────────────────┴─────────┴─────────┴─────────┘
8-bit tag b11111110 (254)
Valeur du canal rouge 8 bits
Valeur du canal vert 8 bits
Valeur du canal bleu 8 bits

La valeur alpha reste inchangée par rapport au pixel précédent.

┌─ QOI_OP_RGBA ───┬─────────┬─────────┬─────────┬─────────┐
     Byte[0]      Byte[1]  Byte[2]  Byte[3]  Byte[4] 
 7 6 5 4 3 2 1 0  7 .. 0   7 .. 0   7 .. 0   7 .. 0  
│─────────────────┼─────────┼─────────┼─────────┼─────────│
 1 1 1 1 1 1 1 1    red     green    blue     alpha  
└─────────────────┴─────────┴─────────┴─────────┴─────────┘
8-bit tag b11111111 (255)
Valeur du canal rouge 8 bits
Valeur du canal vert 8 bits
Valeur du canal bleu 8 bits
Valeur du canal alpha 8 bits
┌─ QOI_OP_INDEX ──┐
   Byte[0]   
 7 6 5 4 3 2 1 0 
│────┼────────────│
0 0  index    (Range: 0..63)
└────┴────────────┘
2-bit tag b00Balise 2 bits b00
Index 6 bits dans le tableau d'indexation des couleurs : 0..63

Un encodeur valide ne doit pas émettre 2 blocs QOI_OP_INDEX consécutifs ou plus vers le même index. QOI_OP_RUN doit être utilisé à la place.

┌─ QOI_OP_DIFF ───┐
    Byte[0]      
 7 6 5 4 3 2 1 0 
│────┼───┼───┼────│
 0 1dr dg  db  (Range: 64 .. 127)
└────┴───┴───┴────┘
2-bit tag b01
2-bit red channel difference from the previous pixel -2..1
2-bit green channel difference from the previous pixel -2..1
2-bit blue channel difference from the previous pixel -2..1

La différence avec les valeurs de canal actuelles utilise une opération de bouclage, donc 1 - 2 donnera 255, tandis que 255 + 1 donnera 0. Les valeurs sont stockées sous forme d'entiers non signés avec un biais de 2. Par exemple − 2 est stocké sous la forme 0 ( b00 ). 1 est stocké en tant que 3 ( b11 ). La valeur alpha reste inchangée par rapport au pixel précédent.

┌─ QOI_OP_LUMA ───┬─────────────────┐
    Byte[0]        Byte[1]        
 7 6 5 4 3 2 1 0  7 6 5 4 3 2 1 0 
│────┼────────────┼────────┼────────│
1 0  diff green dr - dg db - dg  (Byte[0] Range: 128 .. 191)
└────┴────────────┴────────┴────────┘
2-bit tag b10
6-bit green channel difference from the previous pixel -32..31
4-bit red channel difference minus green channel difference -8..7
4-bit blue channel difference minus green channel difference -8..7

Le canal vert est utilisé pour indiquer le sens général du changement et est codé sur 6 bits. Les canaux rouge et bleu (dr et db) fondent leurs diffs sur la différence du canal vert. C'est-à-dire :

dr_dg = (cur_px.r - prev_px.r) - (cur_px.g - prev_px.g)
db_dg = (cur_px.b - prev_px.b) - (cur_px.g - prev_px.g)

La différence avec les valeurs de canal actuelles utilise une opération de bouclage, donc 10 - 13 donnera 253, tandis que 250 + 7 donnera 1. Les valeurs sont stockées sous forme d'entiers non signés avec un biais de 32 pour le canal vert et un biais de 8 pour le canal rouge et bleu. La valeur alpha reste inchangée par rapport au pixel précédent.

┌─ QOI_OP_RUN ────┐
     Byte[0]     
 7 6 5 4 3 2 1 0 
│────┼────────────│
1 1  run         (Range: 192 .. 253)
└────┴────────────┘
2-bit tag b11
6-bit run-length repeating the previous pixel: 1..62  111101   32 16 8 4 1

La longueur d'exécution est stockée avec un biais de − 1. Notez que les longueurs d'exécution 63 et 64 ( b111110 et b111111 ) sont illégales car elles sont occupées par les QOI_OP_RGB et QOI_OP_RGBA[8].

Références[modifier | modifier le code]

  1. (en) [vidéo] Reducible, How PNG Works: Compromising Speed for Quality sur YouTube, (consulté le ).
  2. (en) « Lossless Image Compression in O(n) Time », Phoboslab.org, (consulté le )
  3. (en) « ImageMagick - Image Formats » (consulté le )
  4. (en) « History of IrfanView Changes/Versions », www.irfanview.com (consulté le )
  5. (en) « FFmpeg Changelog - Gitweb », ffmpeg.org (consulté le )
  6. (en) James Hein, « Moving images to the next level », Bangkok Post,‎ (lire en ligne, consulté le ).
  7. (en) Simon Sharwood, « Developer creates ‘Quite OK Image Format’ – but it performs better than just OK », The Register,‎ (lire en ligne)
  8. (en) Dominic Szablewski, « The Quite OK Image Format Specification », (consulté le ) Cet article contient des extraits d'une publication dont le contenu se trouve dans le domaine public.

Voir aussi[modifier | modifier le code]

Liens externes[modifier | modifier le code]