/*========================================================================= 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 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[0]; 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[0], 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 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[0]; 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 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 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 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 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 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; }