2 //-----------------------------------------------------------------------------
3 #include "gdcmHeader.h"
4 #include "gdcmGlobal.h"
11 //-----------------------------------------------------------------------------
12 // Constructor / Destructor
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
22 gdcmHeader::gdcmHeader(const char *InFilename,
23 bool exception_on_error,
24 bool enable_sequences,
26 gdcmParser(InFilename,exception_on_error,enable_sequences,ignore_shadow)
30 guint32 totalSQlength;
31 guint32 alreadyParsedlength;
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.
44 // This IS the right place for the code
46 std::string ImageLocation = GetEntryByNumber(0x0028, 0x0200);
47 if ( ImageLocation == GDCM_UNFOUND ) { // Image Location
48 GrPixel = 0x7fe0; // default value
50 GrPixel = (guint16) atoi( ImageLocation.c_str() );
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.
61 TagKey key = gdcmDictEntry::TranslateToKey(GrPixel, NumPixel);
62 countGrPixel = GetEntry().count(key);
64 // we set the SQ Depth of each Header Entry
68 pileElem pile[100]; // Hope embedded sequence depth is no that long !
70 int currentParsedlength = 0;
71 int totalElementlength;
72 std::ostringstream tab;
76 // Sorry; Dealing with e-film breaker images
77 // will (certainly) cause a lot of troubles ...
78 // I prefer keeping my 'trace' on .
80 for (ListTag::iterator i = listEntries.begin();
81 i != listEntries.end();
83 (*i)->SetSQDepthLevel(countSQ);
84 if ( (*i)->GetVR() == "SQ" && (*i)->GetReadLength() != 0) { // SQ found
89 std::cout << "Kaie ! Kaie! SQ Stack Overflow" << std::endl;
94 std::cout << "\n >>>>> empile niveau " << top
95 << "; Lgr SeQ: " << (*i)->GetReadLength()
99 pile[top].totalSQlength = (*i)->GetReadLength();
100 pile[top].alreadyParsedlength = 0;
101 currentParsedlength = 0;
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'
109 std::cout << "fffe,e0dd : depile" << std::endl;
112 currentParsedlength += 8; // gr:2 elem:2 vr:2 lgt:2
115 pile[top].alreadyParsedlength += currentParsedlength;
117 // we are on a 'standard' elem
118 // or a Zero-length SeQuence
120 totalElementlength = (*i)->GetFullLength();
121 currentParsedlength += totalElementlength;
122 pile[top].alreadyParsedlength += totalElementlength;
124 if (pile[top].totalSQlength == 0xffffffff) {
126 std::cout << "totalSeQlength == 0xffffffff" << std::endl;
130 std::cout << "alrdyPseLgt:"
131 << pile[top].alreadyParsedlength << " totSeQlgt: "
132 << pile[top].totalSQlength << " curPseLgt: "
133 << currentParsedlength
136 while (pile[top].alreadyParsedlength==pile[top].totalSQlength) {
138 std::cout << " \n<<<<<< On depile niveau " << top
139 << " \n" << std::endl;
141 (*i)->SetSQDepthLevel(countSQ);
142 currentParsedlength = pile[top].alreadyParsedlength;
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
151 std::cout << pile[top].alreadyParsedlength << " "
152 << pile[top].totalSQlength << " "
153 << currentParsedlength
158 currentParsedlength = 0;
164 } // end : 'inside a SeQuence'
167 for (int k=0; k<(*i)->GetSQDepthLevel();k++) {
170 (*i)->SetPrintLevel(2);
178 * @param exception_on_error whether we want to throw an exception or not
180 gdcmHeader::gdcmHeader(bool exception_on_error) :
181 gdcmParser(exception_on_error)
186 * \ingroup gdcmHeader
187 * \brief Canonical destructor.
189 gdcmHeader::~gdcmHeader (void) {
192 //-----------------------------------------------------------------------------
195 // see gdcmParser.cxx
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
204 void gdcmHeader::PrintEntryNoSQ(std::ostream & os) {
207 for (ListTag::iterator i = listEntries.begin();
208 i != listEntries.end();
211 depth= (*i)->GetSQDepthLevel();
212 if ( depth != 0 /*|| (*i)->GetVR() =="SQ" */){
215 (*i)->SetPrintLevel(printLevel);
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
228 void gdcmHeader::PrintEntryNiceSQ(std::ostream & os) {
229 std::ostringstream tab;
233 for (ListTag::iterator i = listEntries.begin();
234 i != listEntries.end();
236 depth= (*i)->GetSQDepthLevel();
238 for (int k=0;k<depth;k++)
241 (*i)->SetPrintLevel(printLevel);
247 //-----------------------------------------------------------------------------
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,
259 bool gdcmHeader::IsReadable(void) {
260 if(!gdcmParser::IsReadable()) {
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'
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.
283 bool gdcmHeader::IsJPEGBaseLineProcess1TransferSyntax(void) {
284 gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
287 LoadHeaderEntrySafe(Element);
289 std::string Transfer = Element->GetValue();
290 if ( Transfer == "1.2.840.10008.1.2.4.50" )
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.
301 bool gdcmHeader::IsJPEGExtendedProcess2_4TransferSyntax(void) {
302 gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
305 LoadHeaderEntrySafe(Element);
306 return ( Element->GetValue() == "1.2.840.10008.1.2.4.51" );
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.
315 bool gdcmHeader::IsJPEGExtendedProcess3_5TransferSyntax(void) {
316 gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
319 LoadHeaderEntrySafe(Element);
321 std::string Transfer = Element->GetValue();
322 if ( Transfer == "1.2.840.10008.1.2.4.52" )
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
334 bool gdcmHeader::IsJPEGSpectralSelectionProcess6_8TransferSyntax(void) {
335 gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
338 LoadHeaderEntrySafe(Element);
340 std::string Transfer = Element->GetValue();
341 if ( Transfer == "1.2.840.10008.1.2.4.53" )
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
353 bool gdcmHeader::IsRLELossLessTransferSyntax(void) {
354 gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
357 LoadHeaderEntrySafe(Element);
359 std::string Transfer = Element->GetValue();
360 if ( Transfer == "1.2.840.10008.1.2.5" ) {
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
373 bool gdcmHeader::IsJPEGLossless(void) {
374 gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
375 // faire qq chose d'intelligent a la place de ça
378 LoadHeaderEntrySafe(Element);
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;
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
395 bool gdcmHeader::IsJPEG2000(void) {
396 gdcmHeaderEntry* Element = GetHeaderEntryByNumber(0x0002, 0x0010);
399 LoadHeaderEntrySafe(Element);
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") )
409 * \ingroup gdcmHeader
410 * \brief Predicate for dicom version 3 file.
411 * @return True when the file is a dicom version 3.
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);
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
427 int gdcmHeader::GetXSize(void) {
429 StrSize = GetEntryByNumber(0x0028,0x0011);
430 if (StrSize == GDCM_UNFOUND)
432 return atoi(StrSize.c_str());
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).
442 int gdcmHeader::GetYSize(void) {
443 std::string StrSize = GetEntryByNumber(0x0028,0x0010);
444 if (StrSize != GDCM_UNFOUND)
445 return atoi(StrSize.c_str());
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:
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).
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());
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());
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 !
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());
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 !
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());
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)
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());
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.
528 int gdcmHeader::GetPlanarConfiguration(void) {
529 std::string StrSize = GetEntryByNumber(0x0028,0x0006);
530 if (StrSize == GDCM_UNFOUND)
532 return atoi(StrSize.c_str());
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
541 int gdcmHeader::GetPixelSize(void) {
542 std::string PixelType = GetPixelType();
543 if (PixelType == "8U" || PixelType == "8S")
545 if (PixelType == "16U" || PixelType == "16S")
547 if (PixelType == "32U" || PixelType == "32S")
549 if (PixelType == "FD")
551 dbg.Verbose(0, "gdcmHeader::GetPixelSize: Unknown pixel type");
556 * \ingroup gdcmHeader
557 * \brief Build the Pixel Type of the image.
558 * Possible values are:
559 * - 8U unsigned 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
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");
576 if (BitsAlloc == "64") // )
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)
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");
589 Signed = std::string("U");
591 Signed = std::string("S");
593 return( BitsAlloc + Signed);
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
603 size_t gdcmHeader::GetPixelOffset(void) {
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 ...
613 *std::string icone = GetEntryByNumber(0x0088,0x0200); //icone image sequence
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;
622 PixelElement = (++it.first)->second; // hope there are no more than 2 !
625 return PixelElement->GetOffset();
628 std::cout << "Big trouble : Pixel Element ("
629 << std::hex << GrPixel<<","<< NumPixel<< ") NOT found"
635 // TODO : unify those two (previous one and next one)
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.
644 size_t gdcmHeader::GetPixelAreaLength(void) {
646 IterHT it = GetHeaderEntrySameNumber(GrPixel,NumPixel);
647 TagKey key = gdcmDictEntry::TranslateToKey(GrPixel,NumPixel);
648 gdcmHeaderEntry* PixelElement;
651 PixelElement = (it.first)->second;
653 PixelElement = (++it.first)->second;
656 return PixelElement->GetLength();
659 std::cout << "Big trouble : Pixel Element ("
660 << std::hex << GrPixel<<","<< NumPixel<< ") NOT found"
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
676 bool gdcmHeader::HasLUT(void) {
678 // Check the presence of the LUT Descriptors, and LUT Tables
680 if ( !GetHeaderEntryByNumber(0x0028,0x1101) )
682 // LutDescriptorGreen
683 if ( !GetHeaderEntryByNumber(0x0028,0x1102) )
686 if ( !GetHeaderEntryByNumber(0x0028,0x1103) )
688 // Red Palette Color Lookup Table Data
689 if ( !GetHeaderEntryByNumber(0x0028,0x1201) )
691 // Green Palette Color Lookup Table Data
692 if ( !GetHeaderEntryByNumber(0x0028,0x1202) )
694 // Blue Palette Color Lookup Table Data
695 if ( !GetHeaderEntryByNumber(0x0028,0x1203) )
697 // FIXME : (0x0028,0x3006) : LUT Data (CTX dependent)
698 // NOT taken into account, but we don't know how to use it ...
703 * \ingroup gdcmHeader
704 * \brief gets the info from 0028,1101 : Lookup Table Desc-Red
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
710 int gdcmHeader::GetLUTNbits(void) {
711 std::vector<std::string> tokens;
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)
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());
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
745 unsigned char * gdcmHeader::GetLUTRGBA(void) {
747 // http://www.barre.nom.fr/medical/dicom2/limitations.html#Color%20Lookup%20Tables
749 // if Photometric Interpretation # PALETTE COLOR, no LUT to be done
750 if (GetEntryByNumber(0x0028,0x0004) != "PALETTE COLOR ") {
753 int lengthR, debR, nbitsR;
754 int lengthG, debG, nbitsG;
755 int lengthB, debB, nbitsB;
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)
762 std::string LutDescriptionG = GetEntryByNumber(0x0028,0x1102);
763 if (LutDescriptionG == GDCM_UNFOUND)
765 std::string LutDescriptionB = GetEntryByNumber(0x0028,0x1103);
766 if (LutDescriptionB == GDCM_UNFOUND)
769 std::vector<std::string> tokens;
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)
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)
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)
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);
800 if (!lutR || !lutG || !lutB ) {
803 // forge the 4 * 8 Bits Red/Green/Blue/Alpha LUT
805 unsigned char *LUTRGBA = new (unsigned char)[1024]; // 256 * 4 (R, G, B, Alpha)
809 memset(LUTRGBA, 0, 1024);
812 std::string str_nb = GetEntryByNumber(0x0028,0x0100);
813 if (str_nb == GDCM_UNFOUND ) {
816 nb = atoi(str_nb.c_str() );
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
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)
830 // We give up the checking to avoid some (useless ?)overhead
831 // (optimistic asumption)
836 for(i=0;i<lengthR;i++) {
841 for(i=0;i<lengthG;i++) {
846 for(i=0;i<lengthB;i++) {
852 *a = 1; // Alpha component
856 //How to free the now useless LUTs?
857 //free(LutR); free(LutB); free(LutG); // Seg Fault when used
862 * \ingroup gdcmHeader
863 * \brief gets the info from 0002,0010 : Transfert Syntax and gdcmTS
865 * @return the full Transfert Syntax Name (as oposite to Transfert Syntax UID)
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";
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 ?!
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
888 void gdcmHeader::SetImageDataSize(size_t ImageDataSize) {
889 std::string content1;
892 // Assumes HeaderEntry (GrPixel, NumPixel) is unique ...
893 //\todo deal with multiplicity (see gdcmData/icone.dcm)
894 sprintf(car,"%d",ImageDataSize);
896 gdcmHeaderEntry *a = GetHeaderEntryByNumber(GrPixel, NumPixel);
897 a->SetLength(ImageDataSize);
900 sprintf(car,"%d",ImageDataSize);
902 SetEntryByNumber(content1, GrPixel, NumPixel);
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 ?)
913 * @return true if 'smaller'
915 bool gdcmHeader::operator<(gdcmHeader &header){
919 s1=this->GetEntryByNumber(0x0010,0x0010);
920 s2=header.GetEntryByNumber(0x0010,0x0010);
928 s1=this->GetEntryByNumber(0x0010,0x0020);
929 s2=header.GetEntryByNumber(0x0010,0x0020);
936 // Study Instance UID
937 s1=this->GetEntryByNumber(0x0020,0x000d);
938 s2=header.GetEntryByNumber(0x0020,0x000d);
945 // Serie Instance UID
946 s1=this->GetEntryByNumber(0x0020,0x000e);
947 s2=header.GetEntryByNumber(0x0020,0x000e);
958 bool gdcmHeader::WriteEntry(gdcmHeaderEntry *tag, FILE *_fp,FileType type)
960 guint32 length = tag->GetLength();
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:
966 tag->SetValue(tag->GetValue()+"\0");
967 tag->SetLength(tag->GetReadLength()+1);
970 WriteEntryTagVRLength(tag, _fp, type);
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();
978 if ((group == GrPixel) && (el == NumPixel) ) {
980 if (compte == countGrPixel) {// we passed *all* the GrPixel,NumPixel
984 WriteEntryValue(tag, _fp, type);
988 //-----------------------------------------------------------------------------
992 * \ingroup gdcmHeader
993 * \brief anonymize a Header (removes Patient's personal info)
994 * (read the code to see which ones ...)
996 bool gdcmHeader::anonymizeHeader() {
998 gdcmHeaderEntry *patientNameHE = GetHeaderEntryByNumber (0x0010, 0x0010);
999 // gdcmHeaderEntry *patientIDHE = GetHeaderEntryByNumber (0x0010, 0x0020);
1001 ReplaceIfExistByNumber (" ",0x0010, 0x2154); // Telephone
1002 ReplaceIfExistByNumber (" ",0x0010, 0x1040); // Adress
1003 ReplaceIfExistByNumber (" ",0x0010, 0x0020); // Patient ID
1005 if (patientNameHE) {
1006 std::string StudyInstanceUID = GetEntryByNumber (0x0020, 0x000d);
1007 if (StudyInstanceUID !=GDCM_UNFOUND)
1008 ReplaceOrCreateByNumber(StudyInstanceUID, 0x0010, 0x0010);
1010 ReplaceOrCreateByNumber(std::string("anonymised"), 0x0010, 0x0010);
1014 // (if any) remove or replace all the stuff that contains a Date
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
1066 //-----------------------------------------------------------------------------
1069 //-----------------------------------------------------------------------------