[Dcmlib] gdcm proposal

Mathieu Malaterre mathieu.malaterre at kitware.com
Wed Apr 27 17:45:47 CEST 2005


> Dans le gdcm::FileHelper, les choses sont faites proprement, ce qui veut
> dire que le Document d'origine n'est pas modifié. Il semble par contre
> que le gdcm::File fasse des 'saloperies' que je n'avais pas vues...
> Ca serait en effet a corriger pour la version courante de gdcm.
> Par contre je n'ai pas compris : tu veux que ton gdcm.Writer modifie
> définitivement le gdcm.Document passé en entrée ? ou que la modification
> soit juste temporaire (le temps de l'écriture du fichier) ?

Mon probleme est plus vaste, je voudrais un mechnisme qui interdise de 
modifier l'input d'un filtre. Je concois parfaitement maintenant que 
j'ai souligner le probleme on aille le deplacer ailleurs ou le cacher....

Sinon pour l'ecriture effectivement le 12 qui devient un 16 ca devrait 
etre temporaire. Par exemple un gdcm.Validator ne pourrait pas reporter 
que BitsStorage=12 est une faute, pourtant dans le cas present -et pour 
lontemps- nous sommes incapable de gerer ca. Donc au moment de 
l'ecriture gdcm.Writer fais la suite d'actions:

(version gourmande):
- recopie du gdcm.Document dans un gdcm.Document interne
- ensuite gdcm.Writer regarde gdcm.Image et doit determiner les valeurs 
a changer: BitStord 12 devient 16, dans le Cas clunie, les spacing=0 
deviennent 1 ...
- ensuite on ecrase les anciennes UID (user request ?) par les valeurs 
generer
-> derniere etape on ecris le fichier.


> Ca me plait beaucoup ca. Ce n'etait pas possible il y a plus de 6 mois dû
> à la complexité interne de gdcm. J'y avais déjà pensé, mais c'était 
> infaisable.
> La dessus, je suis 100% d'accord.

cool

> --------------------------------------------------------------------------
> Maintenant voici mes remarques :
> - le File (dérivé du Document) fait déjà une bonne partie de ce que tu
> veux faire. Je pense qu'en effet, il est bien de garder cette structure qui
> convient parfaitement.

c'est le terme parfaitement qui me fais sursaute. Aller je me lance dans 
une autre de mes idee ;-P:
Un truc que j'adore en c++ c'est le polymorphisme, tu peux par exemple 
manipler une classe via des fonction virtuelles sans te rendre compte 
que tu manipule un instance d'une sous classes...ainsi j'aimerais que 
gdcm.File soit en fait:

                       gdcm.Writer
              |           |             |             |
      gdcm.DICOMV3  gdcm.Libido   (gdcm.ACR-NEMA) (gdcm.Papyrus)

je veux pas d'une classes fourre tout ou tu fais du code pour ACRNEMA du 
DICOMV3 et du papyrus justeaucasou ... c'est horrible !


> - le DicomDir (dérivé de Document), tu n'en parles pas. Etant donné
> qu'il n'a pas besoin de la complexité interne du Document, garde-t-on
> cette dérivation ? (elle est à l'heure actuelle utile car le Document 
> contient
> toute la partie Reader/Writer, mais je pense qu'on peut s'en abstraire
> pour la structure que tu proposes).


C'est la ou le bas blesse, je n'ai pas du tout de connaissance sur le 
sujet. Est-ce que ca peut rentrer dans mon archi:

                         gdcm.Writer
                             |
                        gdcm.DICOMDIR


> - Dans la création d'un Element du Document... comment on
> différencie les ValEntry, BinEntry et SeqEntry ? A l'heure actuelle,
> on fait cette différenciation à la création de l'élément en fonction de 
> son VR.
> De plus, un Element (ou DocEntry à l'heure actuelle) est rataché à un 
> DictEntry.


Faux -IMHO- c'est le gdcm.Document qui a connaissance du dict. Mais un 
element est rattache a un Document.
D'ailleurs au passage je ne comprends que deux type d'el un BinEntry et 
un ValEntry. Pour moi un SeqEntry c'est un Document ... je sais pas bien 
encore comment representer ca.
Au passage, pourquoi la distinction BinEntry / ValEntry ? Pour moi tu 
lis un petit bout de fichier tu stocke ca dans un ostringstream et 
basta. Un ValEntry c'est juste que tu 'tautorize a interpreter le flux 
binarie en chaine de caractere... d'ailleurs :


QUESTION: comment on sait qu'un element est une ValEntry dans le cas 
d'un shadow dict. Est-ce qu'on ne pourrait pas tout lire en BinEntry et 
unquiquement lorsqu'on sait ce qu'on fais on dynamic_cast en ValEntry -> 
interprete la chaine de caractere et la on peut effectivement essayer de 
lire par exemple le spacing.

Tout ca pour dire je voudrais pouvoir lire un DICOM plus vite, il y a 
beaucoup de temps passer a je lis un petit bout de DICOM, a tiens c'est 
un ValEntry, je fais un new ValEntry, je copie les valeurs, je passe au 
suivant tiens c'est un BinEntry je fais new ValEntry ... c'est vraiment 
long et je me rend pas bien compte de ce que ca apporte.


> Tu fais ce rattachement à quel moment ? De plus lorsque le dictionnaire 
> publique
> ne contient pas le tuple (group, element, VR) il en crée un nouveau 
> sauvegardé
> dans une zone tampon... garde-t-on cela qui n'est pas tres propre ? ou 
> place-t-on
> le VR de l'Element dans l'Element ? 

Pourquoi exactement on a besoin d'en creer un nouveau ? Un element est 
rattacher a un gdcm.Document et le gdcm.Document peut tout moment 
changer de dictionaire (Siemens, Philips, ...). Donc la VR c'est jamais 
stocker dans l'element, non ?

Dans mon idee c'est le gdcm.Document qui a l'ensemble des dictionaires 
autorises. Donc a partir d'un element+group il doit etre capable de 
renvoyer toutes les informations necessaires correct ? Et comme c'est 
une std::map l'access est en O(1). Le seul probleme dans mon approche 
c'est qu'un group+element n'est pas unique, par ex qu'il soit present 
dans le dict public et le dict prive. Mais  meme dans ce cas je vois pas 
comment vous pourriez le gerer.


>  Ce qui nous donnerait :
> 
> newel = gdcm.Element("OB")    // La VR est affecté au moment de la 
> création de l'objet

pourquoi on a besoin de la VR ?

> newel.Set(0x1234,0x5678)       // On va rechercher le DictEntry 
> correspondant... mais dans quel Dict ?

Ceux attacher au gdcm.Document...

> newel.SetValue("foobar")          // Et pour une valeur binaire ou une 
> séquence, que fait-on ?


J'aime pas la notion de sequence ca reste pour moi qu'un gdcm.Document. 
Mais sinon "foobar" est une valeur valide pour une entree binaire, non ? 
Ou est la difference entre BinEntry et ValEntry que je ne vois toujours 
pas ?

> Mais ceci ne résoudrait pas le problème de création de l'élément en 
> fonction de sa VR.
> Car avec ce choix, l'utilisateur peut créer un Element de VR "SQ" et 
> mettre des données
> textuelles dedans. Une solution serait de mettre le constructeur en 
> protected
> et d'avoir un New avec une création de l'objet en fonction de la VR...



> - Concernant l'écriture des series, tu considères avoir en entrée une 
> liste de Document
> ou un seul Document contenant une image 3D ? Pour moi, la liste de Document
> et nettement plus adaptée car d'un Document à l'autre, de nombreux 
> champs de
> l'entete peuvent varier.


Le probleme est parfaitement souligner: je ne sais pas ce que j'ai en 
entree ! Si je recois une image 4D, je eux pouvoir l'ecrire en serie 
d'image 3D temporelle OU une serie 2D variant en Z direction et en temps 
! et je vois pas ou est le probleme d'archi la dedans.
Il faut garder en tete que c'est une archie que je propose, si le 
programmeur ne sait pas faire il met un 'abort()' dans le code et il 
attends qu'un utilisateur lui envoi le patch pour pouvoir le faire. Je 
ne veux pas avoir de limitation d'entree de jeux.

> - Une question qui ne peut se déduire du code python : la création des 
> objets
> se ferait par quelle méthode ? une méthode New comme en VTK, ou un new
> habituel comme on fait actuellement ?

Reponse: new. le ::New VTK c'est pour des compilos que gdcm ne 
supporte(ra) pas, faut pas deconner qd meme !

> - Concernant les Series, tu fais un SerieReader... qui remplacerait 
> surement
> le SerieHelper actuel... je ne suis pas sur qu'il s'agisse d'une bonne 
> solution
> (d'après ce que j'ai compris de l'intérêt du SerieHelper). Le 
> SerieHelper a pour
> but d'ordonner les différents fichiers d'une meme série. Or 
> l'utilisateur pourrait
> très bien vouloir lire ses différents fichiers d'une part, puis les 
> trier... or s'il passe
> dans un SerieReader, il va de nouveau relire ces fichiers. Il serait 
> mieux d'avoir
> un SerieOrder qui prend en entrée une liste de Document (donc les 
> fichiers sont
> déjà lus) et ce process ne ferai qu'ordonner cette liste (suivant les 
> critères actuels
> du SerieHelper).

Parfaitement raisonne ! Et le SerieOrder est utiliser soit lors de la 
lecture soit lors de l'ecriture (reutilisation de code plutot que 
duplication).

> - Pour la classe Image, je n'ai pas très bien compris comment elle 
> fonctionne.
> A-t-elle une sortie ? et quel type de sortie ? ... simplement un tableau 
> de données comme
> le fait le FileHelper actuel lorsque tu appelle la méthode GetImage ?
> Et lorsque l'utilisateur demande un sous-volume englobant le volume 
> précédement lu,
> réutilise-t-on le volume précédemment lu ?
> 
> image = gdcm.Image()
> image.SetInput( reader.GetOutput())
> image.SetSubVolume(512,512,1,3)  # SetVOI / SetExtent
> image.Construct()
> image.SetSubVolume(512,512,0,10)  # SetVOI / SetExtent englobant le VOI 
> precedent.
> image.Construct()

Ok je reconnais le nom gdcm.Image n'est pas correct. Que pense tu de 
gdcm.Decompress. Dans ce cas gdcm.Decompress repart de zero a chaque 
fois: relecture du fichier / seek / contruire une nouvelle image.
C'est sans doute dommage niveau efficacite mais pour moi je vise le cas 
du thesard qui fais sa registration et il sait quel sous volume 
utiliser. Dans son rep il a une image de 3 Gig il sait qu'il doit 
recaller la partie 0-100 x 0-100 x 0-100 s'il se trompe ben il part du 
principe que ca va prendre un peu de temps a relire la bonne partie du 
fichier.

> - Pour l'ecriture des images créées from scratch. Comment cela se 
> passerait-il ?
> Tu as dit que l'Image replacerait les champs du Document, ce qui veut 
> dire qu'il
> faut passer à la fois une Image et un Document au Writer ? Sur ce point, 
> je crois qu'il
> manque un exemple d'utilisation. J'ai aussi beaucoup de mal à voir ce 
> que tu appelles
> Image...

Est-qu'on peut faire un gdcm.Document contient un gdcm.ElementSet (le 
header) et contiendrait aussi un gdcm.Image. Donc dans le cas d'un 
fichier DICOM raw si on force la lecture meme des element > 4096 en 
taille et que l'utilisateur demande une gdcm.Image de la totalite alors 
oui il aura deux fois l'image en memoire. Et non on ne pourra pas etre 
malin en passant le meme pointeur sur l'image.

> Voila, les remarques sont finies. Certaines rentrent déjà dans 
> l'implémentation, j'espere
> donc que je ne les pose pas trop tot. Je crois sinon que j'ai fait le 
> tour du sujet.
> Sinon, moi aussi je ne vais pas avoir beaucoup de temps à investir dans 
> le développement
> de cette nouvelle mouture de gdcm... mais ce sera un problème à voir 
> lorsqu'on aura
> défini la structure finale de gdcm !
> Je pense que cette fois ci, il ne faut pas commencer à développer avant 
> d'avoir la structure
> finale de la librairie et d'avoir vérifié que les exemples créés 
> conviennent à tout ce
> dont on a besoin.

Si on fait le tour des cas d'utilisation on ne peut rien manquer par 
definition. C"est pour ca qu'il faut passer pas mal de temps a reflechir 
a tout les cas d'utilisation. De mon experience j'ai eu pas mal de 
feedback depuis ITK, vous de votre cote vous devrier avoir du feedback 
de CREATIS directement.


> Désolé pour la longueur du mail. J'ai essayé d'etre le plus constructif 
> possible, et
> ca prend de la place. J'espere que vous serez arrivé jusqu'ici

J'aime les longues histoires avec plein de rebondissements :)

> Benoit
> 
> PS : Mathieu, tes exemples sont une très bonne idée. Je pense qu'il faut 
> continuer à les
> faire et les compléter au fur et à mesure... afin d'avoir l'utilisation 
> complète et finale
> de la librairie.

Merci infiniment et merci a toi d'avoir pris le temps de me repondre, 
c'est extremement apprecie (si si !).



More information about the Dcmlib mailing list