📄 gdcmfilehelper.cxx
字号:
(FileInternal->GetDocEntry(0x0028, 0x0010));
ValEntry *oldCol = dynamic_cast<ValEntry *>
(FileInternal->GetDocEntry(0x0028, 0x0011));
if ( oldRow && oldCol )
{
std::string rows, columns;
ValEntry *newRow=new ValEntry(oldRow->GetDictEntry());
ValEntry *newCol=new ValEntry(oldCol->GetDictEntry());
newRow->Copy(oldCol);
newCol->Copy(oldRow);
newRow->SetValue(oldCol->GetValue());
newCol->SetValue(oldRow->GetValue());
Archive->Push(newRow);
Archive->Push(newCol);
}
ValEntry *libidoCode = CopyValEntry(0x0008,0x0010);
libidoCode->SetValue("ACRNEMA_LIBIDO_1.1");
Archive->Push(libidoCode);
}
/**
* \brief Set the Write not to No Libido format
*/
void FileHelper::SetWriteToNoLibido()
{
ValEntry *recCode = dynamic_cast<ValEntry *>
(FileInternal->GetDocEntry(0x0008,0x0010));
if ( recCode )
{
if ( recCode->GetValue() == "ACRNEMA_LIBIDO_1.1" )
{
ValEntry *libidoCode = CopyValEntry(0x0008,0x0010);
libidoCode->SetValue("");
Archive->Push(libidoCode);
}
}
}
/**
* \brief Restore the Write format
*/
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);
}
/**
* \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 new Val Entry (NULL when creation failed).
*/
ValEntry *FileHelper::CopyValEntry(uint16_t group, uint16_t elem)
{
DocEntry *oldE = FileInternal->GetDocEntry(group, elem);
ValEntry *newE;
if ( oldE )
{
newE = new ValEntry(oldE->GetDictEntry());
newE->Copy(oldE);
}
else
{
newE = GetFile()->NewValEntry(group, elem);
}
return newE;
}
/**
* \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
* 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,
const TagName &vr)
{
DocEntry *oldE = FileInternal->GetDocEntry(group, elem);
BinEntry *newE;
if ( oldE && vr != GDCM_UNKNOWN )
if ( oldE->GetVR() != vr )
oldE = NULL;
if ( oldE )
{
newE = new BinEntry(oldE->GetDictEntry());
newE->Copy(oldE);
}
else
{
newE = GetFile()->NewBinEntry(group, elem, vr);
}
return newE;
}
/**
* \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 they were before he decided to write.
*
* \note
* - 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
*
* \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.
*/
/* -------------------------------------------------------------------------------------
To be moved to User's guide / WIKI ?
We have to deal with 4 *very* different cases :
-1) user created ex nihilo his own image and wants to write it as a Dicom image.
-2) user modified the pixels of an existing image.
-3) user created a new image, using existing images (eg MIP, MPR, cartography image)
-4) user anonymized an image without processing the pixels.
gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases.
1)2)3)4)
0008 0012 Instance Creation Date
0008 0013 Instance Creation Time
0008 0018 SOP Instance UID
are *always* created with the current values; user has *no* possible intervention on
them.
'Serie Instance UID'(0x0020,0x000e)
'Study Instance UID'(0x0020,0x000d) are kept as is if already exist,
created if it doesn't.
The user is allowed to create his own Series/Studies,
keeping the same 'Serie Instance UID' / 'Study Instance UID' for various images
Warning :
The user shouldn't add any image to a 'Manufacturer Serie'
but there is no way no to allowed him to do that
None of the 'shadow elements' are droped out.
1)
'Modality' (0x0008,0x0060) is defaulted to "OT" (other) if missing.
'Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image).
'Study Date', 'Study Time' are defaulted to current Date and Time.
1)2)3)
'Media Storage SOP Class UID' (0x0002,0x0002)
'SOP Class UID' (0x0008,0x0016) are set to
[Secondary Capture Image Storage]
'Image Type' (0x0008,0x0008) is forced to "DERIVED\PRIMARY"
Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image)
2)4)
If 'SOP Class UID' exists in the native image ('true DICOM' image)
we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
--> 'Referenced SOP Class UID' (0x0008, 0x1150)
whose value is the original 'SOP Class UID'
--> 'Referenced SOP Instance UID' (0x0008, 0x1155)
whose value is the original 'SOP Class UID'
3) TODO : find a trick to allow user to pass to the writter the list of the Dicom images
or the Series, (or the Study ?) he used to created his image
(MIP, MPR, cartography image, ...)
These info should be stored (?)
0008 1110 SQ 1 Referenced Study Sequence
0008 1115 SQ 1 Referenced Series Sequence
0008 1140 SQ 1 Referenced Image Sequence
4) When user *knows* he didn't modified the pixels, he may ask the writer to keep some
informations unchanged :
'Media Storage SOP Class UID' (0x0002,0x0002)
'SOP Class UID' (0x0008,0x0016)
'Image Type' (0x0008,0x0008)
'Conversion Type' (0x0008,0x0064)
He has to use gdcm::FileHelper::SetKeepMediaStorageSOPClassUID(true)
(probabely name has to be changed)
Bellow follows the full description (hope so !) of the consistency checks performed
by gdcm::FileHelper::CheckMandatoryElements()
-->'Media Storage SOP Class UID' (0x0002,0x0002)
-->'SOP Class UID' (0x0008,0x0016) are set to
[Secondary Capture Image Storage]
(Potentialy, the image was modified by user, and post-processed;
it's no longer a 'native' image)
Except if user told he wants to keep MediaStorageSOPClassUID,
when *he* knows he didn't modify the image (e.g. : he just anonymized the file)
--> 'Image Type' (0x0008,0x0008)
is forced to "DERIVED\PRIMARY"
(The written image is no longer an 'ORIGINAL' one)
Except if user told he wants to keep MediaStorageSOPClassUID,
when *he* knows he didn't modify the image (e.g. : he just anonymized the file)
--> Conversion Type (0x0008,0x0064)
is forced to 'SYN' (Synthetic Image)
Except if user told he wants to keep MediaStorageSOPClassUID,
when *he* knows he didn't modify the image (e.g. : he just anonymized the file)
--> 'Modality' (0x0008,0x0060)
is defaulted to "OT" (other) if missing.
(a fully user created image belongs to *no* modality)
--> 'Media Storage SOP Instance UID' (0x0002,0x0003)
--> 'Implementation Class UID' (0x0002,0x0012)
are automatically generated; no user intervention possible
--> 'Serie Instance UID'(0x0020,0x000e)
--> 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist
created if it doesn't.
The user is allowed to create his own Series/Studies,
keeping the same 'Serie Instance UID' / 'Study Instance UID'
for various images
Warning :
The user shouldn't add any image to a 'Manufacturer Serie'
but there is no way no to allowed him to do that
--> If 'SOP Class UID' exists in the native image ('true DICOM' image)
we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
--> 'Referenced SOP Class UID' (0x0008, 0x1150)
whose value is the original 'SOP Class UID'
--> 'Referenced SOP Instance UID' (0x0008, 0x1155)
whose value is the original 'SOP Class UID'
--> Bits Stored, Bits Allocated, Hight Bit Position are checked for consistency
--> Pixel Spacing (0x0028,0x0030) is defaulted to "1.0\1.0"
--> Samples Per Pixel (0x0028,0x0002) is defaulted to 1 (grayscale)
--> Imager Pixel Spacing (0x0018,0x1164) : defaulted to Pixel Spacing value
--> Instance Creation Date, Instance Creation Time are forced to current Date and Time
--> Study Date, Study Time are defaulted to current Date and Time
(they remain unchanged if they exist)
--> Patient Orientation : (0x0020,0x0020), if not present, is deduced from
Image Orientation (Patient) : (0020|0037) or from
Image Orientation (RET) : (0020 0035)
--> Study ID, Series Number, Instance Number, Patient Orientation (Type 2)
are created, with empty value if there are missing.
--> Manufacturer, Institution Name, Patient's Name, (Type 2)
are defaulted with a 'gdcm' value.
--> Patient ID, Patient's Birth Date, Patient's Sex, (Type 2)
--> Referring Physician's Name (Type 2)
are created, with empty value if there are missing.
-------------------------------------------------------------------------------------*/
void FileHelper::CheckMandatoryElements()
{
std::string sop = Util::CreateUniqueUID();
// 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 Storage SOP Class UID
//0002 0003 UI 1 Media Storage 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
// Push out 'ACR-NEMA-special' entries, if any
Archive->Push(0x0008,0x0001); // Length to End
Archive->Push(0x0008,0x0010); // Recognition Code
Archive->Push(0x0028,0x0005); // Image Dimension
// Create them if not found
// Always modify the value
// Push the entries to the archive.
CopyMandatoryEntry(0x0002,0x0000,"0");
BinEntry *e_0002_0001 = CopyBinEntry(0x0002,0x0001, "OB");
e_0002_0001->SetBinArea(const_cast<uint8_t*>(Util::GetFileMetaInformationVersion()),
false);
e_0002_0001->SetLength(2);
Archive->Push(e_0002_0001);
// 'Media Storage SOP Class UID' --> [Secondary Capture Image Storage]
// Only if not specified by user, to let people create DERIVED images
ValEntry *e_0002_0002 = FileInternal->GetValEntry(0x0002, 0x0002);
if( !e_0002_0002)
{
CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7");
}
// 'Media Storage SOP Instance UID'
CopyMandatoryEntry(0x0002,0x0003,sop);
// 'Implementation Class UID'
// $ echo "gdcm" | od -b
CopyMandatoryEntry(0x0002,0x0012,"147.144.143.155");
// 'Implementation Version Name'
std::string version = "ITK/GDCM ";
version += Util::GetVersion();
CopyMandatoryEntry(0x0002,0x0013,version);
}
// 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")
{
CopyMandatoryEntry(0x0028,0x0100,"16");
}
// Check if user wasn't drunk ;-)
itksys_ios::ostringstream os;
// check 'Bits Allocated' vs decent values
int nbBitsAllocated = FileInternal->GetBitsAllocated();
if ( (nbBitsAllocated == 0 || nbBitsAllocated > 32)
|| ( nbBitsAllocated > 8 && nbBitsAllocated <16) )
{
CopyMandatoryEntry(0x0028,0x0100,"16");
gdcmWarningMacro("(0028,0100) changed from "
<< nbBitsAllocated << " to 16 for consistency purpose");
nbBitsAllocated = 16;
}
// check 'Bits Stored' vs 'Bits Allocated'
int nbBitsStored = FileInternal->GetBitsStored();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -