]> Creatis software - gdcm.git/blob - src/gdcmHeader.cxx
First try on win32
[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         if (filetype == ExplicitVR) {
306                 string vr = ElVal->GetVR();
307                 if (   (vr != "Implicit")
308                          && ( (vr=="OB") || (vr=="OW") || (vr=="SQ") || (vr=="UN") ) ) {
309                         
310                         // The following two bytes are reserved, so we skip them,
311                         // and we proceed on reading the length on 4 bytes.
312                         fseek(fp, 2L,SEEK_CUR);
313                         length32 = ReadInt32();
314                         
315                 } else {
316                         // Length is encoded on 2 bytes.
317                         length16 = ReadInt16();
318                          
319                         if ( length16 == 0xffff) {
320                                 length32 = 0;
321                         } else {
322                                 length32 = length16;
323                         }
324                 }
325         } else {
326                 // Either implicit VR or an explicit VR that (at least for this
327                 // element) lied a little bit. Length is on 4 bytes.
328                 length32 = ReadInt32();
329         }
330         
331         // Traitement des curiosites sur la longueur
332         if ( length32 == 0xffffffff)
333                 length32=0;
334         
335         ElVal->SetLength(length32);
336 }
337
338
339 /**
340  * \ingroup gdcmHeader
341  * \brief   remet les octets dans un ordre compatible avec celui du processeur
342
343  * @return  longueur retenue pour le champ 
344  */
345 guint32 gdcmHeader::SwapLong(guint32 a) {
346         // FIXME: il pourrait y avoir un pb pour les entiers negatifs ...
347         switch (sw) {
348         case    0 :
349                 break;
350         case 4321 :
351                 a=(   ((a<<24) & 0xff000000) | ((a<<8)  & 0x00ff0000)    | 
352                       ((a>>8)  & 0x0000ff00) | ((a>>24) & 0x000000ff) );
353                 break;
354         
355         case 3412 :
356                 a=(   ((a<<16) & 0xffff0000) | ((a>>16) & 0x0000ffff) );
357                 break;
358         
359         case 2143 :
360                 a=(    ((a<<8) & 0xff00ff00) | ((a>>8) & 0x00ff00ff)  );
361                 break;
362         default :
363                 dbg.Error(" gdcmHeader::SwapLong : unset swap code");
364                 a=0;
365         }
366         return(a);
367 }
368
369 /**
370  * \ingroup gdcmHeader
371  * \brief   remet les octets dans un ordre compatible avec celui du processeur
372
373  * @return  longueur retenue pour le champ 
374  */
375 guint16 gdcmHeader::SwapShort(guint16 a) {
376         if ( (sw==4321)  || (sw==2143) )
377                 a =(((a<<8) & 0x0ff00) | ((a>>8)&0x00ff));
378         return (a);
379 }
380
381 void gdcmHeader::SkipElementValue(ElValue * ElVal) {
382         //FIXME don't dump the returned value
383         (void)fseek(fp, (long)ElVal->GetLength(), SEEK_CUR);
384 }
385
386 void gdcmHeader::LoadElementValue(ElValue * ElVal) {
387         size_t item_read;
388         guint16 group  = ElVal->GetGroup();
389         guint16 elem   = ElVal->GetElement();
390         string  vr     = ElVal->GetVR();
391         guint32 length = ElVal->GetLength();
392         fseek(fp, (long)ElVal->GetOffset(), SEEK_SET);
393         
394         // Sequences not treated yet !
395         if( vr == "SQ" ) {
396                 SkipElementValue(ElVal);
397                 ElVal->SetLength(0);
398                 return;
399         }
400         // A sequence "contains" a set of tags (called items). It looks like
401         // the last tag of a sequence (the one that terminates the sequence)
402         // has a group of 0xfffe (with a dummy length).
403         if( group == 0xfffe) {
404                 SkipElementValue(ElVal);
405                 ElVal->SetLength(0);
406                 return;
407         }
408         
409         if ( IsAnInteger(group, elem, vr, length) ) {
410                 guint32 NewInt;
411                 if( length == 2 ) {
412                         NewInt = ReadInt16();
413                 } else if( length == 4 ) {
414                         NewInt = ReadInt32();
415                 } else
416                         dbg.Error(true, "LoadElementValue: Inconsistency when reading Int.");
417                 
418                 //FIXME: make the following an util fonction
419                 ostringstream s;
420                 s << NewInt;
421                 ElVal->SetValue(s.str());
422                 return;
423         }
424         
425         // FIXME The exact size should be length if we move to strings or whatever
426         char* NewValue = (char*)g_malloc(length+1);
427         if( !NewValue) {
428                 dbg.Verbose(1, "LoadElementValue: Failed to allocate NewValue");
429                 return;
430         }
431         NewValue[length]= 0;
432         
433         // FIXME les elements trop long (seuil a fixer a la main) ne devraient
434         // pas etre charge's !!!! Voir TODO.
435         item_read = fread(NewValue, (size_t)length, (size_t)1, fp);
436         if ( item_read != 1 ) {
437                 g_free(NewValue);
438                 Error::FileReadError(fp, "gdcmHeader::LoadElementValue");
439                 ElVal->SetValue("gdcm::UnRead");
440                 return;
441         }
442         ElVal->SetValue(NewValue);
443 }
444
445
446 guint16 gdcmHeader::ReadInt16(void) {
447         guint16 g;
448         size_t item_read;
449         item_read = fread (&g, (size_t)2,(size_t)1, fp);
450         if ( item_read != 1 )
451                 throw Error::FileReadError(fp, "gdcmHeader::ReadInt16");
452         g = SwapShort(g);
453         return g;
454 }
455
456 guint32 gdcmHeader::ReadInt32(void) {
457         guint32 g;
458         size_t item_read;
459         item_read = fread (&g, (size_t)4,(size_t)1, fp);
460         if ( item_read != 1 )
461                 throw Error::FileReadError(fp, "gdcmHeader::ReadInt32");
462         g = SwapLong(g);
463         return g;
464 }
465
466 /**
467  * \ingroup gdcmHeader
468  * \brief   Read the next tag without loading it's value
469  * @return  On succes the newly created ElValue, NULL on failure.      
470  */
471
472 ElValue * gdcmHeader::ReadNextElement(void) {
473         guint16 g;
474         guint16 n;
475         ElValue * NewElVal;
476         
477         try {
478                 g = ReadInt16();
479                 n = ReadInt16();
480         }
481         catch ( Error::FileReadError ) {
482                 // We reached the EOF (or an error occured) and header parsing
483                 // has to be considered as finished.
484                 return (ElValue *)0;
485         }
486
487         // Find out if the tag we encountered is in the dictionaries:
488         gdcmDictEntry * NewTag = IsInDicts(g, n);
489         if (!NewTag)
490                 NewTag = new gdcmDictEntry(g, n, "Unknown", "Unknown", "Unknown");
491
492         NewElVal = new ElValue(NewTag);
493         if (!NewElVal) {
494                 dbg.Verbose(1, "ReadNextElement: failed to allocate ElValue");
495                 return (ElValue*)0;
496         }
497
498         FindVR(NewElVal);
499         FindLength(NewElVal);
500         NewElVal->SetOffset(ftell(fp));
501         return NewElVal;
502 }
503
504 bool gdcmHeader::IsAnInteger(guint16 group, guint16 element,
505                                      string vr, guint32 length ) {
506         // When we have some semantics on the element we just read, and we
507         // a priori now we are dealing with an integer, then we can swap it's
508         // element value properly.
509         if ( element == 0 )  {  // This is the group length of the group
510                 if (length != 4)
511                         dbg.Error("gdcmHeader::ShouldBeSwaped", "should be four");
512                 return true;
513         }
514         
515         if ( group % 2 != 0 )
516                 // We only have some semantics on documented elements, which are
517                 // the even ones.
518                 return false;
519         
520         if ( (length != 4) && ( length != 2) )
521                 // Swapping only make sense on integers which are 2 or 4 bytes long.
522                 return false;
523         
524         if ( (vr == "UL") || (vr == "US") || (vr == "SL") || (vr == "SS") )
525                 return true;
526         
527         if ( (group == 0x0028) && (element == 0x0005) )
528                 // This tag is retained from ACR/NEMA
529                 // CHECKME Why should "Image Dimensions" be a single integer ?
530                 return true;
531         
532         if ( (group == 0x0028) && (element == 0x0200) )
533                 // This tag is retained from ACR/NEMA
534                 return true;
535         
536         return false;
537 }
538
539 /**
540  * \ingroup gdcmHeader
541  * \brief   Recover the offset (from the beginning of the file) of the pixels.
542  */
543 size_t gdcmHeader::GetPixelOffset(void) {
544         // If this file complies with the norm we should encounter the
545         // "Image Location" tag (0x0028,  0x0200). This tag contains the
546         // the group that contains the pixel data (hence the "Pixel Data"
547         // is found by indirection through the "Image Location").
548         // Inside the group pointed by "Image Location" the searched element
549         // is conventionally the element 0x0010 (when the norm is respected).
550         //    When the "Image Location" is absent we default to group 0x7fe0.
551         guint16 grPixel;
552         guint16 numPixel;
553         string ImageLocation = GetPubElValByName("Image Location");
554         if ( ImageLocation == "UNFOUND" ) {
555                 grPixel = 0x7FE0;
556         } else {
557                 grPixel = (guint16) atoi( ImageLocation.c_str() );
558         }
559         if (grPixel != 0x7fe0)
560                 // FIXME is this still necessary ?
561                 // Now, this looks like an old dirty fix for Philips imager
562                 numPixel = 0x1010;
563         else
564                 numPixel = 0x0010;
565         ElValue* PixelElement = PubElVals.GetElement(grPixel, numPixel);
566         if (PixelElement)
567                 return PixelElement->GetOffset();
568         else
569                 return 0;
570 }
571
572 gdcmDictEntry * gdcmHeader::IsInDicts(guint32 group, guint32 element) {
573         gdcmDictEntry * found = (gdcmDictEntry*)0;
574         if (!RefPubDict && !RefShaDict) {
575                 //FIXME build a default dictionary !
576                 printf("FIXME in gdcmHeader::IsInDicts\n");
577         }
578         if (RefPubDict) {
579                 found = RefPubDict->GetTag(group, element);
580                 if (found)
581                         return found;
582         }
583         if (RefShaDict) {
584                 found = RefShaDict->GetTag(group, element);
585                 if (found)
586                         return found;
587         }
588         return found;
589 }
590
591 string gdcmHeader::GetPubElValByNumber(guint16 group, guint16 element) {
592         return PubElVals.GetElValue(group, element);
593 }
594
595 string gdcmHeader::GetPubElValByName(string TagName) {
596         return PubElVals.GetElValue(TagName);
597 }
598
599 /**
600  * \ingroup gdcmHeader
601  * \brief   Parses the header of the file but does NOT load element values.
602  */
603 void gdcmHeader::ParseHeader(void) {
604         ElValue * newElValue = (ElValue *)0;
605         
606         rewind(fp);
607         CheckSwap();
608         while ( (newElValue = ReadNextElement()) ) {
609                 SkipElementValue(newElValue);
610                 PubElVals.Add(newElValue);
611         }
612 }
613
614 /**
615  * \ingroup gdcmHeader
616  * \brief   Loads the element values of all the elements present in the
617  *          public tag based hash table.
618  */
619 void gdcmHeader::LoadElements(void) {
620         rewind(fp);    
621         TagElValueHT ht = PubElVals.GetTagHt();
622         for (TagElValueHT::iterator tag = ht.begin(); tag != ht.end(); ++tag)
623                 LoadElementValue(tag->second);
624 }
625
626 void gdcmHeader::PrintPubElVal(ostream & os) {
627         PubElVals.Print(os);
628 }
629
630 void gdcmHeader::PrintPubDict(ostream & os) {
631         RefPubDict->Print(os);
632 }