[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