lundi 24 septembre 2018

Les images en DICOM - Partie 1

Dans les articles précédents nous avons vu comment DICOM gère, organise et encode les informations médicales. Voyons maintenant comment le standard représente et stocke les images. Il existe, en DICOM, deux grands types de formats pour stocker les images (cf. PS3.5 - section 8.2):
  • Les formats natifs : Ce sont les premiers formats apparus. Ils permettent de stocker les images au format Bitmaps, c'est-à-dire sans compression.  ;
  • Les formats encapsulés: Ces formats permettent de stocker les images en utilisant des techniques de compressions. Ces dernières peuvent être avec ou sans perte de données.
Afin de traiter ces deux approches, plusieurs articles sont nécessaires pour décomposer les idées et ne pas faire un « gros pavé ».

Dans ce billet nous nous intéresserons uniquement à la première approche, c'est-à-dire les formats natifs. Deux autres billets seront consacrés aux formats encapsulés et aux transferts de syntaxes.

1. Les Images


Dans le monde numérique, une image est une matrice rectangulaire de pixels1 (des petits points de différentes couleurs).

Si nous voulions stocker une image, de quelles informations aurions-nous besoin ?
Tout d'abord, Il nous faudrait savoir les dimensions de l'image (sa hauteur et sa largeur).
Ensuite, il faudrait connaitre la valeur des pixels de l'image. Cependant, un pixel peut prendre différentes couleurs (voir la figure ci-dessus). Il faudrait donc pouvoir spécifier la couleur de chaque pixel. Pour cela, il faut pouvoir dire quel « espace de couleur » (représentation des couleurs dans un système de gestion des couleurs en informatique) est utilisé (comme par exemple RGB, HSV ou encore HSL) car oui, il existe différentes manières de coder une couleur.
Il existe ainsi toute une panoplie d'informations nécessaires pour représenter une image.

DICOM a listé tous ces éléments et les a regroupés dans un module « image ». Dans le dictionnaire de données (PS3.6), tous les « Data Element » concernant une image ont l'identifiant de groupe 0028.

2. Encodage d'une image en DICOM


Parmi les tags DICOM les plus utilisés pour caractériser une image, on trouve:

2.1. Rows & Columns


Les tags (0028, 0010) Rows et (0028, 0011) Columns permettent de définir la dimension de l'image. Le tag « Rows » représente la hauteur de l'image et le tag « Columns » spécifie sa largeur.

2.2. Samples Per Pixel


Le tag (0028, 0002) Samples per Pixel spécifie le nombre de canaux de couleur pour un pixel. Par exemple, pour une image en niveaux de gris, un seul canal suffit. Pour une image en couleur utilisant le système de couleur RGB, 3 canaux sont nécessaires (un pour le rouge, un autre pour le vert et un dernier pour le bleu).

2.3. Photometric Interpretation


Le tag (0028,0004) Photometric Interpretation indique l'espace de couleur utilisé. Suivant le type d'image et le format utilisé, cet attribut DICOM peut prendre la valeur :
  • MONOCHROME1 ou MONOCHROME2 pour les images en niveaux de gris.
    • MONOCHROME1 indique que la valeur la moins élevée d'un pixel sera interprétée par du blanc et la valeur la plus élevée par du noir ;
    • MONOCHROME2 indique que la valeur la moins élevée d'un pixel sera interprétée par du noir et la valeur la plus élevée par du blanc.
  • PALETTE COLOR, RGB ou les différentes variantes de YBR pour les images en couleurs (voir PS3.3 section 7.6.3.1.2 pour les valeurs possibles de YBR et leur signification).

2.4. Planar configuration


Le tag (0028,0006) Planar configuration indique l'arrangement des canaux dans la représentation des pixels. Les valeurs possibles de ce champ DICOM sont « 0 » ou « 1 ».
  • La valeur « 0 » signifie que les canaux sont entrelacés pour représenter un pixel. Cette manière de faire est la façon la plus intuitive et celle utilisée par défaut par DICOM. Pour une image RGB, cela signifie que les pixels seront encodés de la manière suivante : R1, G1, B1, R2, G2, B2, ..., etc.
  • La valeur « 1 » indique que chaque canal est encodé de manière continue. On encode déjà tout un canal puis ensuite un autre, et ainsi de suite. Pour une image RGB, cela implique que les pixels seront encodés de la manière suivante R1, R2, R3, ..., G1, G2, G3, ..., B1, B2, B3, etc.
Remarque 1 : Le tag (0028,0006) Planar configuration n'a de sens que si plusieurs canaux interviennent dans la représentation d'un pixel (tag (0028, 0002) Samples Per Pixel supérieur à 1).

