[Dcmlib] converting itk,vtk or any other 3D images into dicom.

Eric Boix Eric.Boix at creatis.insa-lyon.fr
Tue Nov 9 15:31:41 CET 2004


	Salut,

Quoting Jean-Pierre ROUX <jean-pierre.roux at creatis.insa-lyon.fr>:
Mathieu> >ReplaceOrCreateByNumber et pas ReplaceOrCreateByName.
> 
> On pourrait attendre pour voir si le group de réflexion DICOM sur 
> l'API a une opinion sur la question, mais la sagesse serait de ne 
> *jamais* utiliser les xxxByName.

Maintenant que Jean-Michel Rouet a fort gentiment essuye' les platres pour
nous, et que Mathieu a aussi un peu souffert en voulant ecrire des
images, je pense (comme JPR, mais a plus court terme) qu'il est en effet
assez urgent de parler de l'API.

Je pense paticulierement aux points suivants qu'il faudrait clarifier:
  1/ l'acces au tag
  2/ distinction des operations de modification/creation d'un tag
  3/ definition du mode d'ecriture.

Avant de lancer le[s] troll[s], je cite l'extrait suivant du commentaire
doxygen de la definition de TagKey (typedef) dans src/Common.h:

           TagKey is made to old an "universal" (as in URL, Universal
           Ressource Locator)  key to a DocEntry i.e. a dicom tag.
           A dicom tag allways has a group and an element, but a set of tags
           embeded in various (optionally nested) sequences and sharing
           the same group and element all share the same (group, element)
           "identifier". Hence the (group, element) cannot be used as an
           identifier (in gdcm we shall refer to a "TagKey") of a tag.
           In order to construct a proper tag identifier (i.e. a key) we
           consider the following definition of a TagKey:
           - let Group, Element be the string representation of the
             group and element dicom tag members,
           - let ItemNumber be the string representation of the integer
             index of the considered item number of a sequence,
           Let the key of a tag embeded in a sequence, noted SeqTag, be
           the form:
              /ItemNumber#Group|Element
           where "/", "#" and "|" are characters acting as separators.
           Then the general form of a TagKey is given by:
              Group|Element<SeqTag>
           where <SeqTag> means NO or many instances of SeqTag.
           Hence the TagKey of a tag not "leaving" in a sequence is the
           string e.g.
               0028|1201
           but the TagKey of a tag "embeded" is the first item of
           a sequence, itself nested in the third item of a sequence is the
           string e.g.
               0004|1220/2#0008|0082/0#0008|0090

Bref, la representation interne a gdcm d'une clef de tag (appele' pompeusement
clef generalise'e) est un string de la forme:  
  - 0028|1201 pour un tag "a plat" (pas dans une sequence).
  - 0004|1220/2#0008|0082/0#0008|0090... pour un tag de sequence

Maintenant, on peut discuter (si vous le voulez bien) les differents
points:

######################
Point 1/ acces au tag:
   Une fois un Header parse', l'utilisateur veut legitimement acceder aux
   tags trouve's.
   Question 1a/:
      Comme le rappelait JPR le "nom" d'un tag (i.e. la designation dans
      le dictionnaire) n'est pas unique et cela pose le pb des synonymes
      et ce a deux niveaux differents:
        * Le premier est qu'un tag dont le nom est unique dans le dictionnaire
          peut tout de meme figurer plusieurs fois dans le Header parse' si 
          jamais il apparait dans plusieurs sequences. La clef generalise'e
          semble regler ce point.
        * le second est que des noms comme Raws ne sont pas uniques dans le
          dictionnaire (et la clef generalise'e ne regle rien).
       D'ou la question: ne devrait-on purement et simplement supprimer
                         l'acces au tag par nom ?
   Question 1b/:
       Les acces par clef (methodes de la forme *ByNumber dans gdcmDocument.h,
       entre autres) utilisent actuellement un prototype de la forme
          *ByNumber( ..., uint16_t group, uint16_t elem, ...)
       ce qui present deux inconvenients:
       - on ne peut acceder a des tags dans des sequences (dont la clef
         generalise'e est de la forme 0004|1220/2#0008|0082/0#0008|0090...)
       - cela expose le type uint16_t:
          -- cela impose de le rendre visible dans gdcmCommon.h (a ce sujet
             il serait bon de le mettre dans le namespace non ?).
          -- cela complique le wrappage (Python) puisque qu'il faut definir
             la conversion entre le langage cible et le C++.
       D'ou la question: ne devrait pas choisir des prototypes de la forme
                             *ByNumber( ..., TagKey key, ...) 
                         en substitution de 
                             *ByNumber( ..., uint16_t group, uint16_t elem, ...)
                         et au passage les renommer en *ByKey ?
       Si ce choix est fait, cela impose a l'API d'exposer les utilitaires
       (internes de gdcm) pour construire une clef a la vole'e:
         - DictEntry::TranslateToKey(group,element) )
         - et une version a ecrire pour la clef generalise'e

   Question 1c/:
      Il s'agit ici du parcours d'un Header (l'application type etant
      de presenter un Header sous forme d'un GUI, si possible avec
      les sequences "collapsables" a la e-Film: c'est une demande
      interne assez forte de nos chers utilisateurs WinDoziens).
      La structure d'un Header est recursive (pour la gestion des sequences).
      Dans l'etat actuel de choses deux modes de parcours sont possibles:
        - "Hands-on" mode, i.e. on laisse l'utilisateur parcourir
          recursivement l'arbre d'un Header parse' i.e. l'utilisateur
          doit re-ecrire avec un squelette semblable a celui de
          Document::BuildFlatHashTableRecurse()
        - utiliser TagDocEntryHT* Document::BuildFlatHashTable()
          qui construit un std::map<> "a plat" a partir de la structure
          recursive.
      Les inconvenients/avantages des deux methodes:
        - "Hands-on": 
           -- Inconvenients: 1/ cela expose les mecanismes internes (pb
                                d'encapsulation). En effet un utilisateur
                                recuperant un heritier d'un DocEntry, peut
                                facilement casser le dictionnaire en 
                                faisant par exemple un DocEntry::SetVR !
                             2/ cela duplique du code
                             3/ cela fige les mecanismes internes (puisqu'un
                                utilisateur ecrit du code y faisant reference).
                             4/ cela expose des types internes comme 
                                DictEntry, ValEntry... ce qui pose un pb
                                en Python.
          -- Avantage: y'a rien d'autre pour l'instant !
        - BuildFlatHashTable():
           -- Inconvenient: la structure construite evolue separement du
                            Header de reference. Si on modifie le Header
                            le std::map<> construit ne le reflete pas.
          -- Avantages:  1/ l'utilisateur n'a pas la recursion a ecrire
                         2/ aucun type interne supplementaire n'est expose'.
                         3/ aucun travail supplementaire pour les wrappeurs.
       La question, la question, la question ! Bon, d'accord, voila la
       question:
          Ne peut pas ecrire un Visitor sur un Header ? Et si oui, avec
          quelle syntaxe/API ?

######################
Point 2/ distinction des operations de modification/creation d'un tag
      Actuellement nous offrons ReplaceOrCreateByNumber(). Il serait bon,
      IMHO et meme si cela alourdi un peu le code appelant, de
      distinguer classiquement les operations de:
         - modification d'un tag existant,
         - creation d'un nouveau tag,
         - suppression d'un tag.   (ajout)
      Plusieurs raisons a cela:
       * eclaircissement du code de l'appelant,
       * verification locale des contraintes d'inte'grite dans le cas
         de modification (certains tag sont "lie's" e.g. ecraser brutalement
         le "transfert syntax" sans prendre un minimum de precautions est
         sans doute dangeureux).
       * rendre possible l'ajout/suppresion d'une sequence, ou
         l'ajout/suppresion d'un item de sequence simplement en specialisant
         les classes ad-hoc.

######################
Point 3/ definition du mode d'ecriture.
     Mathieu a deja propose' que l'API soit VTK-like. Il faudrait donc
     ecrire:
         gdcm::File junk;
         junk.SetWriteMode(gdcm::File::DicomImplicitVR);
         junk.Write();
     et NON pas:
         gdcm::File junk;
         junk.WriteDcmImplVR();
         // or junk.WriteDcmExplVR()
         // or junk.WriteAcr()

####################

Si apres une telle incontinence verbale, y'a pas de commentaire, c'est a
se flinguer...

	Eric.



More information about the Dcmlib mailing list