📄 gdcmdocument.cxx
字号:
return IsParsable();
}
/**
* \brief Predicate for dicom version 3 file.
* @return True when the file is a dicom version 3.
*/
bool Document::IsDicomV3()
{
// Checking if Transfer Syntax exists is enough
// Anyway, it's too late check if the 'Preamble' was found ...
// And ... would it be a rich idea to check ?
// (some 'no Preamble' DICOM images exist !)
return GetDocEntry(0x0002, 0x0010) != NULL;
}
/**
* \brief Predicate for Papyrus file
* Dedicated to whomsoever it may concern
* @return True when the file is a Papyrus file.
*/
bool Document::IsPapyrus()
{
// check for Papyrus private Sequence
DocEntry *e = GetDocEntry(0x0041, 0x1050);
if ( !e )
return false;
// check if it's actually a Sequence
if ( !dynamic_cast<SeqEntry*>(e) )
return false;
return true;
}
/**
* \brief returns the File Type
* (ACR, ACR_LIBIDO, ExplicitVR, ImplicitVR, Unknown)
* @return the FileType code
*/
FileType Document::GetFileType()
{
return Filetype;
}
/**
* \brief Accessor to the Transfer Syntax (when present) of the
* current document (it internally handles reading the
* value from disk when only parsing occured).
* @return The encountered Transfer Syntax of the current document, if DICOM.
* GDCM_UNKNOWN for ACR-NEMA files (or broken headers ...)
*/
std::string Document::GetTransferSyntax()
{
DocEntry *entry = GetDocEntry(0x0002, 0x0010);
if ( !entry )
{
return GDCM_UNKNOWN;
}
// The entry might be present but not loaded (parsing and loading
// happen at different stages): try loading and proceed with check...
LoadDocEntrySafe(entry);
if (ValEntry *valEntry = dynamic_cast< ValEntry* >(entry) )
{
std::string transfer = valEntry->GetValue();
// The actual transfer (as read from disk) might be padded. We
// first need to remove the potential padding. We can make the
// weak assumption that padding was not executed with digits...
if ( transfer.length() == 0 )
{
// for brain damaged headers
return GDCM_UNKNOWN;
}
while ( !isdigit((unsigned char)transfer[transfer.length()-1]) )
{
transfer.erase(transfer.length()-1, 1);
if ( transfer.length() == 0 )
{
// for brain damaged headers
gdcmWarningMacro( "Transfer Syntax contains no valid character.");
return GDCM_UNKNOWN;
}
}
return transfer;
}
return GDCM_UNKNOWN;
}
/**
* \brief Accesses the info from 0002,0010 : Transfer Syntax and TS
* @return The full Transfer Syntax Name (as opposed to Transfer Syntax UID)
*/
std::string Document::GetTransferSyntaxName()
{
// use the TS (TS : Transfer Syntax)
std::string transferSyntax = GetEntryValue(0x0002,0x0010);
if ( (transferSyntax.find(GDCM_NOTLOADED) < transferSyntax.length()) )
{
gdcmErrorMacro( "Transfer Syntax not loaded. " << std::endl
<< "Better you increase MAX_SIZE_LOAD_ELEMENT_VALUE" );
return "Uncompressed ACR-NEMA";
}
if ( transferSyntax == GDCM_UNFOUND )
{
gdcmDebugMacro( "Unfound Transfer Syntax (0002,0010)");
return "Uncompressed ACR-NEMA";
}
// we do it only when we need it
const TSKey &tsName = Global::GetTS()->GetValue( transferSyntax );
// Global::GetTS() is a global static you shall never try to delete it!
return tsName;
}
//
// --------------- Swap Code ------------------
/**
* \brief Swaps the bytes so they agree with the processor order
* @return The properly swaped 16 bits integer.
*/
uint16_t Document::SwapShort(uint16_t a)
{
if ( SwapCode == 4321 || SwapCode == 2143 )
{
//a = ((( a << 8 ) & 0xff00 ) | (( a >> 8 ) & 0x00ff ) );
// Save CPU time
a = ( a << 8 ) | ( a >> 8 );
}
return a;
}
/**
* \brief Swaps back the bytes of 4-byte long integer accordingly to
* processor order.
* @return The properly swaped 32 bits integer.
*/
uint32_t Document::SwapLong(uint32_t a)
{
switch (SwapCode)
{
case 1234 :
break;
case 4321 :
// a=( ((a<<24) & 0xff000000) | ((a<<8) & 0x00ff0000) |
// ((a>>8) & 0x0000ff00) | ((a>>24) & 0x000000ff) );
// save CPU time
a=( ( a<<24) | ((a<<8) & 0x00ff0000) |
((a>>8) & 0x0000ff00) | (a>>24) );
break;
case 3412 :
// a=( ((a<<16) & 0xffff0000) | ((a>>16) & 0x0000ffff) );
a=( (a<<16) | (a>>16) );
break;
case 2143 :
a=( ((a<< 8) & 0xff00ff00) | ((a>>8) & 0x00ff00ff) );
break;
default :
gdcmErrorMacro( "Unexpected swap code:" << SwapCode );
a = 0;
}
return a;
}
//
// -----------------File I/O ---------------
/**
* \brief Tries to open the file \ref Document::Filename and
* checks the preamble when existing.
* @return The FILE pointer on success.
*/
std::ifstream *Document::OpenFile()
{
HasDCMPreamble = false;
if (Filename.length() == 0)
{
return 0;
}
if ( Fp )
{
gdcmDebugMacro( "File already open: " << Filename.c_str());
CloseFile();
}
Fp = new std::ifstream(Filename.c_str(), std::ios::in | std::ios::binary);
if ( ! *Fp )
{
// Don't user gdcmErrorMacro :
// a spurious message will appear when you use, for instance
// gdcm::FileHelper *fh = new gdcm::FileHelper( outputFileName );
// to create outputFileName.
// FIXME : if the upper comment is still useful
// --> the constructor is not so good ...
gdcmWarningMacro( "Cannot open file: " << Filename.c_str());
delete Fp;
Fp = 0;
return 0;
//exit(1); // No function is allowed to leave the application instead
// of warning the caller
}
if( !CanReadFile(*Fp, HasDCMPreamble) )
{
// -- Neither ACR/No Preamble Dicom nor DICOMV3 file
CloseFile();
gdcmWarningMacro( "Neither ACR/No Preamble Dicom nor DICOMV3 file: "
<< Filename.c_str());
return 0;
}
return Fp;
}
bool Document::CanReadFile(std::istream &os, bool &hasPreamble)
{
hasPreamble = false;
uint16_t zero = 0;
os.read((char*)&zero, (size_t)2);
if ( os.eof() )
{
return false;
}
//-- Broken ACR or DICOM with no Preamble; may start with a Shadow Group --
// FIXME : We cannot be sure the preable is only zeroes..
// (see ACUSON-24-YBR_FULL-RLE.dcm )
if (
zero == 0x0001 || zero == 0x0100 || zero == 0x0002 || zero == 0x0200 ||
zero == 0x0003 || zero == 0x0300 || zero == 0x0004 || zero == 0x0400 ||
zero == 0x0005 || zero == 0x0500 || zero == 0x0006 || zero == 0x0600 ||
zero == 0x0007 || zero == 0x0700 || zero == 0x0008 || zero == 0x0800 )
{
std::string msg = Util::Format(
"ACR/DICOM starting by 0x(%04x) at the beginning of the file\n", zero);
// FIXME : is it a Warning message, or a Debug message?
gdcmWarningMacro( msg.c_str() );
return true;
}
//-- DICOM --
os.seekg(126L, std::ios::cur); // Once per Document
char dicm[4]; // = {' ',' ',' ',' '};
os.read(dicm, (size_t)4);
if ( os.eof() )
{
return false;
}
if ( memcmp(dicm, "DICM", 4) == 0 )
{
hasPreamble = true;
return true;
}
return false;
}
/**
* \brief closes the file
* @return TRUE if the close was successfull
*/
bool Document::CloseFile()
{
if ( Fp )
{
Fp->close();
delete Fp;
Fp = 0;
}
return true;
}
/**
* \brief Writes in a file all the Entries (Dicom Elements)
* @param fp file pointer on an already open file (actually: Output File Stream)
* @param filetype Type of the File to be written
* (ACR-NEMA, ExplicitVR, ImplicitVR)
*/
void Document::WriteContent(std::ofstream *fp, FileType filetype)
{
// Skip if user wants to write an ACR-NEMA file
if ( filetype == ImplicitVR || filetype == ExplicitVR ||
filetype == JPEG || filetype == JPEG2000 )
{
// writing Dicom File Preamble
char filePreamble[128];
memset(filePreamble, 0, 128);
fp->write(filePreamble, 128);
fp->write("DICM", 4);
}
/*
* \todo rewrite later, if really usefull
* - 'Group Length' element is optional in DICOM
* - but un-updated odd groups lengthes can causes pb
* (xmedcon breaker)
*
* if ( (filetype == ImplicitVR) || (filetype == ExplicitVR) )
* UpdateGroupLength(false,filetype);
* if ( filetype == ACR)
* UpdateGroupLength(true,ACR);
*
* --> Computing group length for groups with embeded Sequences
* --> was too much tricky / we were [in a hurry / too lazy]
* --> We don't write the element 0x0000 (group length)
*/
ElementSet::WriteContent(fp, filetype); // This one is recursive
}
// -----------------------------------------
// Content entries
/**
* \brief Loads (from disk) the element content
* when a string is not suitable
* @param group group number of the Entry
* @param elem element number of the Entry
*/
void Document::LoadEntryBinArea(uint16_t group, uint16_t elem)
{
// Search the corresponding DocEntry
DocEntry *docElement = GetDocEntry(group, elem);
if ( !docElement )
{
gdcmDebugMacro(std::hex << group << "|" << elem
<< " doesn't exist" );
return;
}
BinEntry *binElement = dynamic_cast<BinEntry *>(docElement);
if ( !binElement )
{
gdcmWarningMacro(std::hex << group << "|" << elem
<< "is NOT a BinEntry");
return;
}
LoadEntryBinArea(binElement);
}
/**
* \brief Loads (from disk) the element content
* when a string is not suitable
* @param elem Entry whose binArea is going to be loaded
*/
void Document::LoadEntryBinArea(BinEntry *elem)
{
if (elem->GetBinArea() )
return;
bool openFile = !Fp;
if ( openFile )
OpenFile();
size_t o =(size_t)elem->GetOffset();
Fp->seekg(o, std::ios::beg);
size_t l = elem->GetLength();
uint8_t *a = new uint8_t[l];
if ( !a )
{
gdcmWarningMacro( "Cannot allocate BinEntry content for : "
<< std::hex << elem->GetGroup()
<< "|" << elem->GetElement() );
return;
}
// Read the data
Fp->read((char*)a, l);
if ( Fp->fail() || Fp->eof() )
{
delete[] a;
return;
}
elem->SetBinArea(a);
if ( openFile )
CloseFile();
}
/**
* \brief Loads the element while preserving the current
* underlying file position indicator as opposed to
* LoadDocEntry that modifies it.
* @param entry DocEntry whose value will be loaded.
*/
void Document::LoadDocEntrySafe(DocEntry *entry)
{
if ( Fp )
{
long PositionOnEntry = Fp->tellg();
LoadDocEntry(entry);
Fp->seekg(PositionOnEntry, std::ios::beg);
}
}
/**
* \brief Compares two documents, according to \ref DicomDir rules
* \warning Does NOT work with ACR-NEMA files
* \todo Find a trick to solve the pb (use RET fields ?)
* @param document to compare with current one
* @return true if 'smaller'
*/
bool Document::operator<(Document &document)
{
// Patient Name
std::string s1 = GetEntryValue(0x0010,0x0010);
std::string s2 = document.GetEntryValue(0x0010,0x0010);
if (s1 < s2)
{
return true;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -