X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;ds=inline;f=src%2FgdcmDocument.cxx;h=496742f7391f22854cd4ed42489c862b5bd3a11b;hb=e022eedb7541761440c9f21343db626d918d4980;hp=5dd5ef264c0fae05ec66b2724956abbe631da62e;hpb=1ce6ae86daabc8b64c9309acce4ecf137ee79f07;p=gdcm.git diff --git a/src/gdcmDocument.cxx b/src/gdcmDocument.cxx index 5dd5ef26..496742f7 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/31 12:19:34 $ - Version: $Revision: 1.217 $ + Date: $Date: 2005/02/09 21:37:45 $ + Version: $Revision: 1.226 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -30,6 +30,7 @@ #include #include +#include // For nthos: #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MINGW32__) @@ -42,7 +43,6 @@ namespace gdcm { - //----------------------------------------------------------------------------- // Refer to Document::CheckSwap() //const unsigned int Document::HEADER_LENGTH_TO_READ = 256; @@ -53,12 +53,13 @@ const unsigned int Document::MAX_SIZE_PRINT_ELEMENT_VALUE = 0x7fffffff; //----------------------------------------------------------------------------- // Constructor / Destructor - +// Constructors and destructors are protected to avoid user to invoke directly /** * \brief constructor * @param filename 'Document' (File or DicomDir) to be opened for parsing */ -Document::Document( std::string const &filename ) : ElementSet(-1) +Document::Document( std::string const &filename ) + :ElementSet(-1) { SetMaxSizeLoadEntry(MAX_SIZE_LOAD_ELEMENT_VALUE); Filename = filename; @@ -72,13 +73,12 @@ Document::Document( std::string const &filename ) : ElementSet(-1) Group0002Parsed = false; - gdcmVerboseMacro( "Starting parsing of file: " << Filename.c_str()); - // Fp->seekg( 0, std::ios::beg); - - Fp->seekg(0, std::ios::end); + gdcmWarningMacro( "Starting parsing of file: " << Filename.c_str()); + + Fp->seekg(0, std::ios::end); long lgt = Fp->tellg(); - Fp->seekg( 0, std::ios::beg); + Fp->seekg(0, std::ios::beg); CheckSwap(); long beg = Fp->tellg(); @@ -154,7 +154,8 @@ Document::Document( std::string const &filename ) : ElementSet(-1) * \brief This default constructor doesn't parse the file. You should * then invoke \ref Document::SetFileName and then the parsing. */ -Document::Document() : ElementSet(-1) +Document::Document() + :ElementSet(-1) { Fp = 0; @@ -174,31 +175,6 @@ Document::~Document () RefShaDict = NULL; } -//----------------------------------------------------------------------------- -// Print - -/** - * \brief Prints The Dict Entries of THE public Dicom Dictionary - * @param os ostream to print to - * @return - */ -void Document::PrintPubDict(std::ostream &os) -{ - RefPubDict->SetPrintLevel(PrintLevel); - RefPubDict->Print(os); -} - -/** - * \brief Prints The Dict Entries of THE shadow Dicom Dictionary - * @param os ostream to print to - * @return - */ -void Document::PrintShaDict(std::ostream &os) -{ - RefShaDict->SetPrintLevel(PrintLevel); - RefShaDict->Print(os); -} - //----------------------------------------------------------------------------- // Public /** @@ -249,13 +225,13 @@ bool Document::IsReadable() { if( Filetype == Unknown) { - gdcmVerboseMacro( "Wrong filetype"); + gdcmWarningMacro( "Wrong filetype"); return false; } if ( IsEmpty() ) { - gdcmVerboseMacro( "No tag in internal hash table."); + gdcmWarningMacro( "No tag in internal hash table."); return false; } @@ -356,7 +332,7 @@ std::string Document::GetTransferSyntaxName() } if ( transferSyntax == GDCM_UNFOUND ) { - gdcmVerboseMacro( "Unfound Transfer Syntax (0002,0010)"); + gdcmWarningMacro( "Unfound Transfer Syntax (0002,0010)"); return "Uncompressed ACR-NEMA"; } @@ -376,7 +352,7 @@ uint16_t Document::SwapShort(uint16_t a) { if ( SwapCode == 4321 || SwapCode == 2143 ) { - a = ((( a << 8 ) & 0x0ff00 ) | (( a >> 8 ) & 0x00ff ) ); + a = ((( a << 8 ) & 0xff00 ) | (( a >> 8 ) & 0x00ff ) ); } return a; } @@ -426,7 +402,7 @@ std::ifstream *Document::OpenFile() if(Fp) { - gdcmVerboseMacro( "File already open: " << Filename.c_str()); + gdcmWarningMacro( "File already open: " << Filename.c_str()); CloseFile(); } @@ -439,7 +415,7 @@ std::ifstream *Document::OpenFile() return 0; } - uint16_t zero; + uint16_t zero = 0; Fp->read((char*)&zero, (size_t)2); if( Fp->eof() ) { @@ -456,13 +432,13 @@ std::ifstream *Document::OpenFile() { std::string msg = Util::Format("ACR/DICOM with no preamble: (%04x)\n", zero); - gdcmVerboseMacro( msg.c_str() ); + gdcmWarningMacro( msg.c_str() ); return Fp; } //DICOM Fp->seekg(126L, std::ios::cur); - char dicm[4]; + char dicm[4] = {' ',' ',' ',' '}; Fp->read(dicm, (size_t)4); if( Fp->eof() ) { @@ -476,7 +452,7 @@ std::ifstream *Document::OpenFile() } CloseFile(); - gdcmVerboseMacro( "Not DICOM/ACR (missing preamble)" << Filename.c_str()); + gdcmWarningMacro( "Not DICOM/ACR (missing preamble)" << Filename.c_str()); return 0; } @@ -520,24 +496,23 @@ void Document::WriteContent(std::ofstream *fp, FileType filetype) fp->write("DICM", 4); } -/* - * \todo rewrite later, if really usefull - * - 'Group Length' element is optional in DICOM - * - but un-updated odd groups lengthes can causes pb - * (xmedcon breaker) - * - * if ( (filetype == ImplicitVR) || (filetype == ExplicitVR) ) - * UpdateGroupLength(false,filetype); - * if ( filetype == ACR) - * UpdateGroupLength(true,ACR); - */ - + /* + * \todo rewrite later, if really usefull + * - 'Group Length' element is optional in DICOM + * - but un-updated odd groups lengthes can causes pb + * (xmedcon breaker) + * + * if ( (filetype == ImplicitVR) || (filetype == ExplicitVR) ) + * UpdateGroupLength(false,filetype); + * if ( filetype == ACR) + * UpdateGroupLength(true,ACR); + */ + ElementSet::WriteContent(fp, filetype); // This one is recursive } // ----------------------------------------- // Content entries - /** * \brief Loads (from disk) the element content * when a string is not suitable @@ -579,7 +554,7 @@ void Document::LoadEntryBinArea(BinEntry *elem) uint8_t *a = new uint8_t[l]; if( !a ) { - gdcmVerboseMacro( "Cannot allocate BinEntry content"); + gdcmWarningMacro( "Cannot allocate BinEntry content"); return; } @@ -601,8 +576,7 @@ void Document::LoadEntryBinArea(BinEntry *elem) * \brief Loads the element while preserving the current * underlying file position indicator as opposed to * LoadDocEntry that modifies it. - * @param entry Header Entry whose value will be loaded. - * @return + * @param entry DocEntry whose value will be loaded. */ void Document::LoadDocEntrySafe(DocEntry *entry) { @@ -614,14 +588,76 @@ void Document::LoadDocEntrySafe(DocEntry *entry) } } +/** + * \brief Compares two documents, according to \ref DicomDir rules + * \warning Does NOT work with ACR-NEMA files + * \todo Find a trick to solve the pb (use RET fields ?) + * @param document to compare with current one + * @return true if 'smaller' + */ +bool Document::operator<(Document &document) +{ + // Patient Name + std::string s1 = GetEntryValue(0x0010,0x0010); + std::string s2 = document.GetEntryValue(0x0010,0x0010); + if(s1 < s2) + { + return true; + } + else if( s1 > s2 ) + { + return false; + } + else + { + // Patient ID + s1 = GetEntryValue(0x0010,0x0020); + s2 = document.GetEntryValue(0x0010,0x0020); + if ( s1 < s2 ) + { + return true; + } + else if ( s1 > s2 ) + { + return false; + } + else + { + // Study Instance UID + s1 = GetEntryValue(0x0020,0x000d); + s2 = document.GetEntryValue(0x0020,0x000d); + if ( s1 < s2 ) + { + return true; + } + else if( s1 > s2 ) + { + return false; + } + else + { + // Serie Instance UID + s1 = GetEntryValue(0x0020,0x000e); + s2 = document.GetEntryValue(0x0020,0x000e); + if ( s1 < s2 ) + { + return true; + } + else if( s1 > s2 ) + { + return false; + } + } + } + } + return false; +} + //----------------------------------------------------------------------------- // Protected - -// Constructors and destructors are protected to avoid user to invoke directly - /** * \brief Reads a supposed to be 16 Bits integer - * (swaps it depending on processor endianity) + * (swaps it depending on processor endianness) * @return read value */ uint16_t Document::ReadInt16() @@ -643,7 +679,7 @@ uint16_t Document::ReadInt16() /** * \brief Reads a supposed to be 32 Bits integer - * (swaps it depending on processor endianity) + * (swaps it depending on processor endianness) * @return read value */ uint32_t Document::ReadInt32() @@ -721,6 +757,16 @@ int Document::ComputeGroup0002Length( FileType filetype ) //----------------------------------------------------------------------------- // Private +/** + * \brief Loads all the needed Dictionaries + * \warning NOT end user intended method ! + */ +void Document::Initialize() +{ + RefPubDict = Global::GetDicts()->GetDefaultPubDict(); + RefShaDict = NULL; + Filetype = Unknown; +} /** * \brief Parses a DocEntrySet (Zero-level DocEntries or SQ Item DocEntries) @@ -764,7 +810,7 @@ void Document::ParseDES(DocEntrySet *set, long offset, !Global::GetVR()->IsVROfBinaryRepresentable(vr) ) { ////// Neither ValEntry NOR BinEntry: should mean UNKOWN VR - gdcmVerboseMacro( std::hex << newDocEntry->GetGroup() + gdcmWarningMacro( std::hex << newDocEntry->GetGroup() << "|" << newDocEntry->GetElement() << " : Neither Valentry, nor BinEntry." "Probably unknown VR."); @@ -1112,7 +1158,7 @@ void Document::LoadDocEntry(DocEntry *entry) if( length % 2 ) { newValue = Util::DicomString(str, length+1); - gdcmVerboseMacro("Warning: bad length: " << length << + gdcmWarningMacro("Warning: bad length: " << length << ",For string :" << newValue.c_str()); // Since we change the length of string update it length //entry->SetReadLength(length+1); @@ -1127,7 +1173,7 @@ void Document::LoadDocEntry(DocEntry *entry) { if ( Fp->fail() || Fp->eof()) { - gdcmVerboseMacro("Unread element value"); + gdcmWarningMacro("Unread element value"); valEntry->SetValue(GDCM_UNREAD); return; } @@ -1327,7 +1373,7 @@ uint32_t Document::FindDocEntryLengthOBOrOW() if ( group != 0xfffe || ( ( elem != 0xe0dd ) && ( elem != 0xe000 ) ) ) { long filePosition = Fp->tellg(); - gdcmVerboseMacro( "Neither an Item tag nor a Sequence delimiter tag on :" + gdcmWarningMacro( "Neither an Item tag nor a Sequence delimiter tag on :" << std::hex << group << " , " << elem << ") -before- position x(" << filePosition << ")" ); @@ -1392,9 +1438,6 @@ std::string Document::FindDocEntryVR() */ bool Document::CheckDocEntryVR(VRKey vr) { - // CLEANME searching the dicom_vr at each occurence is expensive. - // PostPone this test in an optional integrity check at the end - // of parsing or only in debug mode. if ( !Global::GetVR()->IsValidVR(vr) ) return false; @@ -1574,7 +1617,7 @@ void Document::FixDocEntryFoundLength(DocEntry *entry, if ( foundLength % 2) { - gdcmVerboseMacro( "Warning : Tag with uneven length " << foundLength + gdcmWarningMacro( "Warning : Tag with uneven length " << foundLength << " in x(" << std::hex << gr << "," << elem <<")"); } @@ -1633,10 +1676,10 @@ void Document::FixDocEntryFoundLength(DocEntry *entry, */ bool Document::IsDocEntryAnInteger(DocEntry *entry) { - uint16_t elem = entry->GetElement(); - uint16_t group = entry->GetGroup(); + uint16_t elem = entry->GetElement(); + uint16_t group = entry->GetGroup(); const std::string &vr = entry->GetVR(); - uint32_t length = entry->GetLength(); + uint32_t length = entry->GetLength(); // When we have some semantics on the element we just read, and if we // a priori know we are dealing with an integer, then we shall be @@ -1657,7 +1700,7 @@ bool Document::IsDocEntryAnInteger(DocEntry *entry) // encounter such an ill-formed image, we simply display a warning // message and proceed on parsing (while crossing fingers). long filePosition = Fp->tellg(); - gdcmVerboseMacro( "Erroneous Group Length element length on : (" + gdcmWarningMacro( "Erroneous Group Length element length on : (" << std::hex << group << " , " << elem << ") -before- position x(" << filePosition << ")" << "lgt : " << length ); @@ -1671,17 +1714,6 @@ bool Document::IsDocEntryAnInteger(DocEntry *entry) return false; } -/** - * \brief Loads all the needed Dictionaries - * \warning NOT end user intended method ! - */ -void Document::Initialize() -{ - RefPubDict = Global::GetDicts()->GetDefaultPubDict(); - RefShaDict = NULL; - Filetype = Unknown; -} - /** * \brief Discover what the swap code is (among little endian, big endian, * bad little endian, bad big endian). @@ -1722,7 +1754,7 @@ bool Document::CheckSwap() char *entCur = deb + 128; if( memcmp(entCur, "DICM", (size_t)4) == 0 ) { - gdcmVerboseMacro( "Looks like DICOM Version3 (preamble + DCM)" ); + gdcmWarningMacro( "Looks like DICOM Version3 (preamble + DCM)" ); // Group 0002 should always be VR, and the first element 0000 // Let's be carefull (so many wrong headers ...) @@ -1754,24 +1786,24 @@ bool Document::CheckSwap() // instead of just checking for UL, OB and UI !? group 0000 { Filetype = ExplicitVR; - gdcmVerboseMacro( "Group 0002 : Explicit Value Representation"); + gdcmWarningMacro( "Group 0002 : Explicit Value Representation"); } else { Filetype = ImplicitVR; - gdcmVerboseMacro( "Group 0002 :Not an explicit Value Representation;" + gdcmWarningMacro( "Group 0002 :Not an explicit Value Representation;" << "Looks like a bugged Header!"); } if ( net2host ) { SwapCode = 4321; - gdcmVerboseMacro( "HostByteOrder != NetworkByteOrder"); + gdcmWarningMacro( "HostByteOrder != NetworkByteOrder"); } else { SwapCode = 1234; - gdcmVerboseMacro( "HostByteOrder = NetworkByteOrder"); + gdcmWarningMacro( "HostByteOrder = NetworkByteOrder"); } // Position the file position indicator at first tag @@ -1784,7 +1816,7 @@ bool Document::CheckSwap() // Alas, this is not a DicomV3 file and whatever happens there is no file // preamble. We can reset the file position indicator to where the data // is (i.e. the beginning of the file). - gdcmVerboseMacro( "Not a DICOM Version3 file"); + gdcmWarningMacro( "Not a DICOM Version3 file"); Fp->seekg(0, std::ios::beg); // Our next best chance would be to be considering a 'clean' ACR/NEMA file. @@ -1859,7 +1891,7 @@ bool Document::CheckSwap() Filetype = ACR; return true; default : - gdcmVerboseMacro( "ACR/NEMA unfound swap info (Really hopeless !)"); + gdcmWarningMacro( "ACR/NEMA unfound swap info (Really hopeless !)"); Filetype = Unknown; return false; } @@ -1871,7 +1903,7 @@ bool Document::CheckSwap() */ void Document::SwitchByteSwapCode() { - gdcmVerboseMacro( "Switching Byte Swap code from "<< SwapCode); + gdcmWarningMacro( "Switching Byte Swap code from "<< SwapCode); if ( SwapCode == 1234 ) { SwapCode = 4321; @@ -1949,7 +1981,6 @@ DocEntry *Document::ReadNextDocEntry() { // We reached the EOF (or an error occured) therefore // header parsing has to be considered as finished. - //std::cout << e; return 0; } @@ -1989,7 +2020,7 @@ DocEntry *Document::ReadNextDocEntry() std::string msg; msg = Util::Format("Entry (%04x,%04x) should be Explicit VR\n", newEntry->GetGroup(), newEntry->GetElement()); - gdcmVerboseMacro( msg.c_str() ); + gdcmWarningMacro( msg.c_str() ); } } newEntry->SetImplicitVR(); @@ -2057,7 +2088,7 @@ void Document::HandleOutOfGroup0002(uint16_t &group, uint16_t &elem) std::string ts = GetTransferSyntax(); if ( !Global::GetTS()->IsTransferSyntax(ts) ) { - gdcmVerboseMacro("True DICOM File, with NO Tansfer Syntax: " << ts ); + gdcmWarningMacro("True DICOM File, with NO Tansfer Syntax: " << ts ); return; } @@ -2072,7 +2103,7 @@ void Document::HandleOutOfGroup0002(uint16_t &group, uint16_t &elem) //'Implicit VR Transfer Syntax (GE Private) if ( Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ExplicitVRBigEndian ) { - gdcmVerboseMacro("Transfer Syntax Name = [" + gdcmWarningMacro("Transfer Syntax Name = [" << GetTransferSyntaxName() << "]" ); SwitchByteSwapCode(); group = SwapShort(group); @@ -2081,183 +2112,8 @@ void Document::HandleOutOfGroup0002(uint16_t &group, uint16_t &elem) } } -// GenerateFreeTagKeyInGroup? -// --> What was it designed for ?!? -/** - * \brief Generate a free TagKey i.e. a TagKey that is not present - * in the TagHt dictionary. - * @param group The generated tag must belong to this group. - * @return The element of tag with given group which is fee. - */ -//uint32_t Document::GenerateFreeTagKeyInGroup(uint16_t group) -//{ -// for (uint32_t elem = 0; elem < UINT32_MAX; elem++) -// { -// TagKey key = DictEntry::TranslateToKey(group, elem); -// if (TagHT.count(key) == 0) -// { -// return elem; -// } -// } -// return UINT32_MAX; -//} - -/** - * \brief Compares two documents, according to \ref DicomDir rules - * \warning Does NOT work with ACR-NEMA files - * \todo Find a trick to solve the pb (use RET fields ?) - * @param document - * @return true if 'smaller' - */ -bool Document::operator<(Document &document) -{ - // Patient Name - std::string s1 = GetEntryValue(0x0010,0x0010); - std::string s2 = document.GetEntryValue(0x0010,0x0010); - if(s1 < s2) - { - return true; - } - else if( s1 > s2 ) - { - return false; - } - else - { - // Patient ID - s1 = GetEntryValue(0x0010,0x0020); - s2 = document.GetEntryValue(0x0010,0x0020); - if ( s1 < s2 ) - { - return true; - } - else if ( s1 > s2 ) - { - return false; - } - else - { - // Study Instance UID - s1 = GetEntryValue(0x0020,0x000d); - s2 = document.GetEntryValue(0x0020,0x000d); - if ( s1 < s2 ) - { - return true; - } - else if( s1 > s2 ) - { - return false; - } - else - { - // Serie Instance UID - s1 = GetEntryValue(0x0020,0x000e); - s2 = document.GetEntryValue(0x0020,0x000e); - if ( s1 < s2 ) - { - return true; - } - else if( s1 > s2 ) - { - return false; - } - } - } - } - return false; -} - -/* - * \brief Walk recursively the given \ref DocEntrySet, and feed - * the given hash table (\ref TagDocEntryHT) with all the - * \ref DocEntry (Dicom entries) encountered. - * This method does the job for \ref BuildFlatHashTable. - * @param builtHT Where to collect all the \ref DocEntry encountered - * when recursively walking the given set. - * @param set The structure to be traversed (recursively). - */ -/*void Document::BuildFlatHashTableRecurse( TagDocEntryHT &builtHT, - DocEntrySet *set ) -{ - if (ElementSet *elementSet = dynamic_cast< ElementSet* > ( set ) ) - { - TagDocEntryHT const ¤tHT = elementSet->GetTagHT(); - for( TagDocEntryHT::const_iterator i = currentHT.begin(); - i != currentHT.end(); - ++i) - { - DocEntry *entry = i->second; - if ( SeqEntry *seqEntry = dynamic_cast(entry) ) - { - const ListSQItem& items = seqEntry->GetSQItems(); - for( ListSQItem::const_iterator item = items.begin(); - item != items.end(); - ++item) - { - BuildFlatHashTableRecurse( builtHT, *item ); - } - continue; - } - builtHT[entry->GetKey()] = entry; - } - return; - } - - if (SQItem *SQItemSet = dynamic_cast< SQItem* > ( set ) ) - { - const ListDocEntry& currentList = SQItemSet->GetDocEntries(); - for (ListDocEntry::const_iterator i = currentList.begin(); - i != currentList.end(); - ++i) - { - DocEntry *entry = *i; - if ( SeqEntry *seqEntry = dynamic_cast(entry) ) - { - const ListSQItem& items = seqEntry->GetSQItems(); - for( ListSQItem::const_iterator item = items.begin(); - item != items.end(); - ++item) - { - BuildFlatHashTableRecurse( builtHT, *item ); - } - continue; - } - builtHT[entry->GetKey()] = entry; - } - - } -}*/ - -/* - * \brief Build a \ref TagDocEntryHT (i.e. a std::map<>) from the current - * Document. - * - * The structure used by a Document (through \ref ElementSet), - * in order to hold the parsed entries of a Dicom header, is a recursive - * one. This is due to the fact that the sequences (when present) - * can be nested. Additionaly, the sequence items (represented in - * gdcm as \ref SQItem) add an extra complexity to the data - * structure. Hence, a gdcm user whishing to visit all the entries of - * a Dicom header will need to dig in the gdcm internals (which - * implies exposing all the internal data structures to the API). - * In order to avoid this burden to the user, \ref BuildFlatHashTable - * recursively builds a temporary hash table, which holds all the - * Dicom entries in a flat structure (a \ref TagDocEntryHT i.e. a - * std::map<>). - * \warning Of course there is NO integrity constrain between the - * returned \ref TagDocEntryHT and the \ref ElementSet used - * to build it. Hence if the underlying \ref ElementSet is - * altered, then it is the caller responsability to invoke - * \ref BuildFlatHashTable again... - * @return The flat std::map<> we juste build. - */ -/*TagDocEntryHT *Document::BuildFlatHashTable() -{ - TagDocEntryHT *FlatHT = new TagDocEntryHT; - BuildFlatHashTableRecurse( *FlatHT, this ); - return FlatHT; -}*/ - -} // end namespace gdcm +//----------------------------------------------------------------------------- +// Print //----------------------------------------------------------------------------- +} // end namespace gdcm