/*========================================================================= 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. =========================================================================*/ /* * 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 &output) { // RLE pass std::vector 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]; unsigned short value = (unsigned short)(v2 * 256 + v1); output.push_back( value ); delta = value; i+=2; } else { unsigned short value = (unsigned short)(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 ) { std::cerr << argv[0] << "input.dcm [output.dcm]" << std::endl; std::cerr << "will default to 'outrle.dcm' unless output.dcm is specified." << std::endl; 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 comprle = "PMSCT_RLE1"; std::string comprgb = "PMSCT_RGB1"; bool isrle = false; bool isrgb = false; if( strncmp( bv->GetPointer(), comprle.c_str(), comprle.size() ) == 0 ) { isrle = true; } if( strncmp( bv->GetPointer(), comprgb.c_str(), comprgb.size() ) == 0 ) { isrgb = true; std::cerr << "See: pmsct_rgb1.cxx instead" << std::endl; return 1; } if( !isrgb && !isrle ) return 1; // check if compressed pixel data reside in private or standard tag const gdcm::PrivateTag tprivatepixeldata(0x07a1,0x100a,"ELSCINT1"); const gdcm::Tag tstandardpixeldata(0x7fe0, 0x0010); gdcm::Tag tpixeldata; if(ds.FindDataElement(tprivatepixeldata)) tpixeldata = tprivatepixeldata; else if(ds.FindDataElement(tstandardpixeldata)) tpixeldata = tstandardpixeldata; if(!ds.FindDataElement(tpixeldata)) return 1; const gdcm::DataElement& compressionpixeldata = ds.GetDataElement( tpixeldata); if ( compressionpixeldata.IsEmpty() ) return 1; const gdcm::ByteValue * bv2 = compressionpixeldata.GetByteValue(); gdcm::Attribute<0x0028,0x0010> at1; at1.SetFromDataSet( ds ); gdcm::Attribute<0x0028,0x0011> at2; at2.SetFromDataSet( ds ); gdcm::DataElement pixeldata; // if standard voxel data element does not exist, create it if( !reader.GetFile().GetDataSet().FindDataElement( tpixeldata ) ) { pixeldata = gdcm::DataElement( tpixeldata, 0, gdcm::VR::OW ); } else{ pixeldata = reader.GetFile().GetDataSet().GetDataElement( tpixeldata ); } pixeldata.SetVR( gdcm::VR::OW ); gdcm::VL bv2l = bv2->GetLength(); gdcm::VL at1l = at1.GetValue() * at2.GetValue() * 2; /* sizeof(unsigned short) == 2 */ // Handle special case that is not compressed: if( bv2l == at1l ) { pixeldata.SetByteValue( bv2->GetPointer(), bv2->GetLength() ); } else { std::vector buffer; delta_decode(bv2->GetPointer(), bv2->GetLength(), buffer); pixeldata.SetByteValue( (char*)&buffer[0], (uint32_t)(buffer.size() * sizeof( unsigned short )) ); } // TODO we should check that decompress byte buffer match the expected size (row*col*...) // Add the pixel data element if( reader.GetFile().GetDataSet().FindDataElement( tpixeldata ) ) { reader.GetFile().GetDataSet().Replace( pixeldata ); } else { reader.GetFile().GetDataSet().ReplaceEmpty( pixeldata ); } reader.GetFile().GetHeader().SetDataSetTransferSyntax( gdcm::TransferSyntax::ExplicitVRLittleEndian); gdcm::Writer writer; writer.SetFile( reader.GetFile() ); // Cleanup stuff: // This makes the code equivalent to Philips workstation IntelliSpace Portal if( writer.GetFile().GetDataSet().FindDataElement( tcompressiontype ) ) { writer.GetFile().GetDataSet().Remove( gdcm::Tag(0x07a1,0x1011) ); } if( writer.GetFile().GetDataSet().FindDataElement( tprivatepixeldata ) ) { writer.GetFile().GetDataSet().Remove( gdcm::Tag(0x07a1,0x100a) ); } 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; }