2 //-----------------------------------------------------------------------------
3 #include "gdcmHeader.h"
4 #include "gdcmGlobal.h"
8 #include "gdcmValEntry.h"
12 //-----------------------------------------------------------------------------
13 // Constructor / Destructor
16 * @param InFilename name of the file whose header we want to analyze
17 * @param exception_on_error whether we want to throw an exception or not
18 * @param enable_sequences = true to allow the header
19 * to be parsed *inside* the SeQuences, when they have an actual length
20 * @param ignore_shadow = true if user wants to skip shadow groups
21 * during parsing, to save memory space
23 gdcmHeader::gdcmHeader(const char *InFilename,
24 bool exception_on_error,
25 bool enable_sequences,
27 gdcmDocument(InFilename,exception_on_error,enable_sequences,ignore_shadow)
31 guint32 totalSQlength;
32 guint32 alreadyParsedlength;
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.
45 // This IS the right place for the code
47 std::string ImageLocation = GetEntryByNumber(0x0028, 0x0200);
48 if ( ImageLocation == GDCM_UNFOUND ) { // Image Location
49 GrPixel = 0x7fe0; // default value
51 GrPixel = (guint16) atoi( ImageLocation.c_str() );
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.
66 * @param exception_on_error whether we want to throw an exception or not
68 gdcmHeader::gdcmHeader(bool exception_on_error) :
69 gdcmDocument(exception_on_error)
75 * \brief Canonical destructor.
77 gdcmHeader::~gdcmHeader (void) {
80 //-----------------------------------------------------------------------------
84 //-----------------------------------------------------------------------------
89 * \brief This predicate, based on hopefully reasonable heuristics,
90 * decides whether or not the current gdcmParser was properly parsed
91 * and contains the mandatory information for being considered as
92 * a well formed and usable Dicom/Acr File.
93 * @return true when gdcmParser is the one of a reasonable Dicom/Acr file,
96 bool gdcmHeader::IsReadable(void) {
97 if(!gdcmDocument::IsReadable()) {
100 std::string res = GetEntryByNumber(0x0028, 0x0005);
101 if ( res != GDCM_UNFOUND && atoi(res.c_str()) > 4 )
102 return false; // Image Dimensions
103 if ( !GetDocEntryByNumber(0x0028, 0x0100) )
104 return false; // "Bits Allocated"
105 if ( !GetDocEntryByNumber(0x0028, 0x0101) )
106 return false; // "Bits Stored"
107 if ( !GetDocEntryByNumber(0x0028, 0x0102) )
108 return false; // "High Bit"
109 if ( !GetDocEntryByNumber(0x0028, 0x0103) )
110 return false; // "Pixel Representation" i.e. 'Sign'
115 * \brief Retrieve the number of columns of image.
116 * @return The encountered size when found, 0 by default.
117 * 0 means the file is NOT USABLE. The caller will have to check
119 int gdcmHeader::GetXSize(void) {
121 StrSize = GetEntryByNumber(0x0028,0x0011);
122 if (StrSize == GDCM_UNFOUND)
124 return atoi(StrSize.c_str());
128 * \ingroup gdcmHeader
129 * \brief Retrieve the number of lines of image.
130 * \warning The defaulted value is 1 as opposed to gdcmHeader::GetXSize()
131 * @return The encountered size when found, 1 by default
132 * (The ACR-MEMA file contains a Signal, not an Image).
134 int gdcmHeader::GetYSize(void) {
135 std::string StrSize = GetEntryByNumber(0x0028,0x0010);
136 if (StrSize != GDCM_UNFOUND)
137 return atoi(StrSize.c_str());
141 // The Rows (0028,0010) entry was optional for ACR/NEMA. It might
142 // hence be a signal (1d image). So we default to 1:
147 * \ingroup gdcmHeader
148 * \brief Retrieve the number of planes of volume or the number
149 * of frames of a multiframe.
150 * \warning When present we consider the "Number of Frames" as the third
151 * dimension. When absent we consider the third dimension as
152 * being the ACR-NEMA "Planes" tag content.
153 * @return The encountered size when found, 1 by default (single image).
155 int gdcmHeader::GetZSize(void) {
156 // Both DicomV3 and ACR/Nema consider the "Number of Frames"
157 // as the third dimension.
158 std::string StrSize = GetEntryByNumber(0x0028,0x0008);
159 if (StrSize != GDCM_UNFOUND)
160 return atoi(StrSize.c_str());
162 // We then consider the "Planes" entry as the third dimension
163 StrSize = GetEntryByNumber(0x0028,0x0012);
164 if (StrSize != GDCM_UNFOUND)
165 return atoi(StrSize.c_str());
170 * \ingroup gdcmHeader
171 * \brief Retrieve the number of Bits Stored (actually used)
172 * (as opposite to number of Bits Allocated)
173 * @return The encountered number of Bits Stored, 0 by default.
174 * 0 means the file is NOT USABLE. The caller has to check it !
176 int gdcmHeader::GetBitsStored(void) {
177 std::string StrSize = GetEntryByNumber(0x0028,0x0101);
178 if (StrSize == GDCM_UNFOUND)
179 return 0; // It's supposed to be mandatory
180 // the caller will have to check
181 return atoi(StrSize.c_str());
185 * \ingroup gdcmHeader
186 * \brief Retrieve the number of Bits Allocated
187 * (8, 12 -compacted ACR-NEMA files, 16, ...)
188 * @return The encountered number of Bits Allocated, 0 by default.
189 * 0 means the file is NOT USABLE. The caller has to check it !
191 int gdcmHeader::GetBitsAllocated(void) {
192 std::string StrSize = GetEntryByNumber(0x0028,0x0100);
193 if (StrSize == GDCM_UNFOUND)
194 return 0; // It's supposed to be mandatory
195 // the caller will have to check
196 return atoi(StrSize.c_str());
200 * \ingroup gdcmHeader
201 * \brief Retrieve the number of Samples Per Pixel
202 * (1 : gray level, 3 : RGB -1 or 3 Planes-)
203 * @return The encountered number of Samples Per Pixel, 1 by default.
204 * (Gray level Pixels)
206 int gdcmHeader::GetSamplesPerPixel(void) {
207 std::string StrSize = GetEntryByNumber(0x0028,0x0002);
208 if (StrSize == GDCM_UNFOUND)
209 return 1; // Well, it's supposed to be mandatory ...
210 // but sometimes it's missing : *we* assume Gray pixels
211 return atoi(StrSize.c_str());
215 * \ingroup gdcmHeader
216 * \brief Retrieve the Planar Configuration for RGB images
217 * (0 : RGB Pixels , 1 : R Plane + G Plane + B Plane)
218 * @return The encountered Planar Configuration, 0 by default.
220 int gdcmHeader::GetPlanarConfiguration(void) {
221 std::string StrSize = GetEntryByNumber(0x0028,0x0006);
222 if (StrSize == GDCM_UNFOUND)
224 return atoi(StrSize.c_str());
228 * \ingroup gdcmHeader
229 * \brief Return the size (in bytes) of a single pixel of data.
230 * @return The size in bytes of a single pixel of data; 0 by default
231 * 0 means the file is NOT USABLE; the caller will have to check
233 int gdcmHeader::GetPixelSize(void) {
234 std::string PixelType = GetPixelType();
235 if (PixelType == "8U" || PixelType == "8S")
237 if (PixelType == "16U" || PixelType == "16S")
239 if (PixelType == "32U" || PixelType == "32S")
241 if (PixelType == "FD")
243 dbg.Verbose(0, "gdcmHeader::GetPixelSize: Unknown pixel type");
248 * \ingroup gdcmHeader
249 * \brief Build the Pixel Type of the image.
250 * Possible values are:
251 * - 8U unsigned 8 bit,
253 * - 16U unsigned 16 bit,
254 * - 16S signed 16 bit,
255 * - 32U unsigned 32 bit,
256 * - 32S signed 32 bit,
257 * - FD floating double 64 bits (Not kosher DICOM, but so usefull!)
258 * \warning 12 bit images appear as 16 bit.
259 * 24 bit images appear as 8 bit
260 * @return 0S if nothing found. NOT USABLE file. The caller has to check
262 std::string gdcmHeader::GetPixelType(void) {
263 std::string BitsAlloc = GetEntryByNumber(0x0028, 0x0100); // Bits Allocated
264 if (BitsAlloc == GDCM_UNFOUND) {
265 dbg.Verbose(0, "gdcmHeader::GetPixelType: unfound Bits Allocated");
266 BitsAlloc = std::string("16");
268 if (BitsAlloc == "64") // )
270 if (BitsAlloc == "12") // It will be unpacked
271 BitsAlloc = std::string("16");
272 else if (BitsAlloc == "24") // (in order no to be messed up
273 BitsAlloc = std::string("8"); // by old RGB images)
275 std::string Signed = GetEntryByNumber(0x0028, 0x0103); // "Pixel Representation"
276 if (Signed == GDCM_UNFOUND) {
277 dbg.Verbose(0, "gdcmHeader::GetPixelType: unfound Pixel Representation");
278 BitsAlloc = std::string("0");
281 Signed = std::string("U");
283 Signed = std::string("S");
285 return( BitsAlloc + Signed);
290 * \ingroup gdcmHeader
291 * \brief Recover the offset (from the beginning of the file)
292 * of *image* pixels (not *icone image* pixels, if any !)
293 * @return Pixel Offset
295 size_t gdcmHeader::GetPixelOffset(void) {
297 gdcmDocEntry* PixelElement = GetDocEntryByNumber(GrPixel,NumPixel);
300 return PixelElement->GetOffset();
303 std::cout << "Big trouble : Pixel Element ("
304 << std::hex << GrPixel<<","<< NumPixel<< ") NOT found"
310 // TODO : unify those two (previous one and next one)
312 * \ingroup gdcmHeader
313 * \brief Recover the pixel area length (in Bytes)
314 * @return Pixel Element Length, as stored in the header
315 * (NOT the memory space necessary to hold the Pixels
316 * -in case of embeded compressed image-)
317 * 0 : NOT USABLE file. The caller has to check.
319 size_t gdcmHeader::GetPixelAreaLength(void) {
321 gdcmDocEntry* PixelElement = GetDocEntryByNumber(GrPixel,NumPixel);
324 return PixelElement->GetLength();
327 std::cout << "Big trouble : Pixel Element ("
328 << std::hex << GrPixel<<","<< NumPixel<< ") NOT found"
336 * \ingroup gdcmHeader
337 * \brief tells us if LUT are used
338 * \warning Right now, 'Segmented xxx Palette Color Lookup Table Data'
339 * are NOT considered as LUT, since nobody knows
340 * how to deal with them
341 * Please warn me if you know sbdy that *does* know ... jprx
342 * @return true if LUT Descriptors and LUT Tables were found
344 bool gdcmHeader::HasLUT(void) {
346 // Check the presence of the LUT Descriptors, and LUT Tables
348 if ( !GetDocEntryByNumber(0x0028,0x1101) )
350 // LutDescriptorGreen
351 if ( !GetDocEntryByNumber(0x0028,0x1102) )
354 if ( !GetDocEntryByNumber(0x0028,0x1103) )
356 // Red Palette Color Lookup Table Data
357 if ( !GetDocEntryByNumber(0x0028,0x1201) )
359 // Green Palette Color Lookup Table Data
360 if ( !GetDocEntryByNumber(0x0028,0x1202) )
362 // Blue Palette Color Lookup Table Data
363 if ( !GetDocEntryByNumber(0x0028,0x1203) )
365 // FIXME : (0x0028,0x3006) : LUT Data (CTX dependent)
366 // NOT taken into account, but we don't know how to use it ...
371 * \ingroup gdcmHeader
372 * \brief gets the info from 0028,1101 : Lookup Table Desc-Red
374 * @return Lookup Table number of Bits , 0 by default
375 * when (0028,0004),Photometric Interpretation = [PALETTE COLOR ]
376 * @ return bit number of each LUT item
378 int gdcmHeader::GetLUTNbits(void) {
379 std::vector<std::string> tokens;
383 //Just hope Lookup Table Desc-Red = Lookup Table Desc-Red = Lookup Table Desc-Blue
384 // Consistency already checked in GetLUTLength
385 std::string LutDescription = GetEntryByNumber(0x0028,0x1101);
386 if (LutDescription == GDCM_UNFOUND)
388 tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
389 Tokenize (LutDescription, tokens, "\\");
390 //LutLength=atoi(tokens[0].c_str());
391 //LutDepth=atoi(tokens[1].c_str());
392 LutNbits=atoi(tokens[2].c_str());
398 * \ingroup gdcmHeader
399 * \brief builts Red/Green/Blue/Alpha LUT from Header
400 * when (0028,0004),Photometric Interpretation = [PALETTE COLOR ]
401 * and (0028,1101),(0028,1102),(0028,1102)
402 * - xxx Palette Color Lookup Table Descriptor - are found
403 * and (0028,1201),(0028,1202),(0028,1202)
404 * - xxx Palette Color Lookup Table Data - are found
405 * \warning does NOT deal with :
406 * 0028 1100 Gray Lookup Table Descriptor (Retired)
407 * 0028 1221 Segmented Red Palette Color Lookup Table Data
408 * 0028 1222 Segmented Green Palette Color Lookup Table Data
409 * 0028 1223 Segmented Blue Palette Color Lookup Table Data
410 * no known Dicom reader deals with them :-(
411 * @return a RGBA Lookup Table
413 unsigned char * gdcmHeader::GetLUTRGBA(void) {
415 // http://www.barre.nom.fr/medical/dicom2/limitations.html#Color%20Lookup%20Tables
417 // if Photometric Interpretation # PALETTE COLOR, no LUT to be done
418 if (GetEntryByNumber(0x0028,0x0004) != "PALETTE COLOR ") {
421 int lengthR, debR, nbitsR;
422 int lengthG, debG, nbitsG;
423 int lengthB, debB, nbitsB;
425 // Get info from Lut Descriptors
426 // (the 3 LUT descriptors may be different)
427 std::string LutDescriptionR = GetEntryByNumber(0x0028,0x1101);
428 if (LutDescriptionR == GDCM_UNFOUND)
430 std::string LutDescriptionG = GetEntryByNumber(0x0028,0x1102);
431 if (LutDescriptionG == GDCM_UNFOUND)
433 std::string LutDescriptionB = GetEntryByNumber(0x0028,0x1103);
434 if (LutDescriptionB == GDCM_UNFOUND)
437 std::vector<std::string> tokens;
439 tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
440 Tokenize (LutDescriptionR, tokens, "\\");
441 lengthR=atoi(tokens[0].c_str()); // Red LUT length in Bytes
442 debR =atoi(tokens[1].c_str()); // subscript of the first Lut Value
443 nbitsR =atoi(tokens[2].c_str()); // Lut item size (in Bits)
446 tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
447 Tokenize (LutDescriptionG, tokens, "\\");
448 lengthG=atoi(tokens[0].c_str()); // Green LUT length in Bytes
449 debG =atoi(tokens[1].c_str()); // subscript of the first Lut Value
450 nbitsG =atoi(tokens[2].c_str()); // Lut item size (in Bits)
453 tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
454 Tokenize (LutDescriptionB, tokens, "\\");
455 lengthB=atoi(tokens[0].c_str()); // Blue LUT length in Bytes
456 debB =atoi(tokens[1].c_str()); // subscript of the first Lut Value
457 nbitsB =atoi(tokens[2].c_str()); // Lut item size (in Bits)
460 // Load LUTs into memory, (as they were stored on disk)
461 unsigned char *lutR = (unsigned char *)
462 GetEntryVoidAreaByNumber(0x0028,0x1201);
463 unsigned char *lutG = (unsigned char *)
464 GetEntryVoidAreaByNumber(0x0028,0x1202);
465 unsigned char *lutB = (unsigned char *)
466 GetEntryVoidAreaByNumber(0x0028,0x1203);
468 if (!lutR || !lutG || !lutB ) {
471 // forge the 4 * 8 Bits Red/Green/Blue/Alpha LUT
473 unsigned char *LUTRGBA = new unsigned char[1024]; // 256 * 4 (R, G, B, Alpha)
477 memset(LUTRGBA, 0, 1024);
480 std::string str_nb = GetEntryByNumber(0x0028,0x0100);
481 if (str_nb == GDCM_UNFOUND ) {
484 nb = atoi(str_nb.c_str() );
488 if (nbitsR==16 && nb==8) // when LUT item size is different than pixel size
489 mult=2; // high byte must be = low byte
490 else // See PS 3.3-2003 C.11.1.1.2 p 619
493 // if we get a black image, let's just remove the '+1'
494 // from 'i*mult+1' and check again
495 // if it works, we shall have to check the 3 Palettes
496 // to see which byte is ==0 (first one, or second one)
498 // We give up the checking to avoid some (useless ?)overhead
499 // (optimistic asumption)
504 for(i=0;i<lengthR;i++) {
509 for(i=0;i<lengthG;i++) {
514 for(i=0;i<lengthB;i++) {
520 *a = 1; // Alpha component
524 //How to free the now useless LUTs?
525 //free(LutR); free(LutB); free(LutG); // Seg Fault when used
530 * \brief Accesses the info from 0002,0010 : Transfert Syntax and gdcmTS
532 * @return The full Transfert Syntax Name (as opposed to Transfert Syntax UID)
534 std::string gdcmHeader::GetTransfertSyntaxName(void) {
535 // use the gdcmTS (TS : Transfert Syntax)
536 std::string TransfertSyntax = GetEntryByNumber(0x0002,0x0010);
537 if (TransfertSyntax == GDCM_UNFOUND) {
538 dbg.Verbose(0, "gdcmHeader::GetTransfertSyntaxName:"
539 " unfound Transfert Syntax (0002,0010)");
540 return "Uncompressed ACR-NEMA";
542 // we do it only when we need it
543 gdcmTS * ts = gdcmGlobal::GetTS();
544 std::string tsName=ts->GetValue(TransfertSyntax);
545 //delete ts; /// \todo Seg Fault when deleted ?!
550 * \brief Sets the Pixel Area size in the Header
551 * --> not-for-rats function
552 * @param ImageDataSize new Pixel Area Size
553 * warning : nothing else is checked
555 void gdcmHeader::SetImageDataSize(size_t ImageDataSize) {
556 std::string content1;
559 sprintf(car,"%d",ImageDataSize);
561 gdcmDocEntry *a = GetDocEntryByNumber(GrPixel, NumPixel);
562 a->SetLength(ImageDataSize);
565 sprintf(car,"%d",ImageDataSize);
567 SetEntryByNumber(content1, GrPixel, NumPixel);
570 //-----------------------------------------------------------------------------
574 * \brief anonymize a Header (removes Patient's personal info)
575 * (read the code to see which ones ...)
577 bool gdcmHeader::anonymizeHeader() {
579 gdcmDocEntry *patientNameHE = GetDocEntryByNumber (0x0010, 0x0010);
581 ReplaceIfExistByNumber (" ",0x0010, 0x2154); // Telephone
582 ReplaceIfExistByNumber (" ",0x0010, 0x1040); // Adress
583 ReplaceIfExistByNumber (" ",0x0010, 0x0020); // Patient ID
586 std::string StudyInstanceUID = GetEntryByNumber (0x0020, 0x000d);
587 if (StudyInstanceUID !=GDCM_UNFOUND)
588 ReplaceOrCreateByNumber(StudyInstanceUID, 0x0010, 0x0010);
590 ReplaceOrCreateByNumber(std::string("anonymised"), 0x0010, 0x0010);
594 // (if any) remove or replace all the stuff that contains a Date
596 //0008 0012 DA ID Instance Creation Date
597 //0008 0020 DA ID Study Date
598 //0008 0021 DA ID Series Date
599 //0008 0022 DA ID Acquisition Date
600 //0008 0023 DA ID Content Date
601 //0008 0024 DA ID Overlay Date
602 //0008 0025 DA ID Curve Date
603 //0008 002a DT ID Acquisition Datetime
604 //0018 9074 DT ACQ Frame Acquisition Datetime
605 //0018 9151 DT ACQ Frame Reference Datetime
606 //0018 a002 DT ACQ Contribution Date Time
607 //0020 3403 SH REL Modified Image Date (RET)
608 //0032 0032 DA SDY Study Verified Date
609 //0032 0034 DA SDY Study Read Date
610 //0032 1000 DA SDY Scheduled Study Start Date
611 //0032 1010 DA SDY Scheduled Study Stop Date
612 //0032 1040 DA SDY Study Arrival Date
613 //0032 1050 DA SDY Study Completion Date
614 //0038 001a DA VIS Scheduled Admission Date
615 //0038 001c DA VIS Scheduled Discharge Date
616 //0038 0020 DA VIS Admitting Date
617 //0038 0030 DA VIS Discharge Date
618 //0040 0002 DA PRC Scheduled Procedure Step Start Date
619 //0040 0004 DA PRC Scheduled Procedure Step End Date
620 //0040 0244 DA PRC Performed Procedure Step Start Date
621 //0040 0250 DA PRC Performed Procedure Step End Date
622 //0040 2004 DA PRC Issue Date of Imaging Service Request
623 //0040 4005 DT PRC Scheduled Procedure Step Start Date and Time
624 //0040 4011 DT PRC Expected Completion Date and Time
625 //0040 a030 DT PRC Verification Date Time
626 //0040 a032 DT PRC Observation Date Time
627 //0040 a120 DT PRC DateTime
628 //0040 a121 DA PRC Date
629 //0040 a13a DT PRC Referenced Datetime
630 //0070 0082 DA ??? Presentation Creation Date
631 //0100 0420 DT ??? SOP Autorization Date and Time
632 //0400 0105 DT ??? Digital Signature DateTime
633 //2100 0040 DA PJ Creation Date
634 //3006 0008 DA SSET Structure Set Date
635 //3008 0024 DA ??? Treatment Control Point Date
636 //3008 0054 DA ??? First Treatment Date
637 //3008 0056 DA ??? Most Recent Treatment Date
638 //3008 0162 DA ??? Safe Position Exit Date
639 //3008 0166 DA ??? Safe Position Return Date
640 //3008 0250 DA ??? Treatment Date
641 //300a 0006 DA RT RT Plan Date
642 //300a 022c DA RT Air Kerma Rate Reference Date
643 //300e 0004 DA RT Review Date
646 //-----------------------------------------------------------------------------
649 //-----------------------------------------------------------------------------