X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=src%2FgdcmFileHelper.cxx;h=d5eacb720fd371b39cdeccd4c6360f6719d23496;hb=de5fc413dc579fc61f7e04397efd1dc1087fe3cf;hp=1a4cf5c2a224b4e5020ce23d0412046c90bc0b22;hpb=4f1f67880bf7de5445714fa3d29094df99de58c8;p=gdcm.git diff --git a/src/gdcmFileHelper.cxx b/src/gdcmFileHelper.cxx index 1a4cf5c2..d5eacb72 100644 --- a/src/gdcmFileHelper.cxx +++ b/src/gdcmFileHelper.cxx @@ -3,8 +3,9 @@ Program: gdcm Module: $RCSfile: gdcmFileHelper.cxx,v $ Language: C++ - Date: $Date: 2005/02/21 17:45:41 $ - Version: $Revision: 1.18 $ + + Date: $Date: 2005/04/19 09:58:19 $ + Version: $Revision: 1.33 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -35,6 +36,71 @@ #include +/* +// ----------------------------- WARNING ------------------------- + +These lines will be moved to the document-to-be 'User's Guide' + +// To read an image, user needs a gdcm::File +gdcm::File *f1 = new gdcm::File(fileName); +// user can now check some values +std::string v = f1->GetEntryValue(groupNb,ElementNb); +// to get the pixels, user needs a gdcm::FileHelper +gdcm::FileHelper *fh1 = new gdcm::FileHelper(f1); +// user may ask not to convert Palette to RGB +uint8_t *pixels = fh1->GetImageDataRaw(); +int imageLength = fh1->GetImageDataRawSize(); +// He can now use the pixels, create a new image, ... +uint8_t *userPixels = ... + +To re-write the image, user re-uses the gdcm::FileHelper + +fh1->SetImageData( userPixels, userPixelsLength); +fh1->SetTypeToRaw(); // Even if it was possible to convert Palette to RGB + // (WriteMode is set) + +fh1->SetWriteTypeToDcmExpl(); // he wants Explicit Value Representation + // Little Endian is the default + // no other value is allowed + (-->SetWriteType(ExplicitVR);) + -->WriteType = ExplicitVR; +fh1->Write(newFileName); // overwrites the file, if any + +// or : +fh1->WriteDcmExplVR(newFileName); + + +// ----------------------------- WARNING ------------------------- + +These lines will be moved to the document-to-be 'Developer's Guide' + +WriteMode : WMODE_RAW / WMODE_RGB +WriteType : ImplicitVR, ExplicitVR, ACR, ACR_LIBIDO + +fh1->Write(newFileName); + SetWriteFileTypeToImplicitVR() / SetWriteFileTypeToExplicitVR(); + (modifies TransferSyntax) + SetWriteToRaw(); / SetWriteToRGB(); + (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 ) + CheckWriteIntegrity(); + (checks user given pixels length) + FileInternal->Write(fileName,WriteType) + fp = opens file(fileName); + ComputeGroup0002Length(writetype); + BitsAllocated 12->16 + RemoveEntryNoDestroy(palettes, etc) + Document::WriteContent(fp, writetype); + RestoreWrite(); + (moves back to the File all the archived elements) + RestoreWriteFileType(); + (pushes back group 0002, with TransferSyntax) +*/ + + + namespace gdcm { @@ -449,7 +515,7 @@ uint8_t* FileHelper::GetLutRGBA() */ bool FileHelper::WriteRawData(std::string const &fileName) { - std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary ); + std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary ); if (!fp1) { gdcmWarningMacro( "Fail to open (write) file:" << fileName.c_str()); @@ -545,17 +611,25 @@ bool FileHelper::Write(std::string const &fileName) SetWriteFileTypeToImplicitVR(); CheckMandatoryElements(); break; + case Unknown: // should never happen; ExplicitVR is the default value case ExplicitVR: SetWriteFileTypeToExplicitVR(); CheckMandatoryElements(); break; case ACR: case ACR_LIBIDO: + // Just to avoid further trouble if user create a file ex-nihilo, + // wants to write it as an ACR-NEMA file, + // and forget to create any Entry belonging to group 0008 + // (shame on him !) + // We add Recognition Code (RET) + if ( ! FileInternal->GetValEntry(0x0008, 0x0010) ) + FileInternal->InsertValEntry("", 0x0008, 0x0010); SetWriteFileTypeToACR(); - break; - default: - SetWriteFileTypeToExplicitVR(); + SetWriteFileTypeToImplicitVR(); CheckMandatoryElements(); + break; + } // -------------------------------------------------------------- @@ -566,6 +640,7 @@ bool FileHelper::Write(std::string const &fileName) // just before writting ... /// \todo the best trick would be *change* the recognition code /// but pb expected if user deals with, e.g. COMPLEX images + if( WriteType == ACR_LIBIDO ) { SetWriteToLibido(); @@ -594,6 +669,7 @@ bool FileHelper::Write(std::string const &fileName) RestoreWrite(); RestoreWriteFileType(); + RestoreWriteMandatory(); // -------------------------------------------------------------- // Special Patch to allow gdcm to re-write ACR-LibIDO formated images @@ -835,7 +911,7 @@ void FileHelper::SetWriteFileTypeToACR() Archive->Push(0x0002,0x0001); Archive->Push(0x0002,0x0002); Archive->Push(0x0002,0x0003); - Archive->Push(0x0002,0x0010);// Only TransferSyntax was pushed out ! + Archive->Push(0x0002,0x0010); Archive->Push(0x0002,0x0012); Archive->Push(0x0002,0x0013); Archive->Push(0x0002,0x0016); @@ -877,17 +953,6 @@ void FileHelper::SetWriteFileTypeToImplicitVR() */ void FileHelper::RestoreWriteFileType() { - // 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); } /** @@ -948,6 +1013,12 @@ void FileHelper::RestoreWriteOfLibido() Archive->Restore(0x0028,0x0010); Archive->Restore(0x0028,0x0011); Archive->Restore(0x0008,0x0010); + + // Restore 'LibIDO-special' entries, if any + Archive->Restore(0x0028,0x0015); + Archive->Restore(0x0028,0x0016); + Archive->Restore(0x0028,0x0017); + Archive->Restore(0x0028,0x00199); } /** @@ -988,7 +1059,7 @@ BinEntry *FileHelper::CopyBinEntry(uint16_t group, uint16_t elem, DocEntry *oldE = FileInternal->GetDocEntry(group, elem); BinEntry *newE; - if( oldE ) + if( oldE ) if( oldE->GetVR()!=vr ) oldE = NULL; @@ -1006,11 +1077,31 @@ BinEntry *FileHelper::CopyBinEntry(uint16_t group, uint16_t elem, } /** - * \brief Checks the Mandatory Elements - * adds the mandatory Entries if not found - * (when user asks to write as a DICOM file, an ACR-NEMA file - * he read before) + * \brief This method is called automatically, just before writting + * in order to produce a 'True Dicom V3' image + * We cannot know *how* the user made the File (reading an old ACR-NEMA + * file or a not very clean DICOM file ...) + * + * Just before writting : + * - we check the Entries + * - we create the mandatory entries if they are missing + * - we modify the values if necessary + * - we push the sensitive entries to the Archive + * The writing process will restore the entries as they where before + * entering FileHelper::CheckMandatoryElements, so the user will always + * see the entries just as he left them. + * + * \todo : - warn the user if we had to add some entries : + * even if a mandatory entry is missing, we add it, with a default value + * (we don't want to give up the writting process if user forgot to + * specify Lena's Patient ID, for instance ...) + * - read the whole PS 3.3 Part of DICOM (890 pages) + * and write a *full* checker (probably one method per Modality ...) + * Any contribution is welcome. + * - write a user callable full checker, to allow post reading + * and/or pre writting image consistency check. */ + void FileHelper::CheckMandatoryElements() { // just to remember : 'official' 0002 group @@ -1025,8 +1116,10 @@ void FileHelper::CheckMandatoryElements() //0002 0016 AE 1 Source Application Entity Title //0002 0100 UI 1 Private Information Creator //0002 0102 OB 1 Private Information - + // Create them if not found + // Always modify the value + // Push the entries to the archive. ValEntry *e_0002_0000 = CopyValEntry(0x0002,0x0000); e_0002_0000->SetValue("0"); // for the moment Archive->Push(e_0002_0000); @@ -1035,6 +1128,7 @@ void FileHelper::CheckMandatoryElements() e_0002_0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(), false); e_0002_0001->SetLength(2); + Archive->Push(e_0002_0001); ValEntry *e_0002_0002 = CopyValEntry(0x0002,0x0002); // [Secondary Capture Image Storage] @@ -1046,11 +1140,6 @@ void FileHelper::CheckMandatoryElements() e_0002_0003->SetValue(Util::CreateUniqueUID()); Archive->Push(e_0002_0003); - ValEntry *e_0002_0010 = CopyValEntry(0x0002,0x0010); - //[Explicit VR - Little Endian] - e_0002_0010->SetValue("1.2.840.10008.1.2.1"); - Archive->Push(e_0002_0010); - // 'Implementation Class UID' ValEntry *e_0002_0012 = CopyValEntry(0x0002,0x0012); e_0002_0012->SetValue(Util::CreateUniqueUID()); @@ -1075,18 +1164,20 @@ void FileHelper::CheckMandatoryElements() // --- Check UID-related Entries --- // If 'SOP Class UID' exists ('true DICOM' image) + // we create the 'Source Image Sequence' SeqEntry + // to hold informations about the Source Image ValEntry *e_0008_0016 = FileInternal->GetValEntry(0x0008, 0x0016); if ( e_0008_0016 != 0 ) { // Create 'Source Image Sequence' SeqEntry SeqEntry *s = new SeqEntry ( - Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x2112) ); + Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x2112) ); SQItem *sqi = new SQItem(1); // (we assume 'SOP Instance UID' exists too) // create 'Referenced SOP Class UID' ValEntry *e_0008_1150 = new ValEntry( - Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1150) ); + Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1150) ); e_0008_1150->SetValue( e_0008_0016->GetValue()); sqi->AddEntry(e_0008_1150); @@ -1101,136 +1192,181 @@ void FileHelper::CheckMandatoryElements() // temporarily replaces any previous 'Source Image Sequence' Archive->Push(s); - // 'Image Type' + // 'Image Type' (The written image is no longer an 'ORIGINAL' one) ValEntry *e_0008_0008 = CopyValEntry(0x0008,0x0008); e_0008_0008->SetValue("DERIVED\\PRIMARY"); Archive->Push(e_0008_0008); } else { - // SOP Class UID - ValEntry *e_0008_0016 = new ValEntry( + // There was no 'SOP Class UID'. + // the source image was NOT a true Dicom one. + // We consider the image is a 'Secondary Capture' one + // SOP Class UID + e_0008_0016 = new ValEntry( Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x0016) ); // [Secondary Capture Image Storage] e_0008_0016 ->SetValue("1.2.840.10008.5.1.4.1.1.7"); Archive->Push(e_0008_0016); } +// ---- The user will never have to take any action on the following ----. + // new value for 'SOP Instance UID' ValEntry *e_0008_0018 = new ValEntry( - Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x0018) ); + Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x0018) ); e_0008_0018->SetValue( Util::CreateUniqueUID() ); Archive->Push(e_0008_0018); // Instance Creation Date - ValEntry *e0008_0012 = CopyValEntry(0x0008,0x0012); + ValEntry *e_0008_0012 = CopyValEntry(0x0008,0x0012); std::string date = Util::GetCurrentDate(); - e0008_0012->SetValue(date.c_str()); - Archive->Push(e0008_0012); + e_0008_0012->SetValue(date.c_str()); + Archive->Push(e_0008_0012); // Instance Creation Time - ValEntry *e0008_0013 = CopyValEntry(0x0008,0x0013); + ValEntry *e_0008_0013 = CopyValEntry(0x0008,0x0013); std::string time = Util::GetCurrentTime(); - e0008_0013->SetValue(time.c_str()); - Archive->Push(e0008_0013); - - // new value for 'Serie Instance UID' - // TODO prevoir booleen pour figer la valeur d'un appel a l'autre - // calculer nouvelle valeur a chaque fois - - ValEntry *e_0020_000e = new ValEntry( - Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0020, 0x000e) ); - e_0020_000e->SetValue( Util::CreateUniqueUID() ); - Archive->Push(e_0020_000e); - - // new value for 'Study Instance UID' - // TODO prevoir flag pour figer la valeurd'un appel a l'autre - // calculer nouvelle valeur a chaque fois - // reutiliser mla valeur image origine - - ValEntry *e_0020_000d = new ValEntry( - Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0020, 0x000d) ); - e_0020_000d->SetValue( Util::CreateUniqueUID() ); - Archive->Push(e_0020_000d); + e_0008_0013->SetValue(time.c_str()); + Archive->Push(e_0008_0013); // ----- Add Mandatory Entries if missing --- // Entries whose type is 1 are mandatory, with a mandatory value -// Entries whose type is 1c are ? +// Entries whose type is 1c are mandatory-inside-a-Sequence // Entries whose type is 2 are mandatory, with a optional value -// Entries whose type is 2c are ? +// Entries whose type is 2c are mandatory-inside-a-Sequence // Entries whose type is 3 are optional - ValEntry *e; - // Modality - e = FileInternal->GetValEntry(0x0008, 0x0060); - if ( !e ) + // 'Serie Instance UID' + // Keep the value if exists + // The user is allowed to create his own Series, + // keeping the same 'Serie Instance UID' for various images + // The user shouldn't add any image to a 'Manufacturer Serie' + // but there is no way no to allowed him to do that + ValEntry *e_0020_000e = FileInternal->GetValEntry(0x0020, 0x000e); + if ( !e_0020_000e ) { - e = InsertValEntry("OT", 0x0008, 0x0060); - Archive->Push(e); + e_0020_000e = new ValEntry( + Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0020, 0x000e) ); + e_0020_000e->SetValue(Util::CreateUniqueUID() ); + Archive->Push(e_0020_000e); } - // Manufacturer - e = FileInternal->GetValEntry(0x0008, 0x0070); - if ( !e ) + + // 'Study Instance UID' + // Keep the value if exists + // The user is allowed to create his own Study, + // keeping the same 'Study Instance UID' for various images + // The user may add images to a 'Manufacturer Study', + // adding new series to an already existing Study + ValEntry *e_0020_000d = FileInternal->GetValEntry(0x0020, 0x000d); + if ( !e_0020_000d ) { - e = InsertValEntry("GDCM Factory", 0x0008, 0x0070); - Archive->Push(e); + e_0020_000d = new ValEntry( + Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0020, 0x000d) ); + e_0020_000d->SetValue(Util::CreateUniqueUID() ); + Archive->Push(e_0020_000d); } - // Institution Name - e = FileInternal->GetValEntry(0x0008, 0x0070); - if ( !e ) + + // Modality : if missing we set it to 'OTher' + ValEntry *e_0008_0060 = FileInternal->GetValEntry(0x0008, 0x0060); + if ( !e_0008_0060 ) { - e = InsertValEntry("GDCM Hospital",0x0008, 0x0080); - Archive->Push(e); - } - // Institution Adress - e = FileInternal->GetValEntry(0x0008, 0x0081); - if ( !e ) + e_0008_0060 = new ValEntry( + Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x0060) ); + e_0008_0060->SetValue("OT"); + Archive->Push(e_0008_0060); + } + + // Manufacturer : if missing we set it to 'GDCM Factory' + ValEntry *e_0008_0070 = FileInternal->GetValEntry(0x0008, 0x0070); + if ( !e_0008_0070 ) { - e = InsertValEntry("@ GDCM", 0x0008, 0x0081); - Archive->Push(e); - } - // Patient's Name - e = FileInternal->GetValEntry(0x0010, 0x0010); - if ( !e ) + e_0008_0070 = new ValEntry( + Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x0070) ); + e_0008_0070->SetValue("GDCM Factory"); + Archive->Push(e_0008_0070); + } + + // Institution Name : if missing we set it to 'GDCM Hospital' + ValEntry *e_0008_0080 = FileInternal->GetValEntry(0x0008, 0x0080); + if ( !e_0008_0080 ) { - e = InsertValEntry("GDCM^patient", 0x0010, 0x0010); - Archive->Push(e); - } - // Patient's ID - e = FileInternal->GetValEntry(0x0010, 0x0020); - if ( !e ) + e_0008_0080 = new ValEntry( + Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x0080) ); + e_0008_0080->SetValue("GDCM Hospital"); + Archive->Push(e_0008_0080); + } + + // Patient's Name : if missing, we set it to 'GDCM^Patient' + ValEntry *e_0010_0010 = FileInternal->GetValEntry(0x0010, 0x0010); + if ( !e_0010_0010 ) { - e = InsertValEntry("GDCM_patient_ID",0x0010, 0x0020); - Archive->Push(e); - } + e_0010_0010 = new ValEntry( + Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0010, 0x0010) ); + e_0010_0010->SetValue("GDCM^Patient"); + Archive->Push(e_0010_0010); + } - // Patient's Birth Date - e = FileInternal->GetValEntry(0x0010, 0x0030); - if ( !e ) + // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory + ValEntry *e_0010_0030 = FileInternal->GetValEntry(0x0010, 0x0030); + if ( !e_0010_0030 ) { - e = InsertValEntry("",0x0010, 0x0030); - Archive->Push(e); + e_0010_0030 = new ValEntry( + Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0010, 0x0030) ); + e_0010_0030->SetValue(""); + Archive->Push(e_0010_0030); } - // Patient's Sex - e = FileInternal->GetValEntry(0x0010, 0x0040); - if ( !e ) + // Patient's Sex :'type 2' entry -> must exist, value not mandatory + ValEntry *e_0010_0040 = FileInternal->GetValEntry(0x0010, 0x0040); + if ( !e_0010_0040 ) { - e = InsertValEntry("",0x0010, 0x0040); - Archive->Push(e); + e_0010_0040 = new ValEntry( + Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0010, 0x0040) ); + e_0010_0040->SetValue(""); + Archive->Push(e_0010_0040); } - // Referring Physician's Name - e = FileInternal->GetValEntry(0x0008, 0x0090); - if ( !e ) + // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory + ValEntry *e_0008_0090 = FileInternal->GetValEntry(0x0008, 0x0090); + if ( !e_0008_0090 ) { - e = InsertValEntry("",0x0008, 0x0090); - Archive->Push(e); + e_0008_0090 = new ValEntry( + Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x0090) ); + e_0008_0090->SetValue(""); + Archive->Push(e_0008_0090); } + + // Remove some inconstencies (probably some more will be added) + // if (0028 0008)Number of Frames exists + // Push out (0020 0052),Frame of Reference UID + // (only meaningfull within a Serie) + ValEntry *e_0028_0008 = FileInternal->GetValEntry(0x0028, 0x0008); + if ( !e_0028_0008 ) + { + Archive->Push(0x0020, 0X0052); + } } +/** + * \brief Restore in the File the initial group 0002 + */ +void FileHelper::RestoreWriteMandatory() +{ + // 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,0x0012); + Archive->Restore(0x0002,0x0013); + Archive->Restore(0x0002,0x0016); + Archive->Restore(0x0002,0x0100); + Archive->Restore(0x0002,0x0102); +} + //----------------------------------------------------------------------------- // Private /** @@ -1241,7 +1377,7 @@ void FileHelper::Initialize() WriteMode = WMODE_RAW; WriteType = ExplicitVR; - PixelReadConverter = new PixelReadConvert; + PixelReadConverter = new PixelReadConvert; PixelWriteConverter = new PixelWriteConvert; Archive = new DocEntryArchive( FileInternal );