Découverte de DICOM, le format d'imagerie médicale - PARTIE 2 : les données
Après un premier article plutôt théorique dans lequel on avait parlé de la structure des fichiers DICOM (format utilisé pour l'imagerie médicale), on se retrouve aujourd'hui dans un second article plus orienté « mains dans le cambouis ». On va cette fois-ci voir concrètement comment confectionner à la main un fichier DICOM avec des données médicales à l'intérieur et tout. 😲️
On va commencer par détailler tous les éléments obligatoires qui composent l'entête (partie « Meta Information ») d'un fichier DICOM, et on verra ensuite de quelle manière aborder le remplissage du Data Set (corps du fichier) avec des données sur le patient et une image issue d'un scanner.
Pour l'entête, je vais aborder les champs dans l'ordre dans lequel ils doivent apparaitre dans le fichier. Pour le corps en revanche je vais les grouper par module logique : ça sera plus simple de procéder ainsi car c'est la manière dont est structuré la spec DICOM. Gardez donc bien à l'esprit qu'il faudra les trier par tag croissant au moment de les écrire dans le fichier !
Notez également que le fichier qu'on va créer ne sera en réalité pas tout à fait complet, sinon l'article serait trop long, mais je vous donnerai toutes les informations utiles pour le compléter par vous-mêmes. 😉️
Je n'ai pas encore réussi à vous faire fuir ? Alors on se lance ! 😄️
- L'entête (partie « Meta Information »)
- File Preamble et DICOM Prefix
- File Meta Information Group Length (0002,0000)
- File Meta Information Version (0002,0001)
- Media Storage SOP Class UID (0002,0002)
- Media Storage SOP Instance UID (0002,0003)
- Transfer Syntax UID (0002,0010)
- Implementation Class UID (0002,0012)
- Implementation Version Name (0002,0013) [Optionnel]
- On finalise l'entête
- Le corps (partie « Data Set »)
- On recolle tout ensemble...
- Compléter le fichier à l'aide de DICOM Standard Browser
- Conclusion
Note
Cet article fait partie d'une série de deux articles sur DICOM, le format d'imagerie médicale :
L'entête (partie « Meta Information »)
Du coup, forcément, on va commencer par l'entête. Vous trouverez la liste de tous les éléments qui le compose dans le tableau PS3.10 7.1-1 de la norme :
Ce tableau donne toutes les informations sur les données à placer dans chaque élément... mais il ne donne pas le type des données (VR). Vous trouverez cette information dans une autre partie de la spec, et plus précisément dans le tableau 7-1 de la 6ème partie :
Et si jamais vous vous demandez à quoi correspond la colonne « Type » du tableau PS3.10 7.1-1, bah sachez que le type 1 signifie que l'attribut est obligatoire, le type 1C signifie que l'élément est obligatoire MAIS dans certaines conditions seulement, et le type 3 indique quant à lui que l'attribut est optionnel... Vous retrouverez cette information avec davantage de détails dans la section PS3.10 5 :
File Preamble et DICOM Prefix
Comme on l'a vu dans le précédent article, un fichier DICOM commence par un préambule et un préfixe qui ne sont pas encapsulés dans une structure de donnée, ce qui nous donne quelque chose comme ça :
Donnée | |
---|---|
File Preamble | |
Interprétation | vide (préambule inutilisé) |
Donnée binaire | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
DICOM Prefix | |
Interprétation | DICM |
Donnée binaire | 44 49 43 4D |
File Meta Information Group Length (0002,0000)
Ensuite vient notre premier élément de donnée structuré. Ce champ donne la longueur restante de la partie Meta Information, donc le nombre d'octets de l'entête sans compter le préambule, le préfixe et cette première structure. Pour le moment je laisse cette valeur vide, on la remplira à la fin puis qu'il faudra compter nos octets... 😅️
(7.1-2) | Tag | VR | VL | Value | |
---|---|---|---|---|---|
Interprétation | (0002,0000) | UL | 4 | ❓️ | |
Donnée binaire | 02 00 | 00 00 | 55 4C | 04 00 | XX XX XX XX |
File Meta Information Version (0002,0001)
On poursuit avec la version de l'entête, qui s'écrit sous forme de flags : la valeur ne s'interprète pas comme un nombre mais les bits passent à 1 pour indiquer le support d'une version particulière, et ces versions sont cumulatives... Bref, on ne va pas rentrer dans les détails, ici il faudra juste toujours mettre la valeur 0x00 0x01. Attention le champ est de type « tableau d'octet », il n'est donc pas question de big ou de little endian ici, il faut écrire la valeur telle quelle ! 😉️
(7.1-1) | Tag | VR | pad. | VL | Value | |
---|---|---|---|---|---|---|
Interprétation | (0002,0001) | OB | 2 | version 1 | ||
Donnée binaire | 02 00 | 01 00 | 4F 42 | 00 00 | 02 00 00 00 | 00 01 |
Media Storage SOP Class UID (0002,0002)
Maintenant on passe au « Media Storage SOP Class UID »... Derrière cette dénomination barbare se cache le type de donnée médicale que l'on va stocker dans le fichier DICOM : mammographie, courbe d'électrocardiogramme, tomodensitométrie (a.k.a scanographie ou scanner), image aux rayons X, etc.
Chaque type d'examen / de donnée se voit attribuer un identifiant unique que vous retrouverez dans le tableau ci-dessous :
Dans notre cas on va déclarer que notre fichier contient une image de type « CT Image » (« Computed Tomography » ou tomodensitométrie en français). Ce type d'image a pour identifiant unique "1.2.840.10008.5.1.4.1.1.2".
(7.1-2) | Tag | VR | VL | Value | (pad?) | |
---|---|---|---|---|---|---|
Interprétation | (0002,0002) | UI | 26 | 1.2.840.10008.5.1.4.1.1.2 | ||
Donnée binaire | 02 00 | 02 00 | 55 49 | 1A 00 | 31 2E 32 2E 38 34 30 2E 31 30 30 30 38 2E 35 2E 31 2E 34 2E 31 2E 31 2E 32 | 00 |
Media Storage SOP Instance UID (0002,0003)
Il s'agit d'un UID que vous pouvez à priori définir à la valeur que vous voulez mais qui doit être unique. Si vous avez une série de fichiers DICOM (ce qui est le cas pour les scanners et les IRM), vous pouvez par exemple donner comme ID 1.2.3.1 pour la première image, puis 1.2.3.2 pour la seconde, etc.
(7.1-2) | Tag | VR | VL | Value | (pad?) | |
---|---|---|---|---|---|---|
Interprétation | (0002,0003) | UI | 6 | 1.2.3 | ||
Donnée binaire | 02 00 | 03 00 | 55 49 | 06 00 | 31 2E 32 2E 33 | 00 |
Transfer Syntax UID (0002,0010)
On en a déjà pas mal parlé dans la première partie de l'article donc pas de surprise ici. Il faut déclarer dans cet attribut l'identifiant de la syntaxe de transfert que l'on va utiliser dans le Data Set. Pour rappel nous on va utiliser la « DICOM Explicit VR Little Endian Transfer Syntax » (PS3.5 A.2), qui a pour identifiant "1.2.840.10008.1.2.1".
(7.1-2) | Tag | VR | VL | Value | (pad?) | |
---|---|---|---|---|---|---|
Interprétation | (0002,0010) | UI | 20 | 1.2.840.10008.1.2.1 | ||
Donnée binaire | 02 00 | 10 00 | 55 49 | 14 00 | 31 2E 32 2E 38 34 30 2E 31 30 30 30 38 2E 31 2E 32 2E 31 | 00 |
Références utiles :
Implementation Class UID (0002,0012)
Bon pour celui-là j'ai pas 100 % compris, mais à priori c'est un identifiant unique qui sert à identifier le logiciel ou le matériel qui a servi à générer le fichier... À priori on peut mettre un peu ce qu'on veut, ça ne semble pas bien important à ce stade. 🤷♂️️
(7.1-2) | Tag | VR | VL | Value | (pad?) | |
---|---|---|---|---|---|---|
Interprétation | (0002,0012) | UI | 8 | 1.2.3.4 | ||
Donnée binaire | 02 00 | 12 00 | 55 49 | 08 00 | 31 2E 32 2E 33 2E 34 | 00 |
Implementation Version Name (0002,0013) [Optionnel]
Pour terminer, on va écrire le nom et la version de notre logiciel dans ce dernier champ [oui il est optionnel, mais ça me semble important de le définir au cas où un jour on chercherait à savoir qui a commis ce fichier DICOM qui ne respecte pas les standards ! 😛️].
(7.1-2) | Tag | VR | VL | Value | (pad?) | |
---|---|---|---|---|---|---|
Interprétation | (0002,0013) | SH | 10 | FLOZz 1.0 | ||
Donnée binaire | 02 00 | 13 00 | 53 48 | 0A 00 | 46 4C 4F 5A 7A 20 31 2E 30 | 20 |
On finalise l'entête
Et voilà on a fait le tour des éléments à définir dans l'entête du fichier. On peut donc à présent compter nos octets pour remplir la toute première structure qu'on a vu. Notre entête fait 124 octets de long (en enlevant le préambule, le préfixe et la première structure). En hexa ça donne 0x7C, on va donc pouvoir écrire 0x7C 0x00 0x00 0x00 comme valeur dans la structure (entier 32 bits non signé encodé en little endian).
Une fois tout recollé ensemble ça nous donne ça :
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080 44 49 43 4D 02 00 00 00 55 4C 04 00 7C 00 00 00 DICM....UL..|... 00000090 02 00 01 00 4F 42 00 00 02 00 00 00 00 01 02 00 ....OB.......... 000000A0 02 00 55 49 1A 00 31 2E 32 2E 38 34 30 2E 31 30 ..UI..1.2.840.10 000000B0 30 30 38 2E 35 2E 31 2E 34 2E 31 2E 31 2E 32 00 008.5.1.4.1.1.2. 000000C0 02 00 03 00 55 49 06 00 31 2E 32 2E 33 00 02 00 ....UI..1.2.3... 000000D0 10 00 55 49 14 00 31 2E 32 2E 38 34 30 2E 31 30 ..UI..1.2.840.10 000000E0 30 30 38 2E 31 2E 32 2E 31 00 02 00 12 00 55 49 008.1.2.1.....UI 000000F0 08 00 31 2E 32 2E 33 2E 34 00 02 00 13 00 53 48 ..1.2.3.4.....SH 00000100 0A 00 46 4C 4F 5A 7A 20 31 2E 30 20 ..FLOZz 1.0
C'est un bon début, passons donc au Data Set ! 😄️
Le corps (partie « Data Set »)
Le Data Set d'un DICOM se compose de modules qui vont définir un certain nombre d'éléments de donnée obligatoires et optionnels qui proviennent de divers groupes. Ces modules ont chacun un objectif précis, comme fournir la fiche du patient, les informations sur un type d'examen particulier ou le stockage d'une image.
Certains éléments de données sont nécessaires à différents modules et la norme les définit donc dans chacun des modules concernés, mais il faudra cependant définir chaque élément qu'une seule fois dans le fichier final.
Comme pour l'entête, la spec des différents modules ne définit pas les types des données (VR), il faudra pour cela se référer au tableau PS3.6 6-1 de la norme DICOM :
Note
Rappel : Comme expliqué en introduction, pour cette partie on va regrouper les éléments de données par module pour que ça soit plus digeste. Il faudra cependant les retrier par tag croissant pour les écrire dans le fichier final.
SOP Common Module (PS3.3 C.12.1)
On commence notre tournée des modules par le « SOP Common Module » qui contient des informations nécessaires à l'identification du Data Set.
Ici on va définir 2 éléments de donnée :
- SOP Class UID (0008,0016),
- et SOP Instance UID (0008,0018).
Si ça vous dit quelque chose, c'est normal car ils correspondent respectivement aux éléments (0002,0002) et (0002,0003) que l'on a déjà définis dans l'entête du fichier et ils doivent contenir strictement la même valeur (enfin techniquement c'est ceux de l'entête qui doivent être identiques à ceux du Data Set mais bon vous avez compris 🙃️).
Voici concrètement à quoi tout ça ressemble :
(7.1-2) | Tag | VR | VL | Value | (pad?) | |
---|---|---|---|---|---|---|
SOP Class UID | ||||||
Interprétation | (0008,0016) | UI | 26 | 1.2.840.10008.5.1.4.1.1.2 | ||
Donnée binaire | 08 00 | 16 00 | 55 49 | 1A 00 | 31 2E 32 2E 38 34 30 2E 31 30 30 30 38 2E 35 2E 31 2E 34 2E 31 2E 31 2E 32 | 00 |
SOP Instance UID | ||||||
Interprétation | (0008,0018) | UI | 6 | 1.2.3 | ||
Donnée binaire | 08 00 | 18 00 | 55 49 | 06 00 | 31 2E 32 2E 33 | 00 |
Références utiles :
Patient Module (PS3.3 C.7.1.1)
Passons ensuite à la fiche du patient. Ici on va se contenter de définir son nom et un identifiant unique pour le représenter, ce qui correspond aux éléments de données suivants :
- Patient's Name (0010,0010),
- et Patient ID (0010,0020).
(7.1-2) | Tag | VR | VL | Value | (pad?) | |
---|---|---|---|---|---|---|
Patient's name | ||||||
Interprétation | (0010,0010) | PN | 14 | Amanda^Ripley | ||
Donnée binaire | 10 00 | 10 00 | 50 4E | 0E 00 | 41 6D 61 6E 64 61 5E 52 69 70 6C 65 79 | 20 |
Patient ID | ||||||
Interprétation | (0010,0020) | LO | 4 | 937 | ||
Donnée binaire | 10 00 | 20 00 | 4C 4F | 04 00 | 39 33 37 | 20 |
Références utiles :
CT Image Module (PS3.3 C.8.2.1)
Comme je l'ai mentionné quand on a parlé du « Media Storage SOP Class UID » dans l'entête, on va écrire une image censée être issue d'un scanner dans notre fichier DICOM. On va donc devoir définir les éléments de donnée issus du module « CT Image ».
Bon ici on ne va en définir qu'un :
- Image Type (0008,0008)
Cet élément permet d'identifier les caractéristiques de l'image. Il s'agit d'une chaine de caractère qui contient (à priori) de 2 à 4 composantes que l'on va dans notre cas définir à "ORIGINAL\PRIMARY\AXIAL". Je ne vais pas rentrer dans les détails ici, mais si vous voulez en savoir plus, vous pouvez vous référer à la section PS3.3 C.7.6.1.1.2 de la norme pour les deux premières composantes, et à la section PS3.3 C.8.2.1.1.1 pour la troisième.
Ce module définit d'autres champs essentiels à l'interprétation de l'image, mais ils sont communs avec le module « Image Pixel Module » donc on en parlera juste après. 😉️
(7.1-2) | Tag | VR | VL | Value | |
---|---|---|---|---|---|
Interprétation | (0008,0008) | CS | 22 | ORIGINAL\PRIMARY\AXIAL | |
Donnée binaire | 08 00 | 08 00 | 43 53 | 16 00 | 4F 52 49 47 49 4E 41 4C 5C 50 52 49 4D 41 52 59 5C 41 58 49 41 4C |
Références utiles :
Image Pixel Module (PS3.3 C.7.6.3)
Passons maintenant à l'image en elle-même. Vous trouverez la liste complète des éléments de données à définir dans le tableau PS3.3 C.7.11a, ainsi que dans le tableau PS3.3 C.7-11c (qui est référencé par le premier) :
- Tableau « Image Pixel Module Attributes » (PS3.3 C.7.11a)
- Tableau « Image Pixel Description Macro Attributes » (PS3.3 C.7-11c)
On va commencer par définir les attributs qui permettent de caractériser l'image. Voici ceux dont on va avoir besoin :
Samples per Pixel (0028,0002) : Nombre de composantes pour chaque pixel de l'image. Dans notre cas il s'agira d'une image en niveau de gris, donc cette valeur sera à définir à 1. Si on avait voulu stocker une image couleur RGB, cette valeur aurait été à 3. Reportez-vous à la section PS3.3 C.7.6.3.1.1 de la spec pour plus d'informations.
Photometric Interpretation (0028,0004) : Interprétation graphique des octets qui composent l'image. Dans notre cas on mettra MONOCHROME2, ce qui signifie qu'un octet à 0 représente un pixel noir et un octet à la valeur maximale (255 pour cet exemple car on va coder les pixels sur 8 bits) représente un pixel blanc. Toutes les valeurs intermédiaires représentent un pixel gris plus ou moins lumineux. Il existe d'autres représentations comme MONOCHROME1 qui est simplement l'inverse du précédent (0 représente le blanc et la valeur maximale le noir), ou encore RGB pour une image en couleurs non indexées. Il en existe plein d'autres que vous retrouverez dans la section PS3.3 C.7.6.3.1.2.
Rows (0028,0010) : Hauteur de l'image. Dans notre cas elle fera 2 px de haut.
Columns (0028,0011) : Largeur de l'image. Dans notre cas elle fera 2 px de large.
Bits Allocated (0028,0100) : Nombre de bits à allouer pour chaque pixel de l'image. Dans notre cas ça sera 8. Les valeurs acceptables sont 1 (image monochrome, sans niveau de gris) ou un multiple de 8.
NOTE : Allouer 8 bits ici implique que notre élément Pixel Data sera de type (VR) OB. Si on avait mis 16 on aurait dû utiliser le type (VR) OW.
Bits Stored (0028,0101) : Sur le nombre de bits alloués, combien de bits sont effectivement utilisés pour représenter l'image. Cette valeur est obligatoirement inférieure ou égale à celle de Bits Allocated. Dans notre cas ça sera 8.
Prenons un second exemple pour clarifier un peu tout ça. Si on voulait avoir une image avec une profondeur de 12 bits par pixel, dans ce cas on aurait dû définir Bits Allocated à 16 et Bits Stored à 12 (donc on va consommer 16 bits par pixel d'espace dans le fichier, mais il n'y a que 12 bits sur les 16 qui sont utilisés).
High Bit (0028,0102) : Numéro du bit le plus significatif. Normalement cette valeur devrait valoir Bit Stored – 1. Dans notre cas ça sera donc 7.
Pixel Representation (0028,0103) : Indique si les nombres utilisés pour représenter les pixels sont signés (complément à 2) ou non signés. Dans notre cas on va utiliser des nombres non signés et donc on stockera 0 dans ce champ.
Voilà comment tout ça se traduit en structures de données :
(7.1-2) | Tag | VR | VL | Value | (pad?) | |
---|---|---|---|---|---|---|
Samples per Pixel | ||||||
Interprétation | (0028,0002) | US | 2 | 1 | ||
Donnée binaire | 28 00 | 02 00 | 55 53 | 02 00 | 01 00 | |
Photometric Interpretation | ||||||
Interprétation | (0028,0004) | CS | 12 | MONOCHROME2 | ||
Donnée binaire | 28 00 | 04 00 | 43 53 | 0C 00 | 4D 4F 4E 4F 43 48 52 4F 4D 45 32 | 20 |
Rows | ||||||
Interprétation | (0028,0010) | US | 2 | 2 | ||
Donnée binaire | 28 00 | 10 00 | 55 53 | 02 00 | 02 00 | |
Columns | ||||||
Interprétation | (0028,0011) | US | 2 | 2 | ||
Donnée binaire | 28 00 | 11 00 | 55 53 | 02 00 | 02 00 | |
Bits Allocated | ||||||
Interprétation | (0028,0100) | US | 2 | 8 | ||
Donnée binaire | 28 00 | 00 01 | 55 53 | 02 00 | 08 00 | |
Bits Stored | ||||||
Interprétation | (0028,0101) | US | 2 | 8 | ||
Donnée binaire | 28 00 | 01 01 | 55 53 | 02 00 | 08 00 | |
High Bit | ||||||
Interprétation | (0028,0102) | US | 2 | 7 | ||
Donnée binaire | 28 00 | 02 01 | 55 53 | 02 00 | 07 00 | |
Pixel Representation | ||||||
Interprétation | (0028,0103) | US | 2 | 0 (non signé) | ||
Donnée binaire | 28 00 | 03 01 | 55 53 | 02 00 | 00 00 |
Maintenant qu'on a donné toutes les caractéristiques de notre image... il nous reste plus qu'à l'écrire en utilisant un élément Pixel Data (7FE0,0010) :
(7.1-1) | Tag | VR | pad. | VL | Value | (pad?) | |
---|---|---|---|---|---|---|---|
Interprétation | (7FE0,0010) | OB | 4 | ▞ | |||
Donnée binaire | E0 7F | 10 00 | 4F 42 | 00 00 | 04 00 00 00 | FF 00 00 FF |
Références utiles :
On recolle tout ensemble...
Si on reprend tout ce qu'on a fait jusqu'à présent (structures de l'entête et du Data Set) et qu'on remet tout dans l'ordre, on arrive au fichier suivant :
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080 44 49 43 4D 02 00 00 00 55 4C 04 00 7C 00 00 00 DICM....UL..|... 00000090 02 00 01 00 4F 42 00 00 02 00 00 00 00 01 02 00 ....OB.......... 000000A0 02 00 55 49 1A 00 31 2E 32 2E 38 34 30 2E 31 30 ..UI..1.2.840.10 000000B0 30 30 38 2E 35 2E 31 2E 34 2E 31 2E 31 2E 32 00 008.5.1.4.1.1.2. 000000C0 02 00 03 00 55 49 06 00 31 2E 32 2E 33 00 02 00 ....UI..1.2.3... 000000D0 10 00 55 49 14 00 31 2E 32 2E 38 34 30 2E 31 30 ..UI..1.2.840.10 000000E0 30 30 38 2E 31 2E 32 2E 31 00 02 00 12 00 55 49 008.1.2.1.....UI 000000F0 08 00 31 2E 32 2E 33 2E 34 00 02 00 13 00 53 48 ..1.2.3.4.....SH 00000100 0A 00 46 4C 4F 5A 7A 20 31 2E 30 20 08 00 08 00 ..FLOZz 1.0 .... 00000110 43 53 16 00 4F 52 49 47 49 4E 41 4C 5C 50 52 49 CS..ORIGINAL\PRI 00000120 4D 41 52 59 5C 41 58 49 41 4C 08 00 16 00 55 49 MARY\AXIAL....UI 00000130 1A 00 31 2E 32 2E 38 34 30 2E 31 30 30 30 38 2E ..1.2.840.10008. 00000140 35 2E 31 2E 34 2E 31 2E 31 2E 32 00 08 00 18 00 5.1.4.1.1.2..... 00000150 55 49 06 00 31 2E 32 2E 33 00 10 00 10 00 50 4E UI..1.2.3.....PN 00000160 0E 00 41 6D 61 6E 64 61 5E 52 69 70 6C 65 79 20 ..Amanda^Ripley 00000170 10 00 20 00 4C 4F 04 00 39 33 37 20 28 00 02 00 .. .LO..937 (... 00000180 55 53 02 00 01 00 28 00 04 00 43 53 0C 00 4D 4F US....(...CS..MO 00000190 4E 4F 43 48 52 4F 4D 45 32 20 28 00 10 00 55 53 NOCHROME2 (...US 000001A0 02 00 02 00 28 00 11 00 55 53 02 00 02 00 28 00 ....(...US....(. 000001B0 00 01 55 53 02 00 08 00 28 00 01 01 55 53 02 00 ..US....(...US.. 000001C0 08 00 28 00 02 01 55 53 02 00 07 00 28 00 03 01 ..(...US....(... 000001D0 55 53 02 00 00 00 E0 7F 10 00 4F 42 00 00 04 00 US........OB.... 000001E0 00 00 FF 00 00 FF ......
Vous pouvez télécharger ce fichier en cliquant sur le bouton ci-dessous :
Et voici ce que ça donne si on ouvre le fichier avec le visualisateur Amide dont je vous avais parlé dans l'article précédent :
Bon c'est juste un damier et j'ai dû mettre le zoom au maximum pour qu'on voie quelque chose vu que notre image ne fait que 2×2 px, mais l'essentiel c'est que ça fonctionne ! 😁️
Compléter le fichier à l'aide de DICOM Standard Browser
On vient de créer notre premier fichier DICOM entièrement à la main, c'est cool, mais on a pas tout à fait terminé. En réalité il lui manque des trucs à notre fichier, et d'ailleurs Amide ne se gêne pas pour nous le faire savoir (toujours en train de se plaindre celui-là ! 😛️) :
Dans le premier article je vous avais mentionné DICOM Standard Browser, un outil permettant de naviguer dans la norme DICOM avec un point de vue orienté sur la pratique... Je vous avais dit qu'on en reparlerait plus longuement eh bah c'est maintenant !
Afin de compléter notre fichier, vous pouvez dès à présent vous rendre sur le site hébergeant l'outil, et je vais vous expliquer comment tout ça fonctionne :
Sur la gauche on retrouve une arborescence composée au premier niveau par les différents types d'examens médicaux (CIOD) qui peuvent être stockés dans un fichier DICOM. Pour chacun d'entre eux sont listés les modules qui peuvent être définis pour cet examen. Et pour chaque module on retrouve tous les éléments de données possibles. Pour chaque élément nous est indiqué son tag, le type de la donnée (VR) et s'il est obligatoire ou non (les fameux types 1, 1C, 2, 2C et 3 qu'on avait déjà vus un peu plus tôt).
En cliquant sur un élément, on retrouvera sur la droite un extrait de la documentation (plus ou moins complet en fonction de l'élément) ainsi que des informations utiles et des exemples de valeurs. Plutôt pratique non ? 😎️
Mais ce n'est pas tout, on peut faire encore mieux : il est possible de lui donner un fichier afin de naviguer dedans et voir tout ce qu'il contient... et ce qu'il lui manque ! 😲️
Pour ouvrir un fichier, cliquez sur l'onglet « File Editor » en haut à droite et drag-&-droppez votre fichier DICOM sur la zone prévue à cet effet :
Une fois le fichier ouvert, DICOM Standard Browser nous affiche un petit résumé du contenu de notre fichier sur la droite, et sur la gauche l'arborescence a pris des couleurs :
- les éléments qu'on a définis se retrouvent surlignés en orange pâle,
- et ceux qui sont obligatoires mais qui sont manquants dans le fichier sont surlignés en rouge pâle.
Ici on peut voir que 9 attributs obligatoires sont manquants dans notre fichier. Je vous laisse le soin de le compléter si vous le souhaitez. Pour cela vous n'avez qu'à cliquer sur les éléments en rouge pour trouver les attributs manquants et à suivre la documentation intégrée pour trouver comment les remplir !
DICOM Standard Browser est l'un des outils les plus indispensables si vous devez développer un logiciel capable de manipuler des fichiers DICOM.
Conclusion
Et voilà, c'est ainsi que s'achève notre petite virée dans l'univers du DICOM. Au final on s'en fait tout un monde car la norme est juste gigantesque, mais une fois qu'on a les bases et qu'on sait par quel bout la prendre, les choses sont tout de suite plus simples !
Bien qu'on ait abordé qu'une toute petite partie de la norme au cours de cet article en deux parties, j'espère qu'il aura pu vous être utile et vous servir de point de départ si vous avez à manipuler ce type de fichier, ou qu'il vous aura au moins intéressés si vous êtes juste curieux ! En tout cas j'ai essayé d'écrire l'article que j'aurai aimé lire quand je me suis lancé tête baissée dans la norme DICOM ! 😄️
À bientôt pour de nouveaux articles sur de tout autres sujets ! 😁️
L'image de couverture est dérivée d'une image médicale de Nevit Dilmen et est diffusée sous licence CC-BY-SA.