📄 gdcmfile.cxx
字号:
bool File::AnonymizeFile()
{
// If Anonymisation list is empty, let's perform some basic anonymization
if ( UserAnonymizeList.begin() == UserAnonymizeList.end() )
{
// If exist, replace by spaces
SetValEntry (" ",0x0010, 0x2154); // Telephone
SetValEntry (" ",0x0010, 0x1040); // Adress
SetValEntry (" ",0x0010, 0x0020); // Patient ID
DocEntry* patientNameHE = GetDocEntry (0x0010, 0x0010);
if ( patientNameHE ) // we replace it by Study Instance UID (why not ?)
{
std::string studyInstanceUID = GetEntryValue (0x0020, 0x000d);
if ( studyInstanceUID != GDCM_UNFOUND )
{
SetValEntry(studyInstanceUID, 0x0010, 0x0010);
}
else
{
SetValEntry("anonymized", 0x0010, 0x0010);
}
}
}
else
{
gdcm::DocEntry *d;
for (ListElements::iterator it = UserAnonymizeList.begin();
it != UserAnonymizeList.end();
++it)
{
d = GetDocEntry( (*it).Group, (*it).Elem);
if ( d == NULL)
continue;
if ( dynamic_cast<SeqEntry *>(d) )
{
gdcmWarningMacro( "You cannot 'Anonymize' a SeqEntry ");
continue;
}
if ( dynamic_cast<BinEntry *>(d) )
{
gdcmWarningMacro( "To 'Anonymize' a BinEntry, better use AnonymizeNoLoad (FIXME) ");
continue;
}
else
SetValEntry ((*it).Value, (*it).Group, (*it).Elem);
}
}
// In order to make definitively impossible any further identification
// remove or replace all the stuff that contains a Date
//0008 0012 DA ID Instance Creation Date
//0008 0020 DA ID Study Date
//0008 0021 DA ID Series Date
//0008 0022 DA ID Acquisition Date
//0008 0023 DA ID Content Date
//0008 0024 DA ID Overlay Date
//0008 0025 DA ID Curve Date
//0008 002a DT ID Acquisition Datetime
//0018 9074 DT ACQ Frame Acquisition Datetime
//0018 9151 DT ACQ Frame Reference Datetime
//0018 a002 DT ACQ Contribution Date Time
//0020 3403 SH REL Modified Image Date (RET)
//0032 0032 DA SDY Study Verified Date
//0032 0034 DA SDY Study Read Date
//0032 1000 DA SDY Scheduled Study Start Date
//0032 1010 DA SDY Scheduled Study Stop Date
//0032 1040 DA SDY Study Arrival Date
//0032 1050 DA SDY Study Completion Date
//0038 001a DA VIS Scheduled Admission Date
//0038 001c DA VIS Scheduled Discharge Date
//0038 0020 DA VIS Admitting Date
//0038 0030 DA VIS Discharge Date
//0040 0002 DA PRC Scheduled Procedure Step Start Date
//0040 0004 DA PRC Scheduled Procedure Step End Date
//0040 0244 DA PRC Performed Procedure Step Start Date
//0040 0250 DA PRC Performed Procedure Step End Date
//0040 2004 DA PRC Issue Date of Imaging Service Request
//0040 4005 DT PRC Scheduled Procedure Step Start Date and Time
//0040 4011 DT PRC Expected Completion Date and Time
//0040 a030 DT PRC Verification Date Time
//0040 a032 DT PRC Observation Date Time
//0040 a120 DT PRC DateTime
//0040 a121 DA PRC Date
//0040 a13a DT PRC Referenced Datetime
//0070 0082 DA ??? Presentation Creation Date
//0100 0420 DT ??? SOP Autorization Date and Time
//0400 0105 DT ??? Digital Signature DateTime
//2100 0040 DA PJ Creation Date
//3006 0008 DA SSET Structure Set Date
//3008 0024 DA ??? Treatment Control Point Date
//3008 0054 DA ??? First Treatment Date
//3008 0056 DA ??? Most Recent Treatment Date
//3008 0162 DA ??? Safe Position Exit Date
//3008 0166 DA ??? Safe Position Return Date
//3008 0250 DA ??? Treatment Date
//300a 0006 DA RT RT Plan Date
//300a 022c DA RT Air Kerma Rate Reference Date
//300e 0004 DA RT Review Date
return true;
}
/**
* \brief Performs some consistency checking on various 'File related'
* (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 writetype type of the file to be written
* (ACR, ExplicitVR, ImplicitVR)
*/
bool File::Write(std::string fileName, FileType writetype)
{
std::ofstream *fp = new std::ofstream(fileName.c_str(),
std::ios::out | std::ios::binary);
if (*fp == NULL)
{
gdcmWarningMacro("Failed to open (write) File: " << fileName.c_str());
return false;
}
// Entry : 0002|0000 = group length -> recalculated
ValEntry *e0000 = GetValEntry(0x0002,0x0000);
if ( e0000 )
{
itksys_ios::ostringstream sLen;
sLen << ComputeGroup0002Length( );
e0000->SetValue(sLen.str());
}
int i_lgPix = GetEntryLength(GrPixel, NumPixel);
if (i_lgPix != -2)
{
// no (GrPixel, NumPixel) element
std::string s_lgPix = Util::Format("%d", i_lgPix+12);
s_lgPix = Util::DicomString( s_lgPix.c_str() );
InsertValEntry(s_lgPix,GrPixel, 0x0000);
}
Document::WriteContent(fp, writetype);
fp->close();
delete fp;
return true;
}
//-----------------------------------------------------------------------------
// Protected
//-----------------------------------------------------------------------------
// Private
/**
* \brief Parse pixel data from disk of [multi-]fragment RLE encoding.
* Compute the RLE extra information and store it in \ref RLEInfo
* for later pixel retrieval usage.
*/
void File::ComputeRLEInfo()
{
std::string ts = GetTransferSyntax();
if ( !Global::GetTS()->IsRLELossless(ts) )
{
return;
}
// Encoded pixel data: for the time being we are only concerned with
// Jpeg or RLE Pixel data encodings.
// As stated in PS 3.5-2003, section 8.2 p44:
// "If sent in Encapsulated Format (i.e. other than the Native Format) the
// value representation OB is used".
// Hence we expect an OB value representation. Concerning OB VR,
// the section PS 3.5-2003, section A.4.c p 58-59, states:
// "For the Value Representations OB and OW, the encoding shall meet the
// following specifications depending on the Data element tag:"
// [...snip...]
// - the first item in the sequence of items before the encoded pixel
// data stream shall be basic offset table item. The basic offset table
// item value, however, is not required to be present"
ReadEncapsulatedBasicOffsetTable();
// Encapsulated RLE Compressed Images (see PS 3.5-2003, Annex G)
// Loop on the individual frame[s] and store the information
// on the RLE fragments in a RLEFramesInfo.
// Note: - when only a single frame is present, this is a
// classical image.
// - when more than one frame are present, then we are in
// the case of a multi-frame image.
long frameLength;
int i=0;
uint32_t sum = 0;
while ( (frameLength = ReadTagLength(0xfffe, 0xe000)) != 0 )
{
// Since we have read the basic offset table, let's check the value were correct
// or else produce a warning:
if ( BasicOffsetTableItemValue )
{
// If a BasicOffsetTableItemValue was read
uint32_t individualLength = BasicOffsetTableItemValue[i];
assert( individualLength == sum ); // REMOVE that if this is a problem
if( individualLength != sum )
{
gdcmWarningMacro( "BasicOffsetTableItemValue differs from the fragment lenght" );
}
sum += frameLength + 8;
i++;
}
// Parse the RLE Header and store the corresponding RLE Segment
// Offset Table information on fragments of this current Frame.
// Note that the fragment pixels themselves are not loaded
// (but just skipped).
long frameOffset = Fp->tellg(); // once per fragment
uint32_t nbRleSegments = ReadInt32();
if ( nbRleSegments > 16 )
{
// There should be at most 15 segments (refer to RLEFrame class)
gdcmWarningMacro( "Too many segments.");
}
uint32_t rleSegmentOffsetTable[16];
for( int k = 1; k <= 15; k++ )
{
rleSegmentOffsetTable[k] = ReadInt32();
}
// Deduce from both RLE Header and frameLength
// the fragment length, and again store this info
// in a RLEFramesInfo.
long rleSegmentLength[15];
// skipping (not reading) RLE Segments
if ( nbRleSegments > 1)
{
for(unsigned int k = 1; k <= nbRleSegments-1; k++)
{
rleSegmentLength[k] = rleSegmentOffsetTable[k+1]
- rleSegmentOffsetTable[k];
SkipBytes(rleSegmentLength[k]);
}
}
rleSegmentLength[nbRleSegments] = frameLength
- rleSegmentOffsetTable[nbRleSegments];
SkipBytes(rleSegmentLength[nbRleSegments]);
// Store the collected info
RLEFrame *newFrame = new RLEFrame;
newFrame->SetNumberOfFragments(nbRleSegments);
for( unsigned int uk = 1; uk <= nbRleSegments; uk++ )
{
newFrame->SetOffset(uk,frameOffset + rleSegmentOffsetTable[uk]);
newFrame->SetLength(uk,rleSegmentLength[uk]);
}
RLEInfo->AddFrame(newFrame);
}
// Make sure that we encounter a 'Sequence Delimiter Item'
// at the end of the item :
if ( !ReadTag(0xfffe, 0xe0dd) ) // once per RLE File
{
gdcmWarningMacro( "No sequence delimiter item at end of RLE item sequence");
}
}
/**
* \brief Parse pixel data from disk of [multi-]fragment Jpeg encoding.
* Compute the jpeg extra information (fragment[s] offset[s] and
* length) and store it[them] in \ref JPEGInfo for later pixel
* retrieval usage.
*/
void File::ComputeJPEGFragmentInfo()
{
// If you need to, look for comments of ComputeRLEInfo().
std::string ts = GetTransferSyntax();
if ( ! Global::GetTS()->IsJPEG(ts) )
{
return;
}
ReadEncapsulatedBasicOffsetTable();
// Loop on the fragments[s] and store the parsed information in a
// JPEGInfo.
long fragmentLength;
int i=0;
uint32_t sum = 0;
while ( (fragmentLength = ReadTagLength(0xfffe, 0xe000)) != 0 )
{
// Since we have read the basic offset table, let's check the value were correct
// or else produce a warning:
// A.4 Transfer syntaxes for encapsulation of encoded pixel data:
// When the Item Value is present, the Basic Offset Table Item Value shall contain
// concatenated 32-bit unsigned integer values that are byte offsets to the first
// byte of the Item Tag of the first fragment for each frame in the Sequence of
// Items. These offsets are measured from the first byte of the first Item Tag
// following the Basic Offset Table item (See Table A.4-2).
if ( BasicOffsetTableItemValue )
{
// If a BasicOffsetTableItemValue was read
uint32_t individualLength = BasicOffsetTableItemValue[i];
//assert( individualLength == sum ); // Seems like 00191113.dcm is off by one ??
if( individualLength != sum )
{
gdcmWarningMacro( "BasicOffsetTableItemValue differs from the fragment lenght:" <<
individualLength << " != " << sum );
}
sum += fragmentLength + 8;
i++;
}
long fragmentOffset = Fp->tellg(); // Once per fragment
// Store the collected info
JPEGFragment *newFragment = new JPEGFragment;
newFragment->SetOffset(fragmentOffset);
newFragment->SetLength(fragmentLength);
JPEGInfo->AddFragment(newFragment);
SkipBytes(fragmentLength);
}
// Make sure that we encounter a 'Sequence Delimiter Item'
// at the end of the item :
if ( !ReadTag(0xfffe, 0xe0dd) )
{
gdcmWarningMacro( "No sequence delimiter item at end of JPEG item sequence");
}
}
/**
* \brief Assuming the internal file pointer \ref Document::Fp
* is placed at the beginning of a tag, check whether this
* tag is (TestGroup, TestElem).
* \warning On success the internal file pointer \ref Document::Fp
* is modified to point after the tag.
* On failure (i.e. when the tag wasn't the expected tag
* (TestGroup, TestElem) the internal file pointer
* \ref Document::Fp is restored to it's original position.
* @param testGroup The expected group of the tag.
* @param testElem The expected Element of the tag.
* @return True on success, false otherwise.
*/
bool File::ReadTag(uint16_t testGroup, uint16_t testElem)
{
long positionOnEntry = Fp->tellg(); // Only when reading fragments
//long currentPosition = positionOnEntry; // On debugging purposes
// Read the Item Tag group and element, and make
// sure they are what we expected:
uint16_t itemTagGroup;
uint16_t itemTagElem;
try
{
itemTagGroup = ReadInt16();
itemTagElem = ReadInt16();
}
catch ( FormatError )
{
gdcmErrorMacro( "Can not read tag for "
<< " We should have found tag ("
<< DictEntry::TranslateToKey(testGroup,testElem) << ")"
) ;
return false;
}
if ( itemTagGroup != testGroup || itemTagElem != testElem )
{
// in order not to pollute output we don't warn on 'delimitors'
if (itemTagGroup != 0xfffe || testGroup != 0xfffe )
gdcmWarningMacro( "Wrong Item Tag found:"
<< " We should have found tag ("
<< std::hex << testGroup << "," << testElem << ")" << std::endl
<< " but instead we encountered tag ("
<< std::hex << itemTagGroup << "," << itemTagElem << ")"
<< " at address: " << " 0x(" << (unsigned int)positionOnEntry << ")"
) ;
Fp->seekg(positionOnEntry, std::ios::beg);
return false;
}
return true;
}
/**
* \brief Assuming the internal file pointer \ref Document::Fp
* is placed at the beginning of a tag (TestGroup, TestElement),
* read the length associated to the Tag.
* \warning On success the internal file pointer \ref Document::Fp
* is modified to point after the tag and it's length.
* On failure (i.e
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -