/*========================================================================= Program: GDCM (Grassroots DICOM). A DICOM library Module: $URL$ Copyright (c) 2006-2010 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. =========================================================================*/ /* * This example shows how to rewrite a ELSCINT1/PMSCT_RLE1 compressed * image so that it is readable by most 3rd party software (DICOM does * not specify this particular encoding). * This is required for the sake of interoperability with any standard * conforming DICOM system. * * 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. * * Special thanks to: * Mauro Maiorca for bringing to our attention on this new ELSCINT1 * compression algorithm : PMSCT_RLE1 (different from the 'LOSSLESS RICE') * See post at: * http://groups.google.com/group/comp.protocols.dicom/msg/f2b99bf706a7f8ca * * Thanks to Jesus Spinola, for more datasets, * http://www.itk.org/pipermail/insight-users/2008-April/025571.html * * And last but not least, a very big thank to Ivo van Poorten, without * whom we would still be looking at this compressed byte stream as if * it was RLE compressed. */ #include "gdcmReader.h" #include "gdcmPrivateTag.h" #include "gdcmAttribute.h" #include "gdcmImageWriter.h" /* FIXME: Why is PhilipsLosslessRice.dcm a 512x512 image ... */ void delta_decode(const char *inbuffer, size_t length, std::vector<unsigned short> &output) { // RLE pass std::vector<char> temp; for(size_t i = 0; i < length; ++i) { if( inbuffer[i] == (char)0xa5 ) { //unsigned char repeat = (unsigned char)inbuffer[i+1] + 1; //assert( (unsigned char)inbuffer[i+1] != 255 ); int repeat = (unsigned char)inbuffer[i+1] + 1; char value = inbuffer[i+2]; while(repeat) { temp.push_back( value ); --repeat; } i+=2; } else { temp.push_back( inbuffer[i] ); } } // Delta encoding pass unsigned short delta = 0; for(size_t i = 0; i < temp.size(); ++i) { if( temp[i] == 0x5a ) { unsigned char v1 = (unsigned char)temp[i+1]; unsigned char v2 = (unsigned char)temp[i+2]; int value = v2 * 256 + v1; output.push_back( value ); delta = value; i+=2; } else { int value = temp[i] + delta; output.push_back( value ); delta = value; } //assert( output[output.size()-1] == ref[output.size()-1] ); } if ( output.size() % 2 ) { output.resize( output.size() - 1 ); } std::cout << length << " -> " << output.size() * 2 << std::endl; } int main(int argc, char *argv []) { if( argc < 2 ) return 1; const char *filename = argv[1]; gdcm::Reader reader; reader.SetFileName( filename ); if( !reader.Read() ) { std::cerr << "Failed to read: " << filename << std::endl; return 1; } const gdcm::DataSet& ds = reader.GetFile().GetDataSet(); // (07a1,1011) CS [PMSCT_RLE1] # 10,1 Tamar Compression Type const gdcm::PrivateTag tcompressiontype(0x07a1,0x0011,"ELSCINT1"); if( !ds.FindDataElement( tcompressiontype ) ) return 1; const gdcm::DataElement& compressiontype = ds.GetDataElement( tcompressiontype ); if ( compressiontype.IsEmpty() ) return 1; const gdcm::ByteValue * bv = compressiontype.GetByteValue(); std::string comp = "PMSCT_RLE1"; if( strncmp( bv->GetPointer(), comp.c_str(), comp.size() ) != 0 ) return 1; const gdcm::PrivateTag tcompressedpixeldata(0x07a1,0x000a,"ELSCINT1"); if( !ds.FindDataElement( tcompressedpixeldata) ) return 1; const gdcm::DataElement& compressionpixeldata = ds.GetDataElement( tcompressedpixeldata); if ( compressionpixeldata.IsEmpty() ) return 1; const gdcm::ByteValue * bv2 = compressionpixeldata.GetByteValue(); std::vector<unsigned short> buffer; delta_decode(bv2->GetPointer(), bv2->GetLength(), buffer); gdcm::DataElement pixeldata( gdcm::Tag(0x7fe0,0x0010) ); pixeldata.SetVR( gdcm::VR::OW ); pixeldata.SetByteValue( (char*)&buffer[0], buffer.size() * sizeof( unsigned short ) ); // TODO we should check that decompress byte buffer match the expected size (row*col*...) // Add the pixel data element reader.GetFile().GetDataSet().Replace( pixeldata ); gdcm::Writer writer; writer.SetFile( reader.GetFile() ); // Cleanup stuff: // remove the compressed pixel data: // FIXME: should I remove more private tags ? all of them ? // oh well this is just an example // use gdcm::Anonymizer::RemovePrivateTags if needed... writer.GetFile().GetDataSet().Remove( compressionpixeldata.GetTag() ); std::string outfilename; if (argc > 2) outfilename = argv[2]; else outfilename = "outrle.dcm"; writer.SetFileName( outfilename.c_str() ); if( !writer.Write() ) { std::cerr << "Failed to write" << std::endl; return 1; } std::cout << "success !" << std::endl; return 0; }