]> Creatis software - gdcm.git/blob - src/gdcmHeader.cxx
* Straightforward temporary fixes for swig to build the python wrappers.
[gdcm.git] / src / gdcmHeader.cxx
1 #include "gdcm.h"
2 extern "C" {
3 #include "glib.h"
4 }
5 #include <stdio.h>
6 // For nthos:
7 #ifdef _MSC_VER
8 #include <winsock.h>
9 #else
10 #include <netinet/in.h>
11 #endif
12 #include <map>
13 #include <sstream>
14 #include "gdcmUtil.h"
15
16 #define HEADER_LENGHT_TO_READ 256 // on ne lit plus que le debut
17
18 namespace Error {
19         struct FileReadError {
20                 FileReadError(FILE* fp, const char* Mesg) {
21                         if (feof(fp))
22                                 dbg.Verbose(1, "EOF encountered :", Mesg);
23                         if (ferror(fp))
24                                 dbg.Verbose(1, "Error on reading :", Mesg);
25                 }
26         };
27 }
28
29 //FIXME: this looks dirty to me...
30 #define str2num(str, typeNum) *((typeNum *)(str))
31
32 VRHT * gdcmHeader::dicom_vr = (VRHT*)0;
33 gdcmDictSet* gdcmHeader::Dicts = new gdcmDictSet();
34
35 void gdcmHeader::Initialise(void) {
36         if (!gdcmHeader::dicom_vr)
37                 InitVRDict();
38         RefPubDict = gdcmHeader::Dicts->GetDefaultPublicDict();
39         RefShaDict = (gdcmDict*)0;
40 }
41
42 gdcmHeader::gdcmHeader (const char* InFilename) {
43         filename = InFilename;
44         Initialise();
45         fp=fopen(InFilename,"rw");
46         dbg.Error(!fp, "gdcmHeader::gdcmHeader cannot open file", InFilename);
47         ParseHeader();
48 }
49
50 gdcmHeader::~gdcmHeader (void) {
51         fclose(fp);
52         return;
53 }
54
55 void gdcmHeader::InitVRDict (void) {
56         if (dicom_vr) {
57                 dbg.Verbose(0, "gdcmHeader::InitVRDict:", "VR dictionary allready set");
58                 return;
59         }
60         VRHT *vr = new VRHT;
61         (*vr)["AE"] = "Application Entity";       // 16 car max
62         (*vr)["AS"] = "Age String";               // 4 car fixe
63         (*vr)["AT"] = "Attribute Tag";            // 2 unsigned short int
64         (*vr)["CS"] = "Code String";              // 16 car max
65         (*vr)["DA"] = "Date";                     // 8 car fixe
66         (*vr)["DS"] = "Decimal String";           // Decimal codé Binaire 16 max
67         (*vr)["DT"] = "Date Time";                // 26 car max
68         (*vr)["FL"] = "Floating Point Single";    // 4 octets IEEE 754:1985
69         (*vr)["FD"] = "Floating Point Double";    // 8 octets IEEE 754:1985
70         (*vr)["IS"] = "Integer String";           // en format externe 12 max
71         (*vr)["LO"] = "Long String";              // 64 octets max
72         (*vr)["LT"] = "Long Text";                // 10240 max
73         (*vr)["OB"] = "Other Byte String";
74         (*vr)["OW"] = "Other Word String";
75         (*vr)["PN"] = "Person Name";
76         (*vr)["SH"] = "Short String";             // 16 car max
77         (*vr)["SL"] = "Signed Long";
78         (*vr)["SQ"] = "Sequence of Items";        // Not Applicable
79         (*vr)["SS"] = "Signed Short";             // 2 octets
80         (*vr)["ST"] = "Short Text";               // 1024 car max
81         (*vr)["TM"] = "Time";                     // 16 car max
82         (*vr)["UI"] = "Unique Identifier";        // 64 car max
83         (*vr)["UN"] = "Unknown";
84         (*vr)["UT"] = "Unlimited Text";           //  2 puissance 32 -1 car max
85         (*vr)["UL"] = "Unsigned Long ";           // 4 octets fixe
86         (*vr)["US"] = "Unsigned Short ";          // 2 octets fixe
87    dicom_vr = vr;       
88 }
89
90 /**
91  * \ingroup gdcmHeader
92  * \brief   La seule maniere sure que l'on aie pour determiner 
93  *          si on est en   LITTLE_ENDIAN,       BIG-ENDIAN, 
94  *          BAD-LITTLE-ENDIAN, BAD-BIG-ENDIAN
95  *          est de trouver l'element qui donne la longueur d'un 'GROUP'
96  *          (on sait que la longueur de cet element vaut 0x00000004)
97  *          et de regarder comment cette longueur est codee en memoire  
98  *          
99  *          Le probleme vient de ce que parfois, il n'y en a pas ...
100  *          
101  *          On fait alors le pari qu'on a a faire a du LITTLE_ENDIAN propre.
102  *          (Ce qui est la norme -pas respectee- depuis ACR-NEMA)
103  *          Si ce n'est pas le cas, on ne peut rien faire.
104  *
105  *          (il faudrait avoir des fonctions auxquelles 
106  *          on passe le code Swap en parametre, pour faire des essais 'manuels')
107  */
108 void gdcmHeader::CheckSwap()
109 {
110         guint32  s;
111         guint32  x=4;  // x : pour ntohs
112         bool net2host; // true when HostByteOrder is the same as NetworkByteOrder
113          
114         int lgrLue;
115         char * entCur;
116         char deb[HEADER_LENGHT_TO_READ];
117          
118         // First, compare HostByteOrder and NetworkByteOrder in order to
119         // determine if we shall need to swap bytes (i.e. the Endian type).
120         if (x==ntohs(x))
121                 net2host = true;
122         else
123                 net2host = false;
124         
125         // The easiest case is the one of a DICOM header, since it possesses a
126         // file preamble where it suffice to look for the sting "DICM".
127         lgrLue = fread(deb, 1, HEADER_LENGHT_TO_READ, fp);
128         
129         entCur = deb + 128;
130         if(memcmp(entCur, "DICM", (size_t)4) == 0) {
131                 filetype = TrueDicom;
132                 dbg.Verbose(0, "gdcmHeader::CheckSwap:", "looks like DICOM Version3");
133         } else {
134                 filetype = Unknown;
135                 dbg.Verbose(0, "gdcmHeader::CheckSwap:", "not a DICOM Version3 file");
136         }
137
138         if(filetype == TrueDicom) {
139                 // Next, determine the value representation (VR). Let's skip to the
140                 // first element (0002, 0000) and check there if we find "UL", in
141                 // which case we (almost) know it is explicit VR.
142                 // WARNING: if it happens to be implicit VR then what we will read
143                 // is the length of the group. If this ascii representation of this
144                 // length happens to be "UL" then we shall believe it is explicit VR.
145                 // FIXME: in order to fix the above warning, we could read the next
146                 // element value (or a couple of elements values) in order to make
147                 // sure we are not commiting a big mistake.
148                 // We need to skip :
149                 // * the 128 bytes of File Preamble (often padded with zeroes),
150                 // * the 4 bytes of "DICM" string,
151                 // * the 4 bytes of the first tag (0002, 0000),
152                 // i.e. a total of  136 bytes.
153                 entCur = deb + 136;
154                 if(memcmp(entCur, "UL", (size_t)2) == 0) {
155                         filetype = ExplicitVR;
156                         dbg.Verbose(0, "gdcmHeader::CheckSwap:",
157                                     "explicit Value Representation");
158                 } else {
159                         filetype = ImplicitVR;
160                         dbg.Verbose(0, "gdcmHeader::CheckSwap:",
161                                     "not an explicit Value Representation");
162                 }
163
164                 if (net2host) {
165                         sw = 4321;
166                         dbg.Verbose(0, "gdcmHeader::CheckSwap:",
167                                        "HostByteOrder != NetworkByteOrder");
168                 } else {
169                         sw = 0;
170                         dbg.Verbose(0, "gdcmHeader::CheckSwap:",
171                                        "HostByteOrder = NetworkByteOrder");
172                 }
173                 
174                 // Position the file position indicator at first tag (i.e.
175                 // after the file preamble and the "DICM" string).
176                 rewind(fp);
177                 fseek (fp, 132L, SEEK_SET);
178                 return;
179         } // End of TrueDicom
180
181         // Alas, this is not a DicomV3 file and whatever happens there is no file
182         // preamble. We can reset the file position indicator to where the data
183         // is (i.e. the beginning of the file).
184         rewind(fp);
185
186         // Our next best chance would be to be considering a 'clean' ACR/NEMA file.
187         // By clean we mean that the length of the first tag is written down.
188         // If this is the case and since the length of the first group HAS to be
189         // four (bytes), then determining the proper swap code is straightforward.
190
191         entCur = deb + 4;
192         s = str2num(entCur, int);
193         
194         switch (s) {
195         case 0x00040000 :
196                 sw=3412;
197                 filetype = ACR;
198                 return;
199         case 0x04000000 :
200                 sw=4321;
201                 filetype = ACR;
202                 return;
203         case 0x00000400 :
204                 sw=2143;
205                 filetype = ACR;
206                 return;
207         case 0x00000004 :
208                 sw=0;
209                 filetype = ACR;
210                 return;
211         default :
212                 dbg.Verbose(0, "gdcmHeader::CheckSwap:",
213                                "ACE/NEMA unfound swap info (time to raise bets)");
214         }
215
216         // We are out of luck. It is not a DicomV3 nor a 'clean' ACR/NEMA file.
217         // It is time for despaired wild guesses. So, let's assume this file
218         // happens to be 'dirty' ACR/NEMA, i.e. the length of the group it
219         // not present. Then the only info we have is the net2host one.
220         //FIXME  Si c'est du RAW, ca degagera + tard
221         
222         if (! net2host )
223                 sw = 0;
224         else
225                 sw = 4321;
226         return;
227 }
228
229 /**
230  * \ingroup   gdcmHeader
231  * \brief     recupere la longueur d'un champ DICOM.
232  *            Preconditions:
233  *            1/ le fichier doit deja avoir ete ouvert,
234  *            2/ CheckSwap() doit avoir ete appele
235  *            3/ la  partie 'group'  ainsi que la  partie 'elem' 
236  *               de l'acr_element doivent avoir ete lues.
237  *
238  *            ACR-NEMA : we allways get
239  *                 GroupNumber   (2 Octets) 
240  *                 ElementNumber (2 Octets) 
241  *                 ElementSize   (4 Octets)
242  *            DICOM en implicit Value Representation :
243  *                 GroupNumber   (2 Octets) 
244  *                 ElementNumber (2 Octets) 
245  *                 ElementSize   (4 Octets)
246  *
247  *            DICOM en explicit Value Representation :
248  *                 GroupNumber         (2 Octets) 
249  *                 ElementNumber       (2 Octets) 
250  *                 ValueRepresentation (2 Octets) 
251  *                 ElementSize         (2 Octets)
252  *
253  *            ATTENTION : dans le cas ou ValueRepresentation = OB, OW, SQ, UN
254  *                 GroupNumber         (2 Octets) 
255  *                 ElementNumber       (2 Octets) 
256  *                 ValueRepresentation (2 Octets)
257  *                 zone reservee       (2 Octets) 
258  *                 ElementSize         (4 Octets)
259  *
260  * @param sw  code swap
261  * @param skippedLength  pointeur sur nombre d'octets que l'on a saute qd
262  *                       la lecture est finie
263  * @param longueurLue    pointeur sur longueur (en nombre d'octets) 
264  *                       effectivement lue
265  * @return               longueur retenue pour le champ 
266  */
267
268 void gdcmHeader::FindVR( ElValue *ElVal) {
269         char VR[3];
270         int lgrLue;
271         long PositionOnEntry = ftell(fp);
272         
273         if (filetype != ExplicitVR)
274                 return;
275
276         lgrLue=fread (&VR, (size_t)2,(size_t)1, fp);
277         VR[2]=0;
278                 
279         // Warning: we believe this is explicit VR (Value Representation) because
280         // we used a heuristic that found "UL" in the first tag. Alas this
281         // doesn't guarantee that all the tags will be in explicit VR. In some
282         // cases (see e-film filtered files) one finds implicit VR tags mixed
283         // within an explicit VR file. Hence we make sure the present tag
284         // is in explicit VR and try to fix things if it happens not to be
285         // the case.
286
287         // FIXME There should be only one occurence returned. Avoid the
288         // first extraction by calling proper method.
289         VRAtr FoundVR = dicom_vr->find(string(VR))->first;
290         if ( ! FoundVR.empty()) {
291                 ElVal->SetVR(FoundVR);
292                 return; 
293         }
294         
295         // We thought this was explicit VR, but we end up with an
296         // implicit VR tag. Let's backtrack.
297         ElVal->SetVR("Implicit");
298         fseek(fp, PositionOnEntry, SEEK_SET);
299 }
300
301 void gdcmHeader::FindLength( ElValue * ElVal) {
302         guint32 length32;
303         guint16 length16;
304         
305         string vr = ElVal->GetVR();
306         
307         if ( (filetype == ExplicitVR) && (vr != "Implicit") ) {
308                 if (   ( vr == "OB" ) || ( vr == "OW" )
309                          || ( vr == "SQ" ) || ( vr == "UN" ) ) {
310                         
311                         // The following two bytes are reserved, so we skip them,
312                         // and we proceed on reading the length on 4 bytes.
313                         fseek(fp, 2L,SEEK_CUR);
314                         length32 = ReadInt32();
315                         
316                 } else {
317                         // Length is encoded on 2 bytes.
318                         length16 = ReadInt16();
319                          
320                         if ( length16 == 0xffff) {
321                                 length32 = 0;
322                         } else {
323                                 length32 = length16;
324                         }
325                 }
326         } else {
327                 // Either implicit VR or an explicit VR that (at least for this
328                 // element) lied a little bit. Length is on 4 bytes.
329                 length32 = ReadInt32();
330         }
331         
332         // Traitement des curiosites sur la longueur
333         if ( length32 == 0xffffffff)
334                 length32=0;
335         
336         ElVal->SetLength(length32);
337 }
338
339
340 /**
341  * \ingroup gdcmHeader
342  * \brief   remet les octets dans un ordre compatible avec celui du processeur
343
344  * @return  longueur retenue pour le champ 
345  */
346 guint32 gdcmHeader::SwapLong(guint32 a) {
347         // FIXME: il pourrait y avoir un pb pour les entiers negatifs ...
348         switch (sw) {
349         case    0 :
350                 break;
351         case 4321 :
352                 a=(   ((a<<24) & 0xff000000) | ((a<<8)  & 0x00ff0000)    | 
353                       ((a>>8)  & 0x0000ff00) | ((a>>24) & 0x000000ff) );
354                 break;
355         
356         case 3412 :
357                 a=(   ((a<<16) & 0xffff0000) | ((a>>16) & 0x0000ffff) );
358                 break;
359         
360         case 2143 :
361                 a=(    ((a<<8) & 0xff00ff00) | ((a>>8) & 0x00ff00ff)  );
362                 break;
363         default :
364                 dbg.Error(" gdcmHeader::SwapLong : unset swap code");
365                 a=0;
366         }
367         return(a);
368 }
369
370 /**
371  * \ingroup gdcmHeader
372  * \brief   remet les octets dans un ordre compatible avec celui du processeur
373
374  * @return  longueur retenue pour le champ 
375  */
376 guint16 gdcmHeader::SwapShort(guint16 a) {
377         if ( (sw==4321)  || (sw==2143) )
378                 a =(((a<<8) & 0x0ff00) | ((a>>8)&0x00ff));
379         return (a);
380 }
381
382 void gdcmHeader::SkipElementValue(ElValue * ElVal) {
383         //FIXME don't dump the returned value
384         (void)fseek(fp, (long)ElVal->GetLength(), SEEK_CUR);
385 }
386
387 void gdcmHeader::LoadElementValue(ElValue * ElVal) {
388         size_t item_read;
389         guint16 group  = ElVal->GetGroup();
390         guint16 elem   = ElVal->GetElement();
391         string  vr     = ElVal->GetVR();
392         guint32 length = ElVal->GetLength();
393         fseek(fp, (long)ElVal->GetOffset(), SEEK_SET);
394         
395         // Sequences not treated yet !
396         if( vr == "SQ" ) {
397                 SkipElementValue(ElVal);
398                 ElVal->SetLength(0);
399                 return;
400         }
401         // A sequence "contains" a set of tags (called items). It looks like
402         // the last tag of a sequence (the one that terminates the sequence)
403         // has a group of 0xfffe (with a dummy length).
404         if( group == 0xfffe) {
405                 SkipElementValue(ElVal);
406                 ElVal->SetLength(0);
407                 return;
408         }
409         
410         if ( IsAnInteger(group, elem, vr, length) ) {
411                 guint32 NewInt;
412                 if( length == 2 ) {
413                         NewInt = ReadInt16();
414                 } else if( length == 4 ) {
415                         NewInt = ReadInt32();
416                 } else
417                         dbg.Error(true, "LoadElementValue: Inconsistency when reading Int.");
418                 
419                 //FIXME: make the following an util fonction
420                 ostringstream s;
421                 s << NewInt;
422                 ElVal->SetValue(s.str());
423                 return;
424         }
425         
426         // FIXME The exact size should be length if we move to strings or whatever
427         char* NewValue = (char*)g_malloc(length+1);
428         if( !NewValue) {
429                 dbg.Verbose(1, "LoadElementValue: Failed to allocate NewValue");
430                 return;
431         }
432         NewValue[length]= 0;
433         
434         // FIXME les elements trop long (seuil a fixer a la main) ne devraient
435         // pas etre charge's !!!! Voir TODO.
436         item_read = fread(NewValue, (size_t)length, (size_t)1, fp);
437         if ( item_read != 1 ) {
438                 g_free(NewValue);
439                 Error::FileReadError(fp, "gdcmHeader::LoadElementValue");
440                 ElVal->SetValue("gdcm::UnRead");
441                 return;
442         }
443         ElVal->SetValue(NewValue);
444 }
445
446
447 guint16 gdcmHeader::ReadInt16(void) {
448         guint16 g;
449         size_t item_read;
450         item_read = fread (&g, (size_t)2,(size_t)1, fp);
451         if ( item_read != 1 )
452                 throw Error::FileReadError(fp, "gdcmHeader::ReadInt16");
453         g = SwapShort(g);
454         return g;
455 }
456
457 guint32 gdcmHeader::ReadInt32(void) {
458         guint32 g;
459         size_t item_read;
460         item_read = fread (&g, (size_t)4,(size_t)1, fp);
461         if ( item_read != 1 )
462                 throw Error::FileReadError(fp, "gdcmHeader::ReadInt32");
463         g = SwapLong(g);
464         return g;
465 }
466
467 /**
468  * \ingroup gdcmHeader
469  * \brief   Read the next tag without loading it's value
470  * @return  On succes the newly created ElValue, NULL on failure.      
471  */
472
473 ElValue * gdcmHeader::ReadNextElement(void) {
474         guint16 g;
475         guint16 n;
476         ElValue * NewElVal;
477         
478         try {
479                 g = ReadInt16();
480                 n = ReadInt16();
481         }
482         catch ( Error::FileReadError ) {
483                 // We reached the EOF (or an error occured) and header parsing
484                 // has to be considered as finished.
485                 return (ElValue *)0;
486         }
487
488         // Find out if the tag we encountered is in the dictionaries:
489         gdcmDictEntry * NewTag = IsInDicts(g, n);
490         if (!NewTag)
491                 NewTag = new gdcmDictEntry(g, n, "Unknown", "Unknown", "Unkown");
492
493         NewElVal = new ElValue(NewTag);
494         if (!NewElVal) {
495                 dbg.Verbose(1, "ReadNextElement: failed to allocate ElValue");
496                 return (ElValue*)0;
497         }
498
499         FindVR(NewElVal);
500         FindLength(NewElVal);
501         NewElVal->SetOffset(ftell(fp));
502         return NewElVal;
503 }
504
505 bool gdcmHeader::IsAnInteger(guint16 group, guint16 element,
506                                      string vr, guint32 length ) {
507         // When we have some semantics on the element we just read, and we
508         // a priori now we are dealing with an integer, then we can swap it's
509         // element value properly.
510         if ( element == 0 )  {  // This is the group length of the group
511                 if (length != 4)
512                         dbg.Error("gdcmHeader::ShouldBeSwaped", "should be four");
513                 return true;
514         }
515         
516         if ( group % 2 != 0 )
517                 // We only have some semantics on documented elements, which are
518                 // the even ones.
519                 return false;
520         
521         if ( (length != 4) && ( length != 2) )
522                 // Swapping only make sense on integers which are 2 or 4 bytes long.
523                 return false;
524         
525         if ( (vr == "UL") || (vr == "US") || (vr == "SL") || (vr == "SS") )
526                 return true;
527         
528         if ( (group == 0x0028) && (element == 0x0005) )
529                 // This tag is retained from ACR/NEMA
530                 // CHECKME Why should "Image Dimensions" be a single integer ?
531                 return true;
532         
533         if ( (group == 0x0028) && (element == 0x0200) )
534                 // This tag is retained from ACR/NEMA
535                 return true;
536         
537         return false;
538 }
539
540 /**
541  * \ingroup gdcmHeader
542  * \brief   Recover the offset (from the beginning of the file) of the pixels.
543  */
544 size_t gdcmHeader::GetPixelOffset(void) {
545         // If this file complies with the norm we should encounter the
546         // "Image Location" tag (0x0028,  0x0200). This tag contains the
547         // the group that contains the pixel data (hence the "Pixel Data"
548         // is found by indirection through the "Image Location").
549         // Inside the group pointed by "Image Location" the searched element
550         // is conventionally the element 0x0010 (when the norm is respected).
551         //    When the "Image Location" is absent we default to group 0x7fe0.
552         guint16 grPixel;
553         guint16 numPixel;
554         string ImageLocation = GetPubElValByName("Image Location");
555         if ( ImageLocation == "UNFOUND" ) {
556                 grPixel = 0x7FE0;
557         } else {
558                 grPixel = (guint16) atoi( ImageLocation.c_str() );
559         }
560         if (grPixel != 0x7fe0)
561                 // FIXME is this still necessary ?
562                 // Now, this looks like an old dirty fix for Philips imager
563                 numPixel = 0x1010;
564         else
565                 numPixel = 0x0010;
566         ElValue* PixelElement = PubElVals.GetElement(grPixel, numPixel);
567         if (PixelElement)
568                 return PixelElement->GetOffset();
569         else
570                 return 0;
571 }
572
573 gdcmDictEntry * gdcmHeader::IsInDicts(guint32 group, guint32 element) {
574         gdcmDictEntry * found = (gdcmDictEntry*)0;
575         if (!RefPubDict && !RefShaDict) {
576                 //FIXME build a default dictionary !
577                 printf("FIXME in gdcmHeader::IsInDicts\n");
578         }
579         if (RefPubDict) {
580                 found = RefPubDict->GetTag(group, element);
581                 if (found)
582                         return found;
583         }
584         if (RefShaDict) {
585                 found = RefShaDict->GetTag(group, element);
586                 if (found)
587                         return found;
588         }
589         return found;
590 }
591
592 string gdcmHeader::GetPubElValByNumber(guint16 group, guint16 element) {
593         return PubElVals.GetElValue(group, element);
594 }
595
596 string gdcmHeader::GetPubElValByName(string TagName) {
597         return PubElVals.GetElValue(TagName);
598 }
599
600 /**
601  * \ingroup gdcmHeader
602  * \brief   Parses the header of the file but does NOT load element values.
603  */
604 void gdcmHeader::ParseHeader(void) {
605         ElValue * newElValue = (ElValue *)0;
606         
607         rewind(fp);
608         CheckSwap();
609         while ( (newElValue = ReadNextElement()) ) {
610                 SkipElementValue(newElValue);
611                 PubElVals.Add(newElValue);
612         }
613 }
614
615 /**
616  * \ingroup gdcmHeader
617  * \brief   Loads the element values of all the elements present in the
618  *          public tag based hash table.
619  */
620 void gdcmHeader::LoadElements(void) {
621         rewind(fp);    
622         TagElValueHT ht = PubElVals.GetTagHt();
623         for (TagElValueHT::iterator tag = ht.begin(); tag != ht.end(); ++tag)
624                 LoadElementValue(tag->second);
625 }
626
627 void gdcmHeader::PrintPubElVal(ostream & os) {
628         PubElVals.Print(os);
629 }
630
631 void gdcmHeader::PrintPubDict(ostream & os) {
632         RefPubDict->Print(os);
633 }