From 86db5f0e37e40668ff8afea3f65f3b5213707115 Mon Sep 17 00:00:00 2001 From: regrain Date: Wed, 26 Jan 2005 17:17:30 +0000 Subject: [PATCH] * move RLEInfo & JPEGInfo from Document to File -- BeNours --- ChangeLog | 3 + src/gdcmDocument.cxx | 450 ++++++++----------------------------------- src/gdcmDocument.h | 41 +--- src/gdcmFile.cxx | 366 ++++++++++++++++++++++++++++++----- src/gdcmFile.h | 27 ++- 5 files changed, 429 insertions(+), 458 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0201f1a5..d17d11c4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2005-01-26 Benoit Regrain + * move RLEInfo & JPEGInfo from Document to File + 2005-01-26 Benoit Regrain * FIX : remove so many friend between classes diff --git a/src/gdcmDocument.cxx b/src/gdcmDocument.cxx index dfe33bb3..a847f5e4 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/26 16:28:58 $ - Version: $Revision: 1.213 $ + Date: $Date: 2005/01/26 17:17:31 $ + Version: $Revision: 1.214 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -25,8 +25,6 @@ #include "gdcmDebug.h" #include "gdcmTS.h" #include "gdcmDictSet.h" -#include "gdcmRLEFramesInfo.h" -#include "gdcmJPEGFragmentsInfo.h" #include "gdcmDocEntrySet.h" #include "gdcmSQItem.h" @@ -62,9 +60,6 @@ const unsigned int Document::MAX_SIZE_PRINT_ELEMENT_VALUE = 0x7fffffff; */ Document::Document( std::string const &filename ) : ElementSet(-1) { - RLEInfo = NULL; - JPEGInfo = NULL; - SetMaxSizeLoadEntry(MAX_SIZE_LOAD_ELEMENT_VALUE); Filename = filename; Initialize(); @@ -161,9 +156,6 @@ Document::Document( std::string const &filename ) : ElementSet(-1) */ Document::Document() : ElementSet(-1) { - RLEInfo = NULL; - JPEGInfo = NULL; - Fp = 0; SetMaxSizeLoadEntry(MAX_SIZE_LOAD_ELEMENT_VALUE); @@ -180,11 +172,6 @@ Document::~Document () { RefPubDict = NULL; RefShaDict = NULL; - - if( RLEInfo ) - delete RLEInfo; - if( JPEGInfo ) - delete JPEGInfo; } //----------------------------------------------------------------------------- @@ -369,6 +356,7 @@ std::ifstream *Document::OpenFile() if(Fp) { gdcmVerboseMacro( "File already open: " << Filename.c_str()); + CloseFile(); } Fp = new std::ifstream(Filename.c_str(), std::ios::in | std::ios::binary); @@ -756,26 +744,6 @@ void Document::ParseDES(DocEntrySet *set, long offset, } } - if ( ( newDocEntry->GetGroup() == 0x7fe0 ) - && ( newDocEntry->GetElement() == 0x0010 ) ) - { - 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 ( Global::GetTS()->IsJPEG(ts) ) - { - long positionOnEntry = Fp->tellg(); - Fp->seekg( newDocEntry->GetOffset(), std::ios::beg ); - ComputeJPEGFragmentInfo(); - Fp->seekg( positionOnEntry, std::ios::beg ); - } - } - // Just to make sure we are at the beginning of next entry. SkipToNextDocEntry(newDocEntry); } @@ -1683,8 +1651,6 @@ void Document::Initialize() { RefPubDict = Global::GetDicts()->GetDefaultPubDict(); RefShaDict = NULL; - RLEInfo = new RLEFramesInfo; - JPEGInfo = new JPEGFragmentsInfo; Filetype = Unknown; } @@ -2138,255 +2104,108 @@ DocEntry *Document::ReadNextDocEntry() //} /** - * \brief Assuming the internal file pointer \ref Document::Fp - * is placed at the beginning of a tag check whether this - * tag is (TestGroup, TestElement). - * \warning On success the internal file pointer \ref Document::Fp - * is modified to point after the tag. - * On failure (i.e. when the tag wasn't the expected tag - * (TestGroup, TestElement) the internal file pointer - * \ref Document::Fp is restored to it's original position. - * @param testGroup The expected group of the tag. - * @param testElement The expected Element of the tag. - * @return True on success, false otherwise. + * \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::ReadTag(uint16_t testGroup, uint16_t testElement) +bool Document::operator<(Document &document) { - long positionOnEntry = Fp->tellg(); - long currentPosition = Fp->tellg(); // On debugging purposes - - //// Read the Item Tag group and element, and make - // sure they are what we expected: - uint16_t itemTagGroup; - uint16_t itemTagElement; - try - { - itemTagGroup = ReadInt16(); - itemTagElement = ReadInt16(); - } - catch ( FormatError e ) + // Patient Name + std::string s1 = GetEntryValue(0x0010,0x0010); + std::string s2 = document.GetEntryValue(0x0010,0x0010); + if(s1 < s2) { - //std::cerr << e << std::endl; - return false; + return true; } - if ( itemTagGroup != testGroup || itemTagElement != testElement ) + else if( s1 > s2 ) { - gdcmVerboseMacro( "Wrong Item Tag found:" - << " We should have found tag (" - << std::hex << testGroup << "," << testElement << ")" << std::endl - << " but instead we encountered tag (" - << std::hex << itemTagGroup << "," << itemTagElement << ")" - << " at address: " << " 0x(" << (unsigned int)currentPosition << ")" - ) ; - Fp->seekg(positionOnEntry, std::ios::beg); - return false; } - return true; -} - -/** - * \brief Assuming the internal file pointer \ref Document::Fp - * is placed at the beginning of a tag (TestGroup, TestElement), - * read the length associated to the Tag. - * \warning On success the internal file pointer \ref Document::Fp - * is modified to point after the tag and it's length. - * On failure (i.e. when the tag wasn't the expected tag - * (TestGroup, TestElement) the internal file pointer - * \ref Document::Fp is restored to it's original position. - * @param testGroup The expected group of the tag. - * @param testElement The expected Element of the tag. - * @return On success returns the length associated to the tag. On failure - * returns 0. - */ -uint32_t Document::ReadTagLength(uint16_t testGroup, uint16_t testElement) -{ - - if ( !ReadTag(testGroup, testElement) ) - { - return 0; - } - - //// Then read the associated Item Length - long currentPosition = Fp->tellg(); - uint32_t itemLength = ReadInt32(); - { - gdcmVerboseMacro( "Basic Item Length is: " - << itemLength << std::endl - << " at address: " << std::hex << (unsigned int)currentPosition); - } - return itemLength; -} - -/** - * \brief When parsing the Pixel Data of an encapsulated file, read - * the basic offset table (when present, and BTW dump it). - */ -void Document::ReadAndSkipEncapsulatedBasicOffsetTable() -{ - //// Read the Basic Offset Table Item Tag length... - uint32_t itemLength = ReadTagLength(0xfffe, 0xe000); - - // When present, read the basic offset table itself. - // Notes: - since the presence of this basic offset table is optional - // we can't rely on it for the implementation, and we will simply - // trash it's content (when present). - // - still, when present, we could add some further checks on the - // lengths, but we won't bother with such fuses for the time being. - if ( itemLength != 0 ) - { - char *basicOffsetTableItemValue = new char[itemLength + 1]; - Fp->read(basicOffsetTableItemValue, itemLength); - -#ifdef GDCM_DEBUG - for (unsigned int i=0; i < itemLength; i += 4 ) - { - uint32_t individualLength = str2num( &basicOffsetTableItemValue[i], - uint32_t); - gdcmVerboseMacro( "Read one length: " << - std::hex << individualLength ); - } -#endif //GDCM_DEBUG - - delete[] basicOffsetTableItemValue; - } -} - -/** - * \brief Parse pixel data from disk of [multi-]fragment RLE encoding. - * Compute the RLE extra information and store it in \ref RLEInfo - * for later pixel retrieval usage. - */ -void Document::ComputeRLEInfo() -{ - std::string ts = GetTransferSyntax(); - if ( !Global::GetTS()->IsRLELossless(ts) ) + else { - return; - } - - // Encoded pixel data: for the time being we are only concerned with - // Jpeg or RLE Pixel data encodings. - // As stated in PS 3.5-2003, section 8.2 p44: - // "If sent in Encapsulated Format (i.e. other than the Native Format) the - // value representation OB is used". - // Hence we expect an OB value representation. Concerning OB VR, - // the section PS 3.5-2003, section A.4.c p 58-59, states: - // "For the Value Representations OB and OW, the encoding shall meet the - // following specifications depending on the Data element tag:" - // [...snip...] - // - the first item in the sequence of items before the encoded pixel - // data stream shall be basic offset table item. The basic offset table - // item value, however, is not required to be present" - - ReadAndSkipEncapsulatedBasicOffsetTable(); - - // Encapsulated RLE Compressed Images (see PS 3.5-2003, Annex G) - // Loop on the individual frame[s] and store the information - // on the RLE fragments in a RLEFramesInfo. - // Note: - when only a single frame is present, this is a - // classical image. - // - when more than one frame are present, then we are in - // the case of a multi-frame image. - long frameLength; - while ( (frameLength = ReadTagLength(0xfffe, 0xe000)) ) - { - // Parse the RLE Header and store the corresponding RLE Segment - // Offset Table information on fragments of this current Frame. - // Note that the fragment pixels themselves are not loaded - // (but just skipped). - long frameOffset = Fp->tellg(); - - uint32_t nbRleSegments = ReadInt32(); - if ( nbRleSegments > 16 ) + // Patient ID + s1 = GetEntryValue(0x0010,0x0020); + s2 = document.GetEntryValue(0x0010,0x0020); + if ( s1 < s2 ) { - // There should be at most 15 segments (refer to RLEFrame class) - gdcmVerboseMacro( "Too many segments."); + return true; } - - uint32_t rleSegmentOffsetTable[16]; - for( int k = 1; k <= 15; k++ ) + else if ( s1 > s2 ) { - rleSegmentOffsetTable[k] = ReadInt32(); + return false; } - - // Deduce from both the RLE Header and the frameLength the - // fragment length, and again store this info in a - // RLEFramesInfo. - long rleSegmentLength[15]; - // skipping (not reading) RLE Segments - if ( nbRleSegments > 1) + else { - for(unsigned int k = 1; k <= nbRleSegments-1; k++) + // Study Instance UID + s1 = GetEntryValue(0x0020,0x000d); + s2 = document.GetEntryValue(0x0020,0x000d); + if ( s1 < s2 ) { - rleSegmentLength[k] = rleSegmentOffsetTable[k+1] - - rleSegmentOffsetTable[k]; - SkipBytes(rleSegmentLength[k]); - } - } - - rleSegmentLength[nbRleSegments] = frameLength - - rleSegmentOffsetTable[nbRleSegments]; - SkipBytes(rleSegmentLength[nbRleSegments]); - - // Store the collected info - RLEFrame *newFrame = new RLEFrame; - newFrame->SetNumberOfFragments(nbRleSegments); - for( unsigned int uk = 1; uk <= nbRleSegments; uk++ ) - { - newFrame->SetOffset(uk,frameOffset + rleSegmentOffsetTable[uk]); - newFrame->SetLength(uk,rleSegmentLength[uk]); - } - RLEInfo->AddFrame(newFrame); - } - - // Make sure that at the end of the item we encounter a 'Sequence - // Delimiter Item': - if ( !ReadTag(0xfffe, 0xe0dd) ) - { - gdcmVerboseMacro( "No sequence delimiter item at end of RLE item sequence"); + 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 Parse pixel data from disk of [multi-]fragment Jpeg encoding. - * Compute the jpeg extra information (fragment[s] offset[s] and - * length) and store it[them] in \ref JPEGInfo for later pixel - * retrieval usage. + * \brief Re-computes the length of a ACR-NEMA/Dicom group from a DcmHeader + * @param filetype Type of the File to be written */ -void Document::ComputeJPEGFragmentInfo() +int Document::ComputeGroup0002Length( FileType filetype ) { - // If you need to, look for comments of ComputeRLEInfo(). - std::string ts = GetTransferSyntax(); - if ( ! Global::GetTS()->IsJPEG(ts) ) + uint16_t gr; + std::string vr; + + int groupLength = 0; + bool found0002 = false; + + // for each zero-level Tag in the DCM Header + DocEntry *entry = GetFirstEntry(); + while(entry) { - return; - } - - ReadAndSkipEncapsulatedBasicOffsetTable(); - - // Loop on the fragments[s] and store the parsed information in a - // JPEGInfo. - long fragmentLength; - while ( (fragmentLength = ReadTagLength(0xfffe, 0xe000)) ) - { - long fragmentOffset = Fp->tellg(); - - // Store the collected info - JPEGFragment *newFragment = new JPEGFragment; - newFragment->SetOffset(fragmentOffset); - newFragment->SetLength(fragmentLength); - JPEGInfo->AddFragment(newFragment); + gr = entry->GetGroup(); - SkipBytes(fragmentLength); - } + if (gr == 0x0002) + { + found0002 = true; + vr = entry->GetVR(); + + if (filetype == ExplicitVR) + { + if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) + { + groupLength += 4; // explicit VR AND OB, OW, SQ : 4 more bytes + } + } + groupLength += 2 + 2 + 4 + entry->GetLength(); + } + else if (found0002 ) + break; - // Make sure that at the end of the item we encounter a 'Sequence - // Delimiter Item': - if ( !ReadTag(0xfffe, 0xe0dd) ) - { - gdcmVerboseMacro( "No sequence delimiter item at end of JPEG item sequence"); + entry = GetNextEntry(); } + return groupLength; } /* @@ -2480,113 +2299,6 @@ void Document::ComputeJPEGFragmentInfo() return FlatHT; }*/ - - -/** - * \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 Re-computes the length of a ACR-NEMA/Dicom group from a DcmHeader - * @param filetype Type of the File to be written - */ -int Document::ComputeGroup0002Length( FileType filetype ) -{ - uint16_t gr; - std::string vr; - - int groupLength = 0; - bool found0002 = false; - - // for each zero-level Tag in the DCM Header - DocEntry *entry = GetFirstEntry(); - while(entry) - { - gr = entry->GetGroup(); - - if (gr == 0x0002) - { - found0002 = true; - vr = entry->GetVR(); - - if (filetype == ExplicitVR) - { - if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) - { - groupLength += 4; // explicit VR AND OB, OW, SQ : 4 more bytes - } - } - groupLength += 2 + 2 + 4 + entry->GetLength(); - } - else if (found0002 ) - break; - - entry = GetNextEntry(); - } - return groupLength; -} - } // end namespace gdcm //----------------------------------------------------------------------------- diff --git a/src/gdcmDocument.h b/src/gdcmDocument.h index 4166b318..45388b1a 100644 --- a/src/gdcmDocument.h +++ b/src/gdcmDocument.h @@ -3,8 +3,8 @@ Program: gdcm Module: $RCSfile: gdcmDocument.h,v $ Language: C++ - Date: $Date: 2005/01/25 15:44:24 $ - Version: $Revision: 1.101 $ + Date: $Date: 2005/01/26 17:17:31 $ + Version: $Revision: 1.102 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -34,8 +34,6 @@ class ValEntry; class BinEntry; class SeqEntry; class Dict; -class RLEFramesInfo; -class JPEGFragmentsInfo; //----------------------------------------------------------------------------- /** @@ -49,10 +47,6 @@ public: FileType GetFileType(); std::string GetTransferSyntax(); - /// returns RLEFramesInfo - RLEFramesInfo *GetRLEInfo() { return RLEInfo; } - /// returns JPEGFragmentsInfo - JPEGFragmentsInfo *GetJPEGInfo() { return JPEGInfo; } // Dictionaries virtual void PrintPubDict (std::ostream &os = std::cout); @@ -108,10 +102,9 @@ protected: Document( std::string const &filename ); virtual ~Document(); - void ReadAndSkipEncapsulatedBasicOffsetTable(); - void ComputeRLEInfo(); - void ComputeJPEGFragmentInfo(); - // Entry + uint16_t ReadInt16() throw ( FormatError ); + uint32_t ReadInt32() throw ( FormatError ); + void SkipBytes(uint32_t); int ComputeGroup0002Length( FileType filetype ); @@ -147,21 +140,13 @@ protected: /// After opening the file, we read HEADER_LENGTH_TO_READ bytes. static const unsigned int HEADER_LENGTH_TO_READ; - /// \brief Elements whose value is longer than MAX_SIZE_LOAD_ELEMENT_VALUE /// are NOT loaded. static const unsigned int MAX_SIZE_LOAD_ELEMENT_VALUE; - /// \brief Elements whose value is longer than MAX_SIZE_PRINT_ELEMENT_VALUE /// are NOT printed. static const unsigned int MAX_SIZE_PRINT_ELEMENT_VALUE; - /// Store the RLE frames info obtained during parsing of pixels. - RLEFramesInfo *RLEInfo; - - /// Store the JPEG fragments info obtained during parsing of pixels. - JPEGFragmentsInfo *JPEGInfo; - private: // Methods // Read @@ -184,12 +169,6 @@ private: void FixDocEntryFoundLength(DocEntry *entry,uint32_t l); bool IsDocEntryAnInteger (DocEntry *entry); - uint16_t ReadInt16() throw ( FormatError ); - uint32_t ReadInt32() throw ( FormatError ); - void SkipBytes(uint32_t); - bool ReadTag(uint16_t, uint16_t); - uint32_t ReadTagLength(uint16_t, uint16_t); - void Initialize(); bool CheckSwap(); void SwitchByteSwapCode(); @@ -199,17 +178,12 @@ private: // DocEntry related utilities DocEntry *ReadNextDocEntry(); -// uint32_t GenerateFreeTagKeyInGroup(uint16_t group); -// void BuildFlatHashTableRecurse( TagDocEntryHT &builtHT, -// DocEntrySet *set ); - void HandleBrokenEndian (uint16_t &group, uint16_t &elem); void HandleOutOfGroup0002(uint16_t &group, uint16_t &elem); // Variables /// Public dictionary used to parse this header Dict *RefPubDict; - /// \brief Optional "shadow dictionary" (private elements) used to parse /// this header Dict *RefShaDict; @@ -225,8 +199,9 @@ private: /// is fixed to 64 bytes. uint32_t MaxSizePrintEntry; -private: - +// uint32_t GenerateFreeTagKeyInGroup(uint16_t group); +// void BuildFlatHashTableRecurse( TagDocEntryHT &builtHT, +// DocEntrySet *set ); }; } // end namespace gdcm diff --git a/src/gdcmFile.cxx b/src/gdcmFile.cxx index c58c8334..aa6db0bd 100644 --- a/src/gdcmFile.cxx +++ b/src/gdcmFile.cxx @@ -3,8 +3,8 @@ Program: gdcm Module: $RCSfile: gdcmFile.cxx,v $ Language: C++ - Date: $Date: 2005/01/26 09:49:54 $ - Version: $Revision: 1.201 $ + Date: $Date: 2005/01/26 17:17:31 $ + Version: $Revision: 1.202 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -23,8 +23,10 @@ #include "gdcmTS.h" #include "gdcmValEntry.h" #include "gdcmBinEntry.h" -#include //sscanf +#include "gdcmRLEFramesInfo.h" +#include "gdcmJPEGFragmentsInfo.h" +#include //sscanf #include namespace gdcm @@ -36,8 +38,11 @@ namespace gdcm * @param filename name of the file whose header we want to analyze */ File::File( std::string const &filename ): - Document( filename ) + Document( filename ) { + RLEInfo = new RLEFramesInfo; + JPEGInfo = new JPEGFragmentsInfo; + // for some ACR-NEMA images GrPixel, NumPixel is *not* 7fe0,0010 // We may encounter the 'RETired' (0x0028, 0x0200) tag // (Image Location") . This entry contains the number of @@ -60,9 +65,9 @@ File::File( std::string const &filename ): GrPixel = (uint16_t) atoi( imgLocation.c_str() ); } - // sometimes Image Location value doesn't follow - // the supposed processor endianness. - // see gdcmData/cr172241.dcm + // sometimes Image Location value doesn't follow + // the supposed processor endianness. + // see gdcmData/cr172241.dcm if ( GrPixel == 0xe07f ) { GrPixel = 0x7fe0; @@ -84,7 +89,17 @@ File::File( std::string const &filename ): DocEntry *entry = GetDocEntry(GrPixel, NumPixel); if ( entry != 0 ) { - + // Compute the RLE or JPEG info + OpenFile(); + std::string ts = GetTransferSyntax(); + Fp->seekg( entry->GetOffset(), std::ios::beg ); + if ( Global::GetTS()->IsRLELossless(ts) ) + ComputeRLEInfo(); + else if ( Global::GetTS()->IsJPEG(ts) ) + ComputeJPEGFragmentInfo(); + CloseFile(); + + // Change the created dict entry std::string PixelVR; // 8 bits allocated is a 'O Bytes' , as well as 24 (old ACR-NEMA RGB) // more than 8 (i.e 12, 16) is a 'O Words' @@ -93,10 +108,9 @@ File::File( std::string const &filename ): else PixelVR = "OW"; - DictEntry* newEntry = NewVirtualDictEntry( - GrPixel, NumPixel, - PixelVR, "PXL", "Pixel Data"); - + DictEntry* newEntry = NewVirtualDictEntry(GrPixel, NumPixel, + PixelVR, "PXL", "Pixel Data"); + // friend class hunting : should we *create* a new entry, // instead of modifying its DictEntry,in order not to use 'friend' ? entry->SetDictEntry( newEntry ); @@ -106,9 +120,11 @@ File::File( std::string const &filename ): /** * \brief Constructor used when we want to generate dicom files from scratch */ -File::File() - :Document() +File::File(): + Document() { + RLEInfo = new RLEFramesInfo; + JPEGInfo = new JPEGFragmentsInfo; InitializeDefaultFile(); } @@ -117,6 +133,10 @@ File::File() */ File::~File () { + if( RLEInfo ) + delete RLEInfo; + if( JPEGInfo ) + delete JPEGInfo; } /** @@ -1192,9 +1212,38 @@ int File::GetLUTNbits() return lutNbits; } +/** + * \brief gets the info from 0020,0037 : Image Orientation Patient + * (needed to organize DICOM files based on their x,y,z position) + * @param iop adress of the (6)float aray to receive values + * @return cosines of image orientation patient + */ +void File::GetImageOrientationPatient( float iop[6] ) +{ + std::string strImOriPat; + //iop is supposed to be float[6] + iop[0] = iop[1] = iop[2] = iop[3] = iop[4] = iop[5] = 0.; -//----------------------------------------------------------------------------- -// Protected + // 0020 0037 DS REL Image Orientation (Patient) + if ( (strImOriPat = GetEntryValue(0x0020,0x0037)) != GDCM_UNFOUND ) + { + if( sscanf( strImOriPat.c_str(), "%f\\%f\\%f\\%f\\%f\\%f", + &iop[0], &iop[1], &iop[2], &iop[3], &iop[4], &iop[5]) != 6 ) + { + gdcmVerboseMacro( "Wrong Image Orientation Patient (0020,0037). Less than 6 values were found." ); + } + } + //For ACR-NEMA + // 0020 0035 DS REL Image Orientation (RET) + else if ( (strImOriPat = GetEntryValue(0x0020,0x0035)) != GDCM_UNFOUND ) + { + if( sscanf( strImOriPat.c_str(), "%f\\%f\\%f\\%f\\%f\\%f", + &iop[0], &iop[1], &iop[2], &iop[3], &iop[4], &iop[5]) != 6 ) + { + gdcmVerboseMacro( "wrong Image Orientation Patient (0020,0035). Less than 6 values were found." ); + } + } +} /** * \brief anonymize a File (removes Patient's personal info) @@ -1277,39 +1326,8 @@ bool File::AnonymizeFile() return true; } -/** - * \brief gets the info from 0020,0037 : Image Orientation Patient - * (needed to organize DICOM files based on their x,y,z position) - * @param iop adress of the (6)float aray to receive values - * @return cosines of image orientation patient - */ -void File::GetImageOrientationPatient( float iop[6] ) -{ - std::string strImOriPat; - //iop is supposed to be float[6] - iop[0] = iop[1] = iop[2] = iop[3] = iop[4] = iop[5] = 0.; - - // 0020 0037 DS REL Image Orientation (Patient) - if ( (strImOriPat = GetEntryValue(0x0020,0x0037)) != GDCM_UNFOUND ) - { - if( sscanf( strImOriPat.c_str(), "%f\\%f\\%f\\%f\\%f\\%f", - &iop[0], &iop[1], &iop[2], &iop[3], &iop[4], &iop[5]) != 6 ) - { - gdcmVerboseMacro( "Wrong Image Orientation Patient (0020,0037). Less than 6 values were found." ); - } - } - //For ACR-NEMA - // 0020 0035 DS REL Image Orientation (RET) - else if ( (strImOriPat = GetEntryValue(0x0020,0x0035)) != GDCM_UNFOUND ) - { - if( sscanf( strImOriPat.c_str(), "%f\\%f\\%f\\%f\\%f\\%f", - &iop[0], &iop[1], &iop[2], &iop[3], &iop[4], &iop[5]) != 6 ) - { - gdcmVerboseMacro( "wrong Image Orientation Patient (0020,0035). Less than 6 values were found." ); - } - } -} - +//----------------------------------------------------------------------------- +// Protected /** * \brief Initialize a default DICOM File that should contain all the * field require by other reader. DICOM standard does not @@ -1389,6 +1407,256 @@ void File::InitializeDefaultFile() //----------------------------------------------------------------------------- // Private +/** + * \brief Parse pixel data from disk of [multi-]fragment RLE encoding. + * Compute the RLE extra information and store it in \ref RLEInfo + * for later pixel retrieval usage. + */ +void File::ComputeRLEInfo() +{ + std::string ts = GetTransferSyntax(); + if ( !Global::GetTS()->IsRLELossless(ts) ) + { + return; + } + + // Encoded pixel data: for the time being we are only concerned with + // Jpeg or RLE Pixel data encodings. + // As stated in PS 3.5-2003, section 8.2 p44: + // "If sent in Encapsulated Format (i.e. other than the Native Format) the + // value representation OB is used". + // Hence we expect an OB value representation. Concerning OB VR, + // the section PS 3.5-2003, section A.4.c p 58-59, states: + // "For the Value Representations OB and OW, the encoding shall meet the + // following specifications depending on the Data element tag:" + // [...snip...] + // - the first item in the sequence of items before the encoded pixel + // data stream shall be basic offset table item. The basic offset table + // item value, however, is not required to be present" + ReadAndSkipEncapsulatedBasicOffsetTable(); + + // Encapsulated RLE Compressed Images (see PS 3.5-2003, Annex G) + // Loop on the individual frame[s] and store the information + // on the RLE fragments in a RLEFramesInfo. + // Note: - when only a single frame is present, this is a + // classical image. + // - when more than one frame are present, then we are in + // the case of a multi-frame image. + long frameLength; + while ( (frameLength = ReadTagLength(0xfffe, 0xe000)) ) + { + // Parse the RLE Header and store the corresponding RLE Segment + // Offset Table information on fragments of this current Frame. + // Note that the fragment pixels themselves are not loaded + // (but just skipped). + long frameOffset = Fp->tellg(); + + uint32_t nbRleSegments = ReadInt32(); + if ( nbRleSegments > 16 ) + { + // There should be at most 15 segments (refer to RLEFrame class) + gdcmVerboseMacro( "Too many segments."); + } + + uint32_t rleSegmentOffsetTable[16]; + for( int k = 1; k <= 15; k++ ) + { + rleSegmentOffsetTable[k] = ReadInt32(); + } + + // Deduce from both the RLE Header and the frameLength the + // fragment length, and again store this info in a + // RLEFramesInfo. + long rleSegmentLength[15]; + // skipping (not reading) RLE Segments + if ( nbRleSegments > 1) + { + for(unsigned int k = 1; k <= nbRleSegments-1; k++) + { + rleSegmentLength[k] = rleSegmentOffsetTable[k+1] + - rleSegmentOffsetTable[k]; + SkipBytes(rleSegmentLength[k]); + } + } + + rleSegmentLength[nbRleSegments] = frameLength + - rleSegmentOffsetTable[nbRleSegments]; + SkipBytes(rleSegmentLength[nbRleSegments]); + + // Store the collected info + RLEFrame *newFrame = new RLEFrame; + newFrame->SetNumberOfFragments(nbRleSegments); + for( unsigned int uk = 1; uk <= nbRleSegments; uk++ ) + { + newFrame->SetOffset(uk,frameOffset + rleSegmentOffsetTable[uk]); + newFrame->SetLength(uk,rleSegmentLength[uk]); + } + RLEInfo->AddFrame(newFrame); + } + + // Make sure that at the end of the item we encounter a 'Sequence + // Delimiter Item': + if ( !ReadTag(0xfffe, 0xe0dd) ) + { + gdcmVerboseMacro( "No sequence delimiter item at end of RLE item sequence"); + } +} + +/** + * \brief Parse pixel data from disk of [multi-]fragment Jpeg encoding. + * Compute the jpeg extra information (fragment[s] offset[s] and + * length) and store it[them] in \ref JPEGInfo for later pixel + * retrieval usage. + */ +void File::ComputeJPEGFragmentInfo() +{ + // If you need to, look for comments of ComputeRLEInfo(). + std::string ts = GetTransferSyntax(); + if ( ! Global::GetTS()->IsJPEG(ts) ) + { + return; + } + + ReadAndSkipEncapsulatedBasicOffsetTable(); + + // Loop on the fragments[s] and store the parsed information in a + // JPEGInfo. + long fragmentLength; + while ( (fragmentLength = ReadTagLength(0xfffe, 0xe000)) ) + { + long fragmentOffset = Fp->tellg(); + + // Store the collected info + JPEGFragment *newFragment = new JPEGFragment; + newFragment->SetOffset(fragmentOffset); + newFragment->SetLength(fragmentLength); + JPEGInfo->AddFragment(newFragment); + + SkipBytes(fragmentLength); + } + + // Make sure that at the end of the item we encounter a 'Sequence + // Delimiter Item': + if ( !ReadTag(0xfffe, 0xe0dd) ) + { + gdcmVerboseMacro( "No sequence delimiter item at end of JPEG item sequence"); + } +} + +/** + * \brief Assuming the internal file pointer \ref Document::Fp + * is placed at the beginning of a tag check whether this + * tag is (TestGroup, TestElement). + * \warning On success the internal file pointer \ref Document::Fp + * is modified to point after the tag. + * On failure (i.e. when the tag wasn't the expected tag + * (TestGroup, TestElement) the internal file pointer + * \ref Document::Fp is restored to it's original position. + * @param testGroup The expected group of the tag. + * @param testElement The expected Element of the tag. + * @return True on success, false otherwise. + */ +bool File::ReadTag(uint16_t testGroup, uint16_t testElement) +{ + long positionOnEntry = Fp->tellg(); + long currentPosition = Fp->tellg(); // On debugging purposes + + //// Read the Item Tag group and element, and make + // sure they are what we expected: + uint16_t itemTagGroup; + uint16_t itemTagElement; + try + { + itemTagGroup = ReadInt16(); + itemTagElement = ReadInt16(); + } + catch ( FormatError e ) + { + //std::cerr << e << std::endl; + return false; + } + if ( itemTagGroup != testGroup || itemTagElement != testElement ) + { + gdcmVerboseMacro( "Wrong Item Tag found:" + << " We should have found tag (" + << std::hex << testGroup << "," << testElement << ")" << std::endl + << " but instead we encountered tag (" + << std::hex << itemTagGroup << "," << itemTagElement << ")" + << " at address: " << " 0x(" << (unsigned int)currentPosition << ")" + ) ; + Fp->seekg(positionOnEntry, std::ios::beg); + + return false; + } + return true; +} + +/** + * \brief Assuming the internal file pointer \ref Document::Fp + * is placed at the beginning of a tag (TestGroup, TestElement), + * read the length associated to the Tag. + * \warning On success the internal file pointer \ref Document::Fp + * is modified to point after the tag and it's length. + * On failure (i.e. when the tag wasn't the expected tag + * (TestGroup, TestElement) the internal file pointer + * \ref Document::Fp is restored to it's original position. + * @param testGroup The expected group of the tag. + * @param testElement The expected Element of the tag. + * @return On success returns the length associated to the tag. On failure + * returns 0. + */ +uint32_t File::ReadTagLength(uint16_t testGroup, uint16_t testElement) +{ + + if ( !ReadTag(testGroup, testElement) ) + { + return 0; + } + + //// Then read the associated Item Length + long currentPosition = Fp->tellg(); + uint32_t itemLength = ReadInt32(); + { + gdcmVerboseMacro( "Basic Item Length is: " + << itemLength << std::endl + << " at address: " << std::hex << (unsigned int)currentPosition); + } + return itemLength; +} + +/** + * \brief When parsing the Pixel Data of an encapsulated file, read + * the basic offset table (when present, and BTW dump it). + */ +void File::ReadAndSkipEncapsulatedBasicOffsetTable() +{ + //// Read the Basic Offset Table Item Tag length... + uint32_t itemLength = ReadTagLength(0xfffe, 0xe000); + + // When present, read the basic offset table itself. + // Notes: - since the presence of this basic offset table is optional + // we can't rely on it for the implementation, and we will simply + // trash it's content (when present). + // - still, when present, we could add some further checks on the + // lengths, but we won't bother with such fuses for the time being. + if ( itemLength != 0 ) + { + char *basicOffsetTableItemValue = new char[itemLength + 1]; + Fp->read(basicOffsetTableItemValue, itemLength); + +#ifdef GDCM_DEBUG + for (unsigned int i=0; i < itemLength; i += 4 ) + { + uint32_t individualLength = str2num( &basicOffsetTableItemValue[i], + uint32_t); + gdcmVerboseMacro( "Read one length: " << + std::hex << individualLength ); + } +#endif //GDCM_DEBUG + + delete[] basicOffsetTableItemValue; + } +} //----------------------------------------------------------------------------- diff --git a/src/gdcmFile.h b/src/gdcmFile.h index d7d8a656..477b55e9 100644 --- a/src/gdcmFile.h +++ b/src/gdcmFile.h @@ -3,8 +3,8 @@ Program: gdcm Module: $RCSfile: gdcmFile.h,v $ Language: C++ - Date: $Date: 2005/01/23 10:12:34 $ - Version: $Revision: 1.99 $ + Date: $Date: 2005/01/26 17:17:31 $ + Version: $Revision: 1.100 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -23,6 +23,8 @@ namespace gdcm { +class RLEFramesInfo; +class JPEGFragmentsInfo; //----------------------------------------------------------------------------- // Dicom Part 3.3 Compliant @@ -91,7 +93,6 @@ enum ModalityType { */ //----------------------------------------------------------------------------- - class GDCM_EXPORT File : public Document { protected: @@ -166,17 +167,29 @@ public: /// Accessor to \ref File::NumPixel uint16_t GetNumPixel() { return NumPixel; } + /// Replace patient's specific information by 'anonymous' + bool AnonymizeFile(); + bool Write(std::string fileName, FileType filetype); + RLEFramesInfo *GetRLEInfo() { return RLEInfo; } + JPEGFragmentsInfo *GetJPEGInfo() { return JPEGInfo; } + +protected: /// Initialize DICOM File when none void InitializeDefaultFile(); -protected: - /// Replace patient's specific information by 'anonymous' - bool AnonymizeFile(); + /// Store the RLE frames info obtained during parsing of pixels. + RLEFramesInfo *RLEInfo; + /// Store the JPEG fragments info obtained during parsing of pixels. + JPEGFragmentsInfo *JPEGInfo; private: - + void ComputeRLEInfo(); + void ComputeJPEGFragmentInfo(); + void ReadAndSkipEncapsulatedBasicOffsetTable(); + bool ReadTag(uint16_t, uint16_t); + uint32_t ReadTagLength(uint16_t, uint16_t); }; } // end namespace gdcm -- 2.45.1