/*========================================================================= 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_RGB1 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: * Jean-Pierre Roux for providing the sample datasets */ #include "gdcmReader.h" #include "gdcmPrivateTag.h" #include "gdcmAttribute.h" #include "gdcmImageWriter.h" void delta_decode(const unsigned char *data_in, size_t data_size, std::vector &new_stream, unsigned short pc, size_t w, size_t h) { const size_t plane_size = h * w; const size_t outputlen = 3 * plane_size; new_stream.resize( outputlen ); assert( data_size != outputlen ); if( data_size == outputlen ) { return; } typedef unsigned char byte; enum { COLORMODE = 0x81, ESCMODE = 0x82, REPEATMODE = 0x83 }; const byte* src = (const byte*)data_in; byte* dest = (byte*)&new_stream[0]; union { byte gray; byte rgb[3]; } pixel; pixel.rgb[0] = pixel.rgb[1] = pixel.rgb[2] = 0; // always start in grayscale mode bool graymode = true; size_t dx = 1; size_t dy = 3; // algorithm works with both planar configuration // It does produce surprising greenish background color for planar // configuration is 0, while the nested Icon SQ display a nice black // background if (pc) { dx = plane_size; dy = 1; } size_t ps = plane_size; // The following is highly unoptimized as we have nested if statement in a while loop // we need to switch from one algorithm to ther other (RGB <-> GRAY) while (ps) { // next byte: byte b = *src++; assert( src < data_in + data_size ); // mode selection: switch ( b ) { case ESCMODE: // Used to treat a byte 81/82/83 as a normal byte if (graymode) { pixel.gray += *src++; dest[0*dx] = pixel.gray; dest[1*dx] = pixel.gray; dest[2*dx] = pixel.gray; } else { pixel.rgb[0] += *src++; pixel.rgb[1] += *src++; pixel.rgb[2] += *src++; dest[0*dx] = pixel.rgb[0]; dest[1*dx] = pixel.rgb[1]; dest[2*dx] = pixel.rgb[2]; } dest += dy; ps--; break; case REPEATMODE: // repeat mode (RLE) b = *src++; ps -= b; if (graymode) { while (b-- > 0) { dest[0*dx] = pixel.gray; dest[1*dx] = pixel.gray; dest[2*dx] = pixel.gray; dest += dy; } } else { while (b-- > 0) { dest[0*dx] = pixel.rgb[0]; dest[1*dx] = pixel.rgb[1]; dest[2*dx] = pixel.rgb[2]; dest += dy; } } break; case COLORMODE: // We are swithing from one mode to the other. The stream contains an intermixed // compression of RGB codec and GRAY codec. Each one not knowing of the other // reset old value to 0. if (graymode) { graymode = false; pixel.rgb[0] = pixel.rgb[1] = pixel.rgb[2] = 0; } else { graymode = true; pixel.gray = 0; } break; default: // This is identical to ESCMODE, it would be nicer to use fall-through if (graymode) { pixel.gray += b; dest[0*dx] = pixel.gray; dest[1*dx] = pixel.gray; dest[2*dx] = pixel.gray; } else { pixel.rgb[0] += b; pixel.rgb[1] += *src++; pixel.rgb[2] += *src++; dest[0*dx] = pixel.rgb[0]; dest[1*dx] = pixel.rgb[1]; dest[2*dx] = pixel.rgb[2]; } dest += dy; ps--; break; } // end switch } // end while } 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_RGB1] # 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; return 1; } if( strncmp( bv->GetPointer(), comprgb.c_str(), comprgb.size() ) == 0 ) { isrgb = true; } if( !isrgb && !isrle ) 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(); gdcm::Attribute<0x0028,0x0006> at0; at0.SetFromDataSet( ds ); gdcm::Attribute<0x0028,0x0010> at1; at1.SetFromDataSet( ds ); gdcm::Attribute<0x0028,0x0011> at2; at2.SetFromDataSet( ds ); std::vector buffer; delta_decode((const unsigned char*)bv2->GetPointer(), bv2->GetLength(), buffer, at0.GetValue(), at1.GetValue(), at2.GetValue() ); gdcm::DataElement pixeldata( gdcm::Tag(0x7fe0,0x0010) ); pixeldata.SetVR( gdcm::VR::OW ); pixeldata.SetByteValue( (char*)&buffer[0], (uint32_t)buffer.size() ); // TODO we should check that decompress byte buffer match the expected size (row*col*...) // Add the pixel data element reader.GetFile().GetDataSet().Replace( pixeldata ); reader.GetFile().GetHeader().SetDataSetTransferSyntax( gdcm::TransferSyntax::ExplicitVRLittleEndian); 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 = "outrgb.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; }