Remarque 2 : Le tag (0028,0006) Planar configuration n'a pas non plus de sens si une technique de compression implique une réorganisation des canaux dans le flux compressé. Dans de tels cas, si l'attribut est requis, la valeur à mettre dans ce champ est spécifiée dans la partie PS 3.5 section 8.2 en fonction de la technique de compression utilisée.

2.5. Bits Allocated, Bits Stored & High Bit


Le tag (0028,0100) Bits Allocated spécifie l'espace alloué pour chaque canal d'un pixel exprimé en bit. Les valeurs de ce champ sont en général 8 ou 16 pour une image en niveaux de gris et 24 pour une image couleur.

Le tag (0028,0101) Bits Stored indique le nombre de bits utilisé parmi ceux alloués pour chaque canal. Il est ainsi possible d'allouer 2 octets (Bits Allocated = 16) mais de n'en utiliser que 12. Par exemple, supposons que l'on ait une image en niveaux de gris pour laquelle chaque pixel utilise 8 bits. Chacun de ces pixels peut prendre une valeur entre 0 et 255 (28 valeurs). Le nombre de niveau de gris est donc de 256. Si maintenant on utilise 12 bits au lieu de 8, chaque pixel peut prendre 212 = 4096 valeurs, soit 4096 niveaux de gris. En résumé, les bits stored permettent de représenter la richesse d'une image en terme de nombre de valeurs.

Remarque: Il est possible que tous les bits alloués ne soient pas exploités. Par exemple, si seulement 12 bits sur 16 sont utilisés, les 4 restants seront « masqués » lors de l'interprétation par une application DICOM. Cet espace non utilisé a, dans les débuts du standard, été exploité pour stocker d'autres informations. Cependant, cette technique est aujourd'hui complètement obsolète.

Le tag (0028,0102) High Bit définit où se trouvent les bits utilisés, à l'intérieur de l'espace alloué. Les bits utilisés sont représentés par un segment continu (les bits utilisés sont adjacents). La valeur correspond à l'index du dernier bit utilisé.

Exemple d'utilisation de ces 3 éléments DICOM - Image Monochrome


Supposons que l'on dispose d'une image en niveaux de gris (Monochrome) de 512x512 pixels et que chaque pixel utilise 12 bits.
  • Bits Allocated : Pour stocker 12 bits il nous faut 2 octets (16 bits). (0028,0100) Bits Allocated aura donc pour valeur 16.
  • Bits Stored : Chaque pixel utilise 12 bits. (0028,0101) Bits Stored aura pour valeur 12.
  • High Bit : Sur les 16 bits (numérotés de 0 à 15), seuls 12 sont nécessaires. Nous souhaitons que ce soit les bits 2 à 13. (0028,0102) High Bit aura donc la valeur 13.


Remarque: En pratique, on commence toujours par stocker les bits à partir de l'offset 0. (0028,0102) High Bit est toujours égal à la valeur de Bits Stored - 1.

La taille de l'image stockée est donc 512 x 512 x 2 = 524288 octets = 512 ko.

Exemple d'utilisation de ces 3 éléments DICOM - Image Couleur


Voyons un autre exemple avec une image de couleur de 512 x 512 pixels dans un espace de couleur de type RGB.
  • Photometric Interpretation : Puisque c'est une image couleur RGB, chaque pixel aura 3 canaux. Photometric Interpretation aura pour valeur 3.
  • Bits Allocated : Pour stocker 12 bits il nous faut 2 octets (16 bits). (0028,0100) Bits Allocated aura donc pour valeur 16.
  • Bits Stored : Chaque composante de chaque pixel utilise 12 bits. (0028,0101) Bits Stored aura pour valeur 12.
  • High Bit : Sur les 16 bits (numérotés de 0 à 15), seuls 12 sont nécessaires. Nous souhaitons que ce soit les bits 0 à 11. (0028,0102) High Bit aura donc la valeur 11.


La taille de l'image stockée est donc 512 x 512 x 2 x 3 = 524288 octets = 1.5 Mo.

2.6. Pixel Representation


Le tag (0028, 0103) Pixel Representation indique si les pixels sont encodés avec des octets signés (Pixel Representation a la valeur 1) ou non signés (Pixel Representation a la valeur 0). L'octet non signé est très souvent utilisé par défaut.

Octet « signé » et « non signé » : L'informatique utilise un système binaire à base de « 0 » et « 1 » mais pas de « + » et « - ». Par conséquent, le seul moyen est de convenir que si un nombre est susceptible d'être négatif, on lui réserve un bit pour indiquer le signe. Ce bit de signe est celui le plus à gauche de l'octet.

2.7. Pixel Data


Le tag (7FE0, 0010) Pixel Data permet de stocker les pixels. L'ordre des pixels encodés est de gauche à droite et de haut en bas. Le coin supérieur gauche (de coordonnées 1,1) est encodé en premier, suivi du reste de la première ligne. Vient ensuite le premier pixel de la seconde ligne (de coordonnées 2,1), puis le reste de la seconde ligne, et ainsi de suite.

Exemple d'encodage d'une image en niveau de gris:

Remarque: L'élément (7FE0, 0010) Pixel Data ne fait étonnement pas partie du groupe 0028 (le groupe « Image »). Le standard ne spécifie pas les raisons, mais en analysant la partie PS3.6 - section 6 du standard, on peut remarquer deux choses:
  • Le groupe 7FE0 ne dispose que de trois élements : (7FE0, 0008) Float Pixel Data, (7FE0, 0009) Double Float Pixel Data et (7FE0, 0010) Pixel Data. ;
  • Le groupe 7FE0 se trouve à la fin du dictionnaire des éléments DICOM.
On peut en déduire que l'identifiant 7FE0 représente le groupe « Pixel » et que DICOM a voulu séparer le stockage des pixels du reste des caractéristiques d'une image.
Pourquoi cette séparation ?
On peut supposer que les concepteurs du standard préféraient stocker les pixels en fin de fichier DICOM, afin de pouvoir extraire facilement les méta données DICOM sans les pixels.

Remarque: La VR de Pixel Data peut être OB ou OW suivant le nombre d'octets nécessaire pour stocker un pixel. Pour rappel, si la VR est OW, il faut effectuer un byte swapping pour interpréter les octets.

Remarque: La VR de Pixel Data doit être OW si l'encodage est en Implicit VR Little Endian.

3. Exemple d'encodage d'une image en DICOM


Supposons que l'on veuille encoder l'image suivante :

Cette image a une dimension de 6 x 6 pixels et utilise un espace de couleur RGB. Pour encoder cette image, nous allons utiliser les valeurs par défaut pour les tags Planar Configuration et Pixel Representation. Puisque l'on veut encoder une image en couleur, 3 canaux sont nécessaires pour chaque pixel. On utilisera un octet par canal (un par composante). Cela représente une taille de 3 x 36 = 108 octets pour encoder les 36 pixels de l'image.

L'encodage DICOM donne le résultat suivant:
(0028,0002) US #2 [3] SamplesPerPixel
(0028,0004) CS #4 [RGB] PhotometricInterpretation
(0028,0006) US #2 [0] PlanarConfiguration
(0028,0010) US #2 [6] Rows
(0028,0011) US #2 [6] Columns
(0028,0100) US #2 [8] BitsAllocated
(0028,0101) US #2 [8] BitsStored
(0028,0102) US #2 [7] HighBit
(0028,0103) US #2 [0] PixelRepresentation
(7FE0,0010) OW #108 [CC CC CC FF EE A9 FF EE A9 FF EE A9 FF EE A9 CC CC CC
                     FF EE A9 FF EE A9 FF EE A9 FF EE A9 FF EE A9 FF EE A9
                     FF EE A9 00 00 00 FF EE A9 FF EE A9 00 00 00 FF EE A9
                     FF EE A9 FF EE A9 FF EE A9 FF EE A9 FF EE A9 FF EE A9
                     FF EE A9 FF EE A9 00 00 00 00 00 00 FF EE A9 FF EE A9
                     CC CC CC FF EE A9 FF EE A9 FF EE A9 FF EE A9 CC CC CC] PixelData


4. Les Multi-frames


Il est possible de stocker plus d'une image dans un objet DICOM. On appelle cela des Multi-frames. Le nombre d'images contenu dans un objet DICOM est indiqué par le tag (0028,0008) Number of Frames. Cet élément contient un entier positif indiquant combien d'images sont présentes dans le tag (7FE0, 0010) Pixel Data.
L'élément (7FE0, 0010) Pixel Data contient les différentes images enchaînées les unes dernières les autres.
Pour rendre un peu plus concrètes les explications, reprenons l'exemple ci-dessus (smiley de dimension 6 x 6). Si nous voulions créer un objet avec deux images de smiley, l'objet DICOM ressemblerait à :

(0028,0002) US #2 [3] SamplesPerPixel
(0028,0004) CS #4 [RGB] PhotometricInterpretation
(0028,0006) US #2 [0] PlanarConfiguration
(0028,0008) IS #2 [2] NumberOfFrames
(0028,0010) US #2 [6] Rows
(0028,0011) US #2 [6] Columns
(0028,0100) US #2 [8] BitsAllocated
(0028,0101) US #2 [8] BitsStored
(0028,0102) US #2 [7] HighBit
(0028,0103) US #2 [0] PixelRepresentation
(7FE0,0010) OW #216 [...] PixelData

On remarque que par rapport à l'objet DICOM précédent, un nouveau tag a fait son apparition et un autre a été modifié. Le tag Pixel Data contient maintenant 216 octets (au lieu de 108). La délimitation entre les images se fait par calcul grâce aux différents tags DICOM indiquant les caractéristiques d'une image.
Une image a une taille de Rows x Colums x SamplesPerPixel x (BitsAllocated / 8) octets. Dans notre cas, chaque image occupe 6 x 6 x 3 x (8 / 8) = 108 octets. Pour aller d'une image à l'autre il faut donc sauter de 108 octets.

5. Les palettes de couleurs


Dans les exemples précédents, nous avons encodé une petite image de 6 x 6 pixels. Supposons maintenant que nous ayons une image couleur de 512 x 512 pixels, qui dispose de 256 couleurs différentes. Utiliser 1 octet par composante RGB est suffisant pour encoder ces 256 couleurs. La taille sera donc de 512 x 512 x 3 = 768 ko.

Les images basées sur un encodage RGB peuvent rapidement occuper un espace de stockage considérable. La question que l'on peut se poser est : N'y aurait-il pas un moyen de réduire cet espace occupé ? Une réponse possible est l'utilisation d'une palette de couleurs.

Les annexes PS3.3 - C.7.6.3.1.5, PS3.3 - C.7.6.3.1.6 et PS3.5 - A.4 décrivent l'utilisation des palettes de couleurs pour encoder une image en couleur.
L'idée est d'utiliser une table de correspondance (appelée « Lookup Table »). À chaque valeur de cette table est associée une clé (appelée « index »). On accède à chaque valeur de la table par sa clé. Côté pixels, on ne stocke plus la couleur (les 3 composantes RGB par exemple) mais la clé permettant d'accéder à la couleur dans la table de correspondance.
Le pixel i fait référence à l'index 23 de la palette de couleur. Il est de couleur verte.
Un tel système d'encodage permet de gagner de la place pour stocker une image.

Reprenons l'exemple ci-dessus de l'image couleur de 512 x 512 pixels avec 256 couleurs. Avec un encodage RGB, l'espace occupé par les pixels est de 768 ko.
En utilisant un encodage par palette de couleur, l'espace occupé serait de
  • pour l'image : 512 x 512 x 1 =  262 144 octets = 256 ko
  • pour la palette de couleur: 256 * 3 = 768 octets
La taille totale de l'image serait donc 262 912 octets ≃ 256 ko, soit 1/3 de la taille avec un encodage RGB.

5.1 Encodage DICOM


DICOM utilise 3 types de Data Element pour décrire l'encodage des palettes de couleurs :
  • (0028, 1101-1103) Palette Color Lookup Table Descriptor: Pour la description des données dans les éléments Palette Color Lookup Table Data ;
  • (0028, 1201-1203) Palette Color Lookup Table Data: Pour stocker les données de la palette de couleurs ;
  • (7FE0, 0010) Pixel Data: Pour stocker l'index de la table de correspondance de chaque couleur de pixel.

5.1.1. Palette Color Lookup Table Descriptor


Ce type de Data Element permet de décrire le format des données de la palette. Les palettes de couleurs utilisent le modèle RGB, il faut donc 1 Data Element pour décrire chacune des 3 couleurs primaires.
  • (0028, 1101) Red Palette Color Lookup Table Descriptor pour la composante rouge ;
  • (0028, 1102) Green Palette Color Lookup Table Descriptor pour la composante verte ;
  • (0028, 1103) Blue Palette Color Lookup Table Descriptor pour la composante bleue.
Chacun de ces éléments est composé de 3 valeurs:
  • La première valeur « Number of entries » permet de spécifier le nombre d'entrées dans la table de correspondance (Lookup Table). Il est possible d'aller jusqu'à 216 entrées. De manière générale, on trouve souvent 256 comme nombre d'entrées (et donc de couleurs).
  • La seconde valeur « First value mapped » indique la première entrée (le premier index) de la table de correspondance. En général, c'est toujours la valeur 0. Si d'aventure un pixel fait référence à un index inférieur à « First value mapped », alors c'est la « First value mapped » qui sera utilisée. De manière similaire, si un pixel fait référence à un index supérieur à la taille du tableau (Number of entries), alors c'est la dernière entrée de la table qui sera utilisée.
  • La dernière valeur « Number of bits » caractérise le nombre de bits utilisés pour chaque  donnée dans Lookup Table Data. Elle peut prendre les valeurs 8 ou 16. 

Remarque: Le tag (0028, 1104) Alpha Palette Color Lookup Table Descriptor existe dans le dictionnaire de données (PS3.6) et dans la description de la section PS3.3 - C.7.6.3.1.5 du standard. Mais une note de la section PS3.3 - C.7.6.3.1.1 explique que l'utilisation du canal Alpha a été retirée du standard (valeur « 4 » interdite dans le tag (0028, 0002) Samples Per Pixel, et valeur « ARGB » interdite dans (0028, 0004) Photometric Interpretation). Le tag concernant le canal Alpha est conservé uniquement à des fins de compatibilité.

5.1.2. Palette Color Lookup Table Data


Ce type de Data Element permet de stocker les données de la palette. Les palettes de couleurs utilisant le modèle RGB, il faut donc 1 Data Element pour chacune des 3 couleurs primaires.
  • (0028, 1201) Red Palette Color Lookup Table Data : Pour stocker les n composantes de rouge des n entrées possibles de la table de correspondance ;
  • (0028, 1202) Green Palette Color Lookup Table Data : Pour stocker les n composantes de vert des n entrées possibles de la table de correspondance ;
  • (0028, 1203) Blue Palette Color Lookup Table Data : Pour stocker les n composantes de bleu des n entrées possibles de la table de correspondance.
Il y a autant de valeurs dans chacun de ces éléments que de valeurs spécifiées par « Number of entries » dans les descriptions des Lookup Table.

Remarque : Les Data Element (0028, 1201-1204) Palette Color Lookup Table Data ont une VR SS ou US. Ces deux VR  utilisent donc 2 octets pour représenter une valeur. Dans le cas ou la valeur « Number of bits » des Data Element (0028, 1101-1104) Palette Color Lookup Table Descriptor est 8 bits (1 octet), il est possible de représenter deux entrées du tableau avec une seule valeur US ou SS.

5.1.3 Pixel Data


Dans le cas d'utilisation d'une palette de couleurs, le tag (7FE0, 0010) Pixel Data stocke l'index d'accès à une couleur dans la table de correspondance (contrairement à l'encodage monochrome ou RGB qui stocke les valeurs de chaque pixel). 

5.1.4 Exemple concret


Reprenons l'image du smiley de 6 x 6 pixels et encodons la avec une palette de couleur
La section PS3.3 - C.7.6.3.1.2 indique que si la valeur PALETTE COLOR est utilisée, alors (0028, 0002) Sample Per Pixels doit avoir la valeur « 1 ». L'encodage DICOM donne le résultat suivant:
(0028,0002) US #2 [1] SamplesPerPixel
(0028,0004) CS #4 [PALETTE COLOR] PhotometricInterpretation
(0028,0006) US #2 [0] PlanarConfiguration
(0028,0010) US #2 [6] Rows
(0028,0011) US #2 [6] Columns
(0028,0100) US #2 [8] BitsAllocated
(0028,0101) US #2 [8] BitsStored
(0028,0102) US #2 [7] HighBit
(0028,0103) US #2 [0] PixelRepresentation
(0028,1101) US #6 [4\0\8] RedPaletteColorLookupTableDescriptor
(0028,1102) US #6 [4\0\8] GreenPaletteColorLookupTableDescriptor
(0028,1103) US #6 [4\0\8] BluePaletteColorLookupTableDescriptor
(0028,1201) US #4 [255 127 0 0] RedPaletteColorLookupTableData
(0028,1201) US #4 [242 127 0 0] GreenPaletteColorLookupTableData
(0028,1201) US #4 [0 127 0 0] BluePaletteColorLookupTableData
(7FE0,0010) OW #36 [01 00 00 00 00 01 00 00 00 00 00 00 00 02 00 00 02 00
                    00 00 00 00 00 00 00 00 02 02 00 00 01 00 00 00 00 01] PixelData


Remarque: L'utilitaire dcmquant du toolkit dcmtk a été utilisé pour générer l'exemple ci-dessus à partir de l'image du smiley encodé en RGB.

Remarque: DICOM impose d'avoir un nombre pair pour la taille de chaque DICOM Element. C'est pourquoi l'outil dcmquant duplique la dernière valeur. Cela ne change rien à l'interprétation des palettes de couleurs car tout index supérieur à la taille du tableau sera interprété comme le dernier index de la table de correspondance.

(1) Pixel : Provient de la locution anglaise picture element, qui signifie « élément d'image ».

Aucun commentaire:

Enregistrer un commentaire