+/**
+ * \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
+ if ( WriteType != ACR && WriteType != ACR_LIBIDO )
+ {
+ // Group 000002 (Meta Elements) already pushed out
+
+ //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
+
+ // 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);
+
+ BinEntry *e_0002_0001 = CopyBinEntry(0x0002,0x0001, "OB");
+ e_0002_0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(),
+ false);
+ e_0002_0001->SetLength(2);
+ Archive->Push(e_0002_0001);
+
+ // 'Media Stored SOP Class UID'
+ ValEntry *e_0002_0002 = CopyValEntry(0x0002,0x0002);
+ // [Secondary Capture Image Storage]
+ e_0002_0002->SetValue("1.2.840.10008.5.1.4.1.1.7");
+ Archive->Push(e_0002_0002);
+
+ // 'Media Stored SOP Instance UID'
+ ValEntry *e_0002_0003 = CopyValEntry(0x0002,0x0003);
+ e_0002_0003->SetValue(Util::CreateUniqueUID());
+ Archive->Push(e_0002_0003);
+
+ // 'Implementation Class UID'
+ ValEntry *e_0002_0012 = CopyValEntry(0x0002,0x0012);
+ e_0002_0012->SetValue(Util::CreateUniqueUID());
+ Archive->Push(e_0002_0012);
+
+ // 'Implementation Version Name'
+ ValEntry *e_0002_0013 = CopyValEntry(0x0002,0x0013);
+ std::string version = "GDCM ";
+ version += Util::GetVersion();
+ e_0002_0013->SetValue(version);
+ Archive->Push(e_0002_0013);
+
+ //'Source Application Entity Title' Not Mandatory
+ //ValEntry *e_0002_0016 = CopyValEntry(0x0002,0x0016);
+ // e_0002_0016->SetValue("1.2.840.10008.5.1.4.1.1.7");
+ // Archive->Push(e_0002_0016);
+ }
+
+ // Push out 'LibIDO-special' entries, if any
+ Archive->Push(0x0028,0x0015);
+ Archive->Push(0x0028,0x0016);
+ Archive->Push(0x0028,0x0017);
+ Archive->Push(0x0028,0x00199);
+
+ // Deal with the pb of (Bits Stored = 12)
+ // - we're gonna write the image as Bits Stored = 16
+ if ( FileInternal->GetEntryValue(0x0028,0x0100) == "12")
+ {
+ ValEntry *e_0028_0100 = CopyValEntry(0x0028,0x0100);
+ e_0028_0100->SetValue("16");
+ Archive->Push(e_0028_0100);
+ }
+
+ // Check if user wasn't drunk ;-)
+
+ std::ostringstream s;
+ // check 'Bits Allocated' vs decent values
+ int nbBitsAllocated = FileInternal->GetBitsAllocated();
+ if ( nbBitsAllocated == 0 || nbBitsAllocated > 32)
+ {
+ ValEntry *e_0028_0100 = CopyValEntry(0x0028,0x0100);
+ e_0028_0100->SetValue("16");
+ Archive->Push(e_0028_0100);
+ gdcmWarningMacro("(0028,0100) changed from "
+ << nbBitsAllocated << " to 16 for consistency purpose");
+ nbBitsAllocated = 16;
+ }
+ // check 'Bits Stored' vs 'Bits Allocated'
+ int nbBitsStored = FileInternal->GetBitsStored();
+ if ( nbBitsStored == 0 || nbBitsStored > nbBitsAllocated )
+ {
+ s << nbBitsAllocated;
+ ValEntry *e_0028_0101 = CopyValEntry(0x0028,0x0101);
+ e_0028_0101->SetValue( s.str() );
+ Archive->Push(e_0028_0101);
+ gdcmWarningMacro("(0028,0101) changed from "
+ << nbBitsStored << " to " << nbBitsAllocated
+ << " for consistency purpose" );
+ nbBitsStored = nbBitsAllocated;
+ }
+ // check 'Hight Bit Position' vs 'Bits Allocated' and 'Bits Stored'
+ int highBitPosition = FileInternal->GetHighBitPosition();
+ if ( highBitPosition == 0 ||
+ highBitPosition > nbBitsAllocated-1 ||
+ highBitPosition < nbBitsStored-1 )
+ {
+ ValEntry *e_0028_0102 = CopyValEntry(0x0028,0x0102);
+
+ s << nbBitsStored - 1;
+ e_0028_0102->SetValue( s.str() );
+ Archive->Push(e_0028_0102);
+ gdcmWarningMacro("(0028,0102) changed from "
+ << highBitPosition << " to " << nbBitsAllocated-1
+ << " for consistency purpose");
+ }
+ // --- 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 *sis = new SeqEntry (
+ 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) );
+ e_0008_1150->SetValue( e_0008_0016->GetValue());
+ sqi->AddEntry(e_0008_1150);
+
+ // create 'Referenced SOP Instance UID'
+ ValEntry *e_0008_0018 = FileInternal->GetValEntry(0x0008, 0x0018);
+ ValEntry *e_0008_1155 = new ValEntry(
+ Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1155) );
+ e_0008_1155->SetValue( e_0008_0018->GetValue());
+ sqi->AddEntry(e_0008_1155);
+
+ sis->AddSQItem(sqi,1);
+ // temporarily replaces any previous 'Source Image Sequence'
+ Archive->Push(sis);
+
+ // '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
+ {
+ // 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) );
+ e_0008_0018->SetValue( Util::CreateUniqueUID() );
+ Archive->Push(e_0008_0018);
+
+ // Instance Creation Date
+ ValEntry *e_0008_0012 = CopyValEntry(0x0008,0x0012);
+ std::string date = Util::GetCurrentDate();
+ e_0008_0012->SetValue(date.c_str());
+ Archive->Push(e_0008_0012);
+
+ // Instance Creation Time
+ ValEntry *e_0008_0013 = CopyValEntry(0x0008,0x0013);
+ std::string time = Util::GetCurrentTime();
+ 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 mandatory-inside-a-Sequence
+// Entries whose type is 2 are mandatory, with a optional value
+// Entries whose type is 2c are mandatory-inside-a-Sequence
+// Entries whose type is 3 are optional
+
+ // '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_0020_000e = new ValEntry(
+ Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0020, 0x000e) );
+ e_0020_000e->SetValue(Util::CreateUniqueUID() );
+ Archive->Push(e_0020_000e);
+ }
+
+ // '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_0020_000d = new ValEntry(
+ Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0020, 0x000d) );
+ e_0020_000d->SetValue(Util::CreateUniqueUID() );
+ Archive->Push(e_0020_000d);
+ }
+
+ // Modality : if missing we set it to 'OTher'
+ ValEntry *e_0008_0060 = FileInternal->GetValEntry(0x0008, 0x0060);
+ if ( !e_0008_0060 )
+ {
+ 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_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_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_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 : 'type 2' entry -> must exist, value not mandatory
+ ValEntry *e_0010_0030 = FileInternal->GetValEntry(0x0010, 0x0030);
+ if ( !e_0010_0030 )
+ {
+ e_0010_0030 = new ValEntry(
+ Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0010, 0x0030) );
+ e_0010_0030->SetValue("");
+ Archive->Push(e_0010_0030);
+ }
+
+ // Patient's Sex :'type 2' entry -> must exist, value not mandatory
+ ValEntry *e_0010_0040 = FileInternal->GetValEntry(0x0010, 0x0040);
+ if ( !e_0010_0040 )
+ {
+ e_0010_0040 = new ValEntry(
+ Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0010, 0x0040) );
+ e_0010_0040->SetValue("");
+ Archive->Push(e_0010_0040);
+ }
+
+ // 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_0008_0090 = new ValEntry(
+ Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x0090) );
+ e_0008_0090->SetValue("");
+ Archive->Push(e_0008_0090);
+ }
+
+ // Pixel Spacing : defaulted to 1.0\1.0
+ ValEntry *e_0028_0030 = FileInternal->GetValEntry(0x0028, 0x0030);
+ if ( !e_0028_0030 )
+ {
+ e_0028_0030 = new ValEntry(
+ Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0028, 0x0030) );
+ e_0028_0030->SetValue("1.0\\1.0");
+ Archive->Push(e_0028_0030);
+ }
+
+ // 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,0x0010);
+ Archive->Restore(0x0002,0x0012);
+ Archive->Restore(0x0002,0x0013);
+ Archive->Restore(0x0002,0x0016);
+ Archive->Restore(0x0002,0x0100);
+ Archive->Restore(0x0002,0x0102);
+
+ Archive->Restore(0x0008,0x0012);
+ Archive->Restore(0x0008,0x0013);
+ Archive->Restore(0x0008,0x0016);
+ Archive->Restore(0x0008,0x0018);
+ Archive->Restore(0x0008,0x0060);
+ Archive->Restore(0x0008,0x0070);
+ Archive->Restore(0x0008,0x0080);
+ Archive->Restore(0x0008,0x0090);
+ Archive->Restore(0x0008,0x2112);
+
+ Archive->Restore(0x0010,0x0010);
+ Archive->Restore(0x0010,0x0030);
+ Archive->Restore(0x0010,0x0040);
+
+ Archive->Restore(0x0020,0x000d);
+ Archive->Restore(0x0020,0x000e);
+
+}
+