dimanche 5 août 2018

TP 1 - Construction d’un objet DICOM simple

Le but de cet exercice est de construire un objet DICOM de type « Encapsulated PDF ». Cet dernier permet d'encapsuler un document PDF dans un objet DICOM. Il est utilisé pour transporter un compte-rendu d'imagerie.
Si l'on devait faire une comparaison dans le monde HL7, cela correspondrait à un message HL7 v2 MDM (cf. le livre blanc « Harmonisation des modalités de communication des documents médicaux » publié par Interop'santé) ou HL7 v3 CDA Niveau 1.

DICOM propose d'autres objets plus structurés pour les compte-rendus (Structured Report). Ces objets étant assez complexes, ils feront le sujet d'article(s) dédié(s). Pour l'heure, étudions un objet DICOM simple.

Pour comprendre tout le vocabulaire utilisé dans ce TP, il est préférable d'avoir lu les articles suivants:
Nous allons également utiliser les parties PS3.3, PS3.5, PS3.6 et PS3.16 du standard DICOM.
  • PS3.3 : pour les spécifications de l'objet « Encapsultaed PDF » ;
  • PS3.5 : pour les types de données (type de VR) ;
  • PS3.6 : Pour le dictionnaire des UID (notamment pour la syntaxe de transfert et la SOP class UID) ;
  • PS3.16 : pour les jeux de valeurs.
Dans le cadre de cet exercice seul les éléments obligatoires seront retenus. L'objectif étant de construire un objet DICOM minimal, mais valide.

1. Convention utilisée en DICOM


1.1. Les modules DICOM


Dans un objet DICOM, certains modules (des regroupements d'attributs DICOM) peuvent être obligatoires, alors que d'autres sont optionnels. DICOM utilise la convention suivante pour indiquer quels sont les modules obligatoires et optionnels (voir DICOM PS 3.3 Annexe A1.3).

M
Module obligatoire (M pour Mandatory)
C
Module obligatoire sous condition (C pour Conditional)
U
Module optionel (U pour User Option)

1.2. Les types de données


Dans un module, certains Attributs (Data Element) sont requis et d'autres optionnels. Le tableau ci-dessous récapitule les types de données tels que décrit dans le standard DICOM PS 3.5 (Annexe 7.4).

Type 1
Élément obligatoire
La valeur doit être connue et valide avec une taille > 0. L'absence d'un élément de type 1 ou de sa valeur dans l'objet  constitue une violation du protocole DICOM.
Type 1C
Élément obligatoire sous condition
Les éléments ayant ce type doivent être inclus sous certaines conditions spécifiées. Les éléments de type 1C ont les mêmes obligations,   exigences et conditions que le type 1.
Type 2
Élément obligatoire avec valeur vide autorisée
La valeur peut être inconnue ou sa taille peut être 0. L'absence d'un élément de type 2 (mais pas de sa valeur) dans l'objet  constitue une violation du protocole DICOM.
Type 2C
Élément obligatoire avec valeur vide autorisée sous condition
Les éléments ayant ce type doivent être inclus sous certaines conditions spécifiées. Les éléments de type 2C ont les mêmes obligations, exigences et conditions que le type 2.
Type 3
Élément optionnel
L'absence d'un élément de type 3 ou de sa valeur dans l'objet ne constitue pas une violation du protocole DICOM.

2. Spécifications d’un objet « Encapsulated PDF »


Les spécifications de cet objet sont décrites dans la partie PS3.3 du standard, annexe A.45.1. D'après ces spécifications, les éléments obligatoires sont les suivants:

Nom Attribut
Tag
VR
Description
Module Patient
Patient's Name
(0010, 0010)
PN
Nom du patient.
Chaîne de caractères utilisant une convention suivante: Nom^prénom
Exemple: DUPOND^JEAN
Patient ID
(0010, 0020)
LO
Identifiant du patient
Chaîne de caractères représentant la valeur de l'identifiant du patient.
Exemple : 001
Patient’s Birth Date
(0010, 0030)
DA
Date de naissance du patient
Chaîne de caractères au format YYYYMMDD ou:
·  YYYY est l'année de naissance;
·  MM le mois de naissance;
·  DD le jour de naissance.
Patient’s Sex
(0010, 0040)
CS
Sexe du patient, peut être vide
Valeurs possibles:
·  M = Masculin
·  F = Féminin
·  O = Other
Module General Study
Study Instance UID
(0020, 000D)
UI
Identifiant unique de l'examen
Chaîne de caractères contenant un UID.
Un UID est une série de chiffres séparés par le caractère "." Longueur maximum 64 caractères.
Study Date
(0008, 0020)
DA
Date de début de l'examen
Chaîne de caractères au format YYYYMMDD ou:
·  YYYY est l'année de naissance;
·  MM le mois de naissance;
·  DD le jour de naissance.
Peut être vide
Study Time
(0008, 0030)
TM
Heure de début de l'examen
Chaîne de caractères au format HHMMSS où
·  HH représente les heures ("00" - "23");
·  MM les minutes ("00" - "59");
·  SS les secondes ("00" - "59").
Peut être vide
Referring Physician's Name
(0008,0090)
PN
Nom de la personne (physique ou morale) responsable du PDF. Auteur du Document.
Sous la forme: <nom>^<prénom>
Peut être vide
Study ID
(0020, 0010)
SH
Identifiant d'examen généré par un utilisateur ou un équipement.
Peut être vide
Accession Number
(0008, 0050)
SH
Numéro généré par le RIS qui identifie la demande d'examen radiologique. Ce champ est utilisé si un seul Accession Number est nécessaire.
Si plusieurs Accession Number sont nécessaires, utiliser l'élément Request Attributes Sequence (0040, 0275).
Peut être vide
Module Encapsulated Document Series
Modality
(0008, 0060)
CS
Type de modalité
Valeur "SC" = Secondary Capture
Series Instance UID
(0020, 000E)
UI
Identifiant unique de la série
Chaîne de caractères contenant un UID.
Un UID est une série de chiffres séparés par le caractère "." Longueur maximum 64 caractères.
Serie Number
(0020, 0011)
IS
Un numéro qui identifie la série
Exemple : 1
Module General Equipment
Manufacturer
(0008, 0070)
LO
Fabriquant ou éditeur qui a produit le PDF.
Peut être vide.
Module SC Equipment
Conversion Type
(0008,0064)
CS
Défini le type de converion de l’image ou du document
Valeurs possibles :
   · DV : Digitized Video;
   · DI : Digital Interface;
   · DF : Digitized Film;
   · WSD : Workstation;
   · SD : Scanned Document;
   · SI : Scanned Image;
   · DRW : Drawing;
   · SYN : Synthetic Image.
Exemple: SD
Module Encapsulated Document
Instance Number
(0020, 0013)
IS
Un numéro qui identifie le document
Valeur possible: 1
Content Date
(0008, 0023)
DA
Date de création du document PDF
Chaîne de caractères au format YYYYMMDD ou:
· YYYY est l'année de naissance;
· MM le mois de naissance;
· DD le jour de naissance.
Peut être vide
Content Time
(0008, 0033)
TM
Heure de création du document PDF
Chaîne de caractères au format HHMMSS où
· HH représente les heures ("00" - "23");
· MM les minutes ("00" - "59");
· SS les secondes ("00" - "59").
Peut être vide
Acquisition DateTime
(0008,002A)
DT
Date et heure de génération des données du document.
Peut être vide
Burned In Annotation
(0028,0301)
CS
Indique si le document contient des annotations pouvant  identifier le patient (ex : nom, prénom, identifiant présent dans le PDF).
Un document anonymisé peut utiliser la valeur NO
Valeurs possibles :
· YES
· NO
Peut être vide
Document Title
(0042,0010)
ST
Titre du document PDF.
Peut-être vide.
Concept Name Code Sequence
(0040, A043)
SQ
Code représentant le titre du document. Ce code est sous forme d'une séquence composée d'un seul élément. 
Les valeurs possibles des 3 composantes de cet élément  sont définies dans le standard DICOM, PS3.16, CID 7020.
Peut-être vide.
> Code Value
(0008, 0100)
SH
Voir le tableau du Context ID 7020 du standard DICOM  PS3.16
Exemple: "18748-4"
> Coding Scheme Designator
(0008, 0102)
SH
Voir le tableau du Context ID 7020 du standard DICOM PS3.16
Exemple: "LN"
> Code Meaning
(0008, 0104)
LO
Voir le tableau du Context ID 7020 du standard DICOM PS3.16
Exemple: "Diagnostic Imaging Report"
MIME Type of Encapsulated Document
(0042, 0012)
LO
Type du document encapsulé.
Valeur : application/pdf
Encapsulated Document
(0042, 0011)
OB
Flux d'octets du document PDF.
Module SOP Common
SOP Class UID
(0008, 0016)
UI
Identifiant sous forme d'un OID permettant d'identifier l'objet DICOM.
La valeur doit être: 1.2.840.10008.5.1.4.1.1.104.1
SOP Instance UID
(0008, 0018)
UI
Identifiant unique de l'objet DICOM.
Chaîne de caractères contenant un UID.
Un UID est une série de chiffres séparés par le caractère "." Longueur maximum 64 caractères.
Specific Character Set
(0008, 0005)
CS
Cet attribut doit avoir la valeur "ISO_IR 100".
Note: ISO_IR 100 représente le système de codage  ISO 8859-1 (Latin 1)

3. Code exemple de génération d'un objet « Encapsulated PDF »


Pour générer cet objet, nous allons utiliser l'utilitaire open source dcm4che. Cet utilitaire propose une API pour manipuler les données DICOM et créer des fichiers (il propose bien plus, mais dans ce TP, seule cette partie nous intéresse).

Le projet maven de ce TP est disponible ici.

Le snippet est également présenté ci-dessous :

package com.blog.dicom.tp1;

import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;

import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Sequence;
import org.dcm4che3.data.Tag;
import org.dcm4che3.data.UID;
import org.dcm4che3.data.VR;
import org.dcm4che3.io.DicomOutputStream;

public class TP1 {
 public static void main(String[] args) throws FileNotFoundException, IOException {
  // Construction d'un objet DICOM
  final Attributes dataset = new Attributes();

  // Module Patient
  dataset.setString(Tag.PatientName, VR.PN, "DESMAUX^NATHALIE");
  dataset.setString(Tag.PatientID, VR.LO, "001");
  dataset.setString(Tag.PatientBirthDate, VR.DA, "19550920");
  dataset.setString(Tag.PatientSex, VR.CS, "F");

  // Module General Study
  final Date now = new Date();
  dataset.setString(Tag.StudyInstanceUID, VR.UI, "1.2.3.4");
  dataset.setDate(Tag.StudyDate, now);
  dataset.setDate(Tag.StudyTime, now);
  dataset.setString(Tag.ReferringPhysicianName, VR.PN, "");
  dataset.setString(Tag.StudyID, VR.SH, "");
  dataset.setString(Tag.AccessionNumber, VR.SH, "");

  // Module Encapsulated Document Series
  dataset.setString(Tag.Modality, VR.CS, "SC");
  dataset.setString(Tag.SeriesInstanceUID, VR.UI, "1.2.3.5");
  dataset.setString(Tag.SeriesNumber, VR.IS, "1");

  // Module General Equipment
  dataset.setString(Tag.Manufacturer, VR.LO, "IDO-IN");

  // Module SC Equipement
  dataset.setString(Tag.ConversionType, VR.CS, "SD");

  // Module Encapsulated Document
  dataset.setString(Tag.InstanceNumber, VR.IS, "1");
  dataset.setString(Tag.ContentDate, VR.DA, "");
  dataset.setString(Tag.ContentTime, VR.TM, "");
  dataset.setString(Tag.AcquisitionDateTime, VR.DT, "");
  dataset.setString(Tag.BurnedInAnnotation, VR.CS, "YES");
  dataset.setString(Tag.DocumentTitle, VR.ST, "Titre du document");
  dataset.setString(Tag.MIMETypeOfEncapsulatedDocument, VR.LO, "application/pdf");
  final Sequence docTitleCode = dataset.newSequence(Tag.ConceptCodeSequence, 3);
  final Attributes codedTitle = new Attributes();
  docTitleCode.add(codedTitle);

  codedTitle.setString(Tag.CodeValue, VR.SH, "18748-4");
  codedTitle.setString(Tag.CodingSchemeDesignator, VR.SH, "LN");
  codedTitle.setString(Tag.CodeMeaning, VR.LO, "Diagnostic Imaging Report");

  byte[] pdfBytes = Files.readAllBytes(Paths.get("src/main/resources/tp1.pdf"));

  final int pdfLen = pdfBytes.length;

  // S'assure d'avoir un nombre pair d'octets (PS3.5 - OB Type)
  if ((pdfLen & 1) != 0) {
   final byte[] pdfBytesEven = new byte[pdfLen + 1];
   System.arraycopy(pdfBytes, 0, pdfBytesEven, 0, pdfLen);
   pdfBytesEven[pdfLen] = 0;
   pdfBytes = pdfBytesEven;
  }
  dataset.setValue(Tag.EncapsulatedDocument, VR.OB, pdfBytes);

  // Module SOP Common
  dataset.setString(Tag.SOPClassUID, VR.UI, UID.EncapsulatedPDFStorage);
  dataset.setString(Tag.SOPInstanceUID, VR.UI, "1.2.3.4.5");

  dataset.setSpecificCharacterSet("ISO_IR_100");

  // Ecriture de l'objet DICOM sur le disque
  writeDcmFile(dataset, Paths.get("src/main/resources/dcmPdf.dcm"));

 }

 public static void writeDcmFile(final Attributes dataset, final Path completeFilePath) throws IOException {
  // Creation des repertoires parents si necessaire
  if (!Files.exists(completeFilePath.getParent())) {
   Files.createDirectories(completeFilePath.getParent());
  }
  // Ecriture d'un fichier en Explicit VR Little Endian
  try (FileOutputStream fout = new FileOutputStream(completeFilePath.toFile());
   BufferedOutputStream bout = new BufferedOutputStream(fout);
   DicomOutputStream out = new DicomOutputStream(bout, UID.ExplicitVRLittleEndian);) {
   out.writeDataset(dataset.createFileMetaInformation(UID.ExplicitVRLittleEndian), dataset);
   out.flush();
   fout.getFD().sync();
  } catch (final IOException e) {
   e.printStackTrace();
  }
 }
}

Aucun commentaire:

Enregistrer un commentaire