DumpPhilipsECHO.cxx
/*=========================================================================
Program: GDCM (Grassroots DICOM). A DICOM library
Copyright (c) 2006-2011 Mathieu Malaterre
All rights reserved.
See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "gdcmReader.h"
#include "gdcmDeflateStream.h"
#include "gdcm_zlib.h"
/*
* This example extract the ZLIB compressed US image from a Philips private tag
*
* Everything done in this code is for the sole purpose of writing interoperable
* software under Sect. 1201 (f) Reverse Engineering exception of the DMCA.
* If you believe anything in this code violates any law or any of your rights,
* please contact us (gdcm-developers@lists.sourceforge.net) so that we can
* find a solution.
*
* Everything you do with this code is at your own risk, since decompression
* algorithm was not written from specification documents.
*
* Usage:
*
* $ DumpPhilipsECHO private_us.dcm raw_us_img.raw
* $ gdcmimg --sop-class-uid 1.2.840.10008.5.1.4.1.1.3.1 --size 608,427,88 raw_us_img.raw raw_us_img.dcm
*/
// header:
struct hframe
{
uint32_t val0; // 800 increment ?
uint16_t val1[2];
uint16_t val2[2];
uint32_t imgsize;
bool operator==(const hframe &h) const
{
return val0 == h.val0 &&
val1[0] == h.val1[0] &&
val1[1] == h.val1[1] &&
val2[0] == h.val2[0] &&
val2[1] == h.val2[1] &&
imgsize == h.imgsize;
}
};
static bool ProcessDeflate( const char *outfilename, const int nslices, const
int buf_size, const char *buf, const std::streampos len,
const char *crcbuf, const size_t crclen )
{
std::vector< hframe > crcheaders;
crcheaders.reserve( nslices );
{
std::istringstream is;
is.str( std::string( crcbuf, crclen ) );
hframe header;
for( int r = 0; r < nslices; ++r )
{
is.read( (char*)&header, sizeof( header ));
#if 0
std::cout << header.val0
<< " " << header.val1[0]
<< " " << header.val1[1]
<< " " << header.val2[0]
<< " " << header.val2[1]
<< " " << header.imgsize << std::endl;
#endif
crcheaders.push_back( header );
}
}
std::istringstream is;
is.str( std::string( buf, (size_t)len ) );
std::streamoff totalsize;
is.read( (char*)&totalsize, sizeof( totalsize ));
assert( totalsize == len );
uint32_t nframes;
is.read( (char*)&nframes, sizeof( nframes ));
assert( nframes == (uint32_t)nslices );
std::vector< std::streamoff > offsets;
offsets.reserve( nframes );
for( uint32_t frame = 0; frame < nframes ; ++frame )
{
uint32_t offset;
is.read( (char*)&offset, sizeof( offset ));
offsets.push_back( offset );
}
std::vector<char> outbuf;
const int size[2] = { 608, 427 }; // FIXME: where does it comes from ?
std::stringstream ss;
ss << outfilename;
ss << '_';
//ss << crcheaders[0].imgsize; // FIXME: Assume all header are identical !
ss << size[0];
ss << '_';
ss << size[1];
ss << '_';
ss << nframes;
ss << ".raw";
std::ofstream os( ss.str().c_str(), std::ios::binary );
assert( buf_size >= size[0] * size[1] );
outbuf.resize( buf_size );
hframe header;
//uint32_t prev = 0;
for( unsigned int r = 0; r < nframes; ++r )
{
is.read( (char*)&header, sizeof( header ));
assert( header == crcheaders[r] );
assert( header.val1[0] == 2000 );
assert( header.val1[1] == 3 );
assert( header.val2[0] == 1 );
assert( header.val2[1] == 1280 );
uLongf destLen = buf_size; // >= 608,427
Bytef *dest = (Bytef*)outbuf.data();
assert( is.tellg() == offsets[r] + 16 );
const Bytef *source = (const Bytef*)buf + offsets[r] + 16;
uLong sourceLen;
if( r + 1 == nframes )
sourceLen = (uLong)totalsize - (uLong)offsets[r] - 16;
else
sourceLen = (uLong)offsets[r+1] - (uLong)offsets[r] - 16;
// FIXME: in-memory decompression:
int ret = uncompress (dest, &destLen, source, sourceLen);
assert( ret == Z_OK ); (void)ret;
assert( destLen >= (uLongf)size[0] * size[1] ); // 16bytes padding ?
assert( header.imgsize == (uint32_t)size[0] * size[1] );
//os.write( &outbuf[0], outbuf.size() );
os.write( outbuf.data(), size[0] * size[1] );
// skip data:
is.seekg( sourceLen, std::ios::cur );
}
os.close();
assert( is.tellg() == totalsize );
return true;
}
static bool ProcessNone( const char *outfilename, const int nslices, const
int buf_size, const char *buf, const std::streampos len,
const char *crcbuf, const size_t crclen )
{
std::vector< hframe > crcheaders;
crcheaders.reserve( nslices );
{
std::istringstream is;
is.str( std::string( crcbuf, crclen ) );
hframe header;
for( int r = 0; r < nslices; ++r )
{
is.read( (char*)&header, sizeof( header ));
#if 0
std::cout << header.val0
<< " " << header.val1[0]
<< " " << header.val1[1]
<< " " << header.val2[0]
<< " " << header.val2[1]
<< " " << header.imgsize << std::endl;
#endif
crcheaders.push_back( header );
}
}
std::istringstream is;
is.str( std::string( buf, (size_t)len ) );
std::streampos totalsize;
is.read( (char*)&totalsize, sizeof( totalsize ));
assert( totalsize == len );
uint32_t nframes;
is.read( (char*)&nframes, sizeof( nframes ));
assert( nframes == (uint32_t)nslices );
std::vector< uint32_t > offsets;
offsets.reserve( nframes );
for( uint32_t frame = 0; frame < nframes ; ++frame )
{
uint32_t offset;
is.read( (char*)&offset, sizeof( offset ));
offsets.push_back( offset );
//std::cout << offset << std::endl;
}
std::vector<char> outbuf;
// No idea how to present the data, I'll just append everything, and present it as 2D
std::stringstream ss;
ss << outfilename;
ss << '_';
ss << crcheaders[0].imgsize; // FIXME: Assume all header are identical !
ss << '_';
ss << nframes;
ss << ".raw";
std::ofstream os( ss.str().c_str(), std::ios::binary );
outbuf.resize( buf_size ); // overallocated + 16
char *buffer = outbuf.data();
hframe header;
for( unsigned int r = 0; r < nframes; ++r )
{
is.read( (char*)&header, sizeof( header ));
#if 0
std::cout << header.val0
<< " " << header.val1[0]
<< " " << header.val1[1]
<< " " << header.val2[0]
<< " " << header.val2[1]
<< " " << header.imgsize << std::endl;
#endif
assert( header == crcheaders[r] );
is.read( buffer, buf_size - 16 );
os.write( buffer, header.imgsize );
}
assert( is.tellg() == totalsize );
os.close();
return true;
}
#ifndef NDEBUG
static const char * const UDM_USD_DATATYPE_STRINGS[] = {
"UDM_USD_DATATYPE_DIN_2D_ECHO",
"UDM_USD_DATATYPE_DIN_2D_ECHO_CONTRAST",
"UDM_USD_DATATYPE_DIN_DOPPLER_CW",
"UDM_USD_DATATYPE_DIN_DOPPLER_PW",
"UDM_USD_DATATYPE_DIN_DOPPLER_PW_TDI",
"UDM_USD_DATATYPE_DIN_2D_COLOR_FLOW",
"UDM_USD_DATATYPE_DIN_2D_COLOR_PMI",
"UDM_USD_DATATYPE_DIN_2D_COLOR_CPA",
"UDM_USD_DATATYPE_DIN_2D_COLOR_TDI",
"UDM_USD_DATATYPE_DIN_MMODE_ECHO",
"UDM_USD_DATATYPE_DIN_MMODE_COLOR",
"UDM_USD_DATATYPE_DIN_MMODE_COLOR_TDI",
"UDM_USD_DATATYPE_DIN_PARAM_BLOCK",
"UDM_USD_DATATYPE_DIN_2D_COLOR_VELOCITY",
"UDM_USD_DATATYPE_DIN_2D_COLOR_POWER",
"UDM_USD_DATATYPE_DIN_2D_COLOR_VARIANCE",
"UDM_USD_DATATYPE_DIN_DOPPLER_AUDIO",
"UDM_USD_DATATYPE_DIN_DOPPLER_HIGHQ",
"UDM_USD_DATATYPE_DIN_PHYSIO",
"UDM_USD_DATATYPE_DIN_2D_COLOR_STRAIN",
"UDM_USD_DATATYPE_DIN_COMPOSITE_RGB",
"UDM_USD_DATATYPE_DIN_XFOV_REALTIME_GRAPHICS",
"UDM_USD_DATATYPE_DIN_XFOV_MOSAIC",
"UDM_USD_DATATYPE_DIN_COMPOSITE_R",
"UDM_USD_DATATYPE_DIN_COMPOSITE_G",
"UDM_USD_DATATYPE_DIN_COMPOSITE_B",
"UDM_USD_DATATYPE_DIN_MMODE_COLOR_VELOCITY",
"UDM_USD_DATATYPE_DIN_MMODE_COLOR_POWER",
"UDM_USD_DATATYPE_DIN_MMODE_COLOR_VARIANCE",
"UDM_USD_DATATYPE_DIN_2D_ELASTO",
};
static inline bool is_valid( const char * datatype_str )
{
static const int n = sizeof( UDM_USD_DATATYPE_STRINGS ) / sizeof( *UDM_USD_DATATYPE_STRINGS );
bool found = false;
if( datatype_str )
{
for( int i = 0; !found && i < n; ++i )
{
found = strcmp( datatype_str, UDM_USD_DATATYPE_STRINGS[i] ) == 0;
}
}
return found;
}
#endif
int main(int argc, char *argv[])
{
if( argc < 2 ) return 1;
using namespace gdcm;
const char *filename = argv[1];
gdcm::Reader reader;
reader.SetFileName( filename );
if( !reader.Read() ) return 1;
gdcm::File &file = reader.GetFile();
gdcm::DataSet &ds1 = file.GetDataSet();
const PrivateTag tseq1(0x200d,0x3cf8,"Philips US Imaging DD 045");
if( !ds1.FindDataElement( tseq1 ) ) return 1;
const DataElement& seq1 = ds1.GetDataElement( tseq1 );
SmartPointer<SequenceOfItems> sqi1 = seq1.GetValueAsSQ();
assert( sqi1->GetNumberOfItems() >= 1 );
const size_t nitems = sqi1->GetNumberOfItems();
for( size_t item = 1; item < nitems; ++item )
{
Item &item1 = sqi1->GetItem(item);
DataSet &ds2 = item1.GetNestedDataSet();
// (200d,300d) LO 28 UDM_USD_DATATYPE_DIN_2D_ECHO
const PrivateTag tdatatype(0x200d,0x300d,"Philips US Imaging DD 033");
if( !ds2.FindDataElement( tdatatype ) ) return 1;
const DataElement& datatype = ds2.GetDataElement( tdatatype );
const ByteValue *bvdatatype = datatype.GetByteValue();
if( !bvdatatype ) return 1;
const PrivateTag tseq2(0x200d,0x3cf1,"Philips US Imaging DD 045");
if( !ds2.FindDataElement( tseq2 ) ) return 1;
const DataElement& seq2 = ds2.GetDataElement( tseq2 );
SmartPointer<SequenceOfItems> sqi2 = seq2.GetValueAsSQ();
assert( sqi2->GetNumberOfItems() >= 1 );
// FIXME: what if not in first Item ?
assert( sqi2->GetNumberOfItems() == 1 );
Item &item2 = sqi2->GetItem(1);
DataSet &ds3 = item2.GetNestedDataSet();
const PrivateTag tzlib(0x200d,0x3cfa,"Philips US Imaging DD 045");
if( !ds3.FindDataElement( tzlib ) ) return 1;
const DataElement& zlib = ds3.GetDataElement( tzlib );
const ByteValue *bv = zlib.GetByteValue();
if( !bv ) return 1;
if( bv->GetLength() != 4 ) return 1;
// (200d,3010) IS 2 88
const PrivateTag tnslices(0x200d,0x3010,"Philips US Imaging DD 033");
if( !ds3.FindDataElement( tnslices ) ) return 1;
const DataElement& nslices = ds3.GetDataElement( tnslices );
Element<VR::IS,VM::VM1> elnslices;
elnslices.SetFromDataElement( nslices );
const int nslicesref = elnslices.GetValue();
assert( nslicesref >= 0 );
// (200d,3011) IS 6 259648
const PrivateTag tzalloc(0x200d,0x3011,"Philips US Imaging DD 033");
if( !ds3.FindDataElement( tzalloc ) ) return 1;
const DataElement& zalloc = ds3.GetDataElement( tzalloc );
Element<VR::IS,VM::VM1> elzalloc;
elzalloc.SetFromDataElement( zalloc );
const int zallocref = elzalloc.GetValue();
assert( zallocref >= 0 );
// (200d,3021) IS 2 0
const PrivateTag tzero(0x200d,0x3021,"Philips US Imaging DD 033");
if( !ds3.FindDataElement( tzero ) ) return 1;
const DataElement& zero = ds3.GetDataElement( tzero );
Element<VR::IS,VM::VM1> elzero;
elzero.SetFromDataElement( zero );
const int zerocref = elzero.GetValue();
assert( zerocref == 0 ); (void)zerocref;
// (200d,3cf3) OB
const PrivateTag tdeflate(0x200d,0x3cf3,"Philips US Imaging DD 045");
if( !ds3.FindDataElement( tdeflate) ) return 1;
const DataElement& deflate = ds3.GetDataElement( tdeflate );
const ByteValue *bv2 = deflate.GetByteValue();
// (200d,3cfb) OB
const PrivateTag tcrc(0x200d,0x3cfb,"Philips US Imaging DD 045");
if( !ds3.FindDataElement( tcrc ) ) return 1;
const DataElement& crc = ds3.GetDataElement( tcrc );
const ByteValue *bv3 = crc.GetByteValue();
std::string outfile = std::string( bvdatatype->GetPointer(), bvdatatype->GetLength() );
outfile = LOComp::Trim( outfile.c_str() );
const char *outfilename = outfile.c_str();
assert( is_valid(outfilename) );
if( bv2 )
{
assert( bv3 );
assert( zallocref > 0 );
assert( nslicesref > 0 );
std::cout << ds2 << std::endl;
if( strncmp(bv->GetPointer(), "ZLib", 4) == 0 )
{
if( !ProcessDeflate( outfilename, nslicesref, zallocref, bv2->GetPointer(),
std::streampos(bv2->GetLength()), bv3->GetPointer(), bv3->GetLength() ) )
{
return 1;
}
}
else if( strncmp(bv->GetPointer(), "None", 4) == 0 )
{
if( !ProcessNone( outfilename, nslicesref, zallocref, bv2->GetPointer(),
std::streampos(bv2->GetLength()), bv3->GetPointer(), bv3->GetLength() ) )
{
return 1;
}
}
else
{
std::string str( bv->GetPointer(), bv->GetLength() );
std::cerr << "Unhandled: " << str << std::endl;
return 1;
}
}
}
return 0;
}
gdcm::ByteValue
Class to represent binary value (array of bytes).
Definition gdcmByteValue.h:35
gdcm::ByteValue::GetPointer
const char * GetPointer() const
Definition gdcmByteValue.h:110
gdcm::ByteValue::GetLength
VL GetLength() const override
Definition gdcmByteValue.h:77
gdcm::DataElement
Class to represent a Data Element either Implicit or Explicit.
Definition gdcmDataElement.h:59
gdcm::DataElement::GetByteValue
const ByteValue * GetByteValue() const
Definition gdcmDataElement.h:133
gdcm::DataElement::GetValueAsSQ
SmartPointer< SequenceOfItems > GetValueAsSQ() const
gdcm::DataSet
Class to represent a Data Set (which contains Data Elements).
Definition gdcmDataSet.h:56
gdcm::DataSet::GetDataElement
const DataElement & GetDataElement(const Tag &t) const
Definition gdcmDataSet.h:188
gdcm::DataSet::FindDataElement
bool FindDataElement(const PrivateTag &t) const
Look up if private tag 't' is present in the dataset:
gdcm::Element
Element class.
Definition gdcmElement.h:70
gdcm::Element::GetValue
const VRToType< TVR >::Type & GetValue(unsigned int idx=0) const
Definition gdcmElement.h:94
gdcm::Element::SetFromDataElement
void SetFromDataElement(DataElement const &de)
Definition gdcmElement.h:110
gdcm::File
a DICOM File
Definition gdcmFile.h:34
gdcm::File::GetDataSet
const DataSet & GetDataSet() const
Get Data Set.
Definition gdcmFile.h:57
gdcm::Item
Class to represent an Item.
Definition gdcmItem.h:46
gdcm::Item::GetNestedDataSet
const DataSet & GetNestedDataSet() const
Definition gdcmItem.h:80
gdcm::PrivateTag
Class to represent a Private DICOM Data Element (Attribute) Tag (Group, Element, Owner).
Definition gdcmPrivateTag.h:39
gdcm::Reader
Reader ala DOM (Document Object Model).
Definition gdcmReader.h:54
gdcm::Reader::GetFile
const File & GetFile() const
Set/Get File.
Definition gdcmReader.h:72
gdcm::Reader::Read
virtual bool Read()
Main function to read a file.
gdcm::Reader::SetFileName
void SetFileName(const char *filename_native)
gdcm::SmartPointer
Class for Smart Pointer.
Definition gdcmSmartPointer.h:40
gdcm::String<'\\', 64 >::Trim
std::string Trim() const
Definition gdcmString.h:96
gdcmDeflateStream.h
gdcmReader.h
gdcm
Definition gdcmASN1.h:21