]> Creatis software - gdcm.git/blob - src/gdcmHeader.cxx
Add a few missing 'group length' Elements
[gdcm.git] / src / gdcmHeader.cxx
1 // gdcmHeader.cxx
2 //-----------------------------------------------------------------------------
3 #include "gdcmHeader.h"
4
5 #include <stdio.h>
6 #include <cerrno>
7 #include <cctype>    // for isalpha
8
9 #include "gdcmUtil.h"
10 #include "gdcmTS.h"
11
12
13 //-----------------------------------------------------------------------------
14 // Constructor / Destructor
15 /**
16  * \ingroup gdcmHeader
17  * \brief   
18  * @param   InFilename
19  * @param   exception_on_error
20  * @param   enable_sequences = true to allow the header 
21  *          to be parsed *inside* the SeQuences, 
22  *          when they have an actual length 
23  * @param   ignore_shadow = true if user wants to skip shadow groups 
24             during parsing, to save memory space
25  *\TODO : may be we need one more bool, 
26  *         to allow skipping the private elements while parsing the header
27  *         in order to save space         
28  */
29 gdcmHeader::gdcmHeader(const char *InFilename, 
30                        bool exception_on_error,
31                        bool enable_sequences, 
32                        bool ignore_shadow):
33    gdcmParser(InFilename,exception_on_error,enable_sequences,ignore_shadow)
34
35    
36    // for some ACR-NEMA images GrPixel, NumPixel is *not* 7fe0,0010
37    // We may encounter the 'RETired' (0x0028, 0x0200) tag
38    // (Image Location") . This Element contains the number of
39    // the group that contains the pixel data (hence the "Pixel Data"
40    // is found by indirection through the "Image Location").
41    // Inside the group pointed by "Image Location" the searched element
42    // is conventionally the element 0x0010 (when the norm is respected).
43    // When the "Image Location" is absent we default to group 0x7fe0.
44    
45    // This IS the right place for the code
46  
47       std::string ImageLocation = GetEntryByNumber(0x0028, 0x0200);
48       if ( ImageLocation == GDCM_UNFOUND ) { // Image Location
49          GrPixel = 0x7fe0;                   // default value
50       } else {
51          GrPixel = (guint16) atoi( ImageLocation.c_str() );
52       }   
53       if (GrPixel == 0xe07f) // sometimes Image Location value doesn't follow 
54          GrPixel = 0x7fe0;   // the supposed processor endianity. 
55                              // see gdcmData/cr172241.dcm      
56       if (GrPixel != 0x7fe0) 
57          // This is a kludge for old dirty Philips imager.
58          NumPixel = 0x1010;
59       else
60          NumPixel = 0x0010;
61          
62       TagKey key = gdcmDictEntry::TranslateToKey(GrPixel, NumPixel);
63       countGrPixel = GetEntry().count(key);       
64 }
65
66 /**
67  * \ingroup gdcmHeader
68  * \brief   
69  * @param   exception_on_error
70  */
71 gdcmHeader::gdcmHeader(bool exception_on_error) :
72    gdcmParser(exception_on_error)
73 {
74 }
75
76 /**
77  * \ingroup gdcmHeader
78  * \brief   Canonical destructor.
79  */
80 gdcmHeader::~gdcmHeader (void) {
81 }
82
83 //-----------------------------------------------------------------------------
84 // Print
85
86 // see gdcmParser.cxx
87 //-----------------------------------------------------------------------------
88 // Public
89 /**
90  * \ingroup gdcmHeader
91  * \brief  This predicate, based on hopefully reasonable heuristics,
92  *         decides whether or not the current gdcmParser was properly parsed
93  *         and contains the mandatory information for being considered as
94  *         a well formed and usable Dicom/Acr File.
95  * @return true when gdcmParser is the one of a reasonable Dicom/Acr file,
96  *         false otherwise. 
97  */
98 bool gdcmHeader::IsReadable(void) {
99    if(!gdcmParser::IsReadable()) {
100       return(false);
101    }
102    std::string res = GetEntryByNumber(0x0028, 0x0005);
103    if ( res != GDCM_UNFOUND && atoi(res.c_str()) > 4 ) 
104       return false; // Image Dimensions
105    if ( !GetHeaderEntryByNumber(0x0028, 0x0100) )
106       return false; // "Bits Allocated"
107    if ( !GetHeaderEntryByNumber(0x0028, 0x0101) )
108       return false; // "Bits Stored"
109    if ( !GetHeaderEntryByNumber(0x0028, 0x0102) )
110       return false; // "High Bit"
111    if ( !GetHeaderEntryByNumber(0x0028, 0x0103) )
112       return false; // "Pixel Representation"
113    return true;
114 }
115
116 /**
117  * \ingroup gdcmHeader
118  * \brief   Determines if the Transfer Syntax was already encountered
119  *          and if it corresponds to a JPEGBaseLineProcess1 one.
120  *
121  * @return  True when JPEGBaseLineProcess1found. False in all other cases.
122  */
123 bool gdcmHeader::IsJPEGBaseLineProcess1TransferSyntax(void) {
124    gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
125    if ( !Element )
126       return false;
127    LoadHeaderEntrySafe(Element);
128
129    std::string Transfer = Element->GetValue();
130    if ( Transfer == "1.2.840.10008.1.2.4.50" )
131       return true;
132    return false;
133 }
134
135 /**
136  * \ingroup gdcmHeader
137  * \brief   Determines if the Transfer Syntax was already encountered
138  *          and if it corresponds to a JPEGExtendedProcess2-4 one.
139  *
140  * @return  True when JPEGExtendedProcess2-4 found. False in all other cases.
141  */
142 bool gdcmHeader::IsJPEGExtendedProcess2_4TransferSyntax(void) {
143    gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
144    if ( !Element )
145       return false;
146    LoadHeaderEntrySafe(Element);
147    return ( Element->GetValue() == "1.2.840.10008.1.2.4.51" );
148 }
149
150 /**
151  * \ingroup gdcmHeader
152  * \brief   Determines if the Transfer Syntax was already encountered
153  *          and if it corresponds to a JPEGExtendeProcess3-5 one.
154  *
155  * @return  True when JPEGExtendedProcess3-5 found. False in all other cases.
156  */
157 bool gdcmHeader::IsJPEGExtendedProcess3_5TransferSyntax(void) {
158    gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
159    if ( !Element )
160       return false;
161    LoadHeaderEntrySafe(Element);
162
163    std::string Transfer = Element->GetValue();
164    if ( Transfer == "1.2.840.10008.1.2.4.52" )
165       return true;
166    return false;
167 }
168
169 /**
170  * \ingroup gdcmHeader
171  * \brief   Determines if the Transfer Syntax was already encountered
172  *          and if it corresponds to a JPEGSpectralSelectionProcess6-8 one.
173  *
174  * @return  True when JPEGSpectralSelectionProcess6-8 found. False in all
175  *          other cases.
176  */
177 bool gdcmHeader::IsJPEGSpectralSelectionProcess6_8TransferSyntax(void) {
178    gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
179    if ( !Element )
180       return false;
181    LoadHeaderEntrySafe(Element);
182
183    std::string Transfer = Element->GetValue();
184    if ( Transfer == "1.2.840.10008.1.2.4.53" )
185       return true;
186    return false;
187 }
188
189 /**
190  * \ingroup gdcmHeader
191  * \brief   Determines if the Transfer Syntax was already encountered
192  *          and if it corresponds to a RLE Lossless one.
193  *
194  * @return  True when RLE Lossless found. False in all
195  *          other cases.
196  */
197 bool gdcmHeader::IsRLELossLessTransferSyntax(void) {
198    gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
199    if ( !Element )
200       return false;
201    LoadHeaderEntrySafe(Element);
202
203    std::string Transfer = Element->GetValue();
204    if ( Transfer == "1.2.840.10008.1.2.5" ) {
205       return true;
206     }
207    return false;
208 }
209
210 /**
211  * \ingroup gdcmHeader
212  * \brief  Determines if Transfer Syntax was already encountered
213  *          and if it corresponds to a JPEG Lossless one. 
214  *
215  * @return  True when RLE Lossless found. False in all
216  *          other cases. 
217  */
218 bool gdcmHeader::IsJPEGLossless(void) {
219    gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
220     // faire qq chose d'intelligent a la place de ça
221    if ( !Element )
222       return false;
223    LoadHeaderEntrySafe(Element);
224
225    const char * Transfert = Element->GetValue().c_str();
226    if ( memcmp(Transfert+strlen(Transfert)-2 ,"70",2)==0) return true;
227    if ( memcmp(Transfert+strlen(Transfert)-2 ,"55",2)==0) return true;
228    if (Element->GetValue() == "1.2.840.10008.1.2.4.57")   return true;
229
230    return false;
231 }
232
233 /**
234  * \ingroup gdcmHeader
235  * \brief   Determines if the Transfer Syntax was already encountered
236  *          and if it corresponds to a JPEG200 one.0
237  *
238  * @return  True when JPEG2000 (Lossly or LossLess) found. False in all
239  *          other cases.
240  */
241 bool gdcmHeader::IsJPEG2000(void) {
242    gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
243    if ( !Element )
244       return false;
245    LoadHeaderEntrySafe(Element);
246
247    std::string Transfer = Element->GetValue();
248    if (    (Transfer == "1.2.840.10008.1.2.4.90") 
249         || (Transfer == "1.2.840.10008.1.2.4.91") )
250       return true;
251    return false;
252 }
253
254 /**
255  * \ingroup gdcmHeader
256  * \brief   Predicate for dicom version 3 file.
257  * @return  True when the file is a dicom version 3.
258  */
259 bool gdcmHeader::IsDicomV3(void) {
260    // Checking if Transfert Syntax exists is enough
261    return (GetHeaderEntryByNumber(0x0002, 0x0010) != NULL);
262 }
263
264 /**
265  * \ingroup gdcmHeader
266  * \brief   Retrieve the number of columns of image.
267  * @return  The encountered size when found, 0 by default.
268  *          0 means the file is NOT USABLE. The caller will have to check
269  */
270 int gdcmHeader::GetXSize(void) {
271    std::string StrSize;
272    StrSize = GetEntryByNumber(0x0028,0x0011);
273    if (StrSize == GDCM_UNFOUND)
274       return 0;
275    return atoi(StrSize.c_str());
276 }
277
278 /**
279  * \ingroup gdcmHeader
280  * \brief   Retrieve the number of lines of image.
281  * \warning The defaulted value is 1 as opposed to gdcmHeader::GetXSize()
282  * @return  The encountered size when found, 1 by default 
283  *          (The file contains a Signal, not an Image).
284  */
285 int gdcmHeader::GetYSize(void) {
286    std::string StrSize = GetEntryByNumber(0x0028,0x0010);
287    if (StrSize != GDCM_UNFOUND)
288       return atoi(StrSize.c_str());
289    if ( IsDicomV3() )
290       return 0;
291    else
292       // The Rows (0028,0010) entry was optional for ACR/NEMA. It might
293       // hence be a signal (1d image). So we default to 1:
294       return 1;
295 }
296
297 /**
298  * \ingroup gdcmHeader
299  * \brief   Retrieve the number of planes of volume or the number
300  *          of frames of a multiframe.
301  * \warning When present we consider the "Number of Frames" as the third
302  *          dimension. When absent we consider the third dimension as
303  *          being the "Planes" tag content.
304  * @return  The encountered size when found, 1 by default (single image).
305  */
306 int gdcmHeader::GetZSize(void) {
307    // Both  DicomV3 and ACR/Nema consider the "Number of Frames"
308    // as the third dimension.
309    std::string StrSize = GetEntryByNumber(0x0028,0x0008);
310    if (StrSize != GDCM_UNFOUND)
311       return atoi(StrSize.c_str());
312
313    // We then consider the "Planes" entry as the third dimension [we
314    // cannot retrieve by name since "Planes tag is present both in
315    // IMG (0028,0012) and OLY (6000,0012) sections of the dictionary]. 
316    StrSize = GetEntryByNumber(0x0028,0x0012);
317    if (StrSize != GDCM_UNFOUND)
318       return atoi(StrSize.c_str());
319    return 1;
320 }
321
322 /**
323  * \ingroup gdcmHeader
324  * \brief   Retrieve the number of Bits Stored (actually used)
325  *          (as opposite to number of Bits Allocated)
326  * 
327  * @return  The encountered number of Bits Stored, 0 by default.
328  *          0 means the file is NOT USABLE. The caller has to check it !
329  */
330 int gdcmHeader::GetBitsStored(void) {  
331    std::string StrSize = GetEntryByNumber(0x0028,0x0101);
332    if (StrSize == GDCM_UNFOUND)
333       return 0;  // It's supposed to be mandatory
334                  // the caller will have to check
335    return atoi(StrSize.c_str());
336 }
337
338 /**
339  * \ingroup gdcmHeader
340  * \brief   Retrieve the number of Bits Allocated
341  *          (8, 12 -compacted ACR-NEMA files, 16, ...)
342  * 
343  * @return  The encountered number of Bits Allocated, 0 by default.
344  *          0 means the file is NOT USABLE. The caller has to check it !
345  */
346 int gdcmHeader::GetBitsAllocated(void) {  // TODO : move to gdcmFile
347    std::string StrSize = GetEntryByNumber(0x0028,0x0100);
348    if (StrSize == GDCM_UNFOUND)
349       return 0; // It's supposed to be mandatory
350                 // the caller will have to check
351    return atoi(StrSize.c_str());
352 }
353
354 /**
355  * \ingroup gdcmHeader
356  * \brief   Retrieve the number of Samples Per Pixel
357  *          (1 : gray level, 3 : RGB -1 or 3 Planes-)
358  * 
359  * @return  The encountered number of Samples Per Pixel, 1 by default.
360  *          (Gray level Pixels)
361  */
362 int gdcmHeader::GetSamplesPerPixel(void) {  // TODO : move to gdcmFile
363    std::string StrSize = GetEntryByNumber(0x0028,0x0002);
364    if (StrSize == GDCM_UNFOUND)
365       return 1; // Well, it's supposed to be mandatory ...
366                 // but sometimes it's missing : we assume Gray pixels
367    return atoi(StrSize.c_str());
368 }
369
370 /**
371  * \ingroup gdcmHeader
372  * \brief   Retrieve the Planar Configuration for RGB images
373  *          (0 : RGB Pixels , 1 : R Plane + G Plane + B Plane)
374  * 
375  * @return  The encountered Planar Configuration, 0 by default.
376  */
377 int gdcmHeader::GetPlanarConfiguration(void) {
378    std::string StrSize = GetEntryByNumber(0x0028,0x0006);
379    if (StrSize == GDCM_UNFOUND)
380       return 0;
381    return atoi(StrSize.c_str());
382 }
383
384 /**
385  * \ingroup gdcmHeader
386  * \brief   Return the size (in bytes) of a single pixel of data.
387  * @return  The size in bytes of a single pixel of data; 0 by default
388  *          0 means the file is NOT USABLE; the caller will have to check        
389  */
390 int gdcmHeader::GetPixelSize(void) {
391    std::string PixelType = GetPixelType();
392    if (PixelType == "8U"  || PixelType == "8S")
393       return 1;
394    if (PixelType == "16U" || PixelType == "16S")
395       return 2;
396    if (PixelType == "32U" || PixelType == "32S")
397       return 4;
398    dbg.Verbose(0, "gdcmHeader::GetPixelSize: Unknown pixel type");
399    return 0;
400 }
401
402 /**
403  * \ingroup gdcmHeader
404  * \brief   Build the Pixel Type of the image.
405  *          Possible values are:
406  *          - 8U  unsigned  8 bit,
407  *          - 8S    signed  8 bit,
408  *          - 16U unsigned 16 bit,
409  *          - 16S   signed 16 bit,
410  *          - 32U unsigned 32 bit,
411  *          - 32S   signed 32 bit,
412  * \warning 12 bit images appear as 16 bit.
413  * \        24 bit images appear as 8 bit
414  * @return  0S if nothing found. NOT USABLE file. The caller has to check
415  */
416 std::string gdcmHeader::GetPixelType(void) { 
417    std::string BitsAlloc = GetEntryByNumber(0x0028, 0x0100); // Bits Allocated
418    if (BitsAlloc == GDCM_UNFOUND) {
419       dbg.Verbose(0, "gdcmHeader::GetPixelType: unfound Bits Allocated");
420       BitsAlloc = std::string("16");
421    }
422    if (BitsAlloc == "12")            // It will be unpacked
423       BitsAlloc = std::string("16");
424    else if (BitsAlloc == "24")       // (in order no to be messed up
425       BitsAlloc = std::string("8");  // by old RGB images)
426      
427    std::string Signed = GetEntryByNumber(0x0028, 0x0103); // "Pixel Representation"
428    if (Signed == GDCM_UNFOUND) {
429       dbg.Verbose(0, "gdcmHeader::GetPixelType: unfound Pixel Representation");
430       BitsAlloc = std::string("0");
431    }
432    if (Signed == "0")
433       Signed = std::string("U");
434    else
435       Signed = std::string("S");
436
437    return( BitsAlloc + Signed);
438 }
439
440
441 /**
442  * \ingroup gdcmHeader
443  * \brief   Recover the offset (from the beginning of the file) 
444  * \        of *image* pixels (not *icone image* pixels, if any !)
445  */
446 size_t gdcmHeader::GetPixelOffset(void) { // TODO : move to gdcmFile
447
448    //
449    // If the element (0x0088,0x0200) 'icone image sequence' is found
450    // (grPixel,numPixel) is stored twice : the first one for the icon
451    // the second one for the image ...
452    // pb : sometimes , (0x0088,0x0200) exists, but doesn't contain *anything*
453    // see gdcmData/MxTwinLossLess.dcm ...
454    
455    
456    /*
457    guint16 grPixel = GrPixel;
458    guint16 numPixel= NumPixel;
459    std::string ImageLocation = GetEntryByNumber(0x0028, 0x0200);
460
461    if ( ImageLocation == GDCM_UNFOUND ) { // Image Location
462       grPixel = 0x7fe0;                   // default value
463    } else {
464       grPixel = (guint16) atoi( ImageLocation.c_str() );
465    }
466    
467    if (grPixel == 0xe07f) // sometimes Image Location value doesn't follow 
468       grPixel = 0x7fe0;   // the supposed processor endianity. 
469                           // see gdcmData/cr172241.dcm
470       
471    if (grPixel != 0x7fe0) 
472       // This is a kludge for old dirty Philips imager.
473       numPixel = 0x1010;
474    else
475       numPixel = 0x0010;
476  */
477       
478    IterHT it = GetHeaderEntrySameNumber(GrPixel,NumPixel);          
479    //std::string icone = GetEntryByNumber(0x0088,0x0200); //icone image sequence
480    TagKey key = gdcmDictEntry::TranslateToKey(GrPixel,NumPixel);
481    gdcmHeaderEntry* PixelElement;
482   
483    //if (tagHT.count(key) == 1)
484    if (countGrPixel == 1)   
485       PixelElement = (it.first)->second;
486    else
487       PixelElement = (++it.first)->second;
488    
489    if (PixelElement) {
490       return PixelElement->GetOffset();
491    }
492    else
493       return 0;
494 }
495 // TODO : unify those two (previous one and next one)
496 /**
497  * \ingroup gdcmHeader
498  * \brief   Recover the pixel area length (in Bytes)
499  *  @return 0 by default. NOT USABLE file. The caller has to check.
500  */
501 size_t gdcmHeader::GetPixelAreaLength(void) {
502
503 /*
504    // If this file complies with the norm we should encounter the
505    // "Image Location" tag (0x0028,  0x0200). This tag contains the
506    // the group that contains the pixel data (hence the "Pixel Data"
507    // is found by indirection through the "Image Location").
508    // Inside the group pointed by "Image Location" the searched element
509    // is conventionally the element 0x0010 (when the norm is respected).
510    // When the "Image Location" is absent we default to group 0x7fe0.
511    
512
513    guint16 grPixel;
514    guint16 numPixel;   
515    std::string ImageLocation = GetEntryByNumber(0x0028, 0x0200);
516    if ( ImageLocation == GDCM_UNFOUND ) { // Image Location
517       grPixel = 0x7fe0;                   // default value
518    } else {
519       grPixel = (guint16) atoi( ImageLocation.c_str() );
520    }
521    if (grPixel == 0xe07f) // sometimes group doesn't follow 
522       grPixel = 0x7fe0;   // the supposed processor endianity. see cr172241.dcm
523       
524    if (grPixel != 0x7fe0)
525       // This is a kludge for old dirty Philips imager.
526       numPixel = 0x1010;
527    else
528       numPixel = 0x0010;
529  */
530               
531    IterHT it = GetHeaderEntrySameNumber(GrPixel,NumPixel);          
532    //std::string icone = GetEntryByNumber(0x0088,0x0200); //icone image sequence
533    TagKey key = gdcmDictEntry::TranslateToKey(GrPixel,NumPixel);
534    gdcmHeaderEntry* PixelElement;
535   
536   // if (tagHT.count(key) == 1) 
537   if (countGrPixel)  
538       PixelElement = (it.first)->second;
539    else
540       PixelElement = (++it.first)->second;
541    
542    if (PixelElement)
543       return PixelElement->GetLength();
544    else {
545       std::cout << "Big trouble : Pixel Element ("
546                 << std::hex << GrPixel<<","<< NumPixel<< ") NOT found" 
547                 << std::endl;
548       return 0;
549    }
550 }
551
552 /**
553   * \ingroup gdcmHeader
554   * \brief tells us if LUT are used
555   * \warning Right now, 'Segmented xxx Palette Color Lookup Table Data'
556   * \        are NOT considered as LUT, since nobody knows
557   * \        how to deal with them
558   * @return a Boolean 
559   */
560 bool gdcmHeader::HasLUT(void) {
561
562    // Check the presence of the LUT Descriptors 
563    
564    // LutDescriptorRed    
565    if ( !GetHeaderEntryByNumber(0x0028,0x1101) )
566       return false;
567    // LutDescriptorGreen 
568    if ( !GetHeaderEntryByNumber(0x0028,0x1102) )
569       return false;
570    // LutDescriptorBlue 
571    if ( !GetHeaderEntryByNumber(0x0028,0x1103) )
572       return false;
573       
574    //  It is not enough :
575    //  we check also 
576    
577    // Red Palette Color Lookup Table Data
578    if ( !GetHeaderEntryByNumber(0x0028,0x1201) )
579       return false; 
580    // Green Palette Color Lookup Table Data       
581    if ( !GetHeaderEntryByNumber(0x0028,0x1202) )
582       return false;
583    // Blue Palette Color Lookup Table Data      
584    if ( !GetHeaderEntryByNumber(0x0028,0x1203) )
585       return false;   
586    return true;
587 }
588
589 /**
590   * \ingroup gdcmHeader
591   * \brief gets the info from 0028,1101 : Lookup Table Desc-Red
592   * \           else 0
593   * @return Lookup Table number of Bits , 0 by default
594   * \       when (0028,0004),Photometric Interpretation = [PALETTE COLOR ] 
595   */
596 int gdcmHeader::GetLUTNbits(void) {
597    std::vector<std::string> tokens;
598    //int LutLength;
599    //int LutDepth;
600    int LutNbits;
601    //Just hope Lookup Table Desc-Red = Lookup Table Desc-Red = Lookup Table Desc-Blue
602    // Consistency already checked in GetLUTLength
603    std::string LutDescription = GetEntryByNumber(0x0028,0x1101);
604    if (LutDescription == GDCM_UNFOUND)
605       return 0;
606    tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
607    Tokenize (LutDescription, tokens, "\\");
608    //LutLength=atoi(tokens[0].c_str());
609    //LutDepth=atoi(tokens[1].c_str());
610    LutNbits=atoi(tokens[2].c_str());
611    tokens.clear();
612    return LutNbits;
613 }
614
615 /**
616   * \ingroup gdcmHeader
617   * \brief builts Red/Green/Blue/Alpha LUT from Header
618   * \       when (0028,0004),Photometric Interpretation = [PALETTE COLOR ]
619   * \        and (0028,1101),(0028,1102),(0028,1102)  
620   * \          - xxx Palette Color Lookup Table Descriptor - are found
621   * \        and (0028,1201),(0028,1202),(0028,1202) 
622   * \          - xxx Palette Color Lookup Table Data - are found 
623   * \warning does NOT deal with :
624   * \ 0028 1100 Gray Lookup Table Descriptor (Retired)
625   * \ 0028 1221 Segmented Red Palette Color Lookup Table Data
626   * \ 0028 1222 Segmented Green Palette Color Lookup Table Data
627   * \ 0028 1223 Segmented Blue Palette Color Lookup Table Data 
628   * \ no known Dicom reader deals with them :-(
629   * @return a RGBA Lookup Table 
630   */ 
631 unsigned char * gdcmHeader::GetLUTRGBA(void) {
632 // Not so easy : see 
633 // http://www.barre.nom.fr/medical/dicom2/limitations.html#Color%20Lookup%20Tables
634
635 //  if Photometric Interpretation # PALETTE COLOR, no LUT to be done
636    if (GetEntryByNumber(0x0028,0x0004) != "PALETTE COLOR ") {
637         return NULL;
638    }  
639    int lengthR, debR, nbitsR;
640    int lengthG, debG, nbitsG;
641    int lengthB, debB, nbitsB;
642    
643 // Get info from Lut Descriptors
644 // (the 3 LUT descriptors may be different)    
645    std::string LutDescriptionR = GetEntryByNumber(0x0028,0x1101);
646    if (LutDescriptionR == GDCM_UNFOUND)
647       return NULL;
648    std::string LutDescriptionG = GetEntryByNumber(0x0028,0x1102);
649    if (LutDescriptionG == GDCM_UNFOUND)
650       return NULL;   
651    std::string LutDescriptionB = GetEntryByNumber(0x0028,0x1103);
652    if (LutDescriptionB == GDCM_UNFOUND)
653       return NULL;
654       
655    std::vector<std::string> tokens;
656       
657    tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
658    Tokenize (LutDescriptionR, tokens, "\\");
659    lengthR=atoi(tokens[0].c_str()); // Red LUT length in Bytes
660    debR   =atoi(tokens[1].c_str()); // subscript of the first Lut Value
661    nbitsR =atoi(tokens[2].c_str()); // Lut item size (in Bits)
662    tokens.clear();
663    
664    tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
665    Tokenize (LutDescriptionG, tokens, "\\");
666    lengthG=atoi(tokens[0].c_str()); // Green LUT length in Bytes
667    debG   =atoi(tokens[1].c_str()); // subscript of the first Lut Value
668    nbitsG =atoi(tokens[2].c_str()); // Lut item size (in Bits)
669    tokens.clear();  
670    
671    tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
672    Tokenize (LutDescriptionB, tokens, "\\");
673    lengthB=atoi(tokens[0].c_str()); // Blue LUT length in Bytes
674    debB   =atoi(tokens[1].c_str()); // subscript of the first Lut Value
675    nbitsB =atoi(tokens[2].c_str()); // Lut item size (in Bits)
676    tokens.clear();
677  
678    // Load LUTs into memory, (as they were stored on disk)
679    unsigned char *lutR = (unsigned char *)
680                          GetEntryVoidAreaByNumber(0x0028,0x1201);
681    unsigned char *lutG = (unsigned char *)
682                          GetEntryVoidAreaByNumber(0x0028,0x1202);
683    unsigned char *lutB = (unsigned char *)
684                          GetEntryVoidAreaByNumber(0x0028,0x1203); 
685    
686    if (!lutR || !lutG || !lutB ) {
687         return NULL;
688    } 
689    // forge the 4 * 8 Bits Red/Green/Blue/Alpha LUT 
690    
691    unsigned char *LUTRGBA = (unsigned char *)calloc(1024,1); // 256 * 4 (R, G, B, Alpha) 
692    if (!LUTRGBA) {
693       return NULL;
694    }
695    memset(LUTRGBA, 0, 1024);
696         // Bits Allocated
697    int nb;
698    std::string str_nb = GetEntryByNumber(0x0028,0x0100);
699    if (str_nb == GDCM_UNFOUND ) {
700       nb = 16;
701    } else {
702       nb = atoi(str_nb.c_str() );
703    }  
704    int mult;
705
706    if (nbitsR==16 && nb==8) // when LUT item size is different than pixel size
707       mult=2;               // high byte must be = low byte 
708    else                     // See PS 3.3-2003 C.11.1.1.2 p 619
709       mult=1; 
710  
711    // if we get a black image, let's just remove the '+1'
712    // from 'i*mult+1' and check again 
713    // if it works, we shall have to check the 3 Palettes
714    // to see which byte is ==0 (first one, or second one)
715    // and fix the code
716    // We give up the checking to avoid some (useless ?)overhead 
717    // (optimistic asumption)
718    unsigned char *a;      
719    int i;
720
721    a = LUTRGBA+0;
722    for(i=0;i<lengthR;i++) {
723       *a = lutR[i*mult+1]; 
724       a+=4;       
725    }        
726    a = LUTRGBA+1;
727    for(i=0;i<lengthG;i++) {
728       *a = lutG[i*mult+1]; 
729       a+=4;       
730    }  
731    a = LUTRGBA+2;
732    for(i=0;i<lengthB;i++) {
733       *a = lutB[i*mult+1]; 
734       a+=4;       
735    }  
736    a = LUTRGBA+3;
737    for(i=0;i<256;i++) {
738       *a = 1; // Alpha component
739       a+=4; 
740    } 
741    
742    //How to free the now useless LUTs?
743    //free(LutR); free(LutB); free(LutG); // Seg Fault when used
744    return(LUTRGBA);   
745
746
747 /**
748  * \ingroup gdcmHeader
749  * \brief gets the info from 0002,0010 : Transfert Syntax
750  * \      else 1.
751  * @return Transfert Syntax Name (as oposite to Transfert Syntax UID)
752  */
753 std::string gdcmHeader::GetTransfertSyntaxName(void) { 
754    // use the gdcmTS (TS : Transfert Syntax)
755    std::string TransfertSyntax = GetEntryByNumber(0x0002,0x0010);
756    if (TransfertSyntax == GDCM_UNFOUND) {
757       dbg.Verbose(0, "gdcmHeader::GetTransfertSyntaxName: unfound Transfert Syntax (0002,0010)");
758       return "Uncompressed ACR-NEMA";
759    }
760    // we do it only when we need it
761    gdcmTS * ts = gdcmGlobal::GetTS();
762    std::string tsName=ts->GetValue(TransfertSyntax);
763    //delete ts; // Seg Fault when deleted ?!
764    return tsName;
765 }
766
767 /**
768  * \ingroup   gdcmFile
769  * \brief Sets the Pixel Area size in the Header
770  *        --> not-for-rats function
771  * 
772  * \warning WARNING doit-etre etre publique ? 
773  * TODO : y aurait il un inconvenient à fusionner ces 2 fonctions
774  *
775  * @param ImageDataSize new Pixel Area Size
776  *        warning : nothing else is checked
777  */
778 void gdcmHeader::SetImageDataSize(size_t ImageDataSize) {
779    std::string content1;
780    char car[20];
781         
782    // Assumes HeaderEntry (GrPixel, NumPixel) is unique ...   
783    // TODO deal with multiplicity (see gdcmData/icone.dcm)      
784    sprintf(car,"%d",ImageDataSize);
785  
786    gdcmHeaderEntry *a = GetHeaderEntryByNumber(GrPixel, NumPixel);
787    a->SetLength(ImageDataSize);
788                 
789    ImageDataSize+=8;
790    sprintf(car,"%d",ImageDataSize);
791    content1=car;        
792    SetEntryByNumber(content1, GrPixel, NumPixel);
793 }
794
795 //-----------------------------------------------------------------------------
796 // Protected
797
798 //-----------------------------------------------------------------------------
799 // Private
800
801 //-----------------------------------------------------------------------------