10 #include <netinet/in.h>
14 #define LGR_ENTETE_A_LIRE 256 // on ne lit plus que le debut
17 //FIXME: this looks dirty to me...
18 #define str2num(str, typeNum) *((typeNum *)(str))
20 VRHT * gdcmHeader::dicom_vr = (VRHT*)0;
22 gdcmHeader::gdcmHeader () {
23 if (!gdcmHeader::dicom_vr)
25 bool grPixelTrouve = false;
26 PixelPosition = (size_t)0;
27 PixelsTrouves = false;
30 void gdcmHeader::InitVRDict (void) {
31 if (dicom_vr && DEBUG) {
32 printf ("InitVRDict : VR dictionary allready set\n");
36 (*vr)["AE"] = "Application Entity"; // 16 car max
37 (*vr)["AS"] = "Age String"; // 4 car fixe
38 (*vr)["AT"] = "Attribute Tag"; // 2 unsigned short int
39 (*vr)["CS"] = "Code String"; // 16 car max
40 (*vr)["DA"] = "Date"; // 8 car fixe
41 (*vr)["DS"] = "Decimal String"; // Decimal codé Binaire 16 max
42 (*vr)["DT"] = "Date Time"; // 26 car max
43 (*vr)["FL"] = "Floating Point Single"; // 4 octets IEEE 754:1985
44 (*vr)["FD"] = "Floating Point Double"; // 8 octets IEEE 754:1985
45 (*vr)["IS"] = "Integer String"; // en format externe 12 max
46 (*vr)["LO"] = "Long String"; // 64 octets max
47 (*vr)["LT"] = "Long Text"; // 10240 max
48 (*vr)["OB"] = "Other Byte String";
49 (*vr)["OW"] = "Other Word String";
50 (*vr)["PN"] = "Person Name";
51 (*vr)["SH"] = "Short String"; // 16 car max
52 (*vr)["SL"] = "Signed Long";
53 (*vr)["SQ"] = "Sequence of Items"; // Not Applicable
54 (*vr)["SS"] = "Signed Short"; // 2 octets
55 (*vr)["ST"] = "Short Text"; // 1024 car max
56 (*vr)["TM"] = "Time"; // 16 car max
57 (*vr)["UI"] = "Unique Identifier"; // 64 car max
58 (*vr)["UN"] = "Unknown";
59 (*vr)["UT"] = "Unlimited Text"; // 2 puissance 32 -1 car max
60 (*vr)["UL"] = "Unsigned Long "; // 4 octets fixe
61 (*vr)["US"] = "Unsigned Short "; // 2 octets fixe
67 * \brief La seule maniere sure que l'on aie pour determiner
68 * si on est en LITTLE_ENDIAN, BIG-ENDIAN,
69 * BAD-LITTLE-ENDIAN, BAD-BIG-ENDIAN
70 * est de trouver l'element qui donne la longueur d'un 'GROUP'
71 * (on sait que la longueur de cet element vaut 0x00000004)
72 * et de regarder comment cette longueur est codee en memoire
74 * Le probleme vient de ce que parfois, il n'y en a pas ...
76 * On fait alors le pari qu'on a a faire a du LITTLE_ENDIAN propre.
77 * (Ce qui est la norme -pas respectee- depuis ACR-NEMA)
78 * Si ce n'est pas le cas, on ne peut rien faire.
80 * (il faudrait avoir des fonctions auxquelles
81 * on passe le code Swap en parametre, pour faire des essais 'manuels')
83 void gdcmHeader::CheckSwap()
86 guint32 x=4; // x : pour ntohs
87 bool net2host; // true when HostByteOrder is the same as NetworkByteOrder
91 char deb[LGR_ENTETE_A_LIRE];
93 // On teste le processeur
100 // On commence par verifier si c'est du DICOM 'actuel'
102 lgrLue = fread(deb,1,LGR_ENTETE_A_LIRE, fp);
105 if(memcmp(entCur, "DICM", (size_t)4) == 0) {
106 filetype = TrueDicom;
107 if (DEBUG) printf ("_IdDcmCheckSwap : C est du DICOM actuel \n");
110 if (DEBUG) printf ("_IdDcmCheckSwap : Ce n'est PAS du DICOM actuel\n");
113 if(filetype == TrueDicom) {
114 // on saute le File Preamble (souvent a ZERO) : 128 Octets
115 // + le DICM (4), et le (0002, 0000) soit 4 (136 = 128 + 4 + 4)
117 if(memcmp(entCur, "UL", (size_t)2) == 0) {
118 // les 2 premiers octets de la lgr peuvent valoir UL --> Explicit VR
119 filetype = ExplicitVR;
120 if (DEBUG) printf ("_IdDcmCheckSwap : Explicit VR\n");
122 filetype = ImplicitVR;
123 if (DEBUG) printf ("_IdDcmCheckSwap : PAS Explicit VR\n");
126 if (net2host) { // HostByteOrder is different from NetworkByteOrder
127 sw = 0; // on est sur PC ou DEC --> LITTLE-ENDIAN -> Rien a faire
128 if (DEBUG) printf("HostByteOrder = NetworkByteOrder\n");
129 } else { /* on est sur une Sun ou une SGI */
131 if (DEBUG) printf("HostByteOrder != NetworkByteOrder\n");
135 fseek (fp, 132L, SEEK_SET); //On se positionne sur le debut des info
137 } // End of TrueDicom
139 // Pas du TrueDicom : permiere hypothese c'est de l'ACR 'propre', auquel
140 // cas la lgr du premier element du groupe est FORCEMENT 4
142 s=str2num(entCur,int);
146 sw=3412; if(DEBUG) printf("s : %08x sw : %d\n",s,sw);
150 sw=4321; if(DEBUG) printf("s : %08x sw : %d\n",s,sw);
154 sw=2143; if(DEBUG) printf("s : %08x sw : %d\n",s,sw);
158 sw=0; if(DEBUG) printf("s : %08x sw : %d\n",s,sw);
163 if (DEBUG) printf (" Pas trouve l info de Swap; On va parier\n");
166 rewind(fp); // les info commencent au debut
171 // Deuxieme hypothese : c'est de l'ACR 'pas propre' i.e. il manque
174 // On n'a pas trouve l'info de swap.
175 // Si c'est du VRAI ACR NEMA et
176 // * si on est sur une DEC ou un PC alors swap=0,
177 // * si on est sur SUN ou SGI, alors swap=4321
178 // Si c'est du RAW, ca degagera + tard
179 if (DEBUG) printf("On force la chance \n");
181 if (x!=ntohs(x)) // HostByteOrder is different from NetworkByteOrder
182 // on est sur PC ou DEC --> LITTLE-ENDIAN -> Rien a faire
185 // on est sur Sun ou SGI
187 rewind(fp); // les info commencent au debut
193 * \ingroup gdcmHeader
194 * \brief Pour les fichiers non TrueDicom, si le recognition
195 * code (0008,0010) s'avere etre "ACR_LIBIDO", alors
196 * valide la reconnaissance du fichier en positionnant
199 void gdcmHeader::setAcrLibido() {
202 if ( filetype != TrueDicom) {
203 printf("_setAcrLibido expects a presumably ACR file\n");
204 // Recognition Code --> n'existe plus en DICOM V3 ...
205 RecCode = GetPubElValByNumber(0x0008, 0x0010);
207 if (RecCode == "ACRNEMA_LIBIDO" ||
208 RecCode == "CANRME_AILIBOD" )
209 filetype = ACR_LIBIDO;
217 * \ingroup gdcmHeader
218 * \brief recupere la longueur d'un champ DICOM.
220 * 1/ le fichier doit deja avoir ete ouvert,
221 * 2/ CheckSwap() doit avoir ete appele
222 * 3/ la partie 'group' ainsi que la partie 'elem'
223 * de l'acr_element doivent avoir ete lues.
225 * ACR-NEMA : we allways get
226 * GroupNumber (2 Octets)
227 * ElementNumber (2 Octets)
228 * ElementSize (4 Octets)
229 * DICOM en implicit Value Representation :
230 * GroupNumber (2 Octets)
231 * ElementNumber (2 Octets)
232 * ElementSize (4 Octets)
234 * DICOM en explicit Value Representation :
235 * GroupNumber (2 Octets)
236 * ElementNumber (2 Octets)
237 * ValueRepresentation (2 Octets)
238 * ElementSize (2 Octets)
240 * ATTENTION : dans le cas ou ValueRepresentation = OB, OW, SQ, UN
241 * GroupNumber (2 Octets)
242 * ElementNumber (2 Octets)
243 * ValueRepresentation (2 Octets)
244 * zone reservee (2 Octets)
245 * ElementSize (4 Octets)
247 * @param sw code swap
248 * @param skippedLength pointeur sur nombre d'octets que l'on a saute qd
249 * la lecture est finie
250 * @param longueurLue pointeur sur longueur (en nombre d'octets)
252 * @return longueur retenue pour le champ
255 // FIXME sw n'est plus un argument necessaire
256 long int gdcmHeader::RecupLgr(
261 unsigned short int l_gr_2;
267 // ATTENTION : nbCode correspond au nombre d'elements dans la table
268 // de type DICOM_VR. A nettoyer.
272 if (filetype == ExplicitVR) {
273 lgrLue=fread (&VR, (size_t)2,(size_t)1, fp);
276 // Warning: we believe this is explicit VR (Value Representation) because
277 // we used a heuristic that found "UL" in the first tag. Alas this
278 // doesn't guarantee that all the tags will be in explicit VR. In some
279 // cases (see e-film filtered files) one finds implicit VR tags mixed
280 // within an explicit VR file. Hence we make sure the present tag
281 // is in explicit VR and try to fix things if it happens not to be
284 // FIXME There should be only one occurence returned. Avoid the
285 // first extraction by calling proper method.
286 VRAtr FoundVR = dicom_vr->find(string(VR))->first;
287 if (FoundVR.empty()) {
288 pleCourant->SetVR(FoundVR);
291 // On est mal : implicit VR repere
292 // mais ce n'est pas un code connu ...
293 // On reconstitue la longueur
296 printf("IdDcmRecupLgr : Explicit VR, mais pas de code connu\n");
297 memcpy(&l_gr, VR,(size_t)2);
299 lgrLue=fread ( ((char*)&l_gr)+2, (size_t)2, (size_t)1, fp);
301 l_gr = SWAP_LONG((guint32)l_gr);
304 printf("IdDcmRecupLgr : lgr deduite : %08x , %d\n",l_gr,l_gr);
306 pleCourant->SetLgrLue(l_gr);
307 if ( (int)l_gr == -1)
312 printf(" 1 : lgr %08x (%d )skippedLength %d\n",
313 l_gr,l_gr, *skippedLength);
317 // On repart dans la sequence 'sensee'
320 printf("VR : [%01x , %01x] (%c%c) en position %d du tableau\n",
321 VR[0],VR[1],VR[0],VR[1],i);
323 if ( (!memcmp( VR,"OB",(size_t)2 )) ||
324 (!memcmp( VR,"OW",(size_t)2 )) ||
325 (!memcmp( VR,"SQ",(size_t)2 )) ||
326 (!memcmp( VR,"UN",(size_t)2 )) ) {
328 // les 2 octets suivants sont reserves: on les saute
330 printf("IdDcmRecupLgr : les 2 octets suivants sont reserves\n");
331 fseek(fp, 2L,SEEK_CUR);
333 //on lit la lgr sur QUATRE octets
334 lgrLue=fread (&l_gr, (size_t)4,(size_t)1, fp);
335 l_gr = SWAP_LONG((guint32)l_gr);
339 //on lit la lgr sur DEUX octets
340 lgrLue=fread (&l_gr_2, (size_t)2,(size_t)1, fp);
342 if(sw) l_gr_2 = SWAP_SHORT((unsigned short)l_gr_2);
344 pleCourant->SetLgrLue(l_gr_2);
347 if ( l_gr_2 == 0xffff) {
356 //on lit la lgr sur QUATRE octets
358 lgrLue=fread (&l_gr, (size_t)4,(size_t)1, fp);
360 l_gr= SWAP_LONG((long)l_gr);
364 pleCourant->SetLgrLue(l_gr);
366 // Traitement des curiosites sur la longueur
368 if ( (int)l_gr == 0xffffffff)
371 if(!memcmp( VR,"SQ",(size_t)2 )) { // ca annonce une SEQUENCE d'items ?!
372 l_gr=0; // on lira donc les items de la sequence
373 if (DEBUG) printf(" SQ trouve : lgr %d \n",l_gr);
377 printf(" 2 : lgr %08x (%d) skippedLength %d\n",l_gr,l_gr, *skippedLength);
382 * \ingroup gdcmHeader
383 * \brief remet les octets dans un ordre compatible avec celui du processeur
385 * @return longueur retenue pour le champ
387 guint32 gdcmHeader::SWAP_LONG(guint32 a) {
388 // FIXME: il pourrait y avoir un pb pour les entiers negatifs ...
391 a=( ((a<<24) & 0xff000000) | ((a<<8) & 0x00ff0000) |
392 ((a>>8) & 0x0000ff00) | ((a>>24) & 0x000000ff) );
396 a=( ((a<<16) & 0xffff0000) | ((a>>16) & 0x0000ffff) );
400 a=( ((a<<8) & 0xff00ff00) | ((a>>8) & 0x00ff00ff) );
403 printf("\n\n\n *******\n erreur code swap ?!?\n\n\n");
410 * \ingroup gdcmHeader
411 * \brief remet les octets dans un ordre compatible avec celui du processeur
413 * @return longueur retenue pour le champ
415 short int gdcmHeader::SWAP_SHORT(short int a) {
416 if ( (sw==4321) || (sw==2143) )
417 a =(((a<<8) & 0x0ff00) | ((a>>8)&0x00ff));
422 * \ingroup gdcmHeader
423 * \brief lit le dicom_element suivant.
424 * (le fichier doit deja avoir ete ouvert,
425 * _IdAcrCheckSwap(ID_DCM_HDR *e) avoir ete appele)
426 * @param e ID_DCM_HDR dans lequel effectuer la recherche.
427 * @param sw code swap.
428 * @return En cas de succes, 1
432 ElValue * gdcmHeader::ReadNextElement(void) {
439 //CLEANME DICOM_ELEMENTS *t;
440 ElValue * nouvDcmElem;
442 if (DEBUG) printf(" ===> entree ds _IdDcmReadNextElement\n");
444 // FIXME la probabilte pour depasser sans s'en rendre compte
445 // est grande avec le test d'egalite' suivant !
446 if(offsetCourant == taille_fich) { // On a atteint la fin du fichier
447 if (DEBUG) printf(" On a atteint la fin du fichier\n");
452 printf("lgrFich %f positionDsFich %f offset courant %f\n",
455 (float)offsetCourant);
459 // ------------------------- Lecture Num group : g
460 lgrLue=fread (&g, (size_t)2,(size_t)1, fp);
463 if (DEBUG) printf("_IdDcmReadNextElement : eof trouve\n");
467 if (DEBUG) printf(" IdDcmReadNextElement : echec lecture NumGr\n");
471 if (DEBUG) printf("_IdDcmReadNextElement : gr %04x\n",g );
473 if (sw) g= SWAP_SHORT(((short)g));
475 //CLEANME nouvDcmElem->Gr=g;
476 //FIXME this might be usefull for detecting at parse time that
477 //something is screwy in the file
478 //e->__NumeroGroupePrecedent =g;
481 // ------------------------- Lecture Num Elem : n
482 lgrLue=fread (&n, (size_t)2,(size_t)1, fp);
485 if (DEBUG) printf("_IdDcmReadNextElement : eof trouve\n");
489 if (DEBUG) printf(" IdDcmReadNextElement : echec lecture NumElem\n");
493 if (DEBUG) printf("_IdDcmReadNextElement : num %04x\n",n );
495 if(sw) n= SWAP_SHORT(((short)n));
496 //CLEANMEnouvDcmElem->Num=n;
498 // Find out if the tag we encountered is in the dictionaries:
499 DictEntry * NewTag = IsInDicts(g, n);
501 NewTag = new DictEntry(g, n, "Unknown", "Unknown");
503 nouvDcmElem = new ElValue(NewTag);
505 printf("Echec alloc ElValue *nouvDcmElem\n");
509 // ------------------------- Lecture longueur element : l
511 l = RecupLgr(nouvDcmElem, &skL);
513 if(g==0xfffe) l=0; // pour sauter les indicateurs de 'SQ'
515 nouvDcmElem->LgrElem=l;
519 printf("_IdDcmReadNextElement : "
520 " gr %04x\tnum %04x\tlong %08x (%d)\n", g,n,l,l);
522 // ------------------------- Lecture Valeur element
524 // FIXME The exact size should be l if we move to strings or whatever
525 // CLEAN ME NEWValue used to be nouvDcmElem->valeurElem
526 char* NewValue = (char*)g_malloc(l+1);
531 printf(" IdDcmReadNextElement : echec Alloc valeurElem lgr : %d\n",l);
535 // FIXME les elements trop long (seuil a fixer a la main) ne devraient
536 // pas etre charge's !!!! Voir TODO.
537 lgrLue=fread (NewValue, (size_t)l,(size_t)1, fp);
539 offsetCourant += 2 + 2 + skL; // gr + num + lgr
540 nouvDcmElem->Offset = offsetCourant;
541 offsetCourant += l; // debut elem suivant
543 // ------------------------- Doit-on le Swapper ?
545 if ((n==0) && sw) { // n=0 : lgr du groupe : guint32
546 *(guint32 *) NewValue = SWAP_LONG ((*(guint32 *) NewValue));
549 if ( (g/2)*2-g==0) { /* on ne teste pas les groupes impairs */
551 if ((l==4)||(l==2)) { // pour eviter de swapper les chaines
554 printf("Consultation Dictionary DICOM g %04x n %0xx l %d\n",
557 // FIXME make reference to nouvDcmElem->GetTag
558 string VR = NewTag->GetVR();
559 if ( (VR == "UL") || (VR == "US")
560 || (VR == "SL") || (VR == "SS")
561 || (g == 0x0028 && ( n == 0x0005 || n == 0x0200) )) {
562 // seuls (28,5) de vr RET et (28,200) sont des entiers
563 // ... jusqu'a preuve du contraire
566 *(guint32 *) NewValue =
567 SWAP_LONG ((*(guint32 *) NewValue));
570 *(unsigned short *) NewValue =
571 SWAP_SHORT ((*(unsigned short *)NewValue));
574 } /* fin if l==2 ==4 */
575 } /* fin if g pair */
578 nouvDcmElem->value = NewValue;
579 SetAsidePixelData(nouvDcmElem);
584 * \ingroup gdcmHeader
585 * \brief If we encountered the offset of the pixels in the file
586 * (Pixel Data) then keep the info aside.
588 void gdcmHeader::SetAsidePixelData(ElValue* elem) {
589 /// FIXME this wall process is bizarre:
590 // on peut pas lire pixel data et pixel location puis
591 // a la fin de la lecture aller interpreter ce qui existe ?
592 // penser a nettoyer les variables globales associes genre
593 // PixelsTrouve ou grPixelTrouve...
595 // They are two cases :
596 // * the pixel data (i.e. the image or the volume) is pointed by it's
597 // default official tag (0x7fe0,0x0010),
598 // * the writer of this file decided to put the image "address" (i.e the
599 // offset from the begining of the file) at a different tag.
600 // Then the "Pixel Data" offset might be found by indirection through
601 // the "Image Location" tag (0x0028, 0x0200). In other terms the Image
602 // Location tag contains the group where the "Pixel Data" offset is and
603 // inside this group the element is conventionally at element 0x0010
604 // (when the norm is respected).
606 // Hence getting our hands on the Pixel Data is a two stage process:
607 // 1/ * find if the "Pixel Data" tag exists.
608 // * if it does not exist, look for the "Pixel Location" tag.
609 // 2/ look at the proper tag ("Pixel Data" or "Pixel Location" when
610 // it exists) what the offset it.
613 g = elem->GetGroup();
614 n = elem->GetElement();
615 if (!grPixelTrouve) { // on n a pas encore trouve les pixels
617 if (n > 0x0200 || g == 0x7FE0 ) { // on a depasse (28,200)
620 grPixelTrouve = true;
622 printf("------------------------grPixel %04x numPixel %04x\n",
625 } else { // on est sur (28,200)
629 char* NewValue = (char*)g_malloc(elem->GetLgrElem()+1);
630 // FIXME: not very elegant conversion
632 *((char*)(&grPixel)+i) = *(NewValue+i);
633 elem->SetValue(NewValue);
636 printf("------------------------GrPixel %04x\n", grPixel);
638 if (grPixel != 0x7FE0) // Vieux pb Philips
639 numPixel = 0x1010; // encore utile ??
643 printf("------------------------grPixel %04x numPixel %04x\n",
648 } else { // on vient de trouver les pixels
651 PixelPosition = elem->Offset;
652 PixelsTrouves = true;
654 printf(" \t===> Pixels Trouves\n");
660 DictEntry * gdcmHeader::IsInDicts(guint32 group, guint32 element) {
661 DictEntry * found = (DictEntry*)0;
662 if (!RefPubDict && !RefShaDict) {
663 //FIXME build a default dictionary !
664 printf("FIXME in gdcmHeader::IsInDicts\n");
667 found = RefPubDict->GetTag(group, element);
672 found = RefShaDict->GetTag(group, element);