2 //-----------------------------------------------------------------------------
5 #include <cctype> // for isalpha
8 #include "gdcmHeader.h"
9 #include "gdcmGlobal.h"
11 #include "gdcmDebug.h"
14 //-----------------------------------------------------------------------------
15 // Constructor / Destructor
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
25 gdcmHeader::gdcmHeader(const char *InFilename,
26 bool exception_on_error,
27 bool enable_sequences,
29 gdcmParser(InFilename,exception_on_error,enable_sequences,ignore_shadow)
33 guint32 totalSQlength;
34 guint32 alreadyParsedlength;
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.
47 // This IS the right place for the code
49 std::string ImageLocation = GetEntryByNumber(0x0028, 0x0200);
50 if ( ImageLocation == GDCM_UNFOUND ) { // Image Location
51 GrPixel = 0x7fe0; // default value
53 GrPixel = (guint16) atoi( ImageLocation.c_str() );
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.
64 TagKey key = gdcmDictEntry::TranslateToKey(GrPixel, NumPixel);
65 countGrPixel = GetEntry().count(key);
67 // we set the SQ Depth of each Header Entry
71 pileElem pile[100]; // Hope embedded sequence depth is no that long !
73 int currentParsedlength = 0;
74 int totalElementlength;
75 std::ostringstream tab;
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 .
82 for (ListTag::iterator i = listEntries.begin();
83 i != listEntries.end();
85 (*i)->SetSQDepthLevel(countSQ);
86 if ( (*i)->GetVR() == "SQ" && (*i)->GetReadLength() != 0) { // SQ found
90 std::cout << "Kaie ! Kaie! SQ Stack Overflow" << std::endl;
93 if (DEBUG) std::cout << "\n >>>>> empile niveau " << top
94 << "; Lgr SeQ: " << (*i)->GetReadLength()
97 pile[top].totalSQlength = (*i)->GetReadLength();
98 pile[top].alreadyParsedlength = 0;
99 currentParsedlength = 0;
101 } else { // non SQ found
103 if (countSQ != 0) { // we are 'inside a SeQuence'
104 if ( (*i)->GetGroup()==0xfffe && (*i)->GetElement()==0xe0dd){
105 // we just found 'end of SeQuence'
108 std::cout << "fffe,e0dd : depile" << std::endl;
109 currentParsedlength += 8; // gr:2 elem:2 vr:2 lgt:2
112 pile[top].alreadyParsedlength += currentParsedlength;
114 // we are on a 'standard' elem
115 // or a Zero-length SeQuence
117 totalElementlength = (*i)->GetFullLength();
118 currentParsedlength += totalElementlength;
119 pile[top].alreadyParsedlength += totalElementlength;
121 if (pile[top].totalSQlength == 0xffffffff) {
123 std::cout << "totalSeQlength == 0xffffffff"
127 std::cout << "alrdyPseLgt:"
128 << pile[top].alreadyParsedlength << " totSeQlgt: "
129 << pile[top].totalSQlength << " curPseLgt: "
130 << currentParsedlength
132 while (pile[top].alreadyParsedlength==pile[top].totalSQlength) {
135 std::cout << " \n<<<<<< On depile niveau " << top
136 << " \n" << std::endl;
137 (*i)->SetSQDepthLevel(countSQ);
138 currentParsedlength = pile[top].alreadyParsedlength;
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
148 std::cout << pile[top].alreadyParsedlength << " "
149 << pile[top].totalSQlength << " "
150 << currentParsedlength
154 currentParsedlength = 0;
160 } // end : 'inside a SeQuence'
163 for (int k=0; k<(*i)->GetSQDepthLevel();k++) {
166 (*i)->SetPrintLevel(2);
174 * @param exception_on_error whether we want to throw an exception or not
176 gdcmHeader::gdcmHeader(bool exception_on_error) :
177 gdcmParser(exception_on_error)
182 * \ingroup gdcmHeader
183 * \brief Canonical destructor.
185 gdcmHeader::~gdcmHeader (void) {
188 //-----------------------------------------------------------------------------
191 // see gdcmParser.cxx
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
200 void gdcmHeader::PrintEntryNoSQ(std::ostream & os) {
203 for (ListTag::iterator i = listEntries.begin();
204 i != listEntries.end();
207 depth= (*i)->GetSQDepthLevel();
208 if ( depth != 0 /*|| (*i)->GetVR() =="SQ" */){
211 (*i)->SetPrintLevel(printLevel);
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
224 void gdcmHeader::PrintEntryNiceSQ(std::ostream & os) {
225 std::ostringstream tab;
229 for (ListTag::iterator i = listEntries.begin();
230 i != listEntries.end();
232 depth= (*i)->GetSQDepthLevel();
234 for (int k=0;k<depth;k++)
237 (*i)->SetPrintLevel(printLevel);
243 //-----------------------------------------------------------------------------
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,
255 bool gdcmHeader::IsReadable(void) {
256 if(!gdcmParser::IsReadable()) {
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'
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.
279 bool gdcmHeader::IsJPEGBaseLineProcess1TransferSyntax(void) {
280 gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
283 LoadHeaderEntrySafe(Element);
285 std::string Transfer = Element->GetValue();
286 if ( Transfer == "1.2.840.10008.1.2.4.50" )
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.
297 bool gdcmHeader::IsJPEGExtendedProcess2_4TransferSyntax(void) {
298 gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
301 LoadHeaderEntrySafe(Element);
302 return ( Element->GetValue() == "1.2.840.10008.1.2.4.51" );
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.
311 bool gdcmHeader::IsJPEGExtendedProcess3_5TransferSyntax(void) {
312 gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
315 LoadHeaderEntrySafe(Element);
317 std::string Transfer = Element->GetValue();
318 if ( Transfer == "1.2.840.10008.1.2.4.52" )
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
330 bool gdcmHeader::IsJPEGSpectralSelectionProcess6_8TransferSyntax(void) {
331 gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
334 LoadHeaderEntrySafe(Element);
336 std::string Transfer = Element->GetValue();
337 if ( Transfer == "1.2.840.10008.1.2.4.53" )
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
349 bool gdcmHeader::IsRLELossLessTransferSyntax(void) {
350 gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
353 LoadHeaderEntrySafe(Element);
355 std::string Transfer = Element->GetValue();
356 if ( Transfer == "1.2.840.10008.1.2.5" ) {
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
369 bool gdcmHeader::IsJPEGLossless(void) {
370 gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
371 // faire qq chose d'intelligent a la place de ça
374 LoadHeaderEntrySafe(Element);
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;
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
391 bool gdcmHeader::IsJPEG2000(void) {
392 gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
395 LoadHeaderEntrySafe(Element);
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") )
405 * \ingroup gdcmHeader
406 * \brief Predicate for dicom version 3 file.
407 * @return True when the file is a dicom version 3.
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);
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
423 int gdcmHeader::GetXSize(void) {
425 StrSize = GetEntryByNumber(0x0028,0x0011);
426 if (StrSize == GDCM_UNFOUND)
428 return atoi(StrSize.c_str());
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).
438 int gdcmHeader::GetYSize(void) {
439 std::string StrSize = GetEntryByNumber(0x0028,0x0010);
440 if (StrSize != GDCM_UNFOUND)
441 return atoi(StrSize.c_str());
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:
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).
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());
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());
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 !
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());
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 !
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());
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)
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());
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.
524 int gdcmHeader::GetPlanarConfiguration(void) {
525 std::string StrSize = GetEntryByNumber(0x0028,0x0006);
526 if (StrSize == GDCM_UNFOUND)
528 return atoi(StrSize.c_str());
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
537 int gdcmHeader::GetPixelSize(void) {
538 std::string PixelType = GetPixelType();
539 if (PixelType == "8U" || PixelType == "8S")
541 if (PixelType == "16U" || PixelType == "16S")
543 if (PixelType == "32U" || PixelType == "32S")
545 if (PixelType == "FD")
547 dbg.Verbose(0, "gdcmHeader::GetPixelSize: Unknown pixel type");
552 * \ingroup gdcmHeader
553 * \brief Build the Pixel Type of the image.
554 * Possible values are:
555 * - 8U unsigned 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
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");
572 if (BitsAlloc == "64") // )
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)
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");
585 Signed = std::string("U");
587 Signed = std::string("S");
589 return( BitsAlloc + Signed);
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
599 size_t gdcmHeader::GetPixelOffset(void) {
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 ...
609 *std::string icone = GetEntryByNumber(0x0088,0x0200); //icone image sequence
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;
618 PixelElement = (++it.first)->second; // hope there are no more than 2 !
621 return PixelElement->GetOffset();
623 /* std::cout << "Big trouble : Pixel Element ("
624 << std::hex << GrPixel<<","<< NumPixel<< ") NOT found"
629 // TODO : unify those two (previous one and next one)
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.
638 size_t gdcmHeader::GetPixelAreaLength(void) {
640 IterHT it = GetHeaderEntrySameNumber(GrPixel,NumPixel);
641 TagKey key = gdcmDictEntry::TranslateToKey(GrPixel,NumPixel);
642 gdcmHeaderEntry* PixelElement;
645 PixelElement = (it.first)->second;
647 PixelElement = (++it.first)->second;
650 return PixelElement->GetLength();
652 /* std::cout << "Big trouble : Pixel Element ("
653 << std::hex << GrPixel<<","<< NumPixel<< ") NOT found"
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
669 bool gdcmHeader::HasLUT(void) {
671 // Check the presence of the LUT Descriptors, and LUT Tables
673 if ( !GetHeaderEntryByNumber(0x0028,0x1101) )
675 // LutDescriptorGreen
676 if ( !GetHeaderEntryByNumber(0x0028,0x1102) )
679 if ( !GetHeaderEntryByNumber(0x0028,0x1103) )
681 // Red Palette Color Lookup Table Data
682 if ( !GetHeaderEntryByNumber(0x0028,0x1201) )
684 // Green Palette Color Lookup Table Data
685 if ( !GetHeaderEntryByNumber(0x0028,0x1202) )
687 // Blue Palette Color Lookup Table Data
688 if ( !GetHeaderEntryByNumber(0x0028,0x1203) )
690 // FIXME : (0x0028,0x3006) : LUT Data (CTX dependent)
691 // NOT taken into account, but we don't know how to use it ...
696 * \ingroup gdcmHeader
697 * \brief gets the info from 0028,1101 : Lookup Table Desc-Red
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
703 int gdcmHeader::GetLUTNbits(void) {
704 std::vector<std::string> tokens;
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)
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());
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
738 unsigned char * gdcmHeader::GetLUTRGBA(void) {
740 // http://www.barre.nom.fr/medical/dicom2/limitations.html#Color%20Lookup%20Tables
742 // if Photometric Interpretation # PALETTE COLOR, no LUT to be done
743 if (GetEntryByNumber(0x0028,0x0004) != "PALETTE COLOR ") {
746 int lengthR, debR, nbitsR;
747 int lengthG, debG, nbitsG;
748 int lengthB, debB, nbitsB;
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)
755 std::string LutDescriptionG = GetEntryByNumber(0x0028,0x1102);
756 if (LutDescriptionG == GDCM_UNFOUND)
758 std::string LutDescriptionB = GetEntryByNumber(0x0028,0x1103);
759 if (LutDescriptionB == GDCM_UNFOUND)
762 std::vector<std::string> tokens;
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)
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)
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)
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);
793 if (!lutR || !lutG || !lutB ) {
796 // forge the 4 * 8 Bits Red/Green/Blue/Alpha LUT
798 unsigned char *LUTRGBA = new (unsigned char)[1024]; // 256 * 4 (R, G, B, Alpha)
802 memset(LUTRGBA, 0, 1024);
805 std::string str_nb = GetEntryByNumber(0x0028,0x0100);
806 if (str_nb == GDCM_UNFOUND ) {
809 nb = atoi(str_nb.c_str() );
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
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)
823 // We give up the checking to avoid some (useless ?)overhead
824 // (optimistic asumption)
829 for(i=0;i<lengthR;i++) {
834 for(i=0;i<lengthG;i++) {
839 for(i=0;i<lengthB;i++) {
845 *a = 1; // Alpha component
849 //How to free the now useless LUTs?
850 //free(LutR); free(LutB); free(LutG); // Seg Fault when used
855 * \ingroup gdcmHeader
856 * \brief gets the info from 0002,0010 : Transfert Syntax and gdcmTS
858 * @return the full Transfert Syntax Name (as oposite to Transfert Syntax UID)
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";
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 ?!
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
881 void gdcmHeader::SetImageDataSize(size_t ImageDataSize) {
882 std::string content1;
885 // Assumes HeaderEntry (GrPixel, NumPixel) is unique ...
886 //\todo deal with multiplicity (see gdcmData/icone.dcm)
887 sprintf(car,"%d",ImageDataSize);
889 gdcmHeaderEntry *a = GetHeaderEntryByNumber(GrPixel, NumPixel);
890 a->SetLength(ImageDataSize);
893 sprintf(car,"%d",ImageDataSize);
895 SetEntryByNumber(content1, GrPixel, NumPixel);
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 ?)
906 * @return true if 'smaller'
908 bool gdcmHeader::operator<(gdcmHeader &header){
912 s1=this->GetEntryByNumber(0x0010,0x0010);
913 s2=header.GetEntryByNumber(0x0010,0x0010);
921 s1=this->GetEntryByNumber(0x0010,0x0020);
922 s2=header.GetEntryByNumber(0x0010,0x0020);
929 // Study Instance UID
930 s1=this->GetEntryByNumber(0x0020,0x000d);
931 s2=header.GetEntryByNumber(0x0020,0x000d);
938 // Serie Instance UID
939 s1=this->GetEntryByNumber(0x0020,0x000e);
940 s2=header.GetEntryByNumber(0x0020,0x000e);
951 bool gdcmHeader::WriteEntry(gdcmHeaderEntry *tag, FILE *_fp,FileType type)
953 guint32 length = tag->GetLength();
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:
959 tag->SetValue(tag->GetValue()+"\0");
960 tag->SetLength(tag->GetReadLength()+1);
963 WriteEntryTagVRLength(tag, _fp, type);
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();
971 if ((group == GrPixel) && (el == NumPixel) ) {
973 if (compte == countGrPixel) {// we passed *all* the GrPixel,NumPixel
977 WriteEntryValue(tag, _fp, type);
981 //-----------------------------------------------------------------------------
985 * \ingroup gdcmHeader
986 * \brief anonymize a Header (removes Patient's personal info)
987 * (read the code to see which ones ...)
989 bool gdcmHeader::anonymizeHeader() {
991 gdcmHeaderEntry *patientNameHE = GetHeaderEntryByNumber (0x0010, 0x0010);
992 // gdcmHeaderEntry *patientIDHE = GetHeaderEntryByNumber (0x0010, 0x0020);
994 ReplaceIfExistByNumber (" ",0x0010, 0x2154); // Telephone
995 ReplaceIfExistByNumber (" ",0x0010, 0x1040); // Adress
996 ReplaceIfExistByNumber (" ",0x0010, 0x0020); // Patient ID
999 std::string StudyInstanceUID = GetEntryByNumber (0x0020, 0x000d);
1000 if (StudyInstanceUID !=GDCM_UNFOUND)
1001 ReplaceOrCreateByNumber(StudyInstanceUID, 0x0010, 0x0010);
1003 ReplaceOrCreateByNumber(std::string("anonymised"), 0x0010, 0x0010);
1007 // (if any) remove or replace all the stuff that contains a Date
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
1059 //-----------------------------------------------------------------------------
1062 //-----------------------------------------------------------------------------