📄 gdcmdocument.cxx
字号:
// 0xffff means that we deal with 'No Length' Sequence
// or 'No Length' SQItem
if ( length16 == 0xffff)
{
length16 = 0;
}
FixDocEntryFoundLength( entry, (uint32_t)length16 );
return;
}
else
{
// Either implicit VR or a non DICOM conformal (see note below) explicit
// VR that ommited the VR of (at least) this element. Farts happen.
// [Note: according to the part 5, PS 3.5-2001, section 7.1 p25
// on Data elements "Implicit and Explicit VR Data Elements shall
// not coexist in a Data Set and Data Sets nested within it".]
// Length is on 4 bytes.
// Well ... group 0002 is always coded in 'Explicit VR Litle Endian'
// even if Transfer Syntax is 'Implicit VR ...'
// --> Except for 'Implicit VR Big Endian Transfer Syntax GE Private'
FixDocEntryFoundLength( entry, ReadInt32() );
return;
}
}
/**
* \brief Find the Length till the next sequence delimiter
* \warning NOT end user intended method !
* @return
*/
uint32_t Document::FindDocEntryLengthOBOrOW()
throw( FormatUnexpected )
{
// See PS 3.5-2001, section A.4 p. 49 on encapsulation of encoded pixel data.
long positionOnEntry = Fp->tellg(); // Only for OB,OW DataElements
bool foundSequenceDelimiter = false;
uint32_t totalLength = 0;
while ( !foundSequenceDelimiter )
{
uint16_t group;
uint16_t elem;
try
{
group = ReadInt16();
elem = ReadInt16();
}
catch ( FormatError )
{
throw FormatError("Unexpected end of file encountered during ",
"Document::FindDocEntryLengthOBOrOW()");
}
// We have to decount the group and element we just read
totalLength += 4;
if ( group != 0xfffe || ( ( elem != 0xe0dd ) && ( elem != 0xe000 ) ) )
{
gdcmWarningMacro(
"Neither an Item tag nor a Sequence delimiter tag on :"
<< std::hex << group << " , " << elem
<< ")" );
Fp->seekg(positionOnEntry, std::ios::beg); // Once per fragment (if any) of OB,OW DataElements
throw FormatUnexpected(
"Neither an Item tag nor a Sequence delimiter tag.");
}
if ( elem == 0xe0dd )
{
foundSequenceDelimiter = true;
}
uint32_t itemLength = ReadInt32();
// We add 4 bytes since we just read the ItemLength with ReadInt32
totalLength += itemLength + 4;
SkipBytes(itemLength);
if ( foundSequenceDelimiter )
{
break;
}
}
Fp->seekg( positionOnEntry, std::ios::beg); // Only for OB,OW DataElements
return totalLength;
}
/**
* \brief Find the Value Representation of the current Dicom Element.
* @return Value Representation of the current Entry
*/
std::string Document::FindDocEntryVR()
{
if ( Filetype != ExplicitVR )
return GDCM_UNKNOWN;
long positionOnEntry = Fp->tellg();
// Warning: we believe this is explicit VR (Value Representation) because
// we used a heuristic that found "UL" in the first tag. Alas this
// doesn't guarantee that all the tags will be in explicit VR. In some
// cases (see e-film filtered files) one finds implicit VR tags mixed
// within an explicit VR file. Hence we make sure the present tag
// is in explicit VR and try to fix things if it happens not to be
// the case.
char vr[3];
Fp->read (vr, (size_t)2);
vr[2] = 0;
if ( !CheckDocEntryVR(vr) )
{
Fp->seekg(positionOnEntry, std::ios::beg);
return GDCM_UNKNOWN;
}
return vr;
}
/**
* \brief Check the correspondance between the VR of the header entry
* and the taken VR. If they are different, the header entry is
* updated with the new VR.
* @param vr Dicom Value Representation
* @return false if the VR is incorrect or if the VR isn't referenced
* otherwise, it returns true
*/
bool Document::CheckDocEntryVR(VRKey vr)
{
if ( !Global::GetVR()->IsValidVR(vr) )
return false;
return true;
}
/**
* \brief Get the transformed value of the header entry. The VR value
* is used to define the transformation to operate on the value
* \warning NOT end user intended method !
* @param entry entry to tranform
* @return Transformed entry value
*/
std::string Document::GetDocEntryValue(DocEntry *entry)
{
if ( IsDocEntryAnInteger(entry) && entry->IsImplicitVR() )
{
std::string val = ((ValEntry *)entry)->GetValue();
std::string vr = entry->GetVR();
uint32_t length = entry->GetLength();
itksys_ios::ostringstream s;
int nbInt;
// When short integer(s) are expected, read and convert the following
// n * 2 bytes properly i.e. as a multivaluated strings
// (each single value is separated fromthe next one by '\'
// as usual for standard multivaluated filels
// Elements with Value Multiplicity > 1
// contain a set of short integers (not a single one)
if ( vr == "US" || vr == "SS" )
{
uint16_t newInt16;
nbInt = length / 2;
for (int i=0; i < nbInt; i++)
{
if ( i != 0 )
{
s << '\\';
}
newInt16 = ( val[2*i+0] & 0xFF ) + ( ( val[2*i+1] & 0xFF ) << 8);
newInt16 = SwapShort( newInt16 );
s << newInt16;
}
}
// When integer(s) are expected, read and convert the following
// n * 4 bytes properly i.e. as a multivaluated strings
// (each single value is separated fromthe next one by '\'
// as usual for standard multivaluated filels
// Elements with Value Multiplicity > 1
// contain a set of integers (not a single one)
else if ( vr == "UL" || vr == "SL" )
{
uint32_t newInt32;
nbInt = length / 4;
for (int i=0; i < nbInt; i++)
{
if ( i != 0)
{
s << '\\';
}
newInt32 = ( val[4*i+0] & 0xFF )
+ (( val[4*i+1] & 0xFF ) << 8 )
+ (( val[4*i+2] & 0xFF ) << 16 )
+ (( val[4*i+3] & 0xFF ) << 24 );
newInt32 = SwapLong( newInt32 );
s << newInt32;
}
}
#ifdef GDCM_NO_ANSI_STRING_STREAM
s << std::ends; // to avoid oddities on Solaris
#endif //GDCM_NO_ANSI_STRING_STREAM
return s.str();
}
return ((ValEntry *)entry)->GetValue();
}
/**
* \brief Get the reverse transformed value of the header entry. The VR
* value is used to define the reverse transformation to operate on
* the value
* \warning NOT end user intended method !
* @param entry Entry to reverse transform
* @return Reverse transformed entry value
*/
std::string Document::GetDocEntryUnvalue(DocEntry *entry)
{
if ( IsDocEntryAnInteger(entry) && entry->IsImplicitVR() )
{
std::string vr = entry->GetVR();
std::vector<std::string> tokens;
itksys_ios::ostringstream s;
if ( vr == "US" || vr == "SS" )
{
uint16_t newInt16;
tokens.erase( tokens.begin(), tokens.end()); // clean any previous value
Util::Tokenize (((ValEntry *)entry)->GetValue(), tokens, "\\");
for (unsigned int i=0; i<tokens.size(); i++)
{
newInt16 = atoi(tokens[i].c_str());
s << ( newInt16 & 0xFF )
<< (( newInt16 >> 8 ) & 0xFF );
}
tokens.clear();
}
if ( vr == "UL" || vr == "SL")
{
uint32_t newInt32;
tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
Util::Tokenize (((ValEntry *)entry)->GetValue(), tokens, "\\");
for (unsigned int i=0; i<tokens.size();i++)
{
newInt32 = atoi(tokens[i].c_str());
s << (char)( newInt32 & 0xFF )
<< (char)(( newInt32 >> 8 ) & 0xFF )
<< (char)(( newInt32 >> 16 ) & 0xFF )
<< (char)(( newInt32 >> 24 ) & 0xFF );
}
tokens.clear();
}
#ifdef GDCM_NO_ANSI_STRING_STREAM
s << std::ends; // to avoid oddities on Solaris
#endif //GDCM_NO_ANSI_STRING_STREAM
return s.str();
}
return ((ValEntry *)entry)->GetValue();
}
/**
* \brief Skip a given Header Entry
* \warning NOT end user intended method !
* @param entry entry to skip
*/
void Document::SkipDocEntry(DocEntry *entry)
{
SkipBytes(entry->GetLength());
}
/**
* \brief Skips to the beginning of the next Header Entry
* \warning NOT end user intended method !
* @param currentDocEntry entry to skip
*/
void Document::SkipToNextDocEntry(DocEntry *currentDocEntry)
{
long l = currentDocEntry->GetReadLength();
if ( (uint32_t)l == (uint32_t)-1 ) // length = 0xffff shouldn't appear here ...
// ... but PMS imagers happen !
return;
Fp->seekg((size_t)(currentDocEntry->GetOffset()), std::ios::beg); //FIXME :each DocEntry
if (currentDocEntry->GetGroup() != 0xfffe) // for fffe pb
{
Fp->seekg( l,std::ios::cur); //FIXME :each DocEntry
}
}
/**
* \brief When the length of an element value is obviously wrong (because
* the parser went Jabberwocky) one can hope improving things by
* applying some heuristics.
* @param entry entry to check
* @param foundLength first assumption about length
*/
void Document::FixDocEntryFoundLength(DocEntry *entry,
uint32_t foundLength)
{
entry->SetReadLength( foundLength );// will be updated only if a bug is found
if ( foundLength == 0xffffffff)
{
foundLength = 0;
}
uint16_t gr = entry->GetGroup();
uint16_t elem = entry->GetElement();
if ( foundLength % 2)
{
gdcmWarningMacro( "Warning : Tag with uneven length " << foundLength
<< " in x(" << std::hex << gr << "," << elem <<")");
}
//////// Fix for some naughty General Electric images.
// Allthough not recent many such GE corrupted images are still present
// on Creatis hard disks. Hence this fix shall remain when such images
// are no longer in use (we are talking a few years, here)...
// Note: XMedCon probably uses such a trick since it is able to read
// those pesky GE images ...
if ( foundLength == 13)
{
// Only happens for this length !
if ( gr != 0x0008 || ( elem != 0x0070 && elem != 0x0080 ) )
{
foundLength = 10;
entry->SetReadLength(10); // a bug is to be fixed !?
}
}
//////// Fix for some brain-dead 'Leonardo' Siemens images.
// Occurence of such images is quite low (unless one leaves close to a
// 'Leonardo' source. Hence, one might consider commenting out the
// following fix on efficiency reasons.
else if ( gr == 0x0009 && ( elem == 0x1113 || elem == 0x1114 ) )
{
// Ideally we should check we are in Explicit and double check
// that VR=UL... this is done properly in gdcm2
if( foundLength == 6 )
{
gdcmWarningMacro( "Replacing Length from 6 into 4" );
foundLength = 4;
entry->SetReadLength(4); // a bug is to be fixed !
}
else if ( foundLength%4 )
{
gdcmErrorMacro( "This looks like to a buggy Siemens DICOM file."
"The length of this tag seems to be wrong" );
}
}
else if ( entry->GetVR() == "SQ" )
{
foundLength = 0; // ReadLength is unchanged
}
//////// We encountered a 'delimiter' element i.e. a tag of the form
// "fffe|xxxx" which is just a marker. Delimiters length should not be
// taken into account.
else if ( gr == 0xfffe )
{
// According to the norm, fffe|0000 shouldn't exist. BUT the Philips
// image gdcmData/gdcm-MR-PHILIPS-16-Multi-Seq.dcm happens to
// causes extra troubles...
if ( entry->GetElement() != 0x0000 )
{
foundLength = 0;
}
}
entry->SetLength(foundLength);
}
/**
* \brief Apply some heuristics to predict whether the considered
* element value contains/represents an integer or not.
* @param entry The element value on which to apply the predicate.
* @return The result of the heuristical predicate.
*/
bool Document::IsDocEntryAnInteger(DocEntry *entry)
{
uint16_t elem = entry->GetElement();
uint16_t group = entry->GetGroup();
(void)group;
const std::string &vr = entry->GetVR();
uint32_t length = entry->GetLength();
// When we have some semantics on the element we just read, and if we
// a priori know we are dealing with an integer, then we shall be
// able to swap it's element value properly.
if ( elem == 0 ) // This is the group length of the group
{
if ( length == 4 )
{
return true;
}
else
{
// Although this should never happen, still some images have a
// corrupted group length [e.g. have a glance at offset x(8336) of
// gdcmData/gdcm-MR-PHILIPS-16-Multi-Seq.dcm.
// Since for dicom compliant and well behaved headers, the present
// test is useless (and might even look a bit paranoid), when we
// encounter such an ill-formed image, we simply display a warning
// message and proceed on parsing (while crossing fingers).
long filePosition = Fp->tellg(); // Only when elem 0x0000 length is not 4 (?!?)
(void)filePosition;
gdcmWarningMacro( "Erroneous Group Length element length on : ("
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -