From 3f99e798955f65c1b906ad65016d1b49bde25ef8 Mon Sep 17 00:00:00 2001 From: jpr Date: Thu, 17 Feb 2005 11:00:33 +0000 Subject: [PATCH] Group 0002 management --- src/gdcmFile.cxx | 51 +++++------ src/gdcmFileHelper.cxx | 191 +++++++++++++++++++++++++++++++---------- src/gdcmFileHelper.h | 15 ++-- 3 files changed, 177 insertions(+), 80 deletions(-) diff --git a/src/gdcmFile.cxx b/src/gdcmFile.cxx index a9638f57..c1381c86 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/02/15 18:12:34 $ - Version: $Revision: 1.224 $ + Date: $Date: 2005/02/17 11:02:47 $ + Version: $Revision: 1.225 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -115,7 +115,6 @@ File::File( std::string const &filename ) // 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) @@ -216,13 +215,7 @@ bool File::IsReadable() */ int File::GetImageNumber() { - // The function i atoi() takes the address of an area of memory as - // parameter and converts the string stored at that location to an integer - // using the external decimal to internal binary conversion rules. This may - // be preferable to sscanf() since atoi() is a much smaller, simpler and - // faster function. sscanf() can do all possible conversions whereas - // atoi() can only do single decimal integer conversions. - //0020 0013 IS REL Image Number + //0020 0013 : Image Number std::string strImNumber = GetEntryValue(0x0020,0x0013); if ( strImNumber != GDCM_UNFOUND ) { @@ -237,7 +230,7 @@ int File::GetImageNumber() */ ModalityType File::GetModality() { - // 0008 0060 CS ID Modality + // 0008 0060 : Modality std::string strModality = GetEntryValue(0x0008,0x0060); if ( strModality != GDCM_UNFOUND ) { @@ -401,7 +394,7 @@ float File::GetXSpacing() if ( xspacing == 0.) { - gdcmWarningMacro("gdcmData/CT-MONO2-8-abdo.dcm problem"); + gdcmWarningMacro("gdcmData/CT-MONO2-8-abdo.dcm-like problem"); // seems to be a bug in the header ... nbValues = sscanf( strSpacing.c_str(), "%f\\0\\%f", &yspacing, &xspacing); gdcmAssertMacro( nbValues == 2 ); @@ -447,15 +440,15 @@ float File::GetYSpacing() */ float File::GetZSpacing() { - // Spacing Between Slices : distance entre le milieu de chaque coupe - // Les coupes peuvent etre : + // Spacing Between Slices : distance between the middle of 2 slices + // Slices may be : // jointives (Spacing between Slices = Slice Thickness) - // chevauchantes (Spacing between Slices < Slice Thickness) + // overlapping (Spacing between Slices < Slice Thickness) // disjointes (Spacing between Slices > Slice Thickness) // Slice Thickness : epaisseur de tissus sur laquelle est acquis le signal - // ca interesse le physicien de l'IRM, pas le visualisateur de volumes ... - // Si le Spacing Between Slices est Missing, - // on suppose que les coupes sont jointives + // It only concerns the MRI guys, not people wanting to visualize volmues + // If Spacing Between Slices is Missing, + // we suppose slices joint together const std::string &strSpacingBSlices = GetEntryValue(0x0018,0x0088); @@ -779,7 +772,7 @@ int File::GetPixelSize() * - 32S signed 32 bit, * - FD floating double 64 bits (Not kosher DICOM, but so usefull!) * \warning 12 bit images appear as 16 bit. - * 24 bit images appear as 8 bit + * 24 bit images appear as 8 bit + photochromatic interp ="RGB " * @return 0S if nothing found. NOT USABLE file. The caller has to check */ std::string File::GetPixelType() @@ -1327,10 +1320,10 @@ bool File::AnonymizeFile() * (as opposed to 'DicomDir related') entries * 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 + * @param writetype Type of the File to be written * (ACR, ExplicitVR, ImplicitVR) */ -bool File::Write(std::string fileName, FileType filetype) +bool File::Write(std::string fileName, FileType writetype) { std::ofstream *fp = new std::ofstream(fileName.c_str(), std::ios::out | std::ios::binary); @@ -1341,12 +1334,12 @@ bool File::Write(std::string fileName, FileType filetype) } // Entry : 0002|0000 = group length -> recalculated - ValEntry *e0002 = GetValEntry(0x0002,0x0000); - if( e0002 ) + ValEntry*e0000 = GetValEntry(0x0002,0x0000); + if( e0000 ) { std::ostringstream sLen; - sLen << ComputeGroup0002Length(filetype); - e0002->SetValue(sLen.str()); + sLen << ComputeGroup0002Length(writetype); + e0000->SetValue(sLen.str()); } // Bits Allocated @@ -1406,7 +1399,7 @@ bool File::Write(std::string fileName, FileType filetype) } } - Document::WriteContent(fp, filetype); + Document::WriteContent(fp, writetype); fp->close(); delete fp; @@ -1420,7 +1413,7 @@ bool File::Write(std::string fileName, FileType filetype) * \brief Initialize a default DICOM File that should contain all the * field require by other reader. DICOM standard does not * explicitely defines those fields, heuristic has been choosen. - * This is not perfect as we are writting a CT image... + * This is not perfect as we are writting a Secondary Capture image... */ void File::InitializeDefaultFile() { @@ -1435,8 +1428,8 @@ void File::InitializeDefaultFile() // Meta Element Group Length InsertValEntry("146 ", 0x0002, 0x0000); - // Media Storage SOP Class UID (CT Image Storage) - InsertValEntry("1.2.840.10008.5.1.4.1.1.2", 0x0002, 0x0002); + // Media Storage SOP Class UID (Secondary Capture Image Storage) + InsertValEntry("1.2.840.10008.5.1.4.1.1.7", 0x0002, 0x0002); // Media Storage SOP Instance UID InsertValEntry(uidMedia.c_str(), 0x0002, 0x0003); // Transfer Syntax UID (Explicit VR Little Endian) diff --git a/src/gdcmFileHelper.cxx b/src/gdcmFileHelper.cxx index 50183d70..996b67ec 100644 --- a/src/gdcmFileHelper.cxx +++ b/src/gdcmFileHelper.cxx @@ -3,8 +3,8 @@ Program: gdcm Module: $RCSfile: gdcmFileHelper.cxx,v $ Language: C++ - Date: $Date: 2005/02/11 11:22:59 $ - Version: $Revision: 1.16 $ + Date: $Date: 2005/02/17 11:00:33 $ + Version: $Revision: 1.17 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -32,6 +32,7 @@ #include + namespace gdcm { //------------------------------------------------------------------------- @@ -131,11 +132,12 @@ FileHelper::~FileHelper() * @param content new value (string) to substitute with * @param group group number of the Dicom Element to modify * @param elem element number of the Dicom Element to modify + * \return false if DocEntry not found */ bool FileHelper::SetValEntry(std::string const &content, uint16_t group, uint16_t elem) { - return FileInternal->SetValEntry(content,group,elem); + return FileInternal->SetValEntry(content, group, elem); } @@ -147,17 +149,18 @@ bool FileHelper::SetValEntry(std::string const &content, * @param lgth new value length * @param group group number of the Dicom Element to modify * @param elem element number of the Dicom Element to modify + * \return false if DocEntry not found */ bool FileHelper::SetBinEntry(uint8_t *content, int lgth, uint16_t group, uint16_t elem) { - return FileInternal->SetBinEntry(content,lgth,group,elem); + return FileInternal->SetBinEntry(content, lgth, group, elem); } /** * \brief Modifies the value of a given DocEntry (Dicom entry) - * when it exists. Create it with the given value when unexistant. - * @param content (string) Value to be set + * when it exists. Creates it with the given value when unexistant. + * @param content (string)value to be set * @param group Group number of the Entry * @param elem Element number of the Entry * \return pointer to the modified/created Dicom entry (NULL when creation @@ -171,9 +174,9 @@ ValEntry *FileHelper::InsertValEntry(std::string const &content, /** * \brief Modifies the value of a given DocEntry (Dicom entry) - * when it exists. Create it with the given value when unexistant. + * when it exists. Creates it with the given value when unexistant. * A copy of the binArea is made to be kept in the Document. - * @param binArea (binary) value to be set + * @param binArea (binary)value to be set * @param lgth new value length * @param group Group number of the Entry * @param elem Element number of the Entry @@ -183,13 +186,12 @@ ValEntry *FileHelper::InsertValEntry(std::string const &content, BinEntry *FileHelper::InsertBinEntry(uint8_t *binArea, int lgth, uint16_t group, uint16_t elem) { - return FileInternal->InsertBinEntry(binArea,lgth,group,elem); + return FileInternal->InsertBinEntry(binArea, lgth, group, elem); } /** * \brief Modifies the value of a given DocEntry (Dicom entry) - * when it exists. Create it with the given value when unexistant. - * A copy of the binArea is made to be kept in the Document. + * when it exists. Creates it, empty (?!) when unexistant. * @param group Group number of the Entry * @param elem Element number of the Entry * \return pointer to the modified/created Dicom entry (NULL when creation @@ -197,7 +199,7 @@ BinEntry *FileHelper::InsertBinEntry(uint8_t *binArea, int lgth, */ SeqEntry *FileHelper::InsertSeqEntry(uint16_t group, uint16_t elem) { - return FileInternal->InsertSeqEntry(group,elem); + return FileInternal->InsertSeqEntry(group, elem); } /** @@ -276,7 +278,7 @@ uint8_t *FileHelper::GetImageData() * Copies the pixel data (image[s]/volume[s]) to newly allocated zone. * DOES NOT transform Grey plane + 3 Palettes into a RGB Plane * @return Pointer to newly allocated pixel data. - * \ NULL if alloc fails + * NULL if alloc fails */ uint8_t *FileHelper::GetImageDataRaw () { @@ -353,26 +355,23 @@ size_t FileHelper::GetImageDataIntoVector (void *destination, size_t maxSize) * user is allowed to pass any kind of pixelsn since the size is * given in bytes) * @param expectedSize total image size, *in Bytes* - * - * @return boolean */ void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize) { - SetUserData(inData,expectedSize); + SetUserData(inData, expectedSize); } /** * \brief Set the image data defined by the user * \warning When writting the file, this data are get as default data to write * @param inData user supplied pixel area (uint8_t* is just for the compiler. - * user is allowed to pass any kind of pixelsn since the size is + * user is allowed to pass any kind of pixels since the size is * given in bytes) - * @param expectedSize total image size, *in Bytes* - + * @param expectedSize total image size, *in Bytes* */ void FileHelper::SetUserData(uint8_t *inData, size_t expectedSize) { - PixelWriteConverter->SetUserData(inData,expectedSize); + PixelWriteConverter->SetUserData(inData, expectedSize); } /** @@ -413,7 +412,7 @@ size_t FileHelper::GetRGBDataSize() /** * \brief Get the image data from the file. - * If a LUT is found, the data are not expanded ! + * Even when a LUT is found, the data are not expanded to RGB! */ uint8_t *FileHelper::GetRawData() { @@ -422,7 +421,7 @@ uint8_t *FileHelper::GetRawData() /** * \brief Get the image data size from the file. - * If a LUT is found, the data are not expanded ! + * Even when a LUT is found, the data are not expanded to RGB! */ size_t FileHelper::GetRawDataSize() { @@ -541,9 +540,11 @@ bool FileHelper::Write(std::string const &fileName) { case ImplicitVR: SetWriteFileTypeToImplicitVR(); + CheckMetaElements(); break; case ExplicitVR: SetWriteFileTypeToExplicitVR(); + CheckMetaElements(); break; case ACR: case ACR_LIBIDO: @@ -551,6 +552,7 @@ bool FileHelper::Write(std::string const &fileName) break; default: SetWriteFileTypeToExplicitVR(); + CheckMetaElements(); } // -------------------------------------------------------------- @@ -574,14 +576,14 @@ bool FileHelper::Write(std::string const &fileName) switch(WriteMode) { case WMODE_RAW : - SetWriteToRaw(); + SetWriteToRaw(); // modifies and pushes to the archive, when necessary break; case WMODE_RGB : - SetWriteToRGB(); + SetWriteToRGB(); // modifies and pushes to the archive, when necessary break; } - bool check = CheckWriteIntegrity(); + bool check = CheckWriteIntegrity(); // verifies length if(check) { check = FileInternal->Write(fileName,WriteType); @@ -604,7 +606,7 @@ bool FileHelper::Write(std::string const &fileName) //----------------------------------------------------------------------------- // Protected /** - * \brief Check the write integrity + * \brief Checks the write integrity * * The tests made are : * - verify the size of the image to write with the possible write @@ -657,7 +659,9 @@ bool FileHelper::CheckWriteIntegrity() } /** - * \brief Update the File to write RAW datas + * \brief Updates the File to write RAW data (as opposed to RGB data) + * (modifies, when necessary, photochromatic interpretation, + * bits allocated, Pixels element VR) */ void FileHelper::SetWriteToRaw() { @@ -698,7 +702,11 @@ void FileHelper::SetWriteToRaw() } /** - * \brief Update the File to write RGB datas + * \brief Updates the File to write RGB data (as opposed to RAW data) + * (modifies, when necessary, photochromatic interpretation, + * samples per pixel, Planar configuration, + * bits allocated, bits stored, high bit -ACR 24 bits- + * Pixels element VR, pushes out the LUT, ) */ void FileHelper::SetWriteToRGB() { @@ -750,7 +758,7 @@ void FileHelper::SetWriteToRGB() Archive->Push(0x0028,0x1202); Archive->Push(0x0028,0x1203); - // For old ACR-NEMA + // For old '24 Bits' ACR-NEMA // Thus, we have a RGB image and the bits allocated = 24 and // samples per pixels = 1 (in the read file) if(FileInternal->GetBitsAllocated()==24) @@ -797,18 +805,43 @@ void FileHelper::RestoreWrite() Archive->Restore(0x0028,0x1201); Archive->Restore(0x0028,0x1202); Archive->Restore(0x0028,0x1203); + + // group 0002 may be pushed out for ACR-NEMA writting purposes + Archive->Restore(0x0002,0x0000); + Archive->Restore(0x0002,0x0001); + Archive->Restore(0x0002,0x0002); + Archive->Restore(0x0002,0x0003); + Archive->Restore(0x0002,0x0010); + Archive->Restore(0x0002,0x0012); + Archive->Restore(0x0002,0x0013); + Archive->Restore(0x0002,0x0016); + Archive->Restore(0x0002,0x0100); + Archive->Restore(0x0002,0x0102); } /** - * \brief Set in the File the write type to ACR + * \brief Pushes out the whole group 0002 + * FIXME : better, set a flag to tell the writer not to write it ... + * FIXME : method should probably have an other name ! + * SetWriteFileTypeToACR is NOT opposed to + * SetWriteFileTypeToExplicitVR and SetWriteFileTypeToImplicitVR */ void FileHelper::SetWriteFileTypeToACR() { - Archive->Push(0x0002,0x0010); + Archive->Push(0x0002,0x0000); + Archive->Push(0x0002,0x0001); + Archive->Push(0x0002,0x0002); + Archive->Push(0x0002,0x0003); + Archive->Push(0x0002,0x0010);// Only TransferSyntax was pushed out ! + Archive->Push(0x0002,0x0012); + Archive->Push(0x0002,0x0013); + Archive->Push(0x0002,0x0016); + Archive->Push(0x0002,0x0100); + Archive->Push(0x0002,0x0102); } /** - * \brief Set in the File the write type to Explicit VR + * \brief Sets in the File the TransferSyntax to 'Explicit VR Little Endian" */ void FileHelper::SetWriteFileTypeToExplicitVR() { @@ -822,7 +855,7 @@ void FileHelper::SetWriteFileTypeToExplicitVR() } /** - * \brief Set in the File the write type to Implicit VR + * \brief Sets in the File the TransferSyntax to 'Implicit VR Little Endian" */ void FileHelper::SetWriteFileTypeToImplicitVR() { @@ -837,11 +870,21 @@ void FileHelper::SetWriteFileTypeToImplicitVR() /** - * \brief Restore in the File the write type + * \brief Restore in the File the initial group 0002 */ void FileHelper::RestoreWriteFileType() { - Archive->Restore(0x0002,0x0010); + // group 0002 may be pushed out for ACR-NEMA writting purposes + Archive->Restore(0x0002,0x0000); + Archive->Restore(0x0002,0x0001); + Archive->Restore(0x0002,0x0002); + Archive->Restore(0x0002,0x0003); + Archive->Restore(0x0002,0x0010);//only TransferSyntax was pushed out/restored + Archive->Restore(0x0002,0x0012); + Archive->Restore(0x0002,0x0013); + Archive->Restore(0x0002,0x0016); + Archive->Restore(0x0002,0x0100); + Archive->Restore(0x0002,0x0102); } /** @@ -905,13 +948,13 @@ void FileHelper::RestoreWriteOfLibido() } /** - * \brief Copy a ValEntry content + * \brief Duplicates a ValEntry or creates it. * @param group Group number of the Entry * @param elem Element number of the Entry - * \return pointer to the modified/created Val Entry (NULL when creation + * \return pointer to the new Val Entry (NULL when creation * failed). */ -ValEntry *FileHelper::CopyValEntry(uint16_t group,uint16_t elem) +ValEntry *FileHelper::CopyValEntry(uint16_t group, uint16_t elem) { DocEntry *oldE = FileInternal->GetDocEntry(group, elem); ValEntry *newE; @@ -923,22 +966,22 @@ ValEntry *FileHelper::CopyValEntry(uint16_t group,uint16_t elem) } else { - newE = GetFile()->NewValEntry(group,elem); + newE = GetFile()->NewValEntry(group, elem); } return newE; } /** - * \brief Modifies the value of a given Bin Entry (Dicom Element) - * when it exists. Create it with the given value when unexistant. + * \brief Duplicates a BinEntry or creates it. * @param group Group number of the Entry * @param elem Element number of the Entry * @param vr Value Representation of the Entry - * \return pointer to the modified/created Bin Entry (NULL when creation + * FIXME : what is it used for? + * \return pointer to the new Bin Entry (NULL when creation * failed). */ -BinEntry *FileHelper::CopyBinEntry(uint16_t group,uint16_t elem, +BinEntry *FileHelper::CopyBinEntry(uint16_t group, uint16_t elem, const std::string &vr) { DocEntry *oldE = FileInternal->GetDocEntry(group, elem); @@ -955,12 +998,72 @@ BinEntry *FileHelper::CopyBinEntry(uint16_t group,uint16_t elem, } else { - newE = GetFile()->NewBinEntry(group,elem,vr); + newE = GetFile()->NewBinEntry(group, elem, vr); } return newE; } +/** + * \brief Checks the MetaElements Group (0002). + * adds the mandatory Entries if not found + * (when user asks to write as a DICOM file, an ACR-NEMA file + * he read before) + */ +void FileHelper::CheckMetaElements() +{ + // just to remember : 'official' 0002 group + + //0002 0000 UL 1 Meta Group Length + //0002 0001 OB 1 File Meta Information Version + //0002 0002 UI 1 Media Stored SOP Class UID + //0002 0003 UI 1 Media Stored SOP Instance UID + //0002 0010 UI 1 Transfer Syntax UID + //0002 0012 UI 1 Implementation Class UID + //0002 0013 SH 1 Implementation Version Name + //0002 0016 AE 1 Source Application Entity Title + //0002 0100 UI 1 Private Information Creator + //0002 0102 OB 1 Private Information + + std::string uid = Util::CreateUniqueUID(); + std::string uidMedia = uid; + + // Create them if not found + ValEntry *e0000 = CopyValEntry(0x0002,0x0000); + e0000->SetValue("0"); // for the moment + Archive->Push(e0000); + + BinEntry *e0001 = CopyBinEntry(0x0002,0x0001, "OB"); + e0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(), false); + e0001->SetLength(2); + + ValEntry *e0002 = CopyValEntry(0x0002,0x0002); + // [Secondary Capture Image Storage] + e0002->SetValue("1.2.840.10008.5.1.4.1.1.7"); + Archive->Push(e0002); + + ValEntry *e0003 = CopyValEntry(0x0002,0x0003); + e0003->SetValue(uidMedia.c_str()); + Archive->Push(e0003); + + ValEntry *e0010 = CopyValEntry(0x0002,0x0010); + //Explicit VR - Little Endian + e0010->SetValue("1.2.840.10008.1.2.1"); + Archive->Push(e0010); + + ValEntry *e0012 = CopyValEntry(0x0002,0x0012); + e0012->SetValue("Implementation.Class.UID"); + Archive->Push(e0012); + + ValEntry *e0013 = CopyValEntry(0x0002,0x0013); + e0013->SetValue("GDCM 1.0"); + Archive->Push(e0013); + + ValEntry *e0016 = CopyValEntry(0x0002,0x0016); + e0016->SetValue("1.2.840.10008.5.1.4.1.1.7"); + Archive->Push(e0016); +} + //----------------------------------------------------------------------------- // Private /** diff --git a/src/gdcmFileHelper.h b/src/gdcmFileHelper.h index f59147eb..ceeae46d 100644 --- a/src/gdcmFileHelper.h +++ b/src/gdcmFileHelper.h @@ -3,8 +3,8 @@ Program: gdcm Module: $RCSfile: gdcmFileHelper.h,v $ Language: C++ - Date: $Date: 2005/02/16 16:41:00 $ - Version: $Revision: 1.13 $ + Date: $Date: 2005/02/17 11:00:33 $ + Version: $Revision: 1.14 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -98,7 +98,7 @@ public: /// \brief Tells the writer we want to keep 'Grey pixels + Palettes color' /// when possible (as opposed to convert 'Palettes color' to RGB) - void SetWriteModeToRaw() { SetWriteMode(WMODE_RAW); }; + void SetWriteModeToRaw() { SetWriteMode(WMODE_RAW); }; /// \brief Tells the writer we want to write RGB image when possible /// (as opposed to 'Grey pixels + Palettes color') void SetWriteModeToRGB() { SetWriteMode(WMODE_RGB); }; @@ -117,10 +117,10 @@ public: void SetWriteTypeToAcr() { SetWriteType(ACR); }; /// \brief Tells the writer we want to write as LibIDO void SetWriteTypeToAcrLibido() { SetWriteType(ACR_LIBIDO); }; - /// \brief Tells the writer which format want to write + /// \brief Tells the writer which format we want to write /// (ImplicitVR, ExplicitVR, ACR, ACR_LIBIDO) void SetWriteType(FileType format) { WriteType = format; }; - /// \brief Gets the format we want to write + /// \brief Gets the format we talled the write we wanted to write /// (ImplicitVR, ExplicitVR, ACR, ACR_LIBIDO) FileType GetWriteType() { return WriteType; }; @@ -152,6 +152,7 @@ protected: ValEntry *CopyValEntry(uint16_t group, uint16_t elem); BinEntry *CopyBinEntry(uint16_t group, uint16_t elem, const std::string &vr); + void CheckMetaElements(); private: void Initialize(); @@ -163,8 +164,8 @@ private: File *FileInternal; /// \brief Whether the underlying \ref gdcm::File was loaded by - /// the constructor or passed to the constructor. When false - /// the destructor is in charge of deletion. + /// the constructor or passed to the constructor. + /// When false the destructor is in charge of deletion. bool SelfHeader; /// Wether already parsed or not -- 2.48.1