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