+ // Deal with the pb of (Bits Stored = 12)
+ // - we're gonna write the image as Bits Stored = 16
+ if ( FileInternal->GetEntryString(0x0028,0x0100) == "12")
+ {
+ CopyMandatoryEntry(0x0028,0x0100,"16","US");
+ }
+
+ // Check if user wasn't drunk ;-)
+
+ std::ostringstream s;
+ // check 'Bits Allocated' vs decent values
+ int nbBitsAllocated = FileInternal->GetBitsAllocated();
+ if ( nbBitsAllocated == 0 || nbBitsAllocated > 32
+ || ( nbBitsAllocated > 8 && nbBitsAllocated <16) )
+ {
+ CopyMandatoryEntry(0x0028,0x0100,"16","US");
+ 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.str("");
+ s << nbBitsAllocated;
+ CopyMandatoryEntry(0x0028,0x0101,s.str(),"US");
+ 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 )
+ {
+ s.str("");
+ s << nbBitsStored - 1;
+ CopyMandatoryEntry(0x0028,0x0102,s.str(),"US");
+ gdcmWarningMacro("(0028,0102) changed from "
+ << highBitPosition << " to " << nbBitsAllocated-1
+ << " for consistency purpose");
+ }
+
+ // check Pixel Representation (default it as 0 -unsigned-)
+
+ DataEntry *e_0028_0103 = FileInternal->GetDataEntry(0x0028, 0x0103);
+ if ( !e_0028_0103 )
+ {
+ gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be mandatory");
+ CopyMandatoryEntry(0x0028, 0x0103,"0","US");
+ }
+ else
+ {
+ int sign = (int)e_0028_0103->GetValue(0);
+ if (sign !=1 && sign !=0)
+ {
+ gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be =1 or =0");
+ CopyMandatoryEntry(0x0028, 0x0103,"0","US");
+ }
+ }
+
+ std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
+ if ( pixelSpacing == GDCM_UNFOUND )
+ {
+ pixelSpacing = "1.0\\1.0";
+ // if missing, Pixel Spacing forced to "1.0\1.0"
+ CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
+ }
+
+ // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
+ // --> This one is the *legal* one !
+ if ( ContentType != USER_OWN_IMAGE)
+ // we write it only when we are *sure* the image comes from
+ // an imager (see also 0008,0x0064)
+ CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
+
+
+
+/*
+///Exact meaning of RETired fields
+
+// See page 73 of ACR-NEMA_300-1988.pdf !
+
+// 0020,0020 : Patient Orientation :
+Patient direction of the first row and
+column of the images. The first entry id the direction of the raws, given by the
+direction of the last pixel in the first row from the first pixel in tha row.
+the second entry is the direction of the columns, given by the direction of the
+last pixel in the first column from the first pixel in that column.
+L : Left, F : Feet, A : Anterior, P : Posterior.
+Up to 3 letters can be used in combination to indicate oblique planes.
+
+//0020,0030 Image Position (RET)
+x,y,z coordinates im mm of the first pixel in the image
+
+// 0020,0035 Image Orientation (RET)
+Direction cosines of the R axis of the image system with respect to the
+equipment coordinate axes x,y,z, followed by direction cosines of the C axis of
+the image system with respect to the same axes
+
+//0020,0050 Location
+An image location reference, standard for the modality (such as CT bed
+position), used to indicate position. Calculation of position for other purposes
+is only from (0020,0030) and (0020,0035)
+*/
+
+/*
+// if imagePositionPatient not found, default it with imagePositionRet, if any
+// if imageOrientationPatient not found, default it with imageOrientationRet, if any
+
+ std::string imagePositionRet = FileInternal->GetEntryString(0x0020,0x0030);
+ std::string imageOrientationRet = FileInternal->GetEntryString(0x0020,0x0035);
+ std::string imagePositionPatient = FileInternal->GetEntryString(0x0020,0x0032);
+ std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037);
+
+ if( imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND
+ && imagePositionRet != GDCM_UNFOUND && imageOrientationRet != GDCM_UNFOUND)
+ {
+ CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
+ Archive->Push(0x0020,0x0030);
+ CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
+ Archive->Push(0x0020,0x0035);
+ }
+*/
+
+ // Samples Per Pixel (type 1) : default to grayscale
+ CheckMandatoryEntry(0x0028,0x0002,"1","US");
+
+ // --- Check UID-related Entries ---
+
+ // At the end, not to overwrite the original ones,
+ // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'
+ // 'SOP Instance UID'
+ CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
+
+ if ( ContentType == USER_OWN_IMAGE)
+ {
+ gdcmDebugMacro( "USER_OWN_IMAGE (2)");
+ // Conversion Type.
+ // Other possible values are :
+ // See PS 3.3, Page 408
+
+ // DV = Digitized Video
+ // DI = Digital Interface
+ // DF = Digitized Film
+ // WSD = Workstation
+ // SD = Scanned Document
+ // SI = Scanned Image
+ // DRW = Drawing
+ // SYN = Synthetic Image
+
+ CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
+ }
+/*
+ if ( ContentType == CREATED_IMAGE)
+ {
+ /// \todo : find a trick to pass the Media Storage SOP Instance UID of the images used to create the current image
+
+ }
+*/
+
+ // ---- The user will never have to take any action on the following ----
+
+ // new value for 'SOP Instance UID'
+ //SetMandatoryEntry(0x0008,0x0018,Util::CreateUniqueUID());
+
+ // Instance Creation Date
+ const std::string &date = Util::GetCurrentDate();
+ CopyMandatoryEntry(0x0008,0x0012,date,"DA");
+
+ // Instance Creation Time
+ const std::string &time = Util::GetCurrentTime();
+ CopyMandatoryEntry(0x0008,0x0013,time,"TM");
+
+ // Study Date
+ CheckMandatoryEntry(0x0008,0x0020,date,"DA");
+ // Study Time
+ CheckMandatoryEntry(0x0008,0x0030,time,"TM");
+
+ // Accession Number
+ //CopyMandatoryEntry(0x0008,0x0050,"");
+ CheckMandatoryEntry(0x0008,0x0050,"","SH");
+
+
+ // ----- 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,
+ // with a mandatory value
+ // Entries whose type is 2 are mandatory, with an optional value
+ // Entries whose type is 2c are mandatory-inside-a-Sequence,
+ // with an optional value
+ // Entries whose type is 3 are optional
+
+ // '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
+ CheckMandatoryEntry(0x0020,0x000d,Util::CreateUniqueUID(),"UI");
+
+ // '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 prevent him for doing that
+ CheckMandatoryEntry(0x0020,0x000e,Util::CreateUniqueUID(),"UI");
+
+ // Study ID
+ CheckMandatoryEntry(0x0020,0x0010,"","SH");
+
+ // Series Number
+ CheckMandatoryEntry(0x0020,0x0011,"","IS");
+
+ // Instance Number
+ CheckMandatoryEntry(0x0020,0x0013,"","IS");
+
+ // Patient Orientation
+ // Can be computed from (0020|0037) : Image Orientation (Patient)
+ gdcm::Orientation *o = gdcm::Orientation::New();
+ std::string ori = o->GetOrientation ( FileInternal );
+ o->Delete();
+ if (ori != "\\" && ori != GDCM_UNFOUND)
+ CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
+ else
+ CheckMandatoryEntry(0x0020,0x0020,"","CS");
+
+ // Default Patient Position to HFS
+ CheckMandatoryEntry(0x0018,0x5100,"HFS","CS");
+
+ // Modality : if missing we set it to 'OTher'
+ CheckMandatoryEntry(0x0008,0x0060,"OT","CS");
+
+ // Manufacturer : if missing we set it to 'GDCM Factory'
+ CheckMandatoryEntry(0x0008,0x0070,"GDCM Factory","LO");
+
+ // Institution Name : if missing we set it to 'GDCM Hospital'
+ CheckMandatoryEntry(0x0008,0x0080,"GDCM Hospital","LO");
+
+ // Patient's Name : if missing, we set it to 'GDCM^Patient'
+ CheckMandatoryEntry(0x0010,0x0010,"GDCM^Patient","PN");
+
+ // Patient ID : some clinical softwares *demand* it although it's a 'type 2' entry.
+ CheckMandatoryEntry(0x0010,0x0020,"gdcm ID","LO");
+
+ // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory
+ CheckMandatoryEntry(0x0010,0x0030,"","DA");
+
+ // Patient's Sex :'type 2' entry -> must exist, value not mandatory
+ CheckMandatoryEntry(0x0010,0x0040,"","CS");
+
+ // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory
+ CheckMandatoryEntry(0x0008,0x0090,"","PN");
+
+ /*
+ // Deal with element 0x0000 (group length) of each group.
+ // First stage : get all the different Groups
+
+ GroupHT grHT;
+ DocEntry *d = FileInternal->GetFirstEntry();
+ while(d)
+ {
+ grHT[d->GetGroup()] = 0;
+ d=FileInternal->GetNextEntry();
+ }
+ // Second stage : add the missing ones (if any)
+ for (GroupHT::iterator it = grHT.begin(); it != grHT.end(); ++it)
+ {
+ CheckMandatoryEntry(it->first, 0x0000, "0");
+ }
+ // Third stage : update all 'zero level' groups length
+*/
+
+}
+
+void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
+{
+ DataEntry *entry = FileInternal->GetDataEntry(group,elem);
+ if ( !entry )
+ {
+ //entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
+ entry = DataEntry::New(group,elem,vr);
+ entry->SetString(value);
+ Archive->Push(entry);
+ entry->Delete();
+ }
+}
+
+/// \todo : what is it used for ? (FileHelper::SetMandatoryEntry)
+void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
+{
+ //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
+ DataEntry *entry = DataEntry::New(group,elem,vr);
+ entry->SetString(value);
+ Archive->Push(entry);
+ entry->Delete();
+}
+
+void FileHelper::CopyMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
+{
+ DataEntry *entry = CopyDataEntry(group,elem,vr);
+ entry->SetString(value);
+ Archive->Push(entry);
+ entry->Delete();
+}
+
+/**
+ * \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);
+
+ // FIXME : Check if none is missing !
+
+ 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);
+}
+
+
+/**
+ * \brief CallStartMethod
+ */
+void FileHelper::CallStartMethod()
+{
+ Progress = 0.0f;
+ Abort = false;
+ CommandManager::ExecuteCommand(this,CMD_STARTPROGRESS);
+}
+
+/**
+ * \brief CallProgressMethod
+ */
+void FileHelper::CallProgressMethod()
+{
+ CommandManager::ExecuteCommand(this,CMD_PROGRESS);
+}
+
+/**
+ * \brief CallEndMethod
+ */
+void FileHelper::CallEndMethod()
+{
+ Progress = 1.0f;
+ CommandManager::ExecuteCommand(this,CMD_ENDPROGRESS);