]> Creatis software - gdcm.git/blob - src/gdcmHeader.cxx
bc3b906abf1952fcede37414cf280210999f204a
[gdcm.git] / src / gdcmHeader.cxx
1 // gdcmHeader.cxx
2 //-----------------------------------------------------------------------------
3 #include <stdio.h>
4 #include <cerrno>
5 #include <cctype>    // for isalpha
6 #include <vector>
7
8 #include "gdcmHeader.h"
9 #include "gdcmGlobal.h"
10 #include "gdcmUtil.h"
11 #include "gdcmDebug.h"
12 #include "gdcmTS.h"
13
14 //-----------------------------------------------------------------------------
15 // Constructor / Destructor
16 /**
17  * \brief  Constructor 
18  * @param  InFilename name of the file whose header we want to analyze
19  * @param  exception_on_error whether we want to throw an exception or not
20  * @param  enable_sequences = true to allow the header 
21  *         to be parsed *inside* the SeQuences, when they have an actual length 
22  * @param  ignore_shadow = true if user wants to skip shadow groups 
23  *         during parsing, to save memory space
24  */
25 gdcmHeader::gdcmHeader(const char *InFilename, 
26                        bool exception_on_error,
27                        bool enable_sequences, 
28                        bool ignore_shadow):
29    gdcmParser(InFilename,exception_on_error,enable_sequences,ignore_shadow)
30
31
32    typedef struct {
33       guint32 totalSQlength;
34       guint32 alreadyParsedlength;
35    } pileElem;
36
37    
38    // for some ACR-NEMA images GrPixel, NumPixel is *not* 7fe0,0010
39    // We may encounter the 'RETired' (0x0028, 0x0200) tag
40    // (Image Location") . This Element contains the number of
41    // the group that contains the pixel data (hence the "Pixel Data"
42    // is found by indirection through the "Image Location").
43    // Inside the group pointed by "Image Location" the searched element
44    // is conventionally the element 0x0010 (when the norm is respected).
45    // When the "Image Location" is absent we default to group 0x7fe0.
46    
47    // This IS the right place for the code
48  
49       std::string ImageLocation = GetEntryByNumber(0x0028, 0x0200);
50       if ( ImageLocation == GDCM_UNFOUND ) { // Image Location
51          GrPixel = 0x7fe0;                   // default value
52       } else {
53          GrPixel = (guint16) atoi( ImageLocation.c_str() );
54       }   
55       if (GrPixel == 0xe07f) // sometimes Image Location value doesn't follow 
56          GrPixel = 0x7fe0;   // the supposed processor endianity. 
57                              // see gdcmData/cr172241.dcm      
58       if (GrPixel != 0x7fe0) 
59          // This is a kludge for old dirty Philips imager.
60          NumPixel = 0x1010;
61       else
62          NumPixel = 0x0010;
63
64       TagKey key = gdcmDictEntry::TranslateToKey(GrPixel, NumPixel);
65       countGrPixel = GetEntry().count(key);
66       
67       // we set the SQ Depth of each Header Entry
68    
69    int top =-1;
70    int countSQ = 0;      
71    pileElem pile[100]; // Hope embedded sequence depth is no that long !
72
73    int currentParsedlength = 0;
74    int totalElementlength;
75    std::ostringstream tab; 
76    tab << "   ";
77    
78    int DEBUG = 0;  // Sorry; Dealing with e-film breaker images
79                    // will (certainly) cause a lot of troubles ...
80                    // I prefer keeping my 'trace' on .             
81    
82    for (ListTag::iterator i = listEntries.begin();  
83        i != listEntries.end();
84        ++i) {
85       (*i)->SetSQDepthLevel(countSQ);
86       if ( (*i)->GetVR() == "SQ" && (*i)->GetReadLength() != 0) {   // SQ found         
87          countSQ++;
88          top ++;         
89             if ( top >= 20) {
90                std::cout << "Kaie ! Kaie! SQ Stack Overflow" << std::endl;
91                return;
92             }
93          if (DEBUG) std::cout << "\n >>>>> empile niveau " << top 
94                          << "; Lgr SeQ: " << (*i)->GetReadLength() 
95                          << "\n" <<std::endl;
96                  
97          pile[top].totalSQlength = (*i)->GetReadLength();
98          pile[top].alreadyParsedlength = 0; 
99          currentParsedlength = 0;                      
100
101       } else {                              // non SQ found
102       
103          if (countSQ != 0) {                // we are 'inside a SeQuence'
104             if ( (*i)->GetGroup()==0xfffe  && (*i)->GetElement()==0xe0dd){
105                // we just found 'end of SeQuence'
106                
107                if (DEBUG)
108                    std::cout << "fffe,e0dd : depile" << std::endl;
109                currentParsedlength += 8; // gr:2 elem:2 vr:2 lgt:2                                     
110                countSQ --;
111                top --; 
112                pile[top].alreadyParsedlength +=  currentParsedlength;
113             } else {
114                // we are on a 'standard' elem
115                // or a Zero-length SeQuence
116                
117                totalElementlength =  (*i)->GetFullLength();            
118                currentParsedlength += totalElementlength;                                
119                pile[top].alreadyParsedlength += totalElementlength;
120                
121                if (pile[top].totalSQlength == 0xffffffff) {
122                   if (DEBUG)
123                      std::cout << "totalSeQlength == 0xffffffff" 
124                                << std::endl; 
125                } else {
126                   if (DEBUG) 
127                        std::cout << "alrdyPseLgt:"
128                        << pile[top].alreadyParsedlength << " totSeQlgt: " 
129                        << pile[top].totalSQlength << " curPseLgt: " 
130                        << currentParsedlength
131                        << std::endl;
132                   while (pile[top].alreadyParsedlength==pile[top].totalSQlength) {
133                   
134                      if (DEBUG) 
135                          std::cout << " \n<<<<<< On depile niveau " << top 
136                                    << " \n" <<  std::endl;
137                      (*i)->SetSQDepthLevel(countSQ);                 
138                      currentParsedlength = pile[top].alreadyParsedlength;
139                      countSQ --;
140                      top --;
141                      if (top >=0) {
142                         
143                         pile[top].alreadyParsedlength +=  currentParsedlength +12;
144                                                 // 12 : length of 'SQ embedded' SQ element
145                         currentParsedlength += 8; // gr:2 elem:2 vr:2 lgt:2
146                                                                                              
147                         if (DEBUG)
148                              std::cout << pile[top].alreadyParsedlength << " " 
149                                        << pile[top].totalSQlength << " " 
150                                        << currentParsedlength
151                                        << std::endl;
152                      }                               
153                      if (top == -1) {
154                         currentParsedlength = 0;
155                         break;
156                      }                                                       
157                   }
158                }                                
159             }              
160          }   // end : 'inside a SeQuence'   
161       } 
162       if (DEBUG) {
163          for (int k=0; k<(*i)->GetSQDepthLevel();k++) {
164             std::cout << tab;
165          }
166          (*i)->SetPrintLevel(2);
167          (*i)->Print();
168       }      
169    } // end for        
170 }
171
172 /**
173  * \brief Constructor  
174  * @param exception_on_error whether we want to throw an exception or not
175  */
176 gdcmHeader::gdcmHeader(bool exception_on_error) :
177    gdcmParser(exception_on_error)
178 {
179 }
180
181 /**
182  * \ingroup gdcmHeader
183  * \brief   Canonical destructor.
184  */
185 gdcmHeader::~gdcmHeader (void) {
186 }
187
188 //-----------------------------------------------------------------------------
189 // Print
190
191 // see gdcmParser.cxx
192
193 /**
194   * \ingroup gdcmHeader
195   * \brief   Prints the Header Entries (Dicom Elements)
196   *          from the chained list
197   *          and skips the elements belonging to a SeQuence
198   * @return
199   */ 
200 void gdcmHeader::PrintEntryNoSQ(std::ostream & os) {
201
202    int depth;
203    for (ListTag::iterator i = listEntries.begin();  
204         i != listEntries.end();
205         ++i)
206    {
207       depth= (*i)->GetSQDepthLevel();
208       if ( depth != 0 /*|| (*i)->GetVR() =="SQ" */){
209          continue;
210       }
211       (*i)->SetPrintLevel(printLevel);
212       (*i)->Print(os); 
213    } 
214 }
215
216 /**
217   * \ingroup gdcmHeader
218   * \brief   Prints the Header Entries (Dicom Elements)
219   *          from the chained list
220   *          and indents the elements belonging to any SeQuence
221   * \warning : will be removed
222   * @return
223   */ 
224 void gdcmHeader::PrintEntryNiceSQ(std::ostream & os) {     
225    std::ostringstream tab; 
226    tab << "   ";
227
228    int depth;   
229    for (ListTag::iterator i = listEntries.begin();  
230       i != listEntries.end();
231        ++i) {
232       depth= (*i)->GetSQDepthLevel();
233       if (depth != 0) { 
234          for (int k=0;k<depth;k++)
235             os << tab.str();
236       } 
237       (*i)->SetPrintLevel(printLevel);
238       (*i)->Print(os);
239              
240    } // end for     
241 }
242
243 //-----------------------------------------------------------------------------
244 // Public
245
246 /**
247  * \ingroup gdcmHeader
248  * \brief  This predicate, based on hopefully reasonable heuristics,
249  *         decides whether or not the current gdcmParser was properly parsed
250  *         and contains the mandatory information for being considered as
251  *         a well formed and usable Dicom/Acr File.
252  * @return true when gdcmParser is the one of a reasonable Dicom/Acr file,
253  *         false otherwise. 
254  */
255 bool gdcmHeader::IsReadable(void) {
256    if(!gdcmParser::IsReadable()) {
257       return(false);
258    }
259    std::string res = GetEntryByNumber(0x0028, 0x0005);
260    if ( res != GDCM_UNFOUND && atoi(res.c_str()) > 4 ) 
261       return false; // Image Dimensions
262    if ( !GetHeaderEntryByNumber(0x0028, 0x0100) )
263       return false; // "Bits Allocated"
264    if ( !GetHeaderEntryByNumber(0x0028, 0x0101) )
265       return false; // "Bits Stored"
266    if ( !GetHeaderEntryByNumber(0x0028, 0x0102) )
267       return false; // "High Bit"
268    if ( !GetHeaderEntryByNumber(0x0028, 0x0103) )
269       return false; // "Pixel Representation" i.e. 'Sign'
270    return true;
271 }
272
273 /**
274  * \ingroup gdcmHeader
275  * \brief   Determines if the Transfer Syntax was already encountered
276  *          and if it corresponds to a JPEGBaseLineProcess1 one.
277  * @return  True when JPEGBaseLineProcess1found. False in all other cases.
278  */
279 bool gdcmHeader::IsJPEGBaseLineProcess1TransferSyntax(void) {
280    gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
281    if ( !Element )
282       return false;
283    LoadHeaderEntrySafe(Element);
284
285    std::string Transfer = Element->GetValue();
286    if ( Transfer == "1.2.840.10008.1.2.4.50" )
287       return true;
288    return false;
289 }
290
291 /**
292  * \ingroup gdcmHeader
293  * \brief   Determines if the Transfer Syntax was already encountered
294  *          and if it corresponds to a JPEGExtendedProcess2-4 one.
295  * @return  True when JPEGExtendedProcess2-4 found. False in all other cases.
296  */
297 bool gdcmHeader::IsJPEGExtendedProcess2_4TransferSyntax(void) {
298    gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
299    if ( !Element )
300       return false;
301    LoadHeaderEntrySafe(Element);
302    return ( Element->GetValue() == "1.2.840.10008.1.2.4.51" );
303 }
304
305 /**
306  * \ingroup gdcmHeader
307  * \brief   Determines if the Transfer Syntax was already encountered
308  *          and if it corresponds to a JPEGExtendeProcess3-5 one.
309  * @return  True when JPEGExtendedProcess3-5 found. False in all other cases.
310  */
311 bool gdcmHeader::IsJPEGExtendedProcess3_5TransferSyntax(void) {
312    gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
313    if ( !Element )
314       return false;
315    LoadHeaderEntrySafe(Element);
316
317    std::string Transfer = Element->GetValue();
318    if ( Transfer == "1.2.840.10008.1.2.4.52" )
319       return true;
320    return false;
321 }
322
323 /**
324  * \ingroup gdcmHeader
325  * \brief   Determines if the Transfer Syntax was already encountered
326  *          and if it corresponds to a JPEGSpectralSelectionProcess6-8 one.
327  * @return  True when JPEGSpectralSelectionProcess6-8 found. False in all
328  *          other cases.
329  */
330 bool gdcmHeader::IsJPEGSpectralSelectionProcess6_8TransferSyntax(void) {
331    gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
332    if ( !Element )
333       return false;
334    LoadHeaderEntrySafe(Element);
335
336    std::string Transfer = Element->GetValue();
337    if ( Transfer == "1.2.840.10008.1.2.4.53" )
338       return true;
339    return false;
340 }
341
342 /**
343  * \ingroup gdcmHeader
344  * \brief   Determines if the Transfer Syntax was already encountered
345  *          and if it corresponds to a RLE Lossless one.
346  * @return  True when RLE Lossless found. False in all
347  *          other cases.
348  */
349 bool gdcmHeader::IsRLELossLessTransferSyntax(void) {
350    gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
351    if ( !Element )
352       return false;
353    LoadHeaderEntrySafe(Element);
354
355    std::string Transfer = Element->GetValue();
356    if ( Transfer == "1.2.840.10008.1.2.5" ) {
357       return true;
358     }
359    return false;
360 }
361
362 /**
363  * \ingroup gdcmHeader
364  * \brief  Determines if Transfer Syntax was already encountered
365  *          and if it corresponds to a JPEG Lossless one. 
366  * @return  True when RLE Lossless found. False in all
367  *          other cases. 
368  */
369 bool gdcmHeader::IsJPEGLossless(void) {
370    gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
371     // faire qq chose d'intelligent a la place de Ã§a
372    if ( !Element )
373       return false;
374    LoadHeaderEntrySafe(Element);
375
376    const char * Transfert = Element->GetValue().c_str();
377    if ( memcmp(Transfert+strlen(Transfert)-2 ,"70",2)==0) return true;
378    if ( memcmp(Transfert+strlen(Transfert)-2 ,"55",2)==0) return true;
379    if (Element->GetValue() == "1.2.840.10008.1.2.4.57")   return true;
380
381    return false;
382 }
383
384 /**
385  * \ingroup gdcmHeader
386  * \brief   Determines if the Transfer Syntax was already encountered
387  *          and if it corresponds to a JPEG2000 one
388  * @return  True when JPEG2000 (Lossly or LossLess) found. False in all
389  *          other cases.
390  */
391 bool gdcmHeader::IsJPEG2000(void) {
392    gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
393    if ( !Element )
394       return false;
395    LoadHeaderEntrySafe(Element);
396
397    std::string Transfer = Element->GetValue();
398    if (    (Transfer == "1.2.840.10008.1.2.4.90") 
399         || (Transfer == "1.2.840.10008.1.2.4.91") )
400       return true;
401    return false;
402 }
403
404 /**
405  * \ingroup gdcmHeader
406  * \brief   Predicate for dicom version 3 file.
407  * @return  True when the file is a dicom version 3.
408  */
409 bool gdcmHeader::IsDicomV3(void) {
410    // Checking if Transfert Syntax exists is enough
411    // Anyway, it's to late check if the 'Preamble' was found ...
412    // And ... would it be a rich idea to check ?
413    // (some 'no Preamble' DICOM images exist !)
414    return (GetHeaderEntryByNumber(0x0002, 0x0010) != NULL);
415 }
416
417 /**
418  * \ingroup gdcmHeader
419  * \brief   Retrieve the number of columns of image.
420  * @return  The encountered size when found, 0 by default.
421  *          0 means the file is NOT USABLE. The caller will have to check
422  */
423 int gdcmHeader::GetXSize(void) {
424    std::string StrSize;
425    StrSize = GetEntryByNumber(0x0028,0x0011);
426    if (StrSize == GDCM_UNFOUND)
427       return 0;
428    return atoi(StrSize.c_str());
429 }
430
431 /**
432  * \ingroup gdcmHeader
433  * \brief   Retrieve the number of lines of image.
434  * \warning The defaulted value is 1 as opposed to gdcmHeader::GetXSize()
435  * @return  The encountered size when found, 1 by default 
436  *          (The ACR-MEMA file contains a Signal, not an Image).
437  */
438 int gdcmHeader::GetYSize(void) {
439    std::string StrSize = GetEntryByNumber(0x0028,0x0010);
440    if (StrSize != GDCM_UNFOUND)
441       return atoi(StrSize.c_str());
442    if ( IsDicomV3() )
443       return 0;
444    else
445       // The Rows (0028,0010) entry was optional for ACR/NEMA. It might
446       // hence be a signal (1d image). So we default to 1:
447       return 1;
448 }
449
450 /**
451  * \ingroup gdcmHeader
452  * \brief   Retrieve the number of planes of volume or the number
453  *          of frames of a multiframe.
454  * \warning When present we consider the "Number of Frames" as the third
455  *          dimension. When absent we consider the third dimension as
456  *          being the ACR-NEMA "Planes" tag content.
457  * @return  The encountered size when found, 1 by default (single image).
458  */
459 int gdcmHeader::GetZSize(void) {
460    // Both  DicomV3 and ACR/Nema consider the "Number of Frames"
461    // as the third dimension.
462    std::string StrSize = GetEntryByNumber(0x0028,0x0008);
463    if (StrSize != GDCM_UNFOUND)
464       return atoi(StrSize.c_str());
465
466    // We then consider the "Planes" entry as the third dimension 
467    StrSize = GetEntryByNumber(0x0028,0x0012);
468    if (StrSize != GDCM_UNFOUND)
469       return atoi(StrSize.c_str());
470    return 1;
471 }
472
473 /**
474  * \ingroup gdcmHeader
475  * \brief   Retrieve the number of Bits Stored (actually used)
476  *          (as opposite to number of Bits Allocated)
477  * @return  The encountered number of Bits Stored, 0 by default.
478  *          0 means the file is NOT USABLE. The caller has to check it !
479  */
480 int gdcmHeader::GetBitsStored(void) {  
481    std::string StrSize = GetEntryByNumber(0x0028,0x0101);
482    if (StrSize == GDCM_UNFOUND)
483       return 0;  // It's supposed to be mandatory
484                  // the caller will have to check
485    return atoi(StrSize.c_str());
486 }
487
488 /**
489  * \ingroup gdcmHeader
490  * \brief   Retrieve the number of Bits Allocated
491  *          (8, 12 -compacted ACR-NEMA files, 16, ...)
492  * @return  The encountered number of Bits Allocated, 0 by default.
493  *          0 means the file is NOT USABLE. The caller has to check it !
494  */
495 int gdcmHeader::GetBitsAllocated(void) {
496    std::string StrSize = GetEntryByNumber(0x0028,0x0100);
497    if (StrSize == GDCM_UNFOUND)
498       return 0; // It's supposed to be mandatory
499                 // the caller will have to check
500    return atoi(StrSize.c_str());
501 }
502
503 /**
504  * \ingroup gdcmHeader
505  * \brief   Retrieve the number of Samples Per Pixel
506  *          (1 : gray level, 3 : RGB -1 or 3 Planes-)
507  * @return  The encountered number of Samples Per Pixel, 1 by default.
508  *          (Gray level Pixels)
509  */
510 int gdcmHeader::GetSamplesPerPixel(void) {
511    std::string StrSize = GetEntryByNumber(0x0028,0x0002);
512    if (StrSize == GDCM_UNFOUND)
513       return 1; // Well, it's supposed to be mandatory ...
514                 // but sometimes it's missing : *we* assume Gray pixels
515    return atoi(StrSize.c_str());
516 }
517
518 /**
519  * \ingroup gdcmHeader
520  * \brief   Retrieve the Planar Configuration for RGB images
521  *          (0 : RGB Pixels , 1 : R Plane + G Plane + B Plane)
522  * @return  The encountered Planar Configuration, 0 by default.
523  */
524 int gdcmHeader::GetPlanarConfiguration(void) {
525    std::string StrSize = GetEntryByNumber(0x0028,0x0006);
526    if (StrSize == GDCM_UNFOUND)
527       return 0;
528    return atoi(StrSize.c_str());
529 }
530
531 /**
532  * \ingroup gdcmHeader
533  * \brief   Return the size (in bytes) of a single pixel of data.
534  * @return  The size in bytes of a single pixel of data; 0 by default
535  *          0 means the file is NOT USABLE; the caller will have to check        
536  */
537 int gdcmHeader::GetPixelSize(void) {
538    std::string PixelType = GetPixelType();
539    if (PixelType == "8U"  || PixelType == "8S")
540       return 1;
541    if (PixelType == "16U" || PixelType == "16S")
542       return 2;
543    if (PixelType == "32U" || PixelType == "32S")
544       return 4;
545    if (PixelType == "FD")
546       return 8;         
547    dbg.Verbose(0, "gdcmHeader::GetPixelSize: Unknown pixel type");
548    return 0;
549 }
550
551 /**
552  * \ingroup gdcmHeader
553  * \brief   Build the Pixel Type of the image.
554  *          Possible values are:
555  *          - 8U  unsigned  8 bit,
556  *          - 8S    signed  8 bit,
557  *          - 16U unsigned 16 bit,
558  *          - 16S   signed 16 bit,
559  *          - 32U unsigned 32 bit,
560  *          - 32S   signed 32 bit,
561  *          - FD floating double 64 bits (Not kosher DICOM, but so usefull!)
562  * \warning 12 bit images appear as 16 bit.
563  *          24 bit images appear as 8 bit
564  * @return  0S if nothing found. NOT USABLE file. The caller has to check
565  */
566 std::string gdcmHeader::GetPixelType(void) { 
567    std::string BitsAlloc = GetEntryByNumber(0x0028, 0x0100); // Bits Allocated
568    if (BitsAlloc == GDCM_UNFOUND) {
569       dbg.Verbose(0, "gdcmHeader::GetPixelType: unfound Bits Allocated");
570       BitsAlloc = std::string("16");
571    }
572    if (BitsAlloc == "64")            // )
573       return ("FD");
574    if (BitsAlloc == "12")            // It will be unpacked
575       BitsAlloc = std::string("16");
576    else if (BitsAlloc == "24")       // (in order no to be messed up
577       BitsAlloc = std::string("8");  // by old RGB images)
578      
579    std::string Signed = GetEntryByNumber(0x0028, 0x0103); // "Pixel Representation"
580    if (Signed == GDCM_UNFOUND) {
581       dbg.Verbose(0, "gdcmHeader::GetPixelType: unfound Pixel Representation");
582       BitsAlloc = std::string("0");
583    }
584    if (Signed == "0")
585       Signed = std::string("U");
586    else
587       Signed = std::string("S");
588
589    return( BitsAlloc + Signed);
590 }
591
592
593 /**
594  * \ingroup gdcmHeader
595  * \brief   Recover the offset (from the beginning of the file) 
596  *          of *image* pixels (not *icone image* pixels, if any !)
597  * @return Pixel Offset
598  */
599 size_t gdcmHeader::GetPixelOffset(void) { 
600    //
601    // If the element (0x0088,0x0200) 'icone image sequence' is found
602    // (grPixel,numPixel) is stored twice : the first one for the icon
603    // the second one for the image ...
604    // pb : sometimes , (0x0088,0x0200) exists, but doesn't contain *anything*
605    // see gdcmData/MxTwinLossLess.dcm ...
606
607    /**
608     * \todo Clean me
609     *std::string icone = GetEntryByNumber(0x0088,0x0200); //icone image sequence
610     */
611       
612    IterHT it = GetHeaderEntrySameNumber(GrPixel,NumPixel);          
613    TagKey key = gdcmDictEntry::TranslateToKey(GrPixel,NumPixel);
614    gdcmHeaderEntry* PixelElement;
615    if (countGrPixel == 1)   
616       PixelElement = (it.first)->second;
617    else {
618       PixelElement = (++it.first)->second; // hope there are no more than 2 !
619    } 
620    if (PixelElement) {
621       return PixelElement->GetOffset();
622    } else {
623 /*      std::cout << "Big trouble : Pixel Element ("
624                 << std::hex << GrPixel<<","<< NumPixel<< ") NOT found"
625                 << std::endl;  */
626       return 0;
627    }     
628 }
629 // TODO : unify those two (previous one and next one)
630 /**
631  * \ingroup gdcmHeader
632  * \brief   Recover the pixel area length (in Bytes)
633  * @return Pixel Element Length, as stored in the header
634  *         (NOT the memory space necessary to hold the Pixels 
635  *          -in case of embeded compressed image-)
636  *         0 : NOT USABLE file. The caller has to check.
637  */
638 size_t gdcmHeader::GetPixelAreaLength(void) { 
639           
640    IterHT it = GetHeaderEntrySameNumber(GrPixel,NumPixel);          
641    TagKey key = gdcmDictEntry::TranslateToKey(GrPixel,NumPixel);
642    gdcmHeaderEntry* PixelElement;
643   
644   if (countGrPixel==1)  
645       PixelElement = (it.first)->second;
646    else
647       PixelElement = (++it.first)->second;
648
649    if (PixelElement) {
650       return PixelElement->GetLength();
651    } else {
652 /*      std::cout << "Big trouble : Pixel Element ("
653                 << std::hex << GrPixel<<","<< NumPixel<< ") NOT found"
654                 << std::endl;
655 */
656       return 0;
657    }
658 }
659
660 /**
661   * \ingroup gdcmHeader
662   * \brief tells us if LUT are used
663   * \warning Right now, 'Segmented xxx Palette Color Lookup Table Data'
664   *          are NOT considered as LUT, since nobody knows
665   *          how to deal with them
666   *          Please warn me if you know sbdy that *does* know ... jprx
667   * @return true if LUT Descriptors and LUT Tables were found 
668   */
669 bool gdcmHeader::HasLUT(void) {
670
671    // Check the presence of the LUT Descriptors, and LUT Tables    
672    // LutDescriptorRed    
673    if ( !GetHeaderEntryByNumber(0x0028,0x1101) )
674       return false;
675    // LutDescriptorGreen 
676    if ( !GetHeaderEntryByNumber(0x0028,0x1102) )
677       return false;
678    // LutDescriptorBlue 
679    if ( !GetHeaderEntryByNumber(0x0028,0x1103) )
680       return false;   
681    // Red Palette Color Lookup Table Data
682    if ( !GetHeaderEntryByNumber(0x0028,0x1201) )
683       return false; 
684    // Green Palette Color Lookup Table Data       
685    if ( !GetHeaderEntryByNumber(0x0028,0x1202) )
686       return false;
687    // Blue Palette Color Lookup Table Data      
688    if ( !GetHeaderEntryByNumber(0x0028,0x1203) )
689       return false;
690    // FIXME : (0x0028,0x3006) : LUT Data (CTX dependent)
691    //         NOT taken into account, but we don't know how to use it ...   
692    return true;
693 }
694
695 /**
696   * \ingroup gdcmHeader
697   * \brief gets the info from 0028,1101 : Lookup Table Desc-Red
698   *             else 0
699   * @return Lookup Table number of Bits , 0 by default
700   *          when (0028,0004),Photometric Interpretation = [PALETTE COLOR ]
701   * @ return bit number of each LUT item 
702   */
703 int gdcmHeader::GetLUTNbits(void) {
704    std::vector<std::string> tokens;
705    //int LutLength;
706    //int LutDepth;
707    int LutNbits;
708    //Just hope Lookup Table Desc-Red = Lookup Table Desc-Red = Lookup Table Desc-Blue
709    // Consistency already checked in GetLUTLength
710    std::string LutDescription = GetEntryByNumber(0x0028,0x1101);
711    if (LutDescription == GDCM_UNFOUND)
712       return 0;
713    tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
714    Tokenize (LutDescription, tokens, "\\");
715    //LutLength=atoi(tokens[0].c_str());
716    //LutDepth=atoi(tokens[1].c_str());
717    LutNbits=atoi(tokens[2].c_str());
718    tokens.clear();
719    return LutNbits;
720 }
721
722 /**
723   * \ingroup gdcmHeader
724   * \brief builts Red/Green/Blue/Alpha LUT from Header
725   *         when (0028,0004),Photometric Interpretation = [PALETTE COLOR ]
726   *          and (0028,1101),(0028,1102),(0028,1102)  
727   *            - xxx Palette Color Lookup Table Descriptor - are found
728   *          and (0028,1201),(0028,1202),(0028,1202) 
729   *            - xxx Palette Color Lookup Table Data - are found 
730   * \warning does NOT deal with :
731   *   0028 1100 Gray Lookup Table Descriptor (Retired)
732   *   0028 1221 Segmented Red Palette Color Lookup Table Data
733   *   0028 1222 Segmented Green Palette Color Lookup Table Data
734   *   0028 1223 Segmented Blue Palette Color Lookup Table Data 
735   *   no known Dicom reader deals with them :-(
736   * @return a RGBA Lookup Table 
737   */ 
738 unsigned char * gdcmHeader::GetLUTRGBA(void) {
739 // Not so easy : see 
740 // http://www.barre.nom.fr/medical/dicom2/limitations.html#Color%20Lookup%20Tables
741
742 //  if Photometric Interpretation # PALETTE COLOR, no LUT to be done
743    if (GetEntryByNumber(0x0028,0x0004) != "PALETTE COLOR ") {
744       return NULL;
745    }  
746    int lengthR, debR, nbitsR;
747    int lengthG, debG, nbitsG;
748    int lengthB, debB, nbitsB;
749    
750 // Get info from Lut Descriptors
751 // (the 3 LUT descriptors may be different)    
752    std::string LutDescriptionR = GetEntryByNumber(0x0028,0x1101);
753    if (LutDescriptionR == GDCM_UNFOUND)
754       return NULL;
755    std::string LutDescriptionG = GetEntryByNumber(0x0028,0x1102);
756    if (LutDescriptionG == GDCM_UNFOUND)
757       return NULL;   
758    std::string LutDescriptionB = GetEntryByNumber(0x0028,0x1103);
759    if (LutDescriptionB == GDCM_UNFOUND)
760       return NULL;
761       
762    std::vector<std::string> tokens;
763       
764    tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
765    Tokenize (LutDescriptionR, tokens, "\\");
766    lengthR=atoi(tokens[0].c_str()); // Red LUT length in Bytes
767    debR   =atoi(tokens[1].c_str()); // subscript of the first Lut Value
768    nbitsR =atoi(tokens[2].c_str()); // Lut item size (in Bits)
769    tokens.clear();
770    
771    tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
772    Tokenize (LutDescriptionG, tokens, "\\");
773    lengthG=atoi(tokens[0].c_str()); // Green LUT length in Bytes
774    debG   =atoi(tokens[1].c_str()); // subscript of the first Lut Value
775    nbitsG =atoi(tokens[2].c_str()); // Lut item size (in Bits)
776    tokens.clear();  
777    
778    tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
779    Tokenize (LutDescriptionB, tokens, "\\");
780    lengthB=atoi(tokens[0].c_str()); // Blue LUT length in Bytes
781    debB   =atoi(tokens[1].c_str()); // subscript of the first Lut Value
782    nbitsB =atoi(tokens[2].c_str()); // Lut item size (in Bits)
783    tokens.clear();
784  
785    // Load LUTs into memory, (as they were stored on disk)
786    unsigned char *lutR = (unsigned char *)
787                          GetEntryVoidAreaByNumber(0x0028,0x1201);
788    unsigned char *lutG = (unsigned char *)
789                          GetEntryVoidAreaByNumber(0x0028,0x1202);
790    unsigned char *lutB = (unsigned char *)
791                          GetEntryVoidAreaByNumber(0x0028,0x1203); 
792    
793    if (!lutR || !lutG || !lutB ) {
794       return NULL;
795    } 
796    // forge the 4 * 8 Bits Red/Green/Blue/Alpha LUT 
797    
798    unsigned char *LUTRGBA = new (unsigned char)[1024]; // 256 * 4 (R, G, B, Alpha) 
799    if (!LUTRGBA) {
800       return NULL;
801    }
802    memset(LUTRGBA, 0, 1024);
803    // Bits Allocated
804    int nb;
805    std::string str_nb = GetEntryByNumber(0x0028,0x0100);
806    if (str_nb == GDCM_UNFOUND ) {
807       nb = 16;
808    } else {
809       nb = atoi(str_nb.c_str() );
810    }  
811    int mult;
812
813    if (nbitsR==16 && nb==8) // when LUT item size is different than pixel size
814       mult=2;               // high byte must be = low byte 
815    else                     // See PS 3.3-2003 C.11.1.1.2 p 619
816       mult=1; 
817  
818    // if we get a black image, let's just remove the '+1'
819    // from 'i*mult+1' and check again 
820    // if it works, we shall have to check the 3 Palettes
821    // to see which byte is ==0 (first one, or second one)
822    // and fix the code
823    // We give up the checking to avoid some (useless ?)overhead 
824    // (optimistic asumption)
825    unsigned char *a;      
826    int i;
827
828    a = LUTRGBA+0;
829    for(i=0;i<lengthR;i++) {
830       *a = lutR[i*mult+1]; 
831       a+=4;       
832    }        
833    a = LUTRGBA+1;
834    for(i=0;i<lengthG;i++) {
835       *a = lutG[i*mult+1]; 
836       a+=4;       
837    }  
838    a = LUTRGBA+2;
839    for(i=0;i<lengthB;i++) {
840       *a = lutB[i*mult+1]; 
841       a+=4;       
842    }  
843    a = LUTRGBA+3;
844    for(i=0;i<256;i++) {
845       *a = 1; // Alpha component
846       a+=4; 
847    } 
848    
849    //How to free the now useless LUTs?
850    //free(LutR); free(LutB); free(LutG); // Seg Fault when used
851    return(LUTRGBA);   
852
853
854 /**
855  * \ingroup gdcmHeader
856  * \brief gets the info from 0002,0010 : Transfert Syntax and gdcmTS
857  *        else 1.
858  * @return the full Transfert Syntax Name (as oposite to Transfert Syntax UID)
859  */
860 std::string gdcmHeader::GetTransfertSyntaxName(void) { 
861    // use the gdcmTS (TS : Transfert Syntax)
862    std::string TransfertSyntax = GetEntryByNumber(0x0002,0x0010);
863    if (TransfertSyntax == GDCM_UNFOUND) {
864       dbg.Verbose(0, "gdcmHeader::GetTransfertSyntaxName: unfound Transfert Syntax (0002,0010)");
865       return "Uncompressed ACR-NEMA";
866    }
867    // we do it only when we need it
868    gdcmTS * ts = gdcmGlobal::GetTS();
869    std::string tsName=ts->GetValue(TransfertSyntax);
870    //delete ts; // Seg Fault when deleted ?!
871    return tsName;
872 }
873
874 /**
875  * \ingroup   gdcmHeader
876  * \brief Sets the Pixel Area size in the Header
877  *        --> not-for-rats function
878  * @param ImageDataSize new Pixel Area Size
879  *        warning : nothing else is checked
880  */
881 void gdcmHeader::SetImageDataSize(size_t ImageDataSize) {
882    std::string content1;
883    char car[20];
884
885    // Assumes HeaderEntry (GrPixel, NumPixel) is unique ...   
886    //\todo deal with multiplicity (see gdcmData/icone.dcm)
887    sprintf(car,"%d",ImageDataSize);
888  
889    gdcmHeaderEntry *a = GetHeaderEntryByNumber(GrPixel, NumPixel);
890    a->SetLength(ImageDataSize);
891
892    ImageDataSize+=8;
893    sprintf(car,"%d",ImageDataSize);
894    content1=car;
895    SetEntryByNumber(content1, GrPixel, NumPixel);
896 }
897
898
899 /**
900  * \ingroup   gdcmHeader
901  * \brief compares 2 Headers, according to DICOMDIR rules
902  *        --> not-for-rats function
903  * \warning does NOT work with ACR-NEMA files
904  * \todo find a trick to solve the pb (use RET fields ?)
905  * @param header 
906  * @return true if 'smaller'
907  */
908  bool gdcmHeader::operator<(gdcmHeader &header){
909    std::string s1,s2;
910
911    // Patient Name
912    s1=this->GetEntryByNumber(0x0010,0x0010);
913    s2=header.GetEntryByNumber(0x0010,0x0010);
914    if(s1 < s2)
915       return(true);
916    else if(s1 > s2)
917       return(false);
918    else
919    {
920       // Patient ID
921       s1=this->GetEntryByNumber(0x0010,0x0020);
922       s2=header.GetEntryByNumber(0x0010,0x0020);
923       if (s1 < s2)
924          return(true);
925       else if (s1 > s2)
926          return(1);
927       else
928       {
929          // Study Instance UID
930          s1=this->GetEntryByNumber(0x0020,0x000d);
931          s2=header.GetEntryByNumber(0x0020,0x000d);
932          if (s1 < s2)
933             return(true);
934          else if(s1 > s2)
935             return(false);
936          else
937          {
938             // Serie Instance UID
939             s1=this->GetEntryByNumber(0x0020,0x000e);
940             s2=header.GetEntryByNumber(0x0020,0x000e);
941             if (s1 < s2)
942                return(true);
943             else if(s1 > s2)
944                return(false);
945          }
946       }
947    }
948    return(false);
949 }
950
951 bool gdcmHeader::WriteEntry(gdcmHeaderEntry *tag, FILE *_fp,FileType type)
952 {
953    guint32 length = tag->GetLength();
954                                                                                 
955    // The value of a tag MUST (see the DICOM norm) be an odd number of
956    // bytes. When this is not the case, pad with an additional byte:
957    if(length%2==1)
958    {
959       tag->SetValue(tag->GetValue()+"\0");
960       tag->SetLength(tag->GetReadLength()+1);
961    }
962                                                                                 
963    WriteEntryTagVRLength(tag, _fp, type);
964                                                                                 
965    // Pixels are never loaded in the element !
966    // we stop writting when Pixel are processed
967    // FIX : we loose trailing elements (RAB, right now)
968    guint16 el     = tag->GetElement();
969    guint16 group  = tag->GetGroup();
970    int compte =0;
971    if ((group == GrPixel) && (el == NumPixel) ) {
972       compte++;
973       if (compte == countGrPixel) {// we passed *all* the GrPixel,NumPixel
974          return false;
975       }
976    }
977    WriteEntryValue(tag, _fp, type);
978    return true;
979 }
980
981 //-----------------------------------------------------------------------------
982 // Protected
983
984 /**
985  * \ingroup   gdcmHeader
986  * \brief anonymize a Header (removes Patient's personal info)
987  *        (read the code to see which ones ...)
988  */
989 bool gdcmHeader::anonymizeHeader() {
990
991   gdcmHeaderEntry *patientNameHE = GetHeaderEntryByNumber (0x0010, 0x0010);
992  // gdcmHeaderEntry *patientIDHE   = GetHeaderEntryByNumber (0x0010, 0x0020); 
993     
994   ReplaceIfExistByNumber ("  ",0x0010, 0x2154); // Telephone   
995   ReplaceIfExistByNumber ("  ",0x0010, 0x1040); // Adress
996   ReplaceIfExistByNumber ("  ",0x0010, 0x0020); // Patient ID
997   
998   if (patientNameHE) {
999      std::string StudyInstanceUID =  GetEntryByNumber (0x0020, 0x000d);
1000      if (StudyInstanceUID !=GDCM_UNFOUND)
1001         ReplaceOrCreateByNumber(StudyInstanceUID, 0x0010, 0x0010);
1002      else
1003         ReplaceOrCreateByNumber(std::string("anonymised"), 0x0010, 0x0010);
1004   }
1005   
1006   // Just for fun :-(
1007   // (if any) remove or replace all the stuff that contains a Date
1008   
1009 //0008 0012 DA ID Instance Creation Date
1010 //0008 0020 DA ID Study Date
1011 //0008 0021 DA ID Series Date
1012 //0008 0022 DA ID Acquisition Date
1013 //0008 0023 DA ID Content Date
1014 //0008 0024 DA ID Overlay Date
1015 //0008 0025 DA ID Curve Date
1016 //0008 002a DT ID Acquisition Datetime
1017 //0018 9074 DT ACQ Frame Acquisition Datetime
1018 //0018 9151 DT ACQ Frame Reference Datetime
1019 //0018 a002 DT ACQ Contribution Date Time
1020 //0020 3403 SH REL Modified Image Date (RET)
1021 //0032 0032 DA SDY Study Verified Date
1022 //0032 0034 DA SDY Study Read Date
1023 //0032 1000 DA SDY Scheduled Study Start Date
1024 //0032 1010 DA SDY Scheduled Study Stop Date
1025 //0032 1040 DA SDY Study Arrival Date
1026 //0032 1050 DA SDY Study Completion Date
1027 //0038 001a DA VIS Scheduled Admission Date
1028 //0038 001c DA VIS Scheduled Discharge Date
1029 //0038 0020 DA VIS Admitting Date
1030 //0038 0030 DA VIS Discharge Date
1031 //0040 0002 DA PRC Scheduled Procedure Step Start Date
1032 //0040 0004 DA PRC Scheduled Procedure Step End Date
1033 //0040 0244 DA PRC Performed Procedure Step Start Date
1034 //0040 0250 DA PRC Performed Procedure Step End Date
1035 //0040 2004 DA PRC Issue Date of Imaging Service Request
1036 //0040 4005 DT PRC Scheduled Procedure Step Start Date and Time
1037 //0040 4011 DT PRC Expected Completion Date and Time
1038 //0040 a030 DT PRC Verification Date Time
1039 //0040 a032 DT PRC Observation Date Time
1040 //0040 a120 DT PRC DateTime
1041 //0040 a121 DA PRC Date
1042 //0040 a13a DT PRC Referenced Datetime
1043 //0070 0082 DA ??? Presentation Creation Date
1044 //0100 0420 DT ??? SOP Autorization Date and Time
1045 //0400 0105 DT ??? Digital Signature DateTime
1046 //2100 0040 DA PJ Creation Date
1047 //3006 0008 DA SSET Structure Set Date
1048 //3008 0024 DA ??? Treatment Control Point Date
1049 //3008 0054 DA ??? First Treatment Date
1050 //3008 0056 DA ??? Most Recent Treatment Date
1051 //3008 0162 DA ??? Safe Position Exit Date
1052 //3008 0166 DA ??? Safe Position Return Date
1053 //3008 0250 DA ??? Treatment Date
1054 //300a 0006 DA RT RT Plan Date
1055 //300a 022c DA RT Air Kerma Rate Reference Date
1056 //300e 0004 DA RT Review Date
1057  return true;  
1058 }
1059 //-----------------------------------------------------------------------------
1060 // Private
1061
1062 //-----------------------------------------------------------------------------