X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=src%2FgdcmFile.cxx;h=ffa7ae08bb998bb79502119382fcd58471e55adf;hb=d0dd94b3bab6007ec04534a451f456ceebd6dca7;hp=4adfcd303d266e474491c21912091fd480a07701;hpb=0591e5cbe0d6136167a7ff5f2086d893f2111d58;p=gdcm.git diff --git a/src/gdcmFile.cxx b/src/gdcmFile.cxx index 4adfcd30..ffa7ae08 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/21 12:37:15 $ - Version: $Revision: 1.196 $ + Date: $Date: 2005/01/28 15:10:56 $ + Version: $Revision: 1.205 $ 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 @@ -45,11 +50,11 @@ File::File( std::string const &filename ): // is found by indirection through the "Image Location"). // Inside the group pointed by "Image Location" the searched element // is conventionally the element 0x0010 (when the norm is respected). - // When the "Image Location" is Missing we default to group 0x7fe0. + // When the "Image Location" is missing we default to group 0x7fe0. // Note: this IS the right place for the code // Image Location - const std::string &imgLocation = GetEntry(0x0028, 0x0200); + const std::string &imgLocation = GetEntryValue(0x0028, 0x0200); if ( imgLocation == GDCM_UNFOUND ) { // default value @@ -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,29 +89,58 @@ File::File( std::string const &filename ): DocEntry *entry = GetDocEntry(GrPixel, NumPixel); if ( entry != 0 ) { - - 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' - if ( GetBitsAllocated() == 8 || GetBitsAllocated() == 24 ) - PixelVR = "OB"; - else - PixelVR = "OW"; - - DictEntry* newEntry = NewVirtualDictEntry( - GrPixel, NumPixel, - PixelVR, "PXL", "Pixel Data"); - - entry->SetDictEntry( newEntry ); + // 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(); + + // Create a new BinEntry to change the the DictEntry + // The changed DictEntry will have + // - a correct PixelVR OB or OW) + // - a VM to "PXL" + // - the name to "Pixel Data" + BinEntry *oldEntry = dynamic_cast(entry); + if(oldEntry) + { + 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' + if ( GetBitsAllocated() == 8 || GetBitsAllocated() == 24 ) + PixelVR = "OB"; + else + PixelVR = "OW"; + + // Change only made if usefull + if( PixelVR != oldEntry->GetVR() ) + { + DictEntry* newDict = NewVirtualDictEntry(GrPixel,NumPixel, + PixelVR,"1","Pixel Data"); + + BinEntry *newEntry = new BinEntry(newDict); + newEntry->Copy(entry); + newEntry->SetBinArea(oldEntry->GetBinArea(),oldEntry->IsSelfArea()); + oldEntry->SetSelfArea(false); + + RemoveEntry(oldEntry); + AddEntry(newEntry); + } + } } } /** * \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(); } @@ -115,6 +149,10 @@ File::File() */ File::~File () { + if( RLEInfo ) + delete RLEInfo; + if( JPEGInfo ) + delete JPEGInfo; } /** @@ -123,7 +161,7 @@ File::~File () * then writes in a file all the (Dicom Elements) included the Pixels * @param fileName file name to write to * @param filetype Type of the File to be written - * (ACR-NEMA, ExplicitVR, ImplicitVR) + * (ACR, ExplicitVR, ImplicitVR) */ bool File::Write(std::string fileName, FileType filetype) { @@ -135,13 +173,20 @@ bool File::Write(std::string fileName, FileType filetype) return false; } - // Bits Allocated - if ( GetEntry(0x0028,0x0100) == "12") + // Entry : 0002|0000 = group length -> recalculated + ValEntry *e0002 = GetValEntry(0x0002,0x0000); + if( e0002 ) { - SetEntry("16", 0x0028,0x0100); + std::ostringstream sLen; + sLen << ComputeGroup0002Length(filetype); + e0002->SetValue(sLen.str()); } - /// \todo correct 'Pixel group' Length if necessary + // Bits Allocated + if ( GetEntryValue(0x0028,0x0100) == "12") + { + SetValEntry("16", 0x0028,0x0100); + } int i_lgPix = GetEntryLength(GrPixel, NumPixel); if (i_lgPix != -2) @@ -149,15 +194,14 @@ bool File::Write(std::string fileName, FileType filetype) // no (GrPixel, NumPixel) element std::string s_lgPix = Util::Format("%d", i_lgPix+12); s_lgPix = Util::DicomString( s_lgPix.c_str() ); - ReplaceOrCreate(s_lgPix,GrPixel, 0x0000); + InsertValEntry(s_lgPix,GrPixel, 0x0000); } // FIXME : should be nice if we could move it to File // (or in future gdcmPixelData class) // Drop Palette Color, if necessary - - if ( GetEntry(0x0028,0x0002).c_str()[0] == '3' ) + if ( GetEntryValue(0x0028,0x0002).c_str()[0] == '3' ) { // if SamplesPerPixel = 3, sure we don't need any LUT ! // Drop 0028|1101, 0028|1102, 0028|1103 @@ -257,7 +301,7 @@ bool File::IsReadable() return false; } - const std::string &res = GetEntry(0x0028, 0x0005); + const std::string &res = GetEntryValue(0x0028, 0x0005); if ( res != GDCM_UNFOUND && atoi(res.c_str()) > 4 ) { return false; // Image Dimensions @@ -289,7 +333,7 @@ bool File::IsReadable() */ int File::GetXSize() { - const std::string &strSize = GetEntry(0x0028,0x0011); + const std::string &strSize = GetEntryValue(0x0028,0x0011); if ( strSize == GDCM_UNFOUND ) { return 0; @@ -306,7 +350,7 @@ int File::GetXSize() */ int File::GetYSize() { - const std::string &strSize = GetEntry(0x0028,0x0010); + const std::string &strSize = GetEntryValue(0x0028,0x0010); if ( strSize != GDCM_UNFOUND ) { return atoi( strSize.c_str() ); @@ -333,14 +377,14 @@ int File::GetZSize() { // Both DicomV3 and ACR/Nema consider the "Number of Frames" // as the third dimension. - const std::string &strSize = GetEntry(0x0028,0x0008); + const std::string &strSize = GetEntryValue(0x0028,0x0008); if ( strSize != GDCM_UNFOUND ) { return atoi( strSize.c_str() ); } // We then consider the "Planes" entry as the third dimension - const std::string &strSize2 = GetEntry(0x0028,0x0012); + const std::string &strSize2 = GetEntryValue(0x0028,0x0012); if ( strSize2 != GDCM_UNFOUND ) { return atoi( strSize2.c_str() ); @@ -357,7 +401,7 @@ int File::GetZSize() float File::GetXSpacing() { float xspacing, yspacing; - const std::string &strSpacing = GetEntry(0x0028,0x0030); + const std::string &strSpacing = GetEntryValue(0x0028,0x0030); if ( strSpacing == GDCM_UNFOUND ) { @@ -405,7 +449,7 @@ float File::GetXSpacing() float File::GetYSpacing() { float yspacing = 1.; - std::string strSpacing = GetEntry(0x0028,0x0030); + std::string strSpacing = GetEntryValue(0x0028,0x0030); if ( strSpacing == GDCM_UNFOUND ) { @@ -440,12 +484,12 @@ float File::GetZSpacing() // Si le Spacing Between Slices est Missing, // on suppose que les coupes sont jointives - const std::string &strSpacingBSlices = GetEntry(0x0018,0x0088); + const std::string &strSpacingBSlices = GetEntryValue(0x0018,0x0088); if ( strSpacingBSlices == GDCM_UNFOUND ) { gdcmVerboseMacro("Unfound Spacing Between Slices (0018,0088)"); - const std::string &strSliceThickness = GetEntry(0x0018,0x0050); + const std::string &strSliceThickness = GetEntryValue(0x0018,0x0050); if ( strSliceThickness == GDCM_UNFOUND ) { gdcmVerboseMacro("Unfound Slice Thickness (0018,0050)"); @@ -472,7 +516,7 @@ float File::GetRescaleIntercept() { float resInter = 0.; /// 0028 1052 DS IMG Rescale Intercept - const std::string &strRescInter = GetEntry(0x0028,0x1052); + const std::string &strRescInter = GetEntryValue(0x0028,0x1052); if ( strRescInter != GDCM_UNFOUND ) { if( sscanf( strRescInter.c_str(), "%f", &resInter) != 1 ) @@ -493,7 +537,7 @@ float File::GetRescaleSlope() { float resSlope = 1.; //0028 1053 DS IMG Rescale Slope - std::string strRescSlope = GetEntry(0x0028,0x1053); + std::string strRescSlope = GetEntryValue(0x0028,0x1053); if ( strRescSlope != GDCM_UNFOUND ) { if( sscanf( strRescSlope.c_str(), "%f", &resSlope) != 1) @@ -522,12 +566,12 @@ int File::GetNumberOfScalarComponents() // 0028 0100 US IMG Bits Allocated // (in order no to be messed up by old RGB images) - if ( GetEntry(0x0028,0x0100) == "24" ) + if ( GetEntryValue(0x0028,0x0100) == "24" ) { return 3; } - std::string strPhotometricInterpretation = GetEntry(0x0028,0x0004); + std::string strPhotometricInterpretation = GetEntryValue(0x0028,0x0004); if ( ( strPhotometricInterpretation == "PALETTE COLOR ") ) { @@ -567,7 +611,7 @@ int File::GetNumberOfScalarComponentsRaw() { // 0028 0100 US IMG Bits Allocated // (in order no to be messed up by old RGB images) - if ( File::GetEntry(0x0028,0x0100) == "24" ) + if ( File::GetEntryValue(0x0028,0x0100) == "24" ) { return 3; } @@ -585,9 +629,6 @@ int File::GetNumberOfScalarComponentsRaw() // or Location (0020,0050) // as the Z coordinate, // 0. for all the coordinates if nothing is found - -// \todo find a way to inform the caller nothing was found -// \todo How to tell the caller a wrong number of values was found? // // --------------------------------------------------------------- // @@ -601,16 +642,15 @@ int File::GetNumberOfScalarComponentsRaw() float File::GetXOrigin() { float xImPos, yImPos, zImPos; - std::string strImPos = GetEntry(0x0020,0x0032); + std::string strImPos = GetEntryValue(0x0020,0x0032); if ( strImPos == GDCM_UNFOUND ) { gdcmVerboseMacro( "Unfound Image Position Patient (0020,0032)"); - strImPos = GetEntry(0x0020,0x0030); // For ACR-NEMA images + strImPos = GetEntryValue(0x0020,0x0030); // For ACR-NEMA images if ( strImPos == GDCM_UNFOUND ) { gdcmVerboseMacro( "Unfound Image Position (RET) (0020,0030)"); - /// \todo How to tell the caller nothing was found ? return 0.; } } @@ -632,16 +672,15 @@ float File::GetXOrigin() float File::GetYOrigin() { float xImPos, yImPos, zImPos; - std::string strImPos = GetEntry(0x0020,0x0032); + std::string strImPos = GetEntryValue(0x0020,0x0032); if ( strImPos == GDCM_UNFOUND) { gdcmVerboseMacro( "Unfound Image Position Patient (0020,0032)"); - strImPos = GetEntry(0x0020,0x0030); // For ACR-NEMA images + strImPos = GetEntryValue(0x0020,0x0030); // For ACR-NEMA images if ( strImPos == GDCM_UNFOUND ) { gdcmVerboseMacro( "Unfound Image Position (RET) (0020,0030)"); - /// \todo How to tell the caller nothing was found ? return 0.; } } @@ -665,7 +704,7 @@ float File::GetYOrigin() float File::GetZOrigin() { float xImPos, yImPos, zImPos; - std::string strImPos = GetEntry(0x0020,0x0032); + std::string strImPos = GetEntryValue(0x0020,0x0032); if ( strImPos != GDCM_UNFOUND ) { @@ -680,7 +719,7 @@ float File::GetZOrigin() } } - strImPos = GetEntry(0x0020,0x0030); // For ACR-NEMA images + strImPos = GetEntryValue(0x0020,0x0030); // For ACR-NEMA images if ( strImPos != GDCM_UNFOUND ) { if( sscanf( strImPos.c_str(), @@ -695,7 +734,7 @@ float File::GetZOrigin() } } - std::string strSliceLocation = GetEntry(0x0020,0x1041); // for *very* old ACR-NEMA images + std::string strSliceLocation = GetEntryValue(0x0020,0x1041); // for *very* old ACR-NEMA images if ( strSliceLocation != GDCM_UNFOUND ) { if( sscanf( strSliceLocation.c_str(), "%f", &zImPos) != 1) @@ -710,7 +749,7 @@ float File::GetZOrigin() } gdcmVerboseMacro( "Unfound Slice Location (0020,1041)"); - std::string strLocation = GetEntry(0x0020,0x0050); + std::string strLocation = GetEntryValue(0x0020,0x0050); if ( strLocation != GDCM_UNFOUND ) { if( sscanf( strLocation.c_str(), "%f", &zImPos) != 1) @@ -741,7 +780,7 @@ int File::GetImageNumber() // faster function. sscanf() can do all possible conversions whereas // atoi() can only do single decimal integer conversions. //0020 0013 IS REL Image Number - std::string strImNumber = GetEntry(0x0020,0x0013); + std::string strImNumber = GetEntryValue(0x0020,0x0013); if ( strImNumber != GDCM_UNFOUND ) { return atoi( strImNumber.c_str() ); @@ -756,7 +795,7 @@ int File::GetImageNumber() ModalityType File::GetModality() { // 0008 0060 CS ID Modality - std::string strModality = GetEntry(0x0008,0x0060); + std::string strModality = GetEntryValue(0x0008,0x0060); if ( strModality != GDCM_UNFOUND ) { if ( strModality.find("AU") < strModality.length()) return AU; @@ -819,7 +858,7 @@ ModalityType File::GetModality() */ int File::GetBitsStored() { - std::string strSize = GetEntry( 0x0028, 0x0101 ); + std::string strSize = GetEntryValue( 0x0028, 0x0101 ); if ( strSize == GDCM_UNFOUND ) { gdcmVerboseMacro("(0028,0101) is supposed to be mandatory"); @@ -837,7 +876,7 @@ int File::GetBitsStored() */ int File::GetHighBitPosition() { - std::string strSize = GetEntry( 0x0028, 0x0102 ); + std::string strSize = GetEntryValue( 0x0028, 0x0102 ); if ( strSize == GDCM_UNFOUND ) { gdcmVerboseMacro( "(0028,0102) is supposed to be mandatory"); @@ -854,7 +893,7 @@ int File::GetHighBitPosition() */ bool File::IsSignedPixelData() { - std::string strSize = GetEntry( 0x0028, 0x0103 ); + std::string strSize = GetEntryValue( 0x0028, 0x0103 ); if ( strSize == GDCM_UNFOUND ) { gdcmVerboseMacro( "(0028,0103) is supposed to be mandatory"); @@ -876,7 +915,7 @@ bool File::IsSignedPixelData() */ int File::GetBitsAllocated() { - std::string strSize = GetEntry(0x0028,0x0100); + std::string strSize = GetEntryValue(0x0028,0x0100); if ( strSize == GDCM_UNFOUND ) { gdcmVerboseMacro( "(0028,0100) is supposed to be mandatory"); @@ -894,7 +933,7 @@ int File::GetBitsAllocated() */ int File::GetSamplesPerPixel() { - const std::string& strSize = GetEntry(0x0028,0x0002); + const std::string& strSize = GetEntryValue(0x0028,0x0002); if ( strSize == GDCM_UNFOUND ) { gdcmVerboseMacro( "(0028,0002) is supposed to be mandatory"); @@ -911,7 +950,7 @@ int File::GetSamplesPerPixel() */ bool File::IsMonochrome() { - const std::string& PhotometricInterp = GetEntry( 0x0028, 0x0004 ); + const std::string& PhotometricInterp = GetEntryValue( 0x0028, 0x0004 ); if ( Util::DicomStringEqual(PhotometricInterp, "MONOCHROME1") || Util::DicomStringEqual(PhotometricInterp, "MONOCHROME2") ) { @@ -931,7 +970,7 @@ bool File::IsMonochrome() */ bool File::IsPaletteColor() { - std::string PhotometricInterp = GetEntry( 0x0028, 0x0004 ); + std::string PhotometricInterp = GetEntryValue( 0x0028, 0x0004 ); if ( PhotometricInterp == "PALETTE COLOR " ) { return true; @@ -950,7 +989,7 @@ bool File::IsPaletteColor() */ bool File::IsYBRFull() { - std::string PhotometricInterp = GetEntry( 0x0028, 0x0004 ); + std::string PhotometricInterp = GetEntryValue( 0x0028, 0x0004 ); if ( PhotometricInterp == "YBR_FULL" ) { return true; @@ -969,7 +1008,7 @@ bool File::IsYBRFull() */ int File::GetPlanarConfiguration() { - std::string strSize = GetEntry(0x0028,0x0006); + std::string strSize = GetEntryValue(0x0028,0x0006); if ( strSize == GDCM_UNFOUND ) { gdcmVerboseMacro( "Not found : Planar Configuration (0028,0006)"); @@ -987,7 +1026,7 @@ int File::GetPixelSize() { // 0028 0100 US IMG Bits Allocated // (in order no to be messed up by old RGB images) - // if (File::GetEntry(0x0028,0x0100) == "24") + // if (File::GetEntryValue(0x0028,0x0100) == "24") // return 3; std::string pixelType = GetPixelType(); @@ -1027,7 +1066,7 @@ int File::GetPixelSize() */ std::string File::GetPixelType() { - std::string bitsAlloc = GetEntry(0x0028, 0x0100); // Bits Allocated + std::string bitsAlloc = GetEntryValue(0x0028, 0x0100); // Bits Allocated if ( bitsAlloc == GDCM_UNFOUND ) { gdcmVerboseMacro( "Missing Bits Allocated (0028,0100)"); @@ -1049,7 +1088,7 @@ std::string File::GetPixelType() bitsAlloc = "8"; // by old RGB images) } - std::string sign = GetEntry(0x0028, 0x0103);//"Pixel Representation" + std::string sign = GetEntryValue(0x0028, 0x0103);//"Pixel Representation" if (sign == GDCM_UNFOUND ) { @@ -1091,7 +1130,6 @@ size_t File::GetPixelOffset() } } -/// \todo TODO : unify those two (previous one and next one) /** * \brief Recover the pixel area length (in Bytes) * @return Pixel Element Length, as stored in the header @@ -1179,7 +1217,7 @@ int File::GetLUTNbits() //Just hope Lookup Table Desc-Red = Lookup Table Desc-Red // = Lookup Table Desc-Blue // Consistency already checked in GetLUTLength - std::string lutDescription = GetEntry(0x0028,0x1101); + std::string lutDescription = GetEntryValue(0x0028,0x1101); if ( lutDescription == GDCM_UNFOUND ) { return 0; @@ -1196,9 +1234,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) @@ -1207,22 +1274,22 @@ int File::GetLUTNbits() bool File::AnonymizeFile() { // If exist, replace by spaces - SetEntry (" ",0x0010, 0x2154); // Telephone - SetEntry (" ",0x0010, 0x1040); // Adress - SetEntry (" ",0x0010, 0x0020); // Patient ID + SetValEntry (" ",0x0010, 0x2154); // Telephone + SetValEntry (" ",0x0010, 0x1040); // Adress + SetValEntry (" ",0x0010, 0x0020); // Patient ID DocEntry* patientNameHE = GetDocEntry (0x0010, 0x0010); if ( patientNameHE ) // we replace it by Study Instance UID (why not) { - std::string studyInstanceUID = GetEntry (0x0020, 0x000d); + std::string studyInstanceUID = GetEntryValue (0x0020, 0x000d); if ( studyInstanceUID != GDCM_UNFOUND ) { - ReplaceOrCreate(studyInstanceUID, 0x0010, 0x0010); + InsertValEntry(studyInstanceUID, 0x0010, 0x0010); } else { - ReplaceOrCreate("anonymised", 0x0010, 0x0010); + InsertValEntry("anonymised", 0x0010, 0x0010); } } @@ -1281,38 +1348,8 @@ bool File::AnonymizeFile() return true; } -/** - * \brief gets the info from 0020,0037 : Image Orientation Patient - * @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 = GetEntry(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 = GetEntry(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 @@ -1332,10 +1369,10 @@ void File::InitializeDefaultFile() std::string time = Util::GetCurrentTime(); std::string uid = Util::CreateUniqueUID(); std::string uidMedia = uid; - std::string uidClass = uid + ".1"; - std::string uidInst = uid + ".10"; - std::string uidStudy = uid + ".100"; - std::string uidSerie = uid + ".1000"; + std::string uidInst = uid; + std::string uidClass = Util::CreateUniqueUID(); + std::string uidStudy = Util::CreateUniqueUID(); + std::string uidSerie = Util::CreateUniqueUID(); static DICOM_DEFAULT_VALUE defaultvalue[] = { { "146 ", 0x0002, 0x0000}, // Meta Element Group Length // FIXME: how to recompute ? @@ -1377,21 +1414,270 @@ void File::InitializeDefaultFile() // Special case this is the image (not a string) GrPixel = 0x7fe0; NumPixel = 0x0010; - ReplaceOrCreate(0, 0, GrPixel, NumPixel); + InsertBinEntry(0, 0, GrPixel, NumPixel); // All remaining strings: unsigned int i = 0; DICOM_DEFAULT_VALUE current = defaultvalue[i]; while( current.value ) { - ReplaceOrCreate(current.value, current.group, current.elem); + InsertValEntry(current.value, current.group, current.elem); current = defaultvalue[++i]; } } - //----------------------------------------------------------------------------- // 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; + } +} //-----------------------------------------------------------------------------