GDCM 2.2.0

- Main Page
- Related Pages
- Namespaces
- Classes
- Files
- Directories
- Examples

pmsct_rgb1.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.

    =========================================================================*/
    /*
     * 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<unsigned char> &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
      };

      byte* src = (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<unsigned char> 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], 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;
    }