GDCM 2.2.0

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

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

    =========================================================================*/
    #include "gdcmReader.h"
    #include "gdcmWriter.h"
    #include "gdcmImageReader.h"

    #include <fstream>

    #include "gdcm_charls.h"

    /*
     * This small example should show how one can handle the famous JAI-JPEGLS bug
     * It will take in as invalid DICOM/JAI-JPEG-LS and write out as Explicit Little
     * Endian. One can use `gdcmconv --jpegls` to recompress properly
     *
     * References:
     * http://charls.codeplex.com/discussions/230307?ProjectName=charls
     * http://charls.codeplex.com/workitem/7297
     * http://www.dcm4che.org/jira/browse/DCM-442
     * http://www.dcm4che.org/jira/browse/DCMEE-1144
     * http://java.net/jira/browse/JAI_IMAGEIO_CORE-183
     *
     * Explanation of the issue:
     *
     * Seems, the error is in the calculation of the default values for thresholds T1,
     * T2, T3, in particular min(MAXVAL, 4095) is not applied in
     *
     * FACTOR = (min(MAXVAL, 4095) + 128)/256
     *
     * as specified in http://www.itu.int/rec/T-REC-T.87-199806-I/en .
     *
     */
    int main(int argc, char *argv[])
    {
      if( argc < 3 )
        {
        std::cerr << argv[0] << " input.dcm output.dcm" << std::endl;
        return 1;
        }
      const char *filename = argv[1];
      const char *outfilename = argv[2];
      gdcm::FileMetaInformation::SetSourceApplicationEntityTitle( "FixJAIBugJPEGLS" );

      gdcm::ImageReader reader;
      reader.SetFileName( filename );
      if( !reader.Read() )
        {
        return 1;
        }

      gdcm::Image &image = reader.GetImage();
      //unsigned long len = image.GetBufferLength();
      const gdcm::DataElement & in =
        reader.GetFile().GetDataSet().GetDataElement( gdcm::Tag(0x7fe0,0x0010) );
      const gdcm::SequenceOfFragments *sf = in.GetSequenceOfFragments();
      if( !sf )
        {
        std::cerr << "No pixel data (or not encapsulated)" << std::endl;
        return 1;
        }
      const unsigned int *dims = image.GetDimensions();
      if ( sf->GetNumberOfFragments() != dims[2] )
        {
        std::cerr << "Unsupported" << std::endl;
        return 1;
        }

    //  unsigned long totalLen = sf->ComputeByteLength();
      std::vector<BYTE> rgbyteOutall;
      for(unsigned int i = 0; i < sf->GetNumberOfFragments(); ++i)
        {
        const gdcm::Fragment &frag = sf->GetFragment(i);
        if( frag.IsEmpty() ) return 1;
        const gdcm::ByteValue *bv = frag.GetByteValue();
        if( !bv ) return 1;
        unsigned long totalLen = bv->GetLength();

        std::vector<char> vbuffer;
        vbuffer.resize( totalLen );
        char *buffer = &vbuffer[0];
        bv->GetBuffer(buffer, totalLen);
        const BYTE* pbyteCompressed0 = (const BYTE*)buffer;
        while( totalLen > 0 && pbyteCompressed0[totalLen-1] != 0xd9 )
          {
          totalLen--;
          }

    #ifdef GDCM_USE_SYSTEM_CHARLS
        JlsParameters metadata;
    #else
        JlsParamaters metadata;
    #endif
        if (JpegLsReadHeader(buffer, totalLen, &metadata) != OK)
          {
          std::cerr << "Cant parse jpegls" << std::endl;
          return false;
          }

        std::cout << metadata.width << std::endl;
        std::cout << metadata.height << std::endl;
        std::cout << metadata.bitspersample << std::endl;

        gdcm::PixelFormat const & pf = image.GetPixelFormat();
        std::cout << pf << std::endl;

        // http://charls.codeplex.com/discussions/230307?ProjectName=charls
        unsigned char marker_lse_13[] = {
          0xFF, 0xF8, 0x00, 0x0D,
          0x01,
          0x1F, 0xFF,
          0x00, 0x22,  // T1 = 34
          0x00, 0x83,  // T2 = 131
          0x02, 0x24,  // T3 = 548
          0x00, 0x40
        };

        unsigned char marker_lse_14[] = {
          0xFF, 0xF8, 0x00, 0x0D,
          0x01,
          0x3F, 0xFF,
          0x00, 0x42, // T1 = 66
          0x01, 0x03, // T2 = 259
          0x04, 0x44, // T3 = 1092
          0x00, 0x40
        };

        unsigned char marker_lse_15[] = {
          0xFF, 0xF8, 0x00, 0x0D,
          0x01,
          0x7F, 0xFF,
          0x00, 0x82, // T1 = 130
          0x02, 0x03, // T2 = 515
          0x08, 0x84, // T3 = 2180
          0x00, 0x40
        };

        unsigned char marker_lse_16[] = {
          0xFF, 0xF8, 0x00, 0x0D,
          0x01,
          0xFF, 0xFF,
          0x01, 0x02, // T1 = 258
          0x04, 0x03, // T2 = 1027
          0x11, 0x04, // T3 = 4356
          0x00, 0x40
        };

        const unsigned char *marker_lse = NULL;
        switch( metadata.bitspersample )
          {
        case 13:
          marker_lse = marker_lse_13;
          break;
        case 14:
          marker_lse = marker_lse_14;
          break;
        case 15:
          marker_lse = marker_lse_15;
          break;
        case 16:
          marker_lse = marker_lse_16;
          break;
          }
        if( !marker_lse )
          {
          std::cerr << "Cant handle: " << metadata.bitspersample << std::endl;
          return 1;
          }

        // FIXME: One should recompute the value for 0x0F
        vbuffer.insert (vbuffer.begin() + 0x0F, marker_lse, marker_lse+15);

    #if 0
        std::ofstream of( "/tmp/d.jls" );
        of.write( &vbuffer[0], vbuffer.size() );
        of.close();
    #endif

        const char *pbyteCompressed = &vbuffer[0];
        unsigned int cbyteCompressed = vbuffer.size(); // updated legnth

    #ifdef GDCM_USE_SYSTEM_CHARLS
        JlsParameters params = {0};
    #else
        JlsParamaters params = {0};
    #endif
        JpegLsReadHeader(pbyteCompressed, cbyteCompressed, &params);

        std::vector<BYTE> rgbyteOut;
        //rgbyteOut.resize( image.GetBufferLength() );
        rgbyteOut.resize(params.height *params.width * ((params.bitspersample + 7)
            / 8) * params.components);

    #ifdef GDCM_USE_SYSTEM_CHARLS
        JLS_ERROR result =
          JpegLsDecode(&rgbyteOut[0], rgbyteOut.size(), pbyteCompressed, cbyteCompressed, &params );
    #else
        JLS_ERROR result =
          JpegLsDecode(&rgbyteOut[0], rgbyteOut.size(), pbyteCompressed, cbyteCompressed );
    #endif
        if (result != OK)
          {
          std::cerr << "Could not patch JAI-JPEGLS" << std::endl;
          return 1;
          }
        rgbyteOutall.insert( rgbyteOutall.end(), rgbyteOut.begin(), rgbyteOut.end() );
        }

      gdcm::DataElement pixeldata( gdcm::Tag(0x7fe0,0x0010) );
      pixeldata.SetVR( gdcm::VR::OW );
      pixeldata.SetByteValue( (char*)&rgbyteOutall[0], rgbyteOutall.size() );

      // Add the pixel data element
      reader.GetFile().GetDataSet().Replace( pixeldata );
      reader.GetFile().GetHeader().SetDataSetTransferSyntax(
        gdcm::TransferSyntax::ExplicitVRLittleEndian);

      gdcm::Writer writer;
      writer.SetFileName( outfilename );
      writer.SetFile( reader.GetFile() );
      writer.Write();

      std::cout << "Success !" << std::endl;

      return 0;
    }