X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=src%2FgdcmParser.cxx;h=5bee740526e38d34d2cb0e458c0d05aa83d3873a;hb=2aac3838df30350813968f97c1432a4955eb3927;hp=74848428bd33b5060881559273b3aaed7a1605ee;hpb=4e6834393e47fa42e6419f8151812fbb02e0e711;p=gdcm.git diff --git a/src/gdcmParser.cxx b/src/gdcmParser.cxx index 74848428..5bee7405 100644 --- a/src/gdcmParser.cxx +++ b/src/gdcmParser.cxx @@ -1,4 +1,4 @@ -// gdcmHeader.cxx +// gdcmParser.cxx //----------------------------------------------------------------------------- #include "gdcmParser.h" #include "gdcmUtil.h" @@ -64,6 +64,19 @@ // DL Delimiters // + // Other usefull abreviations : + // Radiographic view associated with Patient Position (0018,5100). + // Defined Terms: + // + // AP = Anterior/Posterior + // PA = Posterior/Anterior + // LL = Left Lateral + // RL = Right Lateral + // RLD = Right Lateral Decubitus + // LLD = Left Lateral Decubitus + // RLO = Right Lateral Oblique + // LLO = Left Lateral Oblique + //----------------------------------------------------------------------------- // Refer to gdcmParser::CheckSwap() const unsigned int gdcmParser::HEADER_LENGTH_TO_READ = 256; @@ -77,11 +90,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 @@ -91,7 +105,7 @@ 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) { @@ -99,7 +113,7 @@ gdcmParser::gdcmParser(const char *InFilename, ignoreShadow =ignore_shadow; SetMaxSizeLoadEntry(MAX_SIZE_LOAD_ELEMENT_VALUE); - filename = InFilename; + filename = inFilename; Initialise(); if ( !OpenFile(exception_on_error)) @@ -115,7 +129,7 @@ gdcmParser::gdcmParser(const char *InFilename, /** * \ingroup gdcmParser - * \brief + * \brief constructor * @param exception_on_error */ gdcmParser::gdcmParser(bool exception_on_error) { @@ -372,7 +386,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) @@ -438,22 +453,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); } /** @@ -463,20 +484,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); } /** @@ -553,6 +578,7 @@ std::string gdcmParser::GetEntryVRByName(std::string tagName) { return elem->GetVR(); } + /** * \ingroup gdcmParser * \brief Searches within Header Entries (Dicom Elements) parsed with @@ -592,6 +618,21 @@ std::string gdcmParser::GetEntryVRByNumber(guint16 group, guint16 element) { return elem->GetVR(); } +/** + * \ingroup gdcmParser + * \brief Searches within Header Entries (Dicom Elements) parsed with + * the public and private dictionaries + * for the value length of a given tag.. + * @param group Group of the searched tag. + * @param element Element of the searched tag. + * @return Corresponding element length; -2 if not found + */ +int gdcmParser::GetEntryLengthByNumber(guint16 group, guint16 element) { + gdcmHeaderEntry* elem = GetHeaderEntryByNumber(group, element); + if ( !elem ) + return -2; + return elem->GetLength(); +} /** * \ingroup gdcmParser * \brief Sets the value (string) of the Header Entry (Dicom Element) @@ -669,7 +710,7 @@ bool gdcmParser::SetEntryByNumber(std::string content, * @param l new length to substitute with * @param group group of the Entry to modify * @param element element of the Entry to modify - * @return 1 on success, 0 otherwise. + * @return true on success, false otherwise. */ bool gdcmParser::SetEntryLengthByNumber(guint32 l, guint16 group, @@ -689,7 +730,7 @@ bool gdcmParser::SetEntryLengthByNumber(guint32 l, * \brief Gets (from Header) the offset of a 'non string' element value * (LoadElementValues has already be executed) * @param Group group of the Entry - * @param Elem element of the Entry + * @param Elem element of the Entry * @return File Offset of the Element Value */ size_t gdcmParser::GetEntryOffsetByNumber(guint16 Group, guint16 Elem) @@ -777,7 +818,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; @@ -847,7 +888,7 @@ void gdcmParser::UpdateShaEntries(void) { */ gdcmHeaderEntry* gdcmParser::GetHeaderEntryByNumber(guint16 group, guint16 element) { - TagKey key = gdcmDictEntry::TranslateToKey(group, element); + TagKey key = gdcmDictEntry::TranslateToKey(group, element); if ( ! tagHT.count(key)) return NULL; return tagHT.find(key)->second; @@ -990,17 +1031,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; @@ -1012,83 +1050,71 @@ 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) - { - (*tag2)->SetValue((*tag2)->GetValue()+"\0"); - (*tag2)->SetLength((*tag2)->GetLength()+1); + if((tag->GetLength())%2==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 fwrite ( &el,(size_t)2 ,(size_t)1 ,_fp); //element - // if ( (type == ExplicitVR) && (gr <= 0x0002) ) // ?!? < 2 - if ( (type == ExplicitVR) || (type == DICOMDIR) ) - { + if ( (type == ExplicitVR) || (type == DICOMDIR) ) { // EXPLICIT VR guint16 z=0, shortLgr; - if (vr == "unkn") - { // Unknown was 'written' - shortLgr=lgr; + + if (gr == 0xfffe) { // NO Value Representation for 'delimiters' + // 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); + return; // NO value for 'delimiters' + } + + shortLgr=lgr; + if (vr == "unkn") { // Unknown was 'written' + // deal with Little Endian fwrite ( &shortLgr,(size_t)2 ,(size_t)1 ,_fp); fwrite ( &z, (size_t)2 ,(size_t)1 ,_fp); - } - else - { - if (gr != 0xfffe) - { // NO value for 'delimiters' - if (vr == "unkn") // Unknown was 'written' - fwrite(&z,(size_t)2 ,(size_t)1 ,_fp); - else - fwrite (vr.c_str(),(size_t)2 ,(size_t)1 ,_fp); - } - - if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") || gr == 0xfffe) - { - if (gr != 0xfffe) + } else { + 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 { + fwrite ( &shortLgr,(size_t)2 ,(size_t)1 ,_fp); + } } } else // IMPLICIT VR @@ -1098,19 +1124,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; + 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; isecond,_fp,type); + if (itsTimeToWritePixels) + break; } } @@ -1394,7 +1481,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) @@ -1409,15 +1496,15 @@ 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(); guint16 group = Entry->GetGroup(); std::string vr = Entry->GetVR(); guint16 length16; + if( (element == NumPixel) && (group == GrPixel) ) { dbg.SetDebug(GDCM_DEBUG); @@ -1509,18 +1596,18 @@ void gdcmParser::AddHeaderEntry(gdcmHeaderEntry *newHeaderEntry) { // Actually, length= 0xffff means that we deal with // Unknown Sequence Length } - FixHeaderEntryFoundLength(Entry, (guint32)length16); return; } else { - // Either implicit VR or a non DICOM conformal (see not below) explicit + // Either implicit VR or a non DICOM conformal (see note below) explicit // VR that ommited the VR of (at least) this element. Farts happen. // [Note: according to the part 5, PS 3.5-2001, section 7.1 p25 // on Data elements "Implicit and Explicit VR Data Elements shall // not coexist in a Data Set and Data Sets nested within it".] // Length is on 4 bytes. + FixHeaderEntryFoundLength(Entry, ReadInt32()); return; } @@ -1811,29 +1898,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) { - // 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. @@ -1876,13 +1954,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; @@ -1972,7 +2050,7 @@ guint32 gdcmParser::ReadInt32(void) { /** * \ingroup gdcmParser - * \brief + * \brief skips bytes inside the source file * \warning NOT end user intended method ! * @return */ @@ -1983,7 +2061,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) { @@ -2178,7 +2257,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) {