X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=src%2FgdcmDocument.cxx;h=9ddc85488e7267a23f1a820b337479202ec1b134;hb=fbf3fda5a04e43eeeadabbbec32605c2d72378b3;hp=8937fa3ea97638ab36e1d647d066bc983c8867fd;hpb=827d0d72b115d9e54aac8f0b054856dffff862ca;p=gdcm.git diff --git a/src/gdcmDocument.cxx b/src/gdcmDocument.cxx index 8937fa3e..9ddc8548 100644 --- a/src/gdcmDocument.cxx +++ b/src/gdcmDocument.cxx @@ -3,8 +3,8 @@ Program: gdcm Module: $RCSfile: gdcmDocument.cxx,v $ Language: C++ - Date: $Date: 2005/01/11 00:21:48 $ - Version: $Revision: 1.172 $ + Date: $Date: 2005/01/18 15:52:22 $ + Version: $Revision: 1.196 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -35,49 +35,16 @@ #include // For nthos: -#if defined(_MSC_VER) || defined(__BORLANDC__) +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MINGW32__) #include -#else +#endif + +#ifdef CMAKE_HAVE_NETINET_IN_H #include #endif namespace gdcm { -//----------------------------------------------------------------------------- -static const char *TransferSyntaxStrings[] = { - // Implicit VR Little Endian - "1.2.840.10008.1.2", - // Implicit VR Big Endian DLX (G.E Private) - "1.2.840.113619.5.2", - // Explicit VR Little Endian - "1.2.840.10008.1.2.1", - // Deflated Explicit VR Little Endian - "1.2.840.10008.1.2.1.99", - // Explicit VR Big Endian - "1.2.840.10008.1.2.2", - // JPEG Baseline (Process 1) - "1.2.840.10008.1.2.4.50", - // JPEG Extended (Process 2 & 4) - "1.2.840.10008.1.2.4.51", - // JPEG Extended (Process 3 & 5) - "1.2.840.10008.1.2.4.52", - // JPEG Spectral Selection, Non-Hierarchical (Process 6 & 8) - "1.2.840.10008.1.2.4.53", - // JPEG Full Progression, Non-Hierarchical (Process 10 & 12) - "1.2.840.10008.1.2.4.55", - // JPEG Lossless, Non-Hierarchical (Process 14) - "1.2.840.10008.1.2.4.57", - // JPEG Lossless, Hierarchical, First-Order Prediction (Process 14, [Selection Value 1]) - "1.2.840.10008.1.2.4.70", - // JPEG 2000 Lossless - "1.2.840.10008.1.2.4.90", - // JPEG 2000 - "1.2.840.10008.1.2.4.91", - // RLE Lossless - "1.2.840.10008.1.2.5", - // Unknown - "Unknown Transfer Syntax" -}; //----------------------------------------------------------------------------- // Refer to Document::CheckSwap() @@ -106,18 +73,21 @@ Document::Document( std::string const &filename ) : ElementSet(-1) return; } + Group0002Parsed = false; + gdcmVerboseMacro( "Starting parsing of file: " << Filename.c_str()); - Fp->seekg( 0, std::ios::beg); + // Fp->seekg( 0, std::ios::beg); Fp->seekg(0, std::ios::end); long lgt = Fp->tellg(); Fp->seekg( 0, std::ios::beg); + CheckSwap(); long beg = Fp->tellg(); lgt -= beg; - ParseDES( this, beg, lgt, false); // le Load sera fait a la volee + ParseDES( this, beg, lgt, false); // Loading is done during parsing Fp->seekg( 0, std::ios::beg); @@ -130,14 +100,14 @@ Document::Document( std::string const &filename ) : ElementSet(-1) /// FIXME FIXME FIXME /// The tags refered by the three following lines used to be CORRECTLY /// defined as having an US Value Representation in the public - /// dictionnary. BUT the semantics implied by the three following + /// dictionary. BUT the semantics implied by the three following /// lines state that the corresponding tag contents are in fact /// the ones of a BinEntry. - /// In order to fix things "Quick and Dirty" the dictionnary was - /// altered on PURPOUS but now contains a WRONG value. + /// In order to fix things "Quick and Dirty" the dictionary was + /// altered on PURPOSE but now contains a WRONG value. /// In order to fix things and restore the dictionary to its /// correct value, one needs to decided of the semantics by deciding - /// wether the following tags are either: + /// whether the following tags are either: /// - multivaluated US, and hence loaded as ValEntry, but afterwards /// also used as BinEntry, which requires the proper conversion, /// - OW, and hence loaded as BinEntry, but afterwards also used @@ -193,8 +163,9 @@ Document::Document() : ElementSet(-1) SetMaxSizeLoadEntry(MAX_SIZE_LOAD_ELEMENT_VALUE); Initialise(); - SwapCode = 0; + SwapCode = 1234; Filetype = ExplicitVR; + Group0002Parsed = false; } /** @@ -287,7 +258,7 @@ bool Document::IsReadable() } if( TagHT.empty() ) - { + { gdcmVerboseMacro( "No tags in internal hash table."); return false; } @@ -301,12 +272,12 @@ bool Document::IsReadable() * value from disk when only parsing occured). * @return The encountered Transfer Syntax of the current document. */ -TransferSyntaxType Document::GetTransferSyntax() +std::string Document::GetTransferSyntax() { DocEntry *entry = GetDocEntry(0x0002, 0x0010); if ( !entry ) { - return UnknownTS; + return GDCM_UNKNOWN; } // The entry might be present but not loaded (parsing and loading @@ -321,68 +292,15 @@ TransferSyntaxType Document::GetTransferSyntax() if ( transfer.length() == 0 ) { // for brain damaged headers - return UnknownTS; + return GDCM_UNKNOWN; } while ( !isdigit((unsigned char)transfer[transfer.length()-1]) ) { transfer.erase(transfer.length()-1, 1); } - for (int i = 0; TransferSyntaxStrings[i] != NULL; i++) - { - if ( TransferSyntaxStrings[i] == transfer ) - { - return TransferSyntaxType(i); - } - } + return transfer; } - return UnknownTS; -} - -bool Document::IsJPEGLossless() -{ - TransferSyntaxType r = GetTransferSyntax(); - return r == JPEGFullProgressionProcess10_12 - || r == JPEGLosslessProcess14 - || r == JPEGLosslessProcess14_1; -} - -/** - * \brief Determines if the Transfer Syntax was already encountered - * and if it corresponds to a JPEG2000 one - * @return True when JPEG2000 (Lossly or LossLess) found. False in all - * other cases. - */ -bool Document::IsJPEG2000() -{ - TransferSyntaxType r = GetTransferSyntax(); - return r == JPEG2000Lossless || r == JPEG2000; -} - -/** - * \brief Determines if the Transfer Syntax corresponds to any form - * of Jpeg encoded Pixel data. - * @return True when any form of JPEG found. False otherwise. - */ -bool Document::IsJPEG() -{ - TransferSyntaxType r = GetTransferSyntax(); - return r == JPEGBaselineProcess1 - || r == JPEGExtendedProcess2_4 - || r == JPEGExtendedProcess3_5 - || r == JPEGSpectralSelectionProcess6_8 - || IsJPEGLossless() - || IsJPEG2000(); -} - -/** - * \brief Determines if the Transfer Syntax corresponds to encapsulated - * of encoded Pixel Data (as opposed to native). - * @return True when encapsulated. False when native. - */ -bool Document::IsEncapsulate() -{ - TransferSyntaxType r = GetTransferSyntax(); - return IsJPEG() || r == RLELossless; + return GDCM_UNKNOWN; } /** @@ -391,7 +309,7 @@ bool Document::IsEncapsulate() */ bool Document::IsDicomV3() { - // Checking if Transfert Syntax exists is enough + // Checking if Transfer Syntax exists is enough // Anyway, it's to late check if the 'Preamble' was found ... // And ... would it be a rich idea to check ? // (some 'no Preamble' DICOM images exist !) @@ -415,6 +333,8 @@ FileType Document::GetFileType() */ std::ifstream *Document::OpenFile() { + + HasDCMPreamble = false; if (Filename.length() == 0) { return 0; @@ -422,13 +342,13 @@ std::ifstream *Document::OpenFile() if(Fp) { - gdcmVerboseMacro( "Is already opened when opening: " << Filename.c_str()); + gdcmVerboseMacro( "File already open: " << Filename.c_str()); } Fp = new std::ifstream(Filename.c_str(), std::ios::in | std::ios::binary); if( ! *Fp ) { - gdcmVerboseMacro( "Cannot open file: " << Filename.c_str()); + gdcmDebugMacro( "Cannot open file: " << Filename.c_str()); delete Fp; Fp = 0; return 0; @@ -466,6 +386,7 @@ std::ifstream *Document::OpenFile() } if( memcmp(dicm, "DICM", 4) == 0 ) { + HasDCMPreamble = true; return Fp; } @@ -493,7 +414,7 @@ bool Document::CloseFile() /** * \brief Writes in a file all the Header Entries (Dicom Elements) - * @param fp file pointer on an already open file + * @param fp file pointer on an already open file (actually: Output File Stream) * @param filetype Type of the File to be written * (ACR-NEMA, ExplicitVR, ImplicitVR) * \return Always true. @@ -668,8 +589,8 @@ BinEntry *Document::ReplaceOrCreate(uint8_t *binArea, /* * \brief Modifies the value of a given Header Entry (Dicom Element) * when it exists. Create it when unexistant. - * @param Group Group number of the Entry - * @param Elem Element number of the Entry + * @param group Group number of the Entry + * @param elem Element number of the Entry * \return pointer to the modified/created SeqEntry (NULL when creation * failed). */ @@ -733,23 +654,18 @@ bool Document::ReplaceIfExist(std::string const &value, return true; } -std::string Document::GetTransferSyntaxValue(TransferSyntaxType type) -{ - return TransferSyntaxStrings[type]; -} - //----------------------------------------------------------------------------- // Protected /** * \brief Checks if a given Dicom Element exists within the H table - * @param group Group number of the searched Dicom Element - * @param element Element number of the searched Dicom Element + * @param group Group number of the searched Dicom Element + * @param elem Element number of the searched Dicom Element * @return true is found */ -bool Document::CheckIfEntryExist(uint16_t group, uint16_t element ) +bool Document::CheckIfEntryExist(uint16_t group, uint16_t elem ) { - const std::string &key = DictEntry::TranslateToKey(group, element ); + const std::string &key = DictEntry::TranslateToKey(group, elem ); return TagHT.count(key) != 0; } @@ -758,14 +674,14 @@ bool Document::CheckIfEntryExist(uint16_t group, uint16_t element ) * \brief Searches within Header Entries (Dicom Elements) parsed with * the public and private dictionaries * for the element value representation of a given tag. - * @param group Group number of the searched tag. - * @param element Element number of the searched tag. + * @param group Group number of the searched tag. + * @param elem Element number of the searched tag. * @return Corresponding element value representation when it exists, * and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise. */ -std::string Document::GetEntry(uint16_t group, uint16_t element) +std::string Document::GetEntry(uint16_t group, uint16_t elem) { - TagKey key = DictEntry::TranslateToKey(group, element); + TagKey key = DictEntry::TranslateToKey(group, elem); if ( !TagHT.count(key)) { return GDCM_UNFOUND; @@ -778,42 +694,41 @@ std::string Document::GetEntry(uint16_t group, uint16_t element) * \brief Searches within Header Entries (Dicom Elements) parsed with * the public and private dictionaries * for the element value representation of a given tag.. - * * Obtaining the VR (Value Representation) might be needed by caller * to convert the string typed content to caller's native type * (think of C++ vs Python). The VR is actually of a higher level * of semantics than just the native C++ type. - * @param group Group number of the searched tag. - * @param element Element number of the searched tag. + * @param group Group number of the searched tag. + * @param elem Element number of the searched tag. * @return Corresponding element value representation when it exists, * and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise. */ -std::string Document::GetEntryVR(uint16_t group, uint16_t element) +std::string Document::GetEntryVR(uint16_t group, uint16_t elem) { - DocEntry *elem = GetDocEntry(group, element); - if ( !elem ) + DocEntry *element = GetDocEntry(group, elem); + if ( !element ) { return GDCM_UNFOUND; } - return elem->GetVR(); + return element->GetVR(); } /** * \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 number of the searched tag. - * @param element Element number of the searched tag. + * @param group Group number of the searched tag. + * @param elem Element number of the searched tag. * @return Corresponding element length; -2 if not found */ -int Document::GetEntryLength(uint16_t group, uint16_t element) +int Document::GetEntryLength(uint16_t group, uint16_t elem) { - DocEntry *elem = GetDocEntry(group, element); - if ( !elem ) + DocEntry *element = GetDocEntry(group, elem); + if ( !element ) { return -2; //magic number } - return elem->GetLength(); + return element->GetLength(); } /** @@ -821,13 +736,13 @@ int Document::GetEntryLength(uint16_t group, uint16_t element) * through it's (group, element) and modifies it's content with * the given value. * @param content new value (string) to substitute with - * @param group group number of the Dicom Element to modify - * @param element element number of the Dicom Element to modify + * @param group group number of the Dicom Element to modify + * @param elem element number of the Dicom Element to modify */ bool Document::SetEntry(std::string const& content, - uint16_t group, uint16_t element) + uint16_t group, uint16_t elem) { - ValEntry *entry = GetValEntry(group, element); + ValEntry *entry = GetValEntry(group, elem); if (!entry ) { gdcmVerboseMacro( "No corresponding ValEntry (try promotion first)."); @@ -842,13 +757,13 @@ bool Document::SetEntry(std::string const& content, * the given value. * @param content new value (void* -> uint8_t*) to substitute with * @param lgth new value length - * @param group group number of the Dicom Element to modify - * @param element element number of the Dicom Element to modify + * @param group group number of the Dicom Element to modify + * @param elem element number of the Dicom Element to modify */ bool Document::SetEntry(uint8_t*content, int lgth, - uint16_t group, uint16_t element) + uint16_t group, uint16_t elem) { - BinEntry *entry = GetBinEntry(group, element); + BinEntry *entry = GetBinEntry(group, elem); if (!entry ) { gdcmVerboseMacro( "No corresponding ValEntry (try promotion first)."); @@ -879,7 +794,7 @@ bool Document::SetEntry(std::string const &content,ValEntry *entry) * and modifies it's content with the given value. * @param content new value (void* -> uint8_t*) to substitute with * @param entry Entry to be modified - * @param lgth new value length + * @param lgth new value length */ bool Document::SetEntry(uint8_t *content, int lgth, BinEntry *entry) { @@ -946,21 +861,21 @@ void Document::LoadEntryBinArea(uint16_t group, uint16_t elem) /** * \brief Loads (from disk) the element content * when a string is not suitable - * @param element Entry whose binArea is going to be loaded + * @param elem Entry whose binArea is going to be loaded */ -void Document::LoadEntryBinArea(BinEntry *element) +void Document::LoadEntryBinArea(BinEntry *elem) { - if(element->GetBinArea()) + if(elem->GetBinArea()) return; bool openFile = !Fp; if(openFile) OpenFile(); - size_t o =(size_t)element->GetOffset(); + size_t o =(size_t)elem->GetOffset(); Fp->seekg(o, std::ios::beg); - size_t l = element->GetLength(); + size_t l = elem->GetLength(); uint8_t *a = new uint8_t[l]; if( !a ) { @@ -976,7 +891,7 @@ void Document::LoadEntryBinArea(BinEntry *element) return; } - element->SetBinArea(a); + elem->SetBinArea(a); if(openFile) CloseFile(); @@ -985,14 +900,14 @@ void Document::LoadEntryBinArea(BinEntry *element) /** * \brief Sets a 'non string' value to a given Dicom Element * @param area area containing the 'non string' value - * @param group Group number of the searched Dicom Element - * @param element Element number of the searched Dicom Element + * @param group Group number of the searched Dicom Element + * @param elem Element number of the searched Dicom Element * @return */ /*bool Document::SetEntryBinArea(uint8_t *area, - uint16_t group, uint16_t element) + uint16_t group, uint16_t elem) { - DocEntry *currentEntry = GetDocEntry(group, element); + DocEntry *currentEntry = GetDocEntry(group, elem); if ( !currentEntry ) { return false; @@ -1013,13 +928,13 @@ void Document::LoadEntryBinArea(BinEntry *element) * if you think it's NOT UNIQUE, check the count number * and use iterators to retrieve ALL the Dicoms Elements within * a given couple (group, element) - * @param group Group number of the searched Dicom Element - * @param element Element number of the searched Dicom Element + * @param group Group number of the searched Dicom Element + * @param elem Element number of the searched Dicom Element * @return */ -DocEntry *Document::GetDocEntry(uint16_t group, uint16_t element) +DocEntry *Document::GetDocEntry(uint16_t group, uint16_t elem) { - TagKey key = DictEntry::TranslateToKey(group, element); + TagKey key = DictEntry::TranslateToKey(group, elem); if ( !TagHT.count(key)) { return NULL; @@ -1031,11 +946,13 @@ DocEntry *Document::GetDocEntry(uint16_t group, uint16_t element) * \brief Same as \ref Document::GetDocEntry except it only * returns a result when the corresponding entry is of type * ValEntry. + * @param group Group number of the searched Dicom Element + * @param elem Element number of the searched Dicom Element * @return When present, the corresponding ValEntry. */ -ValEntry *Document::GetValEntry(uint16_t group, uint16_t element) +ValEntry *Document::GetValEntry(uint16_t group, uint16_t elem) { - DocEntry *currentEntry = GetDocEntry(group, element); + DocEntry *currentEntry = GetDocEntry(group, elem); if ( !currentEntry ) { return 0; @@ -1053,11 +970,13 @@ ValEntry *Document::GetValEntry(uint16_t group, uint16_t element) * \brief Same as \ref Document::GetDocEntry except it only * returns a result when the corresponding entry is of type * BinEntry. + * @param group Group number of the searched Dicom Element + * @param elem Element number of the searched Dicom Element * @return When present, the corresponding BinEntry. */ -BinEntry *Document::GetBinEntry(uint16_t group, uint16_t element) +BinEntry *Document::GetBinEntry(uint16_t group, uint16_t elem) { - DocEntry *currentEntry = GetDocEntry(group, element); + DocEntry *currentEntry = GetDocEntry(group, elem); if ( !currentEntry ) { return 0; @@ -1075,7 +994,7 @@ BinEntry *Document::GetBinEntry(uint16_t group, uint16_t element) * \brief Loads the element while preserving the current * underlying file position indicator as opposed to * to LoadDocEntry that modifies it. - * @param entry Header Entry whose value shall be loaded. + * @param entry Header Entry whose value will be loaded. * @return */ void Document::LoadDocEntrySafe(DocEntry *entry) @@ -1097,20 +1016,18 @@ uint32_t Document::SwapLong(uint32_t a) { switch (SwapCode) { - case 0 : + case 1234 : break; case 4321 : a=( ((a<<24) & 0xff000000) | ((a<<8) & 0x00ff0000) | ((a>>8) & 0x0000ff00) | ((a>>24) & 0x000000ff) ); - break; - + break; case 3412 : a=( ((a<<16) & 0xffff0000) | ((a>>16) & 0x0000ffff) ); - break; - + break; case 2143 : a=( ((a<< 8) & 0xff00ff00) | ((a>>8) & 0x00ff00ff) ); - break; + break; default : gdcmErrorMacro( "Unset swap code:" << SwapCode ); a = 0; @@ -1165,7 +1082,7 @@ void Document::ParseDES(DocEntrySet *set, long offset, BinEntry *newBinEntry; SeqEntry *newSeqEntry; VRKey vr; - bool used=false; + bool used = false; while (true) { @@ -1174,8 +1091,9 @@ void Document::ParseDES(DocEntrySet *set, long offset, break; } - used=true; + used = true; newDocEntry = ReadNextDocEntry( ); + if ( !newDocEntry ) { break; @@ -1193,20 +1111,22 @@ void Document::ParseDES(DocEntrySet *set, long offset, if ( ! Global::GetVR()->IsVROfBinaryRepresentable(vr) ) { ////// Neither ValEntry NOR BinEntry: should mean UNKOWN VR - gdcmVerboseMacro( "Neither Valentry, nor BinEntry." + gdcmVerboseMacro( std::hex << newDocEntry->GetGroup() + << "|" << newDocEntry->GetElement() + << " : Neither Valentry, nor BinEntry." "Probably unknown VR."); } //////////////////// BinEntry or UNKOWN VR: // When "this" is a Document the Key is simply of the // form ( group, elem )... - if (Document *dummy = dynamic_cast< Document* > ( set ) ) + if (/*Document *dummy =*/ dynamic_cast< Document* > ( set ) ) { - (void)dummy; + //(void)dummy; newBinEntry->SetKey( newBinEntry->GetKey() ); } // but when "this" is a SQItem, we are inserting this new - // valEntry in a sequence item, and the kay has the + // valEntry in a sequence item, and the key has the // generalized form (refer to \ref BaseTagKey): if (SQItem *parentSQItem = dynamic_cast< SQItem* > ( set ) ) { @@ -1227,9 +1147,9 @@ void Document::ParseDES(DocEntrySet *set, long offset, /////////////////////// ValEntry // When "set" is a Document, then we are at the top of the // hierarchy and the Key is simply of the form ( group, elem )... - if (Document *dummy = dynamic_cast< Document* > ( set ) ) + if (/*Document *dummy =*/ dynamic_cast< Document* > ( set ) ) { - (void)dummy; + //(void)dummy; newValEntry->SetKey( newValEntry->GetKey() ); } // ...but when "set" is a SQItem, we are inserting this new @@ -1267,15 +1187,15 @@ void Document::ParseDES(DocEntrySet *set, long offset, if ( ( newDocEntry->GetGroup() == 0x7fe0 ) && ( newDocEntry->GetElement() == 0x0010 ) ) { - TransferSyntaxType ts = GetTransferSyntax(); - if ( ts == RLELossless ) + std::string ts = GetTransferSyntax(); + if ( Global::GetTS()->IsRLELossless(ts) ) { long positionOnEntry = Fp->tellg(); Fp->seekg( newDocEntry->GetOffset(), std::ios::beg ); ComputeRLEInfo(); Fp->seekg( positionOnEntry, std::ios::beg ); } - else if ( IsJPEG() ) + else if ( Global::GetTS()->IsJPEG(ts) ) { long positionOnEntry = Fp->tellg(); Fp->seekg( newDocEntry->GetOffset(), std::ios::beg ); @@ -1309,13 +1229,13 @@ void Document::ParseDES(DocEntrySet *set, long offset, // is a Document, then we are building the first depth level. // Hence the SeqEntry we are building simply has a depth // level of one: - if (Document *dummy = dynamic_cast< Document* > ( set ) ) + if (/*Document *dummy =*/ dynamic_cast< Document* > ( set ) ) { - (void)dummy; + //(void)dummy; newSeqEntry->SetDepthLevel( 1 ); newSeqEntry->SetKey( newSeqEntry->GetKey() ); } - // But when "set" is allready a SQItem, we are building a nested + // But when "set" is already a SQItem, we are building a nested // sequence, and hence the depth level of the new SeqEntry // we are building, is one level deeper: if (SQItem *parentSQItem = dynamic_cast< SQItem* > ( set ) ) @@ -1352,10 +1272,13 @@ void Document::ParseSQ( SeqEntry *seqEntry, { int SQItemNumber = 0; bool dlm_mod; + long offsetStartCurrentSQItem = offset; while (true) { - DocEntry *newDocEntry = ReadNextDocEntry(); + // the first time, we read the fff0,e000 of the first SQItem + DocEntry *newDocEntry = ReadNextDocEntry(); + if ( !newDocEntry ) { // FIXME Should warn user @@ -1374,7 +1297,7 @@ void Document::ParseSQ( SeqEntry *seqEntry, delete newDocEntry; break; } - + // create the current SQItem SQItem *itemSQ = new SQItem( seqEntry->GetDepthLevel() ); std::ostringstream newBase; newBase << seqEntry->GetKey() @@ -1392,10 +1315,22 @@ void Document::ParseSQ( SeqEntry *seqEntry, { dlm_mod = false; } - - ParseDES(itemSQ, newDocEntry->GetOffset(), l, dlm_mod); + // FIXME, TODO + // when we're here, element fffe,e000 is already passed. + // it's lost for the SQItem we're going to process !! + + //ParseDES(itemSQ, newDocEntry->GetOffset(), l, dlm_mod); + //delete newDocEntry; // FIXME well ... it's too late to use it ! + + // Let's try :------------ + // remove fff0,e000, created out of the SQItem delete newDocEntry; - + Fp->seekg(offsetStartCurrentSQItem, std::ios::beg); + // fill up the current SQItem, starting at the beginning of fff0,e000 + ParseDES(itemSQ, offsetStartCurrentSQItem, l+8, dlm_mod); + offsetStartCurrentSQItem = Fp->tellg(); + // end try ----------------- + seqEntry->AddEntry( itemSQ, SQItemNumber ); SQItemNumber++; if ( !delim_mode && ((long)(Fp->tellg())-offset ) >= l_max ) @@ -1585,7 +1520,6 @@ void Document::LoadDocEntry(DocEntry *entry) void Document::FindDocEntryLength( DocEntry *entry ) throw ( FormatError ) { - uint16_t element = entry->GetElement(); std::string vr = entry->GetVR(); uint16_t length16; @@ -1633,6 +1567,14 @@ void Document::FindDocEntryLength( DocEntry *entry ) // Length is encoded on 2 bytes. length16 = ReadInt16(); + + // FIXME : This heuristic supposes that the first group following + // group 0002 *has* and element 0000. + // BUT ... Element 0000 is optionnal :-( + + + // Fixed using : HandleOutOfGroup0002() + // (first hereafter strategy ...) // We can tell the current file is encoded in big endian (like // Data/US-RGB-8-epicard) when we find the "Transfer Syntax" tag @@ -1653,6 +1595,8 @@ void Document::FindDocEntryLength( DocEntry *entry ) // ones with zero as element number) has to be of 4 (0x0004). When we // encounter 1024 (0x0400) chances are the encoding changed and we // found a group with big endian encoding. + //---> Unfortunately, element 0000 is optional. + //---> This will not work when missing! // We shall use this second strategy. In order to make sure that we // can interpret the presence of an apparently big endian encoded // length of a "Group Length" without committing a big mistake, we @@ -1661,37 +1605,40 @@ void Document::FindDocEntryLength( DocEntry *entry ) // endian encoding". When this is the case, chances are we have got our // hands on a big endian encoded file: we switch the swap code to // big endian and proceed... - if ( element == 0x0000 && length16 == 0x0400 ) - { - TransferSyntaxType ts = GetTransferSyntax(); - if ( ts != ExplicitVRBigEndian ) - { - throw FormatError( "Document::FindDocEntryLength()", - " not explicit VR." ); - return; - } - length16 = 4; - SwitchSwapToBigEndian(); + + // + // if ( element == 0x0000 && length16 == 0x0400 ) + // { + // std::string ts = GetTransferSyntax(); + // if ( Global::GetTS()->GetSpecialTransferSyntax(ts) + // != TS::ExplicitVRBigEndian ) + // { + // throw FormatError( "Document::FindDocEntryLength()", + // " not explicit VR." ); + // return; + // } + // length16 = 4; + // SwitchByteSwapCode(); // Restore the unproperly loaded values i.e. the group, the element // and the dictionary entry depending on them. - uint16_t correctGroup = SwapShort( entry->GetGroup() ); - uint16_t correctElem = SwapShort( entry->GetElement() ); - DictEntry *newTag = GetDictEntry( correctGroup, correctElem ); - if ( !newTag ) - { +// uint16_t correctGroup = SwapShort( entry->GetGroup() ); +// uint16_t correctElem = SwapShort( entry->GetElement() ); +// DictEntry *newTag = GetDictEntry( correctGroup, correctElem ); if ( !newTag ) +// { // This correct tag is not in the dictionary. Create a new one. - newTag = NewVirtualDictEntry(correctGroup, correctElem); - } +// newTag = NewVirtualDictEntry(correctGroup, correctElem); +// } // FIXME this can create a memory leaks on the old entry that be // left unreferenced. - entry->SetDictEntry( newTag ); - } - - // Heuristic: well, some files are really ill-formed. +// entry->SetDictEntry( newTag ); +// } + + + // 0xffff means that we deal with 'No Length' Sequence + // or 'No Length' SQItem if ( length16 == 0xffff) - { - // 0xffff means that we deal with 'Unknown Length' Sequence + { length16 = 0; } FixDocEntryFoundLength( entry, (uint32_t)length16 ); @@ -1909,7 +1856,8 @@ void Document::SkipDocEntry(DocEntry *entry) void Document::SkipToNextDocEntry(DocEntry *currentDocEntry) { Fp->seekg((long)(currentDocEntry->GetOffset()), std::ios::beg); - Fp->seekg( (long)(currentDocEntry->GetReadLength()),std::ios::cur); + if (currentDocEntry->GetGroup() != 0xfffe) // for fffe pb + Fp->seekg( (long)(currentDocEntry->GetReadLength()),std::ios::cur); } /** @@ -2194,25 +2142,24 @@ bool Document::CheckSwap() net2host = false; } - // The easiest case is the one of a DICOM header, since it possesses a - // file preamble where it suffice to look for the string "DICM". + // The easiest case is the one of a 'true' DICOM header, we just have + // to look for the string "DICM" inside the file preamble. Fp->read(deb, 256); char *entCur = deb + 128; if( memcmp(entCur, "DICM", (size_t)4) == 0 ) { - gdcmVerboseMacro( "Looks like DICOM Version3" ); + gdcmVerboseMacro( "Looks like DICOM Version3 (preamble + DCM)" ); - // Next, determine the value representation (VR). Let's skip to the - // first element (0002, 0000) and check there if we find "UL" - // - or "OB" if the 1st one is (0002,0001) -, + // Group 0002 should always be VR, and the first element 0000 + // Let's be carefull (so many wrong headers ...) + // and determine the value representation (VR) : + // Let's skip to the first element (0002,0000) and check there if we find + // "UL" - or "OB" if the 1st one is (0002,0001) -, // in which case we (almost) know it is explicit VR. // WARNING: if it happens to be implicit VR then what we will read // is the length of the group. If this ascii representation of this // length happens to be "UL" then we shall believe it is explicit VR. - // FIXME: in order to fix the above warning, we could read the next - // element value (or a couple of elements values) in order to make - // sure we are not commiting a big mistake. // We need to skip : // * the 128 bytes of File Preamble (often padded with zeroes), // * the 4 bytes of "DICM" string, @@ -2220,27 +2167,27 @@ bool Document::CheckSwap() // i.e. a total of 136 bytes. entCur = deb + 136; - // FIXME : FIXME: - // Sometimes (see : gdcmData/icone.dcm) group 0x0002 *is* Explicit VR, - // but elem 0002,0010 (Transfert Syntax) tells us the file is - // *Implicit* VR. -and it is !- + // group 0x0002 *is always* Explicit VR Sometimes , + // even if elem 0002,0010 (Transfer Syntax) tells us the file is + // *Implicit* VR (see former 'gdcmData/icone.dcm') if( memcmp(entCur, "UL", (size_t)2) == 0 || memcmp(entCur, "OB", (size_t)2) == 0 || memcmp(entCur, "UI", (size_t)2) == 0 || memcmp(entCur, "CS", (size_t)2) == 0 ) // CS, to remove later - // when Write DCM *adds* + // when Write DCM *adds* // FIXME // Use Document::dicom_vr to test all the possibilities // instead of just checking for UL, OB and UI !? group 0000 { Filetype = ExplicitVR; - gdcmVerboseMacro( "Explicit Value Representation"); + gdcmVerboseMacro( "Group 0002 : Explicit Value Representation"); } else { Filetype = ImplicitVR; - gdcmVerboseMacro( "Not an explicit Value Representation"); + gdcmVerboseMacro( "Group 0002 :Not an explicit Value Representation;" + << "Looks like a bugged Header!"); } if ( net2host ) @@ -2250,12 +2197,12 @@ bool Document::CheckSwap() } else { - SwapCode = 0; + SwapCode = 1234; gdcmVerboseMacro( "HostByteOrder = NetworkByteOrder"); } - // Position the file position indicator at first tag (i.e. - // after the file preamble and the "DICM" string). + // Position the file position indicator at first tag + // (i.e. after the file preamble and the "DICM" string). Fp->seekg(0, std::ios::beg); Fp->seekg ( 132L, std::ios::beg); return true; @@ -2293,7 +2240,7 @@ bool Document::CheckSwap() Filetype = ACR; return true; case 0x00000004 : - SwapCode = 0; + SwapCode = 1234; Filetype = ACR; return true; default : @@ -2324,7 +2271,7 @@ bool Document::CheckSwap() case 0x0006 : case 0x0007 : case 0x0008 : - SwapCode = 0; + SwapCode = 1234; Filetype = ACR; return true; case 0x0100 : @@ -2343,31 +2290,24 @@ bool Document::CheckSwap() Filetype = Unknown; return false; } - // Then the only info we have is the net2host one. - //if (! net2host ) - // SwapCode = 0; - //else - // SwapCode = 4321; - //return; } } /** - * \brief Restore the unproperly loaded values i.e. the group, the element - * and the dictionary entry depending on them. + * \brief Change the Byte Swap code. */ -void Document::SwitchSwapToBigEndian() +void Document::SwitchByteSwapCode() { - gdcmVerboseMacro( "Switching to BigEndian mode."); - if ( SwapCode == 0 ) + gdcmVerboseMacro( "Switching Byte Swap code from "<< SwapCode); + if ( SwapCode == 1234 ) { SwapCode = 4321; } else if ( SwapCode == 4321 ) { - SwapCode = 0; + SwapCode = 1234; } else if ( SwapCode == 3412 ) { @@ -2426,7 +2366,7 @@ void Document::SetMaxSizePrintEntry(long newSize) * apparent reason * @return no return */ -void Document::HandleBrokenEndian(uint16_t group, uint16_t elem) +void Document::HandleBrokenEndian(uint16_t &group, uint16_t &elem) { // Endian reversion. Some files contain groups of tags with reversed endianess. static int reversedEndian = 0; @@ -2435,7 +2375,7 @@ void Document::HandleBrokenEndian(uint16_t group, uint16_t elem) { // start endian swap mark for group found reversedEndian++; - SwitchSwapToBigEndian(); + SwitchByteSwapCode(); // fix the tag group = 0xfffe; elem = 0xe000; @@ -2444,14 +2384,77 @@ void Document::HandleBrokenEndian(uint16_t group, uint16_t elem) { // end of reversed endian group reversedEndian--; - SwitchSwapToBigEndian(); + SwitchByteSwapCode(); + } +} + +/** + * \brief Accesses the info from 0002,0010 : Transfer Syntax and TS + * else 1. + * @return The full Transfer Syntax Name (as opposed to Transfer Syntax UID) + */ +std::string Document::GetTransferSyntaxName() +{ + // use the TS (TS : Transfer Syntax) + std::string transferSyntax = GetEntry(0x0002,0x0010); + + if ( (transferSyntax.find(GDCM_NOTLOADED) < transferSyntax.length()) ) + { + gdcmErrorMacro( "Transfer Syntax not loaded. " << std::endl + << "Better you increase MAX_SIZE_LOAD_ELEMENT_VALUE" ); + return "Uncompressed ACR-NEMA"; + } + if ( transferSyntax == GDCM_UNFOUND ) + { + gdcmVerboseMacro( "Unfound Transfer Syntax (0002,0010)"); + return "Uncompressed ACR-NEMA"; + } + + // we do it only when we need it + const TSKey &tsName = Global::GetTS()->GetValue( transferSyntax ); + + // Global::GetTS() is a global static you shall never try to delete it! + return tsName; +} + +/** + * \brief Group 0002 is always coded Little Endian + * whatever Transfer Syntax is + * @return no return + */ +void Document::HandleOutOfGroup0002(uint16_t &group, uint16_t &elem) +{ + // Endian reversion. Some files contain groups of tags with reversed endianess. + if ( !Group0002Parsed && group != 0x0002) + { + Group0002Parsed = true; + // we just came out of group 0002 + // if Transfer syntax is Big Endian we have to change CheckSwap + + std::string ts = GetTransferSyntax(); + if ( !Global::GetTS()->IsTransferSyntax(ts) ) + { + gdcmVerboseMacro("True DICOM File, with NO Tansfer Syntax: " << ts ); + return; + } + + // FIXME Strangely, this works with + //'Implicit VR Transfer Syntax (GE Private) + if ( Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ExplicitVRBigEndian ) + { + gdcmVerboseMacro("Transfer Syntax Name = [" + << GetTransferSyntaxName() << "]" ); + SwitchByteSwapCode(); + group = SwapShort(group); + elem = SwapShort(elem); + } } } /** * \brief Read the next tag but WITHOUT loading it's value * (read the 'Group Number', the 'Element Number', - * gets the Dict Entry + * gets the Dict Entry * gets the VR, gets the length, gets the offset value) * @return On succes the newly created DocEntry, NULL on failure. */ @@ -2473,7 +2476,13 @@ DocEntry *Document::ReadNextDocEntry() return 0; } + // Sometimes file contains groups of tags with reversed endianess. HandleBrokenEndian(group, elem); + +// In 'true DICOM' files Group 0002 is always little endian + if ( HasDCMPreamble ) + HandleOutOfGroup0002(group, elem); + std::string vr = FindDocEntryVR(); std::string realVR = vr; @@ -2497,11 +2506,14 @@ DocEntry *Document::ReadNextDocEntry() if( Filetype == ExplicitVR ) { // We thought this was explicit VR, but we end up with an - // implicit VR tag. Let's backtrack. - std::string msg; - msg = Util::Format("Falsely explicit vr file (%04x,%04x)\n", - newEntry->GetGroup(), newEntry->GetElement()); - gdcmVerboseMacro( msg.c_str() ); + // implicit VR tag. Let's backtrack. + if ( newEntry->GetGroup() != 0xfffe ) + { + std::string msg; + msg = Util::Format("Entry (%04x,%04x) should be Explicit VR\n", + newEntry->GetGroup(), newEntry->GetElement()); + gdcmVerboseMacro( msg.c_str() ); + } } newEntry->SetImplicitVR(); } @@ -2582,8 +2594,8 @@ bool Document::ReadTag(uint16_t testGroup, uint16_t testElement) << std::hex << testGroup << "," << testElement << ")" << std::endl << " but instead we encountered tag (" << std::hex << itemTagGroup << "," << itemTagElement << ")" - << std::dec - << " at address: " << (unsigned int)currentPosition ); + << " at address: " << " 0x(" << (unsigned int)currentPosition << ")" + ) ; Fp->seekg(positionOnEntry, std::ios::beg); return false; @@ -2607,8 +2619,6 @@ bool Document::ReadTag(uint16_t testGroup, uint16_t testElement) */ uint32_t Document::ReadTagLength(uint16_t testGroup, uint16_t testElement) { - long positionOnEntry = Fp->tellg(); - (void)positionOnEntry; if ( !ReadTag(testGroup, testElement) ) { @@ -2621,7 +2631,7 @@ uint32_t Document::ReadTagLength(uint16_t testGroup, uint16_t testElement) { gdcmVerboseMacro( "Basic Item Length is: " << itemLength << std::endl - << " at address: " << (unsigned int)currentPosition); + << " at address: " << std::hex << (unsigned int)currentPosition); } return itemLength; } @@ -2667,8 +2677,8 @@ void Document::ReadAndSkipEncapsulatedBasicOffsetTable() */ void Document::ComputeRLEInfo() { - TransferSyntaxType ts = GetTransferSyntax(); - if ( ts != RLELossless ) + std::string ts = GetTransferSyntax(); + if ( !Global::GetTS()->IsRLELossless(ts) ) { return; } @@ -2765,7 +2775,8 @@ void Document::ComputeRLEInfo() void Document::ComputeJPEGFragmentInfo() { // If you need to, look for comments of ComputeRLEInfo(). - if ( ! IsJPEG() ) + std::string ts = GetTransferSyntax(); + if ( ! Global::GetTS()->IsJPEG(ts) ) { return; } @@ -2805,7 +2816,7 @@ void Document::ComputeJPEGFragmentInfo() * when recursively walking the given set. * @param set The structure to be traversed (recursively). */ -void Document::BuildFlatHashTableRecurse( TagDocEntryHT &builtHT, +/*void Document::BuildFlatHashTableRecurse( TagDocEntryHT &builtHT, DocEntrySet *set ) { if (ElementSet *elementSet = dynamic_cast< ElementSet* > ( set ) ) @@ -2855,7 +2866,7 @@ void Document::BuildFlatHashTableRecurse( TagDocEntryHT &builtHT, } } -} +}*/ /** * \brief Build a \ref TagDocEntryHT (i.e. a std::map<>) from the current @@ -2880,12 +2891,12 @@ void Document::BuildFlatHashTableRecurse( TagDocEntryHT &builtHT, * \ref BuildFlatHashTable again... * @return The flat std::map<> we juste build. */ -TagDocEntryHT *Document::BuildFlatHashTable() +/*TagDocEntryHT *Document::BuildFlatHashTable() { TagDocEntryHT *FlatHT = new TagDocEntryHT; BuildFlatHashTableRecurse( *FlatHT, this ); return FlatHT; -} +}*/ @@ -2968,10 +2979,7 @@ int Document::ComputeGroup0002Length( FileType filetype ) bool found0002 = false; // for each zero-level Tag in the DCM Header - DocEntry *entry; - - Initialize(); - entry = GetNextEntry(); + DocEntry *entry = GetFirstEntry(); while(entry) { gr = entry->GetGroup();