X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=src%2FgdcmParser.cxx;h=871c8086f4b78cfcf61dd3f1380ba04d0343983d;hb=c38654db595b51f0295f607c51172dae56d1d65d;hp=2090c155173db2500d045507b7a2027b819fc5c5;hpb=22f785a2627d1e7823701193bd7624739a3e033b;p=gdcm.git diff --git a/src/gdcmParser.cxx b/src/gdcmParser.cxx index 2090c155..871c8086 100644 --- a/src/gdcmParser.cxx +++ b/src/gdcmParser.cxx @@ -1,8 +1,7 @@ // gdcmParser.cxx //----------------------------------------------------------------------------- -#include "gdcmParser.h" -#include "gdcmUtil.h" #include +#include // For nthos: #ifdef _MSC_VER @@ -19,6 +18,10 @@ #endif # include +#include "gdcmParser.h" +#include "gdcmUtil.h" +#include "gdcmDebug.h" + #define UI1_2_840_10008_1_2 "1.2.840.10008.1.2" #define UI1_2_840_10008_1_2_1 "1.2.840.10008.1.2.1" #define UI1_2_840_10008_1_2_2 "1.2.840.10008.1.2.2" @@ -65,8 +68,8 @@ // // Other usefull abreviations : - //Radiographic view associated with Patient Position (0018,5100). - // Defined Terms: + // Radiographic view associated with Patient Position (0018,5100). + // Defined Terms: // // AP = Anterior/Posterior // PA = Posterior/Anterior @@ -90,11 +93,12 @@ const unsigned int gdcmParser::MAX_SIZE_PRINT_ELEMENT_VALUE = 64; //----------------------------------------------------------------------------- // Constructor / Destructor + /** * \ingroup gdcmParser - * \brief - * @param InFilename - * @param exception_on_error + * \brief constructor + * @param inFilename + * @param exception_on_error whether we throw an exception or not * @param enable_sequences = true to allow the header * to be parsed *inside* the SeQuences, * when they have an actual length @@ -104,15 +108,15 @@ const unsigned int gdcmParser::MAX_SIZE_PRINT_ELEMENT_VALUE = 64; * with a FALSE value for the 'enable_sequence' param. * ('public elements' may be embedded in 'shadow Sequences') */ -gdcmParser::gdcmParser(const char *InFilename, +gdcmParser::gdcmParser(const char *inFilename, bool exception_on_error, bool enable_sequences, bool ignore_shadow) { enableSequences=enable_sequences; ignoreShadow =ignore_shadow; - SetMaxSizeLoadEntry(MAX_SIZE_LOAD_ELEMENT_VALUE); - filename = InFilename; + SetMaxSizeLoadEntry(MAX_SIZE_LOAD_ELEMENT_VALUE); + filename = inFilename; Initialise(); if ( !OpenFile(exception_on_error)) @@ -128,7 +132,7 @@ gdcmParser::gdcmParser(const char *InFilename, /** * \ingroup gdcmParser - * \brief + * \brief constructor * @param exception_on_error */ gdcmParser::gdcmParser(bool exception_on_error) { @@ -182,7 +186,7 @@ void gdcmParser::PrintPubDict(std::ostream & os) { /** * \ingroup gdcmParser - * \brief Prints The Dict Entries of THE shadow Dicom Dictionnry + * \brief Prints The Dict Entries of THE shadow Dicom Dictionnary * @return */ void gdcmParser::PrintShaDict(std::ostream & os) { @@ -385,7 +389,8 @@ bool gdcmParser::CloseFile(void) { /** * \ingroup gdcmParser - * \brief + * \brief writes on disc all the Header Entries (Dicom Elements) + * of the Chained List * @param fp file pointer on an already open file * @param type type of the File to be written * (ACR-NEMA, ExplicitVR, ImplicitVR) @@ -435,7 +440,7 @@ bool gdcmParser::Write(FILE *fp, FileType type) { SetEntryLengthByNumber(20, 0x0002, 0x0010); } -/* TODO : rewrite later +/* TODO : rewrite later, if really usefull if ( (type == ImplicitVR) || (type == ExplicitVR) ) UpdateGroupLength(false,type); @@ -451,22 +456,28 @@ bool gdcmParser::Write(FILE *fp, FileType type) { * \ingroup gdcmParser * \brief Modifies the value of a given Header Entry (Dicom Element) * if it exists; Creates it with the given value if it doesn't + * \warning : adds the Header Entry to the HTable, NOT to the chained List * @param Value passed as a std::string - * @param Group - * @param Elem - * \return false only if new element creation fails + * @param Group group of the Entry + * @param Elem element of the Entry + * \return pointer to the created Header Entry + * NULL if creation failed */ -bool gdcmParser::ReplaceOrCreateByNumber(std::string Value, +gdcmHeaderEntry * gdcmParser::ReplaceOrCreateByNumber( + std::string Value, guint16 Group, - guint16 Elem ){ - if (CheckIfEntryExistByNumber(Group, Elem) == 0) { + guint16 Elem ){ + gdcmHeaderEntry* a; + a = GetHeaderEntryByNumber( Group, Elem); + if (a == NULL) { gdcmHeaderEntry *a =NewHeaderEntryByNumber(Group, Elem); if (a == NULL) - return false; + return NULL; AddHeaderEntry(a); } - SetEntryByNumber(Value, Group, Elem); - return(true); + //SetEntryByNumber(Value, Group, Elem); + a->SetValue(Value); + return(a); } /** @@ -476,20 +487,24 @@ bool gdcmParser::ReplaceOrCreateByNumber(std::string Value, * @param Value passed as a char* * @param Group group of the Entry * @param Elem element of the Entry - * \return boolean + * \return pointer to the created Header Entry + * NULL if creation failed * */ -bool gdcmParser::ReplaceOrCreateByNumber(char* Value, guint16 Group, guint16 Elem ) { +gdcmHeaderEntry * gdcmParser::ReplaceOrCreateByNumber( + char* Value, + guint16 Group, + guint16 Elem ) { gdcmHeaderEntry* nvHeaderEntry=NewHeaderEntryByNumber(Group, Elem); if(!nvHeaderEntry) - return(false); + return(NULL); AddHeaderEntry(nvHeaderEntry); std::string v = Value; SetEntryByNumber(v, Group, Elem); - return(true); + return(nvHeaderEntry); } /** @@ -806,7 +821,7 @@ bool gdcmParser::SetEntryVoidAreaByNumber(void * area, /** * \ingroup gdcmParser * \brief Update the entries with the shadow dictionary. - * Only non even entries are analyzed + * Only non even entries are analyzed */ void gdcmParser::UpdateShaEntries(void) { gdcmDictEntry *entry; @@ -1019,17 +1034,14 @@ void gdcmParser::UpdateGroupLength(bool SkipSequence, FileType type) { /** * \ingroup gdcmParser * \brief writes on disc according to the requested format - * (ACR-NEMA, ExplicitVR, ImplicitVR) the image - * \warning does NOT add the missing elements in the header : - * it's up to the user doing it ! - * (function CheckHeaderCoherence to be written) - * \warning DON'T try, right now, to write a DICOM image - * from an ACR Header (meta elements will be missing!) + * (ACR-NEMA, ExplicitVR, ImplicitVR) ONE + * gdcmHeaderEntry + * @param tag pointer on the gdcmHeaderEntry to be written * @param type type of the File to be written * (ACR-NEMA, ExplicitVR, ImplicitVR) * @param _fp already open file pointer */ -void gdcmParser::WriteEntries(FILE *_fp,FileType type) +void gdcmParser::WriteEntry(gdcmHeaderEntry *tag, FILE *_fp,FileType type) { guint16 gr, el; guint32 lgr; @@ -1041,46 +1053,36 @@ void gdcmParser::WriteEntries(FILE *_fp,FileType type) guint16 valZero =0; void *voidArea; std::vector tokens; - - // TODO : function CheckHeaderCoherence to be written - - // uses now listEntries to iterate, not TagHt! - // - // pb : gdcmParser.Add does NOT update listEntries - // TODO : find a trick (in STL?) to do it, at low cost ! void *ptr; int ff=0xffffffff; // TODO (?) tester les echecs en ecriture (apres chaque fwrite) int compte =0; - - for (ListTag::iterator tag2=listEntries.begin(); - tag2 != listEntries.end(); - ++tag2) - { + itsTimeToWritePixels = false; + + gr = tag->GetGroup(); + el = tag->GetElement(); + lgr = tag->GetReadLength(); + val = tag->GetValue().c_str(); + vr = tag->GetVR(); + voidArea = tag->GetVoidArea(); + // === Deal with the length // -------------------- - if(((*tag2)->GetLength())%2==1) + if((tag->GetLength())%2==1) { - (*tag2)->SetValue((*tag2)->GetValue()+"\0"); - (*tag2)->SetLength((*tag2)->GetReadLength()+1); + tag->SetValue(tag->GetValue()+"\0"); + tag->SetLength(tag->GetReadLength()+1); } - - gr = (*tag2)->GetGroup(); - el = (*tag2)->GetElement(); - lgr = (*tag2)->GetReadLength(); - val = (*tag2)->GetValue().c_str(); - vr = (*tag2)->GetVR(); - voidArea = (*tag2)->GetVoidArea(); if ( type == ACR ) { - if (gr < 0x0008) continue; // ignore pure DICOM V3 groups - if (gr %2) continue; // ignore shadow groups - if (vr == "SQ" ) continue; // ignore Sequences + if (gr < 0x0008) return; // ignore pure DICOM V3 groups + if (gr %2) return; // ignore shadow groups + if (vr == "SQ" ) return; // ignore Sequences // TODO : find a trick to *skip* the SeQuences ! // Not only ignore the SQ element - if (gr == 0xfffe ) continue; // ignore delimiters + if (gr == 0xfffe ) return; // ignore delimiters } fwrite ( &gr,(size_t)2 ,(size_t)1 ,_fp); //group @@ -1091,9 +1093,16 @@ void gdcmParser::WriteEntries(FILE *_fp,FileType type) guint16 z=0, shortLgr; if (gr == 0xfffe) { // NO Value Representation for 'delimiters' - // no length : write ffffffff + // no length : write ffffffff + + // special patch to make some MR PHILIPS + if (el == 0x0000) return; // images e-film readable // see gdcmData/gdcm-MR-PHILIPS-16-Multi-Seq.dcm + // from Hospital Guy de Chauliac, + // Montpellier + // we just ignore spurious fffe|0000 tag ! + fwrite (&ff,(size_t)4 ,(size_t)1 ,_fp); - continue; // NO value for 'delimiters' + return; // NO value for 'delimiters' } shortLgr=lgr; @@ -1118,19 +1127,19 @@ void gdcmParser::WriteEntries(FILE *_fp,FileType type) // === Deal with the value // ------------------- - if (vr == "SQ") continue; // no "value" to write for the SEQuences - if (gr == 0xfffe)continue; // no "value" to write for the delimiters + if (vr == "SQ") return; // no "value" to write for the SEQuences + if (gr == 0xfffe)return; // no "value" to write for the delimiters if (voidArea != NULL) { // there is a 'non string' LUT, overlay, etc fwrite ( voidArea,(size_t)lgr ,(size_t)1 ,_fp); // Elem value - continue; + return; } if (vr == "US" || vr == "SS") { tokens.erase(tokens.begin(),tokens.end()); // clean any previous value - Tokenize ((*tag2)->GetValue(), tokens, "\\"); + Tokenize (tag->GetValue(), tokens, "\\"); for (unsigned int i=0; iGetValue(), tokens, "\\"); + Tokenize (tag->GetValue(), tokens, "\\"); for (unsigned int i=0; i tokens; - void *ptr; - - // Tout ceci ne marche QUE parce qu'on est sur un proc Little Endian // restent a tester les echecs en ecriture (apres chaque fwrite) for (TagHeaderEntryHT::iterator tag2=tagHT.begin(); tag2 != tagHT.end(); ++tag2){ - - gr = tag2->second->GetGroup(); - el = tag2->second->GetElement(); - lgr = tag2->second->GetLength(); - val = tag2->second->GetValue().c_str(); - vr = tag2->second->GetVR(); - - // std::cout << "Tag "<< std::hex << gr << " " << el << std::endl; - - if ( type == ACR ) { - if (gr < 0x0008) continue; // ignore pure DICOM V3 groups - if (gr %2) continue; // ignore shadow groups - if (vr == "SQ" ) continue; // ignore Sequences - if (gr == 0xfffe ) continue; // ignore delimiters - } - - fwrite ( &gr,(size_t)2 ,(size_t)1 ,_fp); //group - fwrite ( &el,(size_t)2 ,(size_t)1 ,_fp); //element - - if ( (type == ExplicitVR) && (gr <= 0x0002) ) { - // EXPLICIT VR - guint16 z=0, shortLgr; - fwrite (vr.c_str(),(size_t)2 ,(size_t)1 ,_fp); - - if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) { - fwrite ( &z, (size_t)2 ,(size_t)1 ,_fp); - fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp); - - } else { - shortLgr=lgr; - fwrite ( &shortLgr,(size_t)2 ,(size_t)1 ,_fp); - } - } else { // IMPLICIT VR - fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp); - } - - if (vr == "US" || vr == "SS") { - tokens.erase(tokens.begin(),tokens.end()); // clean any previous value - Tokenize (tag2->second->GetValue(), tokens, "\\"); - for (unsigned int i=0; isecond->GetValue(), tokens, "\\"); - for (unsigned int i=0; isecond,_fp,type); + if (itsTimeToWritePixels) + break; } } - - - - - /** * \ingroup gdcmParser * \brief Swaps back the bytes of 4-byte long integer accordingly to @@ -1510,7 +1476,7 @@ void gdcmParser::LoadHeaderEntry(gdcmHeaderEntry *Entry) { } if( (vr == "UI") ) // Because of correspondance with the VR dic - Entry->SetValue(NewValue.c_str()); // ??? JPR ??? + Entry->SetValue(NewValue.c_str()); else Entry->SetValue(NewValue); } @@ -1518,7 +1484,7 @@ void gdcmParser::LoadHeaderEntry(gdcmHeaderEntry *Entry) { /** * \ingroup gdcmParser * \brief add a new Dicom Element pointer to - * the H Table and to the chained List + * the H Table and at the end of the chained List * \warning push_bash in listEntries ONLY during ParseHeader * \todo something to allow further Elements addition, * (at their right place in the chained list) @@ -1533,9 +1499,8 @@ void gdcmParser::AddHeaderEntry(gdcmHeaderEntry *newHeaderEntry) { /** * \ingroup gdcmParser - * \brief - * @param Entry Header Entry whose length of the value shall be loaded. - * @return + * \brief Find the value Length of the passed Header Entry + * @param Entry Header Entry whose length of the value shall be loaded. */ void gdcmParser::FindHeaderEntryLength (gdcmHeaderEntry *Entry) { guint16 element = Entry->GetElement(); @@ -1936,31 +1901,20 @@ void gdcmParser::FixHeaderEntryFoundLength(gdcmHeaderEntry *Entry, guint32 Found } // a SeQuence Element is beginning - // Let's forget it's length - // (we want to 'go inside') - - // Pb : *normaly* fffe|e000 is just a marker, its length *should be* zero - // in gdcm-MR-PHILIPS-16-Multi-Seq.dcm we find lengthes as big as 28800 - // if we set the length to zero IsHeaderEntryAnInteger() breaks... - // if we don't, we lost 28800 characters from the Header :-( - + // fffe|e000 is just a marker, its length *should be* zero else if(Entry->GetGroup() == 0xfffe) { - // cout << "ReadLength " <GetReadLength() << " UsableLength " << FoundLength << endl; - // Entry->Print(); - // sometimes, length seems to be wrong - FoundLength =0; // some more clever checking to be done ! - // I give up! - // only gdcm-MR-PHILIPS-16-Multi-Seq.dcm - // causes troubles :-( - } - + // *normally, fffe|0000 doesn't exist ! + if( Entry->GetElement() != 0x0000 ) // gdcm-MR-PHILIPS-16-Multi-Seq.dcm + // causes extra troubles :-( + FoundLength =0; + } Entry->SetUsableLength(FoundLength); } /** * \ingroup gdcmParser - * \brief Apply some heuristics to predict wether the considered + * \brief Apply some heuristics to predict whether the considered * element value contains/represents an integer or not. * @param Entry The element value on which to apply the predicate. * @return The result of the heuristical predicate. @@ -2003,13 +1957,13 @@ bool gdcmParser::IsHeaderEntryAnInteger(gdcmHeaderEntry *Entry) { return false; } - /** * \ingroup gdcmParser - * \brief + * \brief Find the Length till the next sequence delimiter * \warning NOT end user intended method ! * @return */ + guint32 gdcmParser::FindHeaderEntryLengthOB(void) { // See PS 3.5-2001, section A.4 p. 49 on encapsulation of encoded pixel data. guint16 g; @@ -2027,7 +1981,7 @@ bool gdcmParser::IsHeaderEntryAnInteger(gdcmHeaderEntry *Entry) { return 0; TotalLength += 4; // We even have to decount the group and element - if ( g != 0xfffe && g!=0xb00c ) /*for bogus header */ + if ( g != 0xfffe && g!=0xb00c ) //for bogus header { char msg[100]; // for sprintf. Sorry sprintf(msg,"wrong group (%04x) for an item sequence (%04x,%04x)\n",g, g,n); @@ -2035,7 +1989,7 @@ bool gdcmParser::IsHeaderEntryAnInteger(gdcmHeaderEntry *Entry) { errno = 1; return 0; } - if ( n == 0xe0dd || ( g==0xb00c && n==0x0eb6 ) ) /* for bogus header */ + if ( n == 0xe0dd || ( g==0xb00c && n==0x0eb6 ) ) // for bogus header FoundSequenceDelimiter = true; else if ( n != 0xe000 ) { @@ -2099,7 +2053,7 @@ guint32 gdcmParser::ReadInt32(void) { /** * \ingroup gdcmParser - * \brief + * \brief skips bytes inside the source file * \warning NOT end user intended method ! * @return */ @@ -2110,7 +2064,8 @@ void gdcmParser::SkipBytes(guint32 NBytes) { /** * \ingroup gdcmParser - * \brief + * \brief Loads all the needed Dictionaries + * \warning NOT end user intended method ! */ void gdcmParser::Initialise(void) { @@ -2305,7 +2260,8 @@ bool gdcmParser::CheckSwap() { /** * \ingroup gdcmParser - * \brief + * \brief Restore the unproperly loaded values i.e. the group, the element + * and the dictionary entry depending on them. */ void gdcmParser::SwitchSwapToBigEndian(void) { @@ -2450,15 +2406,15 @@ gdcmHeaderEntry *gdcmParser::ReadNextHeaderEntry(void) { // header parsing has to be considered as finished. return (gdcmHeaderEntry *)0; -/* Pb : how to propagate the element length (used in SkipHeaderEntry) +// Pb : how to propagate the element length (used in SkipHeaderEntry) // direct call to SkipBytes ? - if (ignoreShadow == 1 && g%2 ==1) //JPR +// if (ignoreShadow == 1 && g%2 ==1) // if user wants to skip shadow groups // and current element *is* a shadow element // we don't create anything - return (gdcmHeaderEntry *)1; // to tell caller it's NOT finished -*/ +// return (gdcmHeaderEntry *)1; // to tell caller it's NOT finished + NewEntry = NewHeaderEntryByNumber(g, n); FindHeaderEntryVR(NewEntry); FindHeaderEntryLength(NewEntry);