changeset 3884:83061cdc7703 transcoding

moving old tests to the graveyard
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 05 May 2020 14:37:29 +0200
parents 795c9ca5eb91
children e23026566536
files Resources/Graveyard/TestTranscoding.cpp UnitTestsSources/FromDcmtkTests.cpp
diffstat 2 files changed, 938 insertions(+), 922 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Graveyard/TestTranscoding.cpp	Tue May 05 14:37:29 2020 +0200
@@ -0,0 +1,922 @@
+#include <dcmtk/dcmdata/dcostrmb.h>
+#include <dcmtk/dcmdata/dcpixel.h>
+#include <dcmtk/dcmdata/dcpxitem.h>
+
+#include "../Core/DicomParsing/Internals/DicomFrameIndex.h"
+
+namespace Orthanc
+{
+  class IParsedDicomImage : public boost::noncopyable
+  {
+  public:
+    virtual ~IParsedDicomImage()
+    {
+    }
+
+    virtual DicomTransferSyntax GetTransferSyntax() = 0;
+
+    virtual std::string GetSopClassUid() = 0;
+
+    virtual std::string GetSopInstanceUid() = 0;
+
+    virtual unsigned int GetFramesCount() = 0;
+
+    // Can return NULL, for compressed transfer syntaxes
+    virtual ImageAccessor* GetUncompressedFrame(unsigned int frame) = 0;
+    
+    virtual void GetCompressedFrame(std::string& target,
+                                    unsigned int frame) = 0;
+    
+    virtual void WriteToMemoryBuffer(std::string& target) = 0;
+  };
+
+
+  class IDicomImageReader : public boost::noncopyable
+  {
+  public:
+    virtual ~IDicomImageReader()
+    {
+    }
+
+    virtual IParsedDicomImage* Read(const void* data,
+                                    size_t size) = 0;
+
+    virtual IParsedDicomImage* Transcode(const void* data,
+                                         size_t size,
+                                         DicomTransferSyntax syntax,
+                                         bool allowNewSopInstanceUid) = 0;
+  };
+
+
+  class DcmtkImageReader : public IDicomImageReader
+  {
+  private:
+    class Image : public IParsedDicomImage
+    {
+    private:
+      std::unique_ptr<DcmFileFormat>    dicom_;
+      std::unique_ptr<DicomFrameIndex>  index_;
+      DicomTransferSyntax               transferSyntax_;
+      std::string                       sopClassUid_;
+      std::string                       sopInstanceUid_;
+
+      static std::string GetStringTag(DcmDataset& dataset,
+                                      const DcmTagKey& tag)
+      {
+        const char* value = NULL;
+
+        if (!dataset.findAndGetString(tag, value).good() ||
+            value == NULL)
+        {
+          throw OrthancException(ErrorCode_BadFileFormat,
+                                 "Missing SOP class/instance UID in DICOM instance");
+        }
+        else
+        {
+          return std::string(value);
+        }
+      }
+
+    public:
+      Image(DcmFileFormat* dicom,
+            DicomTransferSyntax syntax) :
+        dicom_(dicom),
+        transferSyntax_(syntax)
+      {
+        if (dicom == NULL ||
+            dicom_->getDataset() == NULL)
+        {
+          throw OrthancException(ErrorCode_NullPointer);
+        }
+
+        DcmDataset& dataset = *dicom_->getDataset();
+        index_.reset(new DicomFrameIndex(dataset));
+
+        sopClassUid_ = GetStringTag(dataset, DCM_SOPClassUID);
+        sopInstanceUid_ = GetStringTag(dataset, DCM_SOPInstanceUID);
+      }
+
+      virtual DicomTransferSyntax GetTransferSyntax() ORTHANC_OVERRIDE
+      {
+        return transferSyntax_;
+      }
+
+      virtual std::string GetSopClassUid() ORTHANC_OVERRIDE
+      {
+        return sopClassUid_;
+      }
+    
+      virtual std::string GetSopInstanceUid() ORTHANC_OVERRIDE
+      {
+        return sopInstanceUid_;
+      }
+
+      virtual unsigned int GetFramesCount() ORTHANC_OVERRIDE
+      {
+        return index_->GetFramesCount();
+      }
+
+      virtual void WriteToMemoryBuffer(std::string& target) ORTHANC_OVERRIDE
+      {
+        assert(dicom_.get() != NULL);
+        if (!FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, transferSyntax_))
+        {
+          throw OrthancException(ErrorCode_InternalError,
+                                 "Cannot write the DICOM instance to a memory buffer");
+        }
+      }
+
+      virtual ImageAccessor* GetUncompressedFrame(unsigned int frame) ORTHANC_OVERRIDE
+      {
+        assert(dicom_.get() != NULL &&
+               dicom_->getDataset() != NULL);
+        return DicomImageDecoder::Decode(*dicom_->getDataset(), frame);
+      }
+
+      virtual void GetCompressedFrame(std::string& target,
+                                      unsigned int frame) ORTHANC_OVERRIDE
+      {
+        assert(index_.get() != NULL);
+        index_->GetRawFrame(target, frame);
+      }
+    };
+
+    unsigned int lossyQuality_;
+
+    static DicomTransferSyntax DetectTransferSyntax(DcmFileFormat& dicom)
+    {
+      if (dicom.getDataset() == NULL)
+      {
+        throw OrthancException(ErrorCode_InternalError);
+      }
+        
+      DcmDataset& dataset = *dicom.getDataset();
+
+      E_TransferSyntax xfer = dataset.getCurrentXfer();
+      if (xfer == EXS_Unknown)
+      {
+        dataset.updateOriginalXfer();
+        xfer = dataset.getCurrentXfer();
+        if (xfer == EXS_Unknown)
+        {
+          throw OrthancException(ErrorCode_BadFileFormat,
+                                 "Cannot determine the transfer syntax of the DICOM instance");
+        }
+      }
+
+      DicomTransferSyntax syntax;
+      if (FromDcmtkBridge::LookupOrthancTransferSyntax(syntax, xfer))
+      {
+        return syntax;
+      }
+      else
+      {
+        throw OrthancException(
+          ErrorCode_BadFileFormat,
+          "Unsupported transfer syntax: " + boost::lexical_cast<std::string>(xfer));
+      }
+    }
+
+
+    static uint16_t GetBitsStored(DcmFileFormat& dicom)
+    {
+      if (dicom.getDataset() == NULL)
+      {
+        throw OrthancException(ErrorCode_InternalError);
+      }
+
+      uint16_t bitsStored;
+      if (dicom.getDataset()->findAndGetUint16(DCM_BitsStored, bitsStored).good())
+      {
+        return bitsStored;
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_BadFileFormat,
+                               "Missing \"Bits Stored\" tag in DICOM instance");
+      }      
+    }
+    
+      
+  public:
+    DcmtkImageReader() :
+      lossyQuality_(90)
+    {
+    }
+
+    void SetLossyQuality(unsigned int quality)
+    {
+      if (quality <= 0 ||
+          quality > 100)
+      {
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+      else
+      {
+        lossyQuality_ = quality;
+      }
+    }
+
+    unsigned int GetLossyQuality() const
+    {
+      return lossyQuality_;
+    }
+
+    virtual IParsedDicomImage* Read(const void* data,
+                                    size_t size)
+    {
+      std::unique_ptr<DcmFileFormat> dicom(FromDcmtkBridge::LoadFromMemoryBuffer(data, size));
+      if (dicom.get() == NULL)
+      {
+        throw OrthancException(ErrorCode_BadFileFormat);
+      }
+
+      DicomTransferSyntax transferSyntax = DetectTransferSyntax(*dicom);
+
+      return new Image(dicom.release(), transferSyntax);
+    }
+
+    virtual IParsedDicomImage* Transcode(const void* data,
+                                         size_t size,
+                                         DicomTransferSyntax syntax,
+                                         bool allowNewSopInstanceUid)
+    {
+      std::unique_ptr<DcmFileFormat> dicom(FromDcmtkBridge::LoadFromMemoryBuffer(data, size));
+      if (dicom.get() == NULL)
+      {
+        throw OrthancException(ErrorCode_BadFileFormat);
+      }
+
+      const uint16_t bitsStored = GetBitsStored(*dicom);
+
+      if (syntax == DetectTransferSyntax(*dicom))
+      {
+        // No transcoding is needed
+        return new Image(dicom.release(), syntax);
+      }
+      
+      if (syntax == DicomTransferSyntax_LittleEndianImplicit &&
+          FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_LittleEndianImplicit, NULL))
+      {
+        return new Image(dicom.release(), syntax);
+      }
+
+      if (syntax == DicomTransferSyntax_LittleEndianExplicit &&
+          FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_LittleEndianExplicit, NULL))
+      {
+        return new Image(dicom.release(), syntax);
+      }
+      
+      if (syntax == DicomTransferSyntax_BigEndianExplicit &&
+          FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_BigEndianExplicit, NULL))
+      {
+        return new Image(dicom.release(), syntax);
+      }
+
+      if (syntax == DicomTransferSyntax_DeflatedLittleEndianExplicit &&
+          FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_DeflatedLittleEndianExplicit, NULL))
+      {
+        return new Image(dicom.release(), syntax);
+      }
+
+#if ORTHANC_ENABLE_JPEG == 1
+      if (syntax == DicomTransferSyntax_JPEGProcess1 &&
+          allowNewSopInstanceUid &&
+          bitsStored == 8)
+      {
+        DJ_RPLossy rpLossy(lossyQuality_);
+        
+        if (FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_JPEGProcess1, &rpLossy))
+        {
+          return new Image(dicom.release(), syntax);
+        }
+      }
+#endif
+      
+#if ORTHANC_ENABLE_JPEG == 1
+      if (syntax == DicomTransferSyntax_JPEGProcess2_4 &&
+          allowNewSopInstanceUid &&
+          bitsStored <= 12)
+      {
+        DJ_RPLossy rpLossy(lossyQuality_);
+        if (FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_JPEGProcess2_4, &rpLossy))
+        {
+          return new Image(dicom.release(), syntax);
+        }
+      }
+#endif
+
+      //LOG(INFO) << "Unable to transcode DICOM image using the built-in reader";
+      return NULL;
+    }
+  };
+  
+
+  
+  class IDicomTranscoder1 : public boost::noncopyable
+  {
+  public:
+    virtual ~IDicomTranscoder1()
+    {
+    }
+
+    virtual DcmFileFormat& GetDicom() = 0;
+
+    virtual DicomTransferSyntax GetTransferSyntax() = 0;
+
+    virtual std::string GetSopClassUid() = 0;
+
+    virtual std::string GetSopInstanceUid() = 0;
+
+    virtual unsigned int GetFramesCount() = 0;
+
+    virtual ImageAccessor* DecodeFrame(unsigned int frame) = 0;
+
+    virtual void GetCompressedFrame(std::string& target,
+                                    unsigned int frame) = 0;
+
+    // NB: Transcoding can change the value of "GetSopInstanceUid()"
+    // and "GetTransferSyntax()" if lossy compression is applied
+    virtual bool Transcode(std::string& target,
+                           DicomTransferSyntax syntax,
+                           bool allowNewSopInstanceUid) = 0;
+
+    virtual void WriteToMemoryBuffer(std::string& target) = 0;
+  };
+
+
+  class DcmtkTranscoder2 : public IDicomTranscoder1
+  {
+  private:
+    std::unique_ptr<DcmFileFormat>    dicom_;
+    std::unique_ptr<DicomFrameIndex>  index_;
+    DicomTransferSyntax               transferSyntax_;
+    std::string                       sopClassUid_;
+    std::string                       sopInstanceUid_;
+    uint16_t                          bitsStored_;
+    unsigned int                      lossyQuality_;
+
+    static std::string GetStringTag(DcmDataset& dataset,
+                                    const DcmTagKey& tag)
+    {
+      const char* value = NULL;
+
+      if (!dataset.findAndGetString(tag, value).good() ||
+          value == NULL)
+      {
+        throw OrthancException(ErrorCode_BadFileFormat,
+                               "Missing SOP class/instance UID in DICOM instance");
+      }
+      else
+      {
+        return std::string(value);
+      }
+    }
+
+    void Setup(DcmFileFormat* dicom)
+    {
+      lossyQuality_ = 90;
+      
+      dicom_.reset(dicom);
+      
+      if (dicom == NULL ||
+          dicom_->getDataset() == NULL)
+      {
+        throw OrthancException(ErrorCode_NullPointer);
+      }
+
+      DcmDataset& dataset = *dicom_->getDataset();
+      index_.reset(new DicomFrameIndex(dataset));
+
+      E_TransferSyntax xfer = dataset.getCurrentXfer();
+      if (xfer == EXS_Unknown)
+      {
+        dataset.updateOriginalXfer();
+        xfer = dataset.getCurrentXfer();
+        if (xfer == EXS_Unknown)
+        {
+          throw OrthancException(ErrorCode_BadFileFormat,
+                                 "Cannot determine the transfer syntax of the DICOM instance");
+        }
+      }
+
+      if (!FromDcmtkBridge::LookupOrthancTransferSyntax(transferSyntax_, xfer))
+      {
+        throw OrthancException(
+          ErrorCode_BadFileFormat,
+          "Unsupported transfer syntax: " + boost::lexical_cast<std::string>(xfer));
+      }
+
+      if (!dataset.findAndGetUint16(DCM_BitsStored, bitsStored_).good())
+      {
+        throw OrthancException(ErrorCode_BadFileFormat,
+                               "Missing \"Bits Stored\" tag in DICOM instance");
+      }      
+
+      sopClassUid_ = GetStringTag(dataset, DCM_SOPClassUID);
+      sopInstanceUid_ = GetStringTag(dataset, DCM_SOPInstanceUID);
+    }
+    
+  public:
+    DcmtkTranscoder2(DcmFileFormat* dicom)  // Takes ownership
+    {
+      Setup(dicom);
+    }
+
+    DcmtkTranscoder2(const void* dicom,
+                    size_t size)
+    {
+      Setup(FromDcmtkBridge::LoadFromMemoryBuffer(dicom, size));
+    }
+
+    void SetLossyQuality(unsigned int quality)
+    {
+      if (quality <= 0 ||
+          quality > 100)
+      {
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+      else
+      {
+        lossyQuality_ = quality;
+      }
+    }
+
+    unsigned int GetLossyQuality() const
+    {
+      return lossyQuality_;
+    }
+
+    unsigned int GetBitsStored() const
+    {
+      return bitsStored_;
+    }
+
+    virtual DcmFileFormat& GetDicom()
+    {
+      assert(dicom_ != NULL);
+      return *dicom_;
+    }
+
+    virtual DicomTransferSyntax GetTransferSyntax() ORTHANC_OVERRIDE
+    {
+      return transferSyntax_;
+    }
+
+    virtual std::string GetSopClassUid() ORTHANC_OVERRIDE
+    {
+      return sopClassUid_;
+    }
+    
+    virtual std::string GetSopInstanceUid() ORTHANC_OVERRIDE
+    {
+      return sopInstanceUid_;
+    }
+
+    virtual unsigned int GetFramesCount() ORTHANC_OVERRIDE
+    {
+      return index_->GetFramesCount();
+    }
+
+    virtual void WriteToMemoryBuffer(std::string& target) ORTHANC_OVERRIDE
+    {
+      if (!FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_))
+      {
+        throw OrthancException(ErrorCode_InternalError,
+                               "Cannot write the DICOM instance to a memory buffer");
+      }
+    }
+
+    virtual ImageAccessor* DecodeFrame(unsigned int frame) ORTHANC_OVERRIDE
+    {
+      assert(dicom_->getDataset() != NULL);
+      return DicomImageDecoder::Decode(*dicom_->getDataset(), frame);
+    }
+
+    virtual void GetCompressedFrame(std::string& target,
+                                    unsigned int frame) ORTHANC_OVERRIDE
+    {
+      index_->GetRawFrame(target, frame);
+    }
+
+    virtual bool Transcode(std::string& target,
+                           DicomTransferSyntax syntax,
+                           bool allowNewSopInstanceUid) ORTHANC_OVERRIDE
+    {
+      assert(dicom_ != NULL &&
+             dicom_->getDataset() != NULL);
+      
+      if (syntax == GetTransferSyntax())
+      {
+        printf("NO TRANSCODING\n");
+        
+        // No change in the transfer syntax => simply serialize the current dataset
+        WriteToMemoryBuffer(target);
+        return true;
+      }
+      
+      printf(">> %d\n", bitsStored_);
+
+      if (syntax == DicomTransferSyntax_LittleEndianImplicit &&
+          FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) &&
+          FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax))
+      {
+        transferSyntax_ = DicomTransferSyntax_LittleEndianImplicit;
+        return true;
+      }
+
+      if (syntax == DicomTransferSyntax_LittleEndianExplicit &&
+          FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) &&
+          FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax))
+      {
+        transferSyntax_ = DicomTransferSyntax_LittleEndianExplicit;
+        return true;
+      }
+      
+      if (syntax == DicomTransferSyntax_BigEndianExplicit &&
+          FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) &&
+          FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax))
+      {
+        transferSyntax_ = DicomTransferSyntax_BigEndianExplicit;
+        return true;
+      }
+
+      if (syntax == DicomTransferSyntax_DeflatedLittleEndianExplicit &&
+          FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) &&
+          FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax))
+      {
+        transferSyntax_ = DicomTransferSyntax_DeflatedLittleEndianExplicit;
+        return true;
+      }
+
+#if ORTHANC_ENABLE_JPEG == 1
+      if (syntax == DicomTransferSyntax_JPEGProcess1 &&
+          allowNewSopInstanceUid &&
+          GetBitsStored() == 8)
+      {
+        DJ_RPLossy rpLossy(lossyQuality_);
+        
+        if (FromDcmtkBridge::Transcode(*dicom_, syntax, &rpLossy) &&
+            FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax))
+        {
+          transferSyntax_ = DicomTransferSyntax_JPEGProcess1;
+          sopInstanceUid_ = GetStringTag(*dicom_->getDataset(), DCM_SOPInstanceUID);
+          return true;
+        }
+      }
+#endif
+      
+#if ORTHANC_ENABLE_JPEG == 1
+      if (syntax == DicomTransferSyntax_JPEGProcess2_4 &&
+          allowNewSopInstanceUid &&
+          GetBitsStored() <= 12)
+      {
+        DJ_RPLossy rpLossy(lossyQuality_);
+        if (FromDcmtkBridge::Transcode(*dicom_, syntax, &rpLossy) &&
+            FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax))
+        {
+          transferSyntax_ = DicomTransferSyntax_JPEGProcess2_4;
+          sopInstanceUid_ = GetStringTag(*dicom_->getDataset(), DCM_SOPInstanceUID);
+          return true;
+        }
+      }
+#endif
+
+      return false;
+    }
+  };
+}
+
+
+
+
+#include <boost/filesystem.hpp>
+
+
+static void TestFile(const std::string& path)
+{
+  static unsigned int count = 0;
+  count++;
+  
+
+  printf("** %s\n", path.c_str());
+
+  std::string s;
+  SystemToolbox::ReadFile(s, path);
+
+  Orthanc::DcmtkTranscoder2 transcoder(s.c_str(), s.size());
+
+  /*if (transcoder.GetBitsStored() != 8)  // TODO
+    return; */
+
+  {
+    char buf[1024];
+    sprintf(buf, "/tmp/source-%06d.dcm", count);
+    printf(">> %s\n", buf);
+    Orthanc::SystemToolbox::WriteFile(s, buf);
+  }
+
+  printf("[%s] [%s] [%s] %d %d\n", GetTransferSyntaxUid(transcoder.GetTransferSyntax()),
+         transcoder.GetSopClassUid().c_str(), transcoder.GetSopInstanceUid().c_str(),
+         transcoder.GetFramesCount(), transcoder.GetTransferSyntax());
+
+  for (size_t i = 0; i < transcoder.GetFramesCount(); i++)
+  {
+    std::string f;
+    transcoder.GetCompressedFrame(f, i);
+
+    if (i == 0)
+    {
+      char buf[1024];
+      sprintf(buf, "/tmp/frame-%06d.raw", count);
+      printf(">> %s\n", buf);
+      Orthanc::SystemToolbox::WriteFile(f, buf);
+    }
+  }
+
+  {
+    std::string t;
+    transcoder.WriteToMemoryBuffer(t);
+
+    Orthanc::DcmtkTranscoder2 transcoder2(t.c_str(), t.size());
+    printf(">> %d %d ; %lu bytes\n", transcoder.GetTransferSyntax(), transcoder2.GetTransferSyntax(), t.size());
+  }
+
+  {
+    std::string a = transcoder.GetSopInstanceUid();
+    DicomTransferSyntax b = transcoder.GetTransferSyntax();
+    
+    DicomTransferSyntax syntax = DicomTransferSyntax_JPEGProcess2_4;
+    //DicomTransferSyntax syntax = DicomTransferSyntax_LittleEndianExplicit;
+
+    std::string t;
+    bool ok = transcoder.Transcode(t, syntax, true);
+    printf("Transcoding: %d\n", ok);
+
+    if (ok)
+    {
+      printf("[%s] => [%s]\n", a.c_str(), transcoder.GetSopInstanceUid().c_str());
+      printf("[%s] => [%s]\n", GetTransferSyntaxUid(b),
+             GetTransferSyntaxUid(transcoder.GetTransferSyntax()));
+      
+      {
+        char buf[1024];
+        sprintf(buf, "/tmp/transcoded-%06d.dcm", count);
+        printf(">> %s\n", buf);
+        Orthanc::SystemToolbox::WriteFile(t, buf);
+      }
+
+      Orthanc::DcmtkTranscoder2 transcoder2(t.c_str(), t.size());
+      printf("  => transcoded transfer syntax %d ; %lu bytes\n", transcoder2.GetTransferSyntax(), t.size());
+    }
+  }
+  
+  printf("\n");
+}
+
+TEST(Toto, DISABLED_Transcode)
+{
+  //OFLog::configure(OFLogger::DEBUG_LOG_LEVEL);
+
+  if (1)
+  {
+    const char* const PATH = "/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes";
+    
+    for (boost::filesystem::directory_iterator it(PATH);
+         it != boost::filesystem::directory_iterator(); ++it)
+    {
+      if (boost::filesystem::is_regular_file(it->status()))
+      {
+        TestFile(it->path().string());
+      }
+    }
+  }
+
+  if (0)
+  {
+    TestFile("/home/jodogne/Subversion/orthanc-tests/Database/Multiframe.dcm");
+    TestFile("/home/jodogne/Subversion/orthanc-tests/Database/Issue44/Monochrome1-Jpeg.dcm");
+  }
+
+  if (0)
+  {
+    TestFile("/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/1.2.840.10008.1.2.1.dcm");
+  }
+}
+
+
+TEST(Toto, DISABLED_Transcode2)
+{
+  for (int i = 0; i <= DicomTransferSyntax_XML; i++)
+  {
+    DicomTransferSyntax a = (DicomTransferSyntax) i;
+
+    std::string path = ("/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/" +
+                        std::string(GetTransferSyntaxUid(a)) + ".dcm");
+    if (Orthanc::SystemToolbox::IsRegularFile(path))
+    {
+      printf("\n======= %s\n", GetTransferSyntaxUid(a));
+
+      std::string source;
+      Orthanc::SystemToolbox::ReadFile(source, path);
+
+      DcmtkImageReader reader;
+
+      {
+        std::unique_ptr<IParsedDicomImage> image(
+          reader.Read(source.c_str(), source.size()));
+        ASSERT_TRUE(image.get() != NULL);
+        ASSERT_EQ(a, image->GetTransferSyntax());
+
+        std::string target;
+        image->WriteToMemoryBuffer(target);
+      }
+
+      for (int j = 0; j <= DicomTransferSyntax_XML; j++)
+      {
+        DicomTransferSyntax b = (DicomTransferSyntax) j;
+        //if (a == b) continue;
+
+        std::unique_ptr<IParsedDicomImage> image(
+          reader.Transcode(source.c_str(), source.size(), b, true));
+        if (image.get() != NULL)
+        {
+          printf("[%s] -> [%s]\n", GetTransferSyntaxUid(a), GetTransferSyntaxUid(b));
+
+          std::string target;
+          image->WriteToMemoryBuffer(target);
+
+          char buf[1024];
+          sprintf(buf, "/tmp/%s-%s.dcm", GetTransferSyntaxUid(a), GetTransferSyntaxUid(b));
+          
+          SystemToolbox::WriteFile(target, buf);
+        }
+        else if (a != DicomTransferSyntax_JPEG2000 &&
+                 a != DicomTransferSyntax_JPEG2000LosslessOnly)
+        {
+          ASSERT_TRUE(b != DicomTransferSyntax_LittleEndianImplicit &&
+                      b != DicomTransferSyntax_LittleEndianExplicit &&
+                      b != DicomTransferSyntax_BigEndianExplicit &&
+                      b != DicomTransferSyntax_DeflatedLittleEndianExplicit);
+        }
+      }
+    }
+  }
+}
+
+
+#include "../Core/DicomNetworking/DicomAssociation.h"
+#include "../Core/DicomNetworking/DicomControlUserConnection.h"
+#include "../Core/DicomNetworking/DicomStoreUserConnection.h"
+
+TEST(Toto, DISABLED_DicomAssociation)
+{
+  DicomAssociationParameters params;
+  params.SetLocalApplicationEntityTitle("ORTHANC");
+  params.SetRemoteApplicationEntityTitle("PACS");
+  params.SetRemotePort(2001);
+
+#if 0
+  DicomAssociation assoc;
+  assoc.ProposeGenericPresentationContext(UID_StorageCommitmentPushModelSOPClass);
+  assoc.ProposeGenericPresentationContext(UID_VerificationSOPClass);
+  assoc.ProposePresentationContext(UID_ComputedRadiographyImageStorage,
+                                   DicomTransferSyntax_JPEGProcess1);
+  assoc.ProposePresentationContext(UID_ComputedRadiographyImageStorage,
+                                   DicomTransferSyntax_JPEGProcess2_4);
+  assoc.ProposePresentationContext(UID_ComputedRadiographyImageStorage,
+                                   DicomTransferSyntax_JPEG2000);
+  
+  assoc.Open(params);
+
+  int presID = ASC_findAcceptedPresentationContextID(&assoc.GetDcmtkAssociation(), UID_ComputedRadiographyImageStorage);
+  printf(">> %d\n", presID);
+    
+  std::map<DicomTransferSyntax, uint8_t> pc;
+  printf(">> %d\n", assoc.LookupAcceptedPresentationContext(pc, UID_ComputedRadiographyImageStorage));
+  
+  for (std::map<DicomTransferSyntax, uint8_t>::const_iterator
+         it = pc.begin(); it != pc.end(); ++it)
+  {
+    printf("[%s] => %d\n", GetTransferSyntaxUid(it->first), it->second);
+  }
+#else
+  {
+    DicomControlUserConnection assoc(params);
+
+    try
+    {
+      printf(">> %d\n", assoc.Echo());
+    }
+    catch (OrthancException&)
+    {
+    }
+  }
+    
+  params.SetRemoteApplicationEntityTitle("PACS");
+  params.SetRemotePort(2000);
+
+  {
+    DicomControlUserConnection assoc(params);
+    printf(">> %d\n", assoc.Echo());
+  }
+
+#endif
+}
+
+static void TestTranscode(DicomStoreUserConnection& scu,
+                          const std::string& sopClassUid,
+                          DicomTransferSyntax transferSyntax)
+{
+  std::set<DicomTransferSyntax> accepted;
+
+  scu.LookupTranscoding(accepted, sopClassUid, transferSyntax);
+  if (accepted.empty())
+  {
+    throw OrthancException(ErrorCode_NetworkProtocol,
+                           "The SOP class is not supported by the remote modality");
+  }
+
+  {
+    unsigned int count = 0;
+    for (std::set<DicomTransferSyntax>::const_iterator
+           it = accepted.begin(); it != accepted.end(); ++it)
+    {
+      LOG(INFO) << "available for transcoding " << (count++) << ": " << sopClassUid
+                << " / " << GetTransferSyntaxUid(*it);
+    }
+  }
+  
+  if (accepted.find(transferSyntax) != accepted.end())
+  {
+    printf("**** OK, without transcoding !! [%s]\n", GetTransferSyntaxUid(transferSyntax));
+  }
+  else
+  {
+    // Transcoding - only in Orthanc >= 1.7.0
+
+    const DicomTransferSyntax uncompressed[] = {
+      DicomTransferSyntax_LittleEndianImplicit,  // Default transfer syntax
+      DicomTransferSyntax_LittleEndianExplicit,
+      DicomTransferSyntax_BigEndianExplicit
+    };
+
+    bool found = false;
+    for (size_t i = 0; i < 3; i++)
+    {
+      if (accepted.find(uncompressed[i]) != accepted.end())
+      {
+        printf("**** TRANSCODING to %s\n", GetTransferSyntaxUid(uncompressed[i]));
+        found = true;
+        break;
+      }
+    }
+
+    if (!found)
+    {
+      printf("**** KO KO KO\n");
+    }
+  }
+}
+
+
+TEST(Toto, DISABLED_Store)
+{
+  DicomAssociationParameters params;
+  params.SetLocalApplicationEntityTitle("ORTHANC");
+  params.SetRemoteApplicationEntityTitle("STORESCP");
+  params.SetRemotePort(2000);
+
+  DicomStoreUserConnection assoc(params);
+  assoc.RegisterStorageClass(UID_MRImageStorage, DicomTransferSyntax_JPEGProcess1);
+  assoc.RegisterStorageClass(UID_MRImageStorage, DicomTransferSyntax_JPEGProcess2_4);
+  //assoc.RegisterStorageClass(UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit);
+
+  //assoc.SetUncompressedSyntaxesProposed(false);  // Necessary for transcoding
+  assoc.SetCommonClassesProposed(false);
+  assoc.SetRetiredBigEndianProposed(true);
+  TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit);
+  TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_JPEG2000);
+  TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_JPEG2000);
+}
+
+
+TEST(Toto, DISABLED_Store2)
+{
+  DicomAssociationParameters params;
+  params.SetLocalApplicationEntityTitle("ORTHANC");
+  params.SetRemoteApplicationEntityTitle("STORESCP");
+  params.SetRemotePort(2000);
+
+  DicomStoreUserConnection assoc(params);
+  //assoc.SetCommonClassesProposed(false);
+  assoc.SetRetiredBigEndianProposed(true);
+
+  std::string s;
+  Orthanc::SystemToolbox::ReadFile(s, "/tmp/i/" + std::string(GetTransferSyntaxUid(DicomTransferSyntax_BigEndianExplicit)) +".dcm");
+
+  std::string c, i;
+  assoc.Store(c, i, s.c_str(), s.size());
+  printf("[%s] [%s]\n", c.c_str(), i.c_str());
+}
+
--- a/UnitTestsSources/FromDcmtkTests.cpp	Tue May 05 14:11:47 2020 +0200
+++ b/UnitTestsSources/FromDcmtkTests.cpp	Tue May 05 14:37:29 2020 +0200
@@ -1924,11 +1924,8 @@
 
 #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
 
-#include "../Core/DicomParsing/Internals/DicomFrameIndex.h"
+#include "../Core/DicomNetworking/DicomStoreUserConnection.h"
 
-#include <dcmtk/dcmdata/dcostrmb.h>
-#include <dcmtk/dcmdata/dcpixel.h>
-#include <dcmtk/dcmdata/dcpxitem.h>
 #include <dcmtk/dcmjpeg/djrploss.h>   // for DJ_RPLossy
 #include <dcmtk/dcmjpeg/djrplol.h>    // for DJ_RPLossless
 
@@ -1945,923 +1942,6 @@
 
 namespace Orthanc
 {
-  class IParsedDicomImage : public boost::noncopyable
-  {
-  public:
-    virtual ~IParsedDicomImage()
-    {
-    }
-
-    virtual DicomTransferSyntax GetTransferSyntax() = 0;
-
-    virtual std::string GetSopClassUid() = 0;
-
-    virtual std::string GetSopInstanceUid() = 0;
-
-    virtual unsigned int GetFramesCount() = 0;
-
-    // Can return NULL, for compressed transfer syntaxes
-    virtual ImageAccessor* GetUncompressedFrame(unsigned int frame) = 0;
-    
-    virtual void GetCompressedFrame(std::string& target,
-                                    unsigned int frame) = 0;
-    
-    virtual void WriteToMemoryBuffer(std::string& target) = 0;
-  };
-
-
-  class IDicomImageReader : public boost::noncopyable
-  {
-  public:
-    virtual ~IDicomImageReader()
-    {
-    }
-
-    virtual IParsedDicomImage* Read(const void* data,
-                                    size_t size) = 0;
-
-    virtual IParsedDicomImage* Transcode(const void* data,
-                                         size_t size,
-                                         DicomTransferSyntax syntax,
-                                         bool allowNewSopInstanceUid) = 0;
-  };
-
-
-  class DcmtkImageReader : public IDicomImageReader
-  {
-  private:
-    class Image : public IParsedDicomImage
-    {
-    private:
-      std::unique_ptr<DcmFileFormat>    dicom_;
-      std::unique_ptr<DicomFrameIndex>  index_;
-      DicomTransferSyntax               transferSyntax_;
-      std::string                       sopClassUid_;
-      std::string                       sopInstanceUid_;
-
-      static std::string GetStringTag(DcmDataset& dataset,
-                                      const DcmTagKey& tag)
-      {
-        const char* value = NULL;
-
-        if (!dataset.findAndGetString(tag, value).good() ||
-            value == NULL)
-        {
-          throw OrthancException(ErrorCode_BadFileFormat,
-                                 "Missing SOP class/instance UID in DICOM instance");
-        }
-        else
-        {
-          return std::string(value);
-        }
-      }
-
-    public:
-      Image(DcmFileFormat* dicom,
-            DicomTransferSyntax syntax) :
-        dicom_(dicom),
-        transferSyntax_(syntax)
-      {
-        if (dicom == NULL ||
-            dicom_->getDataset() == NULL)
-        {
-          throw OrthancException(ErrorCode_NullPointer);
-        }
-
-        DcmDataset& dataset = *dicom_->getDataset();
-        index_.reset(new DicomFrameIndex(dataset));
-
-        sopClassUid_ = GetStringTag(dataset, DCM_SOPClassUID);
-        sopInstanceUid_ = GetStringTag(dataset, DCM_SOPInstanceUID);
-      }
-
-      virtual DicomTransferSyntax GetTransferSyntax() ORTHANC_OVERRIDE
-      {
-        return transferSyntax_;
-      }
-
-      virtual std::string GetSopClassUid() ORTHANC_OVERRIDE
-      {
-        return sopClassUid_;
-      }
-    
-      virtual std::string GetSopInstanceUid() ORTHANC_OVERRIDE
-      {
-        return sopInstanceUid_;
-      }
-
-      virtual unsigned int GetFramesCount() ORTHANC_OVERRIDE
-      {
-        return index_->GetFramesCount();
-      }
-
-      virtual void WriteToMemoryBuffer(std::string& target) ORTHANC_OVERRIDE
-      {
-        assert(dicom_.get() != NULL);
-        if (!FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, transferSyntax_))
-        {
-          throw OrthancException(ErrorCode_InternalError,
-                                 "Cannot write the DICOM instance to a memory buffer");
-        }
-      }
-
-      virtual ImageAccessor* GetUncompressedFrame(unsigned int frame) ORTHANC_OVERRIDE
-      {
-        assert(dicom_.get() != NULL &&
-               dicom_->getDataset() != NULL);
-        return DicomImageDecoder::Decode(*dicom_->getDataset(), frame);
-      }
-
-      virtual void GetCompressedFrame(std::string& target,
-                                      unsigned int frame) ORTHANC_OVERRIDE
-      {
-        assert(index_.get() != NULL);
-        index_->GetRawFrame(target, frame);
-      }
-    };
-
-    unsigned int lossyQuality_;
-
-    static DicomTransferSyntax DetectTransferSyntax(DcmFileFormat& dicom)
-    {
-      if (dicom.getDataset() == NULL)
-      {
-        throw OrthancException(ErrorCode_InternalError);
-      }
-        
-      DcmDataset& dataset = *dicom.getDataset();
-
-      E_TransferSyntax xfer = dataset.getCurrentXfer();
-      if (xfer == EXS_Unknown)
-      {
-        dataset.updateOriginalXfer();
-        xfer = dataset.getCurrentXfer();
-        if (xfer == EXS_Unknown)
-        {
-          throw OrthancException(ErrorCode_BadFileFormat,
-                                 "Cannot determine the transfer syntax of the DICOM instance");
-        }
-      }
-
-      DicomTransferSyntax syntax;
-      if (FromDcmtkBridge::LookupOrthancTransferSyntax(syntax, xfer))
-      {
-        return syntax;
-      }
-      else
-      {
-        throw OrthancException(
-          ErrorCode_BadFileFormat,
-          "Unsupported transfer syntax: " + boost::lexical_cast<std::string>(xfer));
-      }
-    }
-
-
-    static uint16_t GetBitsStored(DcmFileFormat& dicom)
-    {
-      if (dicom.getDataset() == NULL)
-      {
-        throw OrthancException(ErrorCode_InternalError);
-      }
-
-      uint16_t bitsStored;
-      if (dicom.getDataset()->findAndGetUint16(DCM_BitsStored, bitsStored).good())
-      {
-        return bitsStored;
-      }
-      else
-      {
-        throw OrthancException(ErrorCode_BadFileFormat,
-                               "Missing \"Bits Stored\" tag in DICOM instance");
-      }      
-    }
-    
-      
-  public:
-    DcmtkImageReader() :
-      lossyQuality_(90)
-    {
-    }
-
-    void SetLossyQuality(unsigned int quality)
-    {
-      if (quality <= 0 ||
-          quality > 100)
-      {
-        throw OrthancException(ErrorCode_ParameterOutOfRange);
-      }
-      else
-      {
-        lossyQuality_ = quality;
-      }
-    }
-
-    unsigned int GetLossyQuality() const
-    {
-      return lossyQuality_;
-    }
-
-    virtual IParsedDicomImage* Read(const void* data,
-                                    size_t size)
-    {
-      std::unique_ptr<DcmFileFormat> dicom(FromDcmtkBridge::LoadFromMemoryBuffer(data, size));
-      if (dicom.get() == NULL)
-      {
-        throw OrthancException(ErrorCode_BadFileFormat);
-      }
-
-      DicomTransferSyntax transferSyntax = DetectTransferSyntax(*dicom);
-
-      return new Image(dicom.release(), transferSyntax);
-    }
-
-    virtual IParsedDicomImage* Transcode(const void* data,
-                                         size_t size,
-                                         DicomTransferSyntax syntax,
-                                         bool allowNewSopInstanceUid)
-    {
-      std::unique_ptr<DcmFileFormat> dicom(FromDcmtkBridge::LoadFromMemoryBuffer(data, size));
-      if (dicom.get() == NULL)
-      {
-        throw OrthancException(ErrorCode_BadFileFormat);
-      }
-
-      const uint16_t bitsStored = GetBitsStored(*dicom);
-
-      if (syntax == DetectTransferSyntax(*dicom))
-      {
-        // No transcoding is needed
-        return new Image(dicom.release(), syntax);
-      }
-      
-      if (syntax == DicomTransferSyntax_LittleEndianImplicit &&
-          FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_LittleEndianImplicit, NULL))
-      {
-        return new Image(dicom.release(), syntax);
-      }
-
-      if (syntax == DicomTransferSyntax_LittleEndianExplicit &&
-          FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_LittleEndianExplicit, NULL))
-      {
-        return new Image(dicom.release(), syntax);
-      }
-      
-      if (syntax == DicomTransferSyntax_BigEndianExplicit &&
-          FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_BigEndianExplicit, NULL))
-      {
-        return new Image(dicom.release(), syntax);
-      }
-
-      if (syntax == DicomTransferSyntax_DeflatedLittleEndianExplicit &&
-          FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_DeflatedLittleEndianExplicit, NULL))
-      {
-        return new Image(dicom.release(), syntax);
-      }
-
-#if ORTHANC_ENABLE_JPEG == 1
-      if (syntax == DicomTransferSyntax_JPEGProcess1 &&
-          allowNewSopInstanceUid &&
-          bitsStored == 8)
-      {
-        DJ_RPLossy rpLossy(lossyQuality_);
-        
-        if (FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_JPEGProcess1, &rpLossy))
-        {
-          return new Image(dicom.release(), syntax);
-        }
-      }
-#endif
-      
-#if ORTHANC_ENABLE_JPEG == 1
-      if (syntax == DicomTransferSyntax_JPEGProcess2_4 &&
-          allowNewSopInstanceUid &&
-          bitsStored <= 12)
-      {
-        DJ_RPLossy rpLossy(lossyQuality_);
-        if (FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_JPEGProcess2_4, &rpLossy))
-        {
-          return new Image(dicom.release(), syntax);
-        }
-      }
-#endif
-
-      //LOG(INFO) << "Unable to transcode DICOM image using the built-in reader";
-      return NULL;
-    }
-  };
-  
-
-  
-  class IDicomTranscoder1 : public boost::noncopyable
-  {
-  public:
-    virtual ~IDicomTranscoder1()
-    {
-    }
-
-    virtual DcmFileFormat& GetDicom() = 0;
-
-    virtual DicomTransferSyntax GetTransferSyntax() = 0;
-
-    virtual std::string GetSopClassUid() = 0;
-
-    virtual std::string GetSopInstanceUid() = 0;
-
-    virtual unsigned int GetFramesCount() = 0;
-
-    virtual ImageAccessor* DecodeFrame(unsigned int frame) = 0;
-
-    virtual void GetCompressedFrame(std::string& target,
-                                    unsigned int frame) = 0;
-
-    // NB: Transcoding can change the value of "GetSopInstanceUid()"
-    // and "GetTransferSyntax()" if lossy compression is applied
-    virtual bool Transcode(std::string& target,
-                           DicomTransferSyntax syntax,
-                           bool allowNewSopInstanceUid) = 0;
-
-    virtual void WriteToMemoryBuffer(std::string& target) = 0;
-  };
-
-
-  class DcmtkTranscoder2 : public IDicomTranscoder1
-  {
-  private:
-    std::unique_ptr<DcmFileFormat>    dicom_;
-    std::unique_ptr<DicomFrameIndex>  index_;
-    DicomTransferSyntax               transferSyntax_;
-    std::string                       sopClassUid_;
-    std::string                       sopInstanceUid_;
-    uint16_t                          bitsStored_;
-    unsigned int                      lossyQuality_;
-
-    static std::string GetStringTag(DcmDataset& dataset,
-                                    const DcmTagKey& tag)
-    {
-      const char* value = NULL;
-
-      if (!dataset.findAndGetString(tag, value).good() ||
-          value == NULL)
-      {
-        throw OrthancException(ErrorCode_BadFileFormat,
-                               "Missing SOP class/instance UID in DICOM instance");
-      }
-      else
-      {
-        return std::string(value);
-      }
-    }
-
-    void Setup(DcmFileFormat* dicom)
-    {
-      lossyQuality_ = 90;
-      
-      dicom_.reset(dicom);
-      
-      if (dicom == NULL ||
-          dicom_->getDataset() == NULL)
-      {
-        throw OrthancException(ErrorCode_NullPointer);
-      }
-
-      DcmDataset& dataset = *dicom_->getDataset();
-      index_.reset(new DicomFrameIndex(dataset));
-
-      E_TransferSyntax xfer = dataset.getCurrentXfer();
-      if (xfer == EXS_Unknown)
-      {
-        dataset.updateOriginalXfer();
-        xfer = dataset.getCurrentXfer();
-        if (xfer == EXS_Unknown)
-        {
-          throw OrthancException(ErrorCode_BadFileFormat,
-                                 "Cannot determine the transfer syntax of the DICOM instance");
-        }
-      }
-
-      if (!FromDcmtkBridge::LookupOrthancTransferSyntax(transferSyntax_, xfer))
-      {
-        throw OrthancException(
-          ErrorCode_BadFileFormat,
-          "Unsupported transfer syntax: " + boost::lexical_cast<std::string>(xfer));
-      }
-
-      if (!dataset.findAndGetUint16(DCM_BitsStored, bitsStored_).good())
-      {
-        throw OrthancException(ErrorCode_BadFileFormat,
-                               "Missing \"Bits Stored\" tag in DICOM instance");
-      }      
-
-      sopClassUid_ = GetStringTag(dataset, DCM_SOPClassUID);
-      sopInstanceUid_ = GetStringTag(dataset, DCM_SOPInstanceUID);
-    }
-    
-  public:
-    DcmtkTranscoder2(DcmFileFormat* dicom)  // Takes ownership
-    {
-      Setup(dicom);
-    }
-
-    DcmtkTranscoder2(const void* dicom,
-                    size_t size)
-    {
-      Setup(FromDcmtkBridge::LoadFromMemoryBuffer(dicom, size));
-    }
-
-    void SetLossyQuality(unsigned int quality)
-    {
-      if (quality <= 0 ||
-          quality > 100)
-      {
-        throw OrthancException(ErrorCode_ParameterOutOfRange);
-      }
-      else
-      {
-        lossyQuality_ = quality;
-      }
-    }
-
-    unsigned int GetLossyQuality() const
-    {
-      return lossyQuality_;
-    }
-
-    unsigned int GetBitsStored() const
-    {
-      return bitsStored_;
-    }
-
-    virtual DcmFileFormat& GetDicom()
-    {
-      assert(dicom_ != NULL);
-      return *dicom_;
-    }
-
-    virtual DicomTransferSyntax GetTransferSyntax() ORTHANC_OVERRIDE
-    {
-      return transferSyntax_;
-    }
-
-    virtual std::string GetSopClassUid() ORTHANC_OVERRIDE
-    {
-      return sopClassUid_;
-    }
-    
-    virtual std::string GetSopInstanceUid() ORTHANC_OVERRIDE
-    {
-      return sopInstanceUid_;
-    }
-
-    virtual unsigned int GetFramesCount() ORTHANC_OVERRIDE
-    {
-      return index_->GetFramesCount();
-    }
-
-    virtual void WriteToMemoryBuffer(std::string& target) ORTHANC_OVERRIDE
-    {
-      if (!FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_))
-      {
-        throw OrthancException(ErrorCode_InternalError,
-                               "Cannot write the DICOM instance to a memory buffer");
-      }
-    }
-
-    virtual ImageAccessor* DecodeFrame(unsigned int frame) ORTHANC_OVERRIDE
-    {
-      assert(dicom_->getDataset() != NULL);
-      return DicomImageDecoder::Decode(*dicom_->getDataset(), frame);
-    }
-
-    virtual void GetCompressedFrame(std::string& target,
-                                    unsigned int frame) ORTHANC_OVERRIDE
-    {
-      index_->GetRawFrame(target, frame);
-    }
-
-    virtual bool Transcode(std::string& target,
-                           DicomTransferSyntax syntax,
-                           bool allowNewSopInstanceUid) ORTHANC_OVERRIDE
-    {
-      assert(dicom_ != NULL &&
-             dicom_->getDataset() != NULL);
-      
-      if (syntax == GetTransferSyntax())
-      {
-        printf("NO TRANSCODING\n");
-        
-        // No change in the transfer syntax => simply serialize the current dataset
-        WriteToMemoryBuffer(target);
-        return true;
-      }
-      
-      printf(">> %d\n", bitsStored_);
-
-      if (syntax == DicomTransferSyntax_LittleEndianImplicit &&
-          FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) &&
-          FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax))
-      {
-        transferSyntax_ = DicomTransferSyntax_LittleEndianImplicit;
-        return true;
-      }
-
-      if (syntax == DicomTransferSyntax_LittleEndianExplicit &&
-          FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) &&
-          FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax))
-      {
-        transferSyntax_ = DicomTransferSyntax_LittleEndianExplicit;
-        return true;
-      }
-      
-      if (syntax == DicomTransferSyntax_BigEndianExplicit &&
-          FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) &&
-          FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax))
-      {
-        transferSyntax_ = DicomTransferSyntax_BigEndianExplicit;
-        return true;
-      }
-
-      if (syntax == DicomTransferSyntax_DeflatedLittleEndianExplicit &&
-          FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) &&
-          FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax))
-      {
-        transferSyntax_ = DicomTransferSyntax_DeflatedLittleEndianExplicit;
-        return true;
-      }
-
-#if ORTHANC_ENABLE_JPEG == 1
-      if (syntax == DicomTransferSyntax_JPEGProcess1 &&
-          allowNewSopInstanceUid &&
-          GetBitsStored() == 8)
-      {
-        DJ_RPLossy rpLossy(lossyQuality_);
-        
-        if (FromDcmtkBridge::Transcode(*dicom_, syntax, &rpLossy) &&
-            FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax))
-        {
-          transferSyntax_ = DicomTransferSyntax_JPEGProcess1;
-          sopInstanceUid_ = GetStringTag(*dicom_->getDataset(), DCM_SOPInstanceUID);
-          return true;
-        }
-      }
-#endif
-      
-#if ORTHANC_ENABLE_JPEG == 1
-      if (syntax == DicomTransferSyntax_JPEGProcess2_4 &&
-          allowNewSopInstanceUid &&
-          GetBitsStored() <= 12)
-      {
-        DJ_RPLossy rpLossy(lossyQuality_);
-        if (FromDcmtkBridge::Transcode(*dicom_, syntax, &rpLossy) &&
-            FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax))
-        {
-          transferSyntax_ = DicomTransferSyntax_JPEGProcess2_4;
-          sopInstanceUid_ = GetStringTag(*dicom_->getDataset(), DCM_SOPInstanceUID);
-          return true;
-        }
-      }
-#endif
-
-      return false;
-    }
-  };
-}
-
-
-
-
-#include <boost/filesystem.hpp>
-
-
-static void TestFile(const std::string& path)
-{
-  static unsigned int count = 0;
-  count++;
-  
-
-  printf("** %s\n", path.c_str());
-
-  std::string s;
-  SystemToolbox::ReadFile(s, path);
-
-  Orthanc::DcmtkTranscoder2 transcoder(s.c_str(), s.size());
-
-  /*if (transcoder.GetBitsStored() != 8)  // TODO
-    return; */
-
-  {
-    char buf[1024];
-    sprintf(buf, "/tmp/source-%06d.dcm", count);
-    printf(">> %s\n", buf);
-    Orthanc::SystemToolbox::WriteFile(s, buf);
-  }
-
-  printf("[%s] [%s] [%s] %d %d\n", GetTransferSyntaxUid(transcoder.GetTransferSyntax()),
-         transcoder.GetSopClassUid().c_str(), transcoder.GetSopInstanceUid().c_str(),
-         transcoder.GetFramesCount(), transcoder.GetTransferSyntax());
-
-  for (size_t i = 0; i < transcoder.GetFramesCount(); i++)
-  {
-    std::string f;
-    transcoder.GetCompressedFrame(f, i);
-
-    if (i == 0)
-    {
-      char buf[1024];
-      sprintf(buf, "/tmp/frame-%06d.raw", count);
-      printf(">> %s\n", buf);
-      Orthanc::SystemToolbox::WriteFile(f, buf);
-    }
-  }
-
-  {
-    std::string t;
-    transcoder.WriteToMemoryBuffer(t);
-
-    Orthanc::DcmtkTranscoder2 transcoder2(t.c_str(), t.size());
-    printf(">> %d %d ; %lu bytes\n", transcoder.GetTransferSyntax(), transcoder2.GetTransferSyntax(), t.size());
-  }
-
-  {
-    std::string a = transcoder.GetSopInstanceUid();
-    DicomTransferSyntax b = transcoder.GetTransferSyntax();
-    
-    DicomTransferSyntax syntax = DicomTransferSyntax_JPEGProcess2_4;
-    //DicomTransferSyntax syntax = DicomTransferSyntax_LittleEndianExplicit;
-
-    std::string t;
-    bool ok = transcoder.Transcode(t, syntax, true);
-    printf("Transcoding: %d\n", ok);
-
-    if (ok)
-    {
-      printf("[%s] => [%s]\n", a.c_str(), transcoder.GetSopInstanceUid().c_str());
-      printf("[%s] => [%s]\n", GetTransferSyntaxUid(b),
-             GetTransferSyntaxUid(transcoder.GetTransferSyntax()));
-      
-      {
-        char buf[1024];
-        sprintf(buf, "/tmp/transcoded-%06d.dcm", count);
-        printf(">> %s\n", buf);
-        Orthanc::SystemToolbox::WriteFile(t, buf);
-      }
-
-      Orthanc::DcmtkTranscoder2 transcoder2(t.c_str(), t.size());
-      printf("  => transcoded transfer syntax %d ; %lu bytes\n", transcoder2.GetTransferSyntax(), t.size());
-    }
-  }
-  
-  printf("\n");
-}
-
-TEST(Toto, DISABLED_Transcode)
-{
-  //OFLog::configure(OFLogger::DEBUG_LOG_LEVEL);
-
-  if (1)
-  {
-    const char* const PATH = "/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes";
-    
-    for (boost::filesystem::directory_iterator it(PATH);
-         it != boost::filesystem::directory_iterator(); ++it)
-    {
-      if (boost::filesystem::is_regular_file(it->status()))
-      {
-        TestFile(it->path().string());
-      }
-    }
-  }
-
-  if (0)
-  {
-    TestFile("/home/jodogne/Subversion/orthanc-tests/Database/Multiframe.dcm");
-    TestFile("/home/jodogne/Subversion/orthanc-tests/Database/Issue44/Monochrome1-Jpeg.dcm");
-  }
-
-  if (0)
-  {
-    TestFile("/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/1.2.840.10008.1.2.1.dcm");
-  }
-}
-
-
-TEST(Toto, DISABLED_Transcode2)
-{
-  for (int i = 0; i <= DicomTransferSyntax_XML; i++)
-  {
-    DicomTransferSyntax a = (DicomTransferSyntax) i;
-
-    std::string path = ("/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/" +
-                        std::string(GetTransferSyntaxUid(a)) + ".dcm");
-    if (Orthanc::SystemToolbox::IsRegularFile(path))
-    {
-      printf("\n======= %s\n", GetTransferSyntaxUid(a));
-
-      std::string source;
-      Orthanc::SystemToolbox::ReadFile(source, path);
-
-      DcmtkImageReader reader;
-
-      {
-        std::unique_ptr<IParsedDicomImage> image(
-          reader.Read(source.c_str(), source.size()));
-        ASSERT_TRUE(image.get() != NULL);
-        ASSERT_EQ(a, image->GetTransferSyntax());
-
-        std::string target;
-        image->WriteToMemoryBuffer(target);
-      }
-
-      for (int j = 0; j <= DicomTransferSyntax_XML; j++)
-      {
-        DicomTransferSyntax b = (DicomTransferSyntax) j;
-        //if (a == b) continue;
-
-        std::unique_ptr<IParsedDicomImage> image(
-          reader.Transcode(source.c_str(), source.size(), b, true));
-        if (image.get() != NULL)
-        {
-          printf("[%s] -> [%s]\n", GetTransferSyntaxUid(a), GetTransferSyntaxUid(b));
-
-          std::string target;
-          image->WriteToMemoryBuffer(target);
-
-          char buf[1024];
-          sprintf(buf, "/tmp/%s-%s.dcm", GetTransferSyntaxUid(a), GetTransferSyntaxUid(b));
-          
-          SystemToolbox::WriteFile(target, buf);
-        }
-        else if (a != DicomTransferSyntax_JPEG2000 &&
-                 a != DicomTransferSyntax_JPEG2000LosslessOnly)
-        {
-          ASSERT_TRUE(b != DicomTransferSyntax_LittleEndianImplicit &&
-                      b != DicomTransferSyntax_LittleEndianExplicit &&
-                      b != DicomTransferSyntax_BigEndianExplicit &&
-                      b != DicomTransferSyntax_DeflatedLittleEndianExplicit);
-        }
-      }
-    }
-  }
-}
-
-
-#include "../Core/DicomNetworking/DicomAssociation.h"
-#include "../Core/DicomNetworking/DicomControlUserConnection.h"
-#include "../Core/DicomNetworking/DicomStoreUserConnection.h"
-
-TEST(Toto, DISABLED_DicomAssociation)
-{
-  DicomAssociationParameters params;
-  params.SetLocalApplicationEntityTitle("ORTHANC");
-  params.SetRemoteApplicationEntityTitle("PACS");
-  params.SetRemotePort(2001);
-
-#if 0
-  DicomAssociation assoc;
-  assoc.ProposeGenericPresentationContext(UID_StorageCommitmentPushModelSOPClass);
-  assoc.ProposeGenericPresentationContext(UID_VerificationSOPClass);
-  assoc.ProposePresentationContext(UID_ComputedRadiographyImageStorage,
-                                   DicomTransferSyntax_JPEGProcess1);
-  assoc.ProposePresentationContext(UID_ComputedRadiographyImageStorage,
-                                   DicomTransferSyntax_JPEGProcess2_4);
-  assoc.ProposePresentationContext(UID_ComputedRadiographyImageStorage,
-                                   DicomTransferSyntax_JPEG2000);
-  
-  assoc.Open(params);
-
-  int presID = ASC_findAcceptedPresentationContextID(&assoc.GetDcmtkAssociation(), UID_ComputedRadiographyImageStorage);
-  printf(">> %d\n", presID);
-    
-  std::map<DicomTransferSyntax, uint8_t> pc;
-  printf(">> %d\n", assoc.LookupAcceptedPresentationContext(pc, UID_ComputedRadiographyImageStorage));
-  
-  for (std::map<DicomTransferSyntax, uint8_t>::const_iterator
-         it = pc.begin(); it != pc.end(); ++it)
-  {
-    printf("[%s] => %d\n", GetTransferSyntaxUid(it->first), it->second);
-  }
-#else
-  {
-    DicomControlUserConnection assoc(params);
-
-    try
-    {
-      printf(">> %d\n", assoc.Echo());
-    }
-    catch (OrthancException&)
-    {
-    }
-  }
-    
-  params.SetRemoteApplicationEntityTitle("PACS");
-  params.SetRemotePort(2000);
-
-  {
-    DicomControlUserConnection assoc(params);
-    printf(">> %d\n", assoc.Echo());
-  }
-
-#endif
-}
-
-static void TestTranscode(DicomStoreUserConnection& scu,
-                          const std::string& sopClassUid,
-                          DicomTransferSyntax transferSyntax)
-{
-  std::set<DicomTransferSyntax> accepted;
-
-  scu.LookupTranscoding(accepted, sopClassUid, transferSyntax);
-  if (accepted.empty())
-  {
-    throw OrthancException(ErrorCode_NetworkProtocol,
-                           "The SOP class is not supported by the remote modality");
-  }
-
-  {
-    unsigned int count = 0;
-    for (std::set<DicomTransferSyntax>::const_iterator
-           it = accepted.begin(); it != accepted.end(); ++it)
-    {
-      LOG(INFO) << "available for transcoding " << (count++) << ": " << sopClassUid
-                << " / " << GetTransferSyntaxUid(*it);
-    }
-  }
-  
-  if (accepted.find(transferSyntax) != accepted.end())
-  {
-    printf("**** OK, without transcoding !! [%s]\n", GetTransferSyntaxUid(transferSyntax));
-  }
-  else
-  {
-    // Transcoding - only in Orthanc >= 1.7.0
-
-    const DicomTransferSyntax uncompressed[] = {
-      DicomTransferSyntax_LittleEndianImplicit,  // Default transfer syntax
-      DicomTransferSyntax_LittleEndianExplicit,
-      DicomTransferSyntax_BigEndianExplicit
-    };
-
-    bool found = false;
-    for (size_t i = 0; i < 3; i++)
-    {
-      if (accepted.find(uncompressed[i]) != accepted.end())
-      {
-        printf("**** TRANSCODING to %s\n", GetTransferSyntaxUid(uncompressed[i]));
-        found = true;
-        break;
-      }
-    }
-
-    if (!found)
-    {
-      printf("**** KO KO KO\n");
-    }
-  }
-}
-
-
-TEST(Toto, DISABLED_Store)
-{
-  DicomAssociationParameters params;
-  params.SetLocalApplicationEntityTitle("ORTHANC");
-  params.SetRemoteApplicationEntityTitle("STORESCP");
-  params.SetRemotePort(2000);
-
-  DicomStoreUserConnection assoc(params);
-  assoc.RegisterStorageClass(UID_MRImageStorage, DicomTransferSyntax_JPEGProcess1);
-  assoc.RegisterStorageClass(UID_MRImageStorage, DicomTransferSyntax_JPEGProcess2_4);
-  //assoc.RegisterStorageClass(UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit);
-
-  //assoc.SetUncompressedSyntaxesProposed(false);  // Necessary for transcoding
-  assoc.SetCommonClassesProposed(false);
-  assoc.SetRetiredBigEndianProposed(true);
-  TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit);
-  TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_JPEG2000);
-  TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_JPEG2000);
-}
-
-
-TEST(Toto, DISABLED_Store2)
-{
-  DicomAssociationParameters params;
-  params.SetLocalApplicationEntityTitle("ORTHANC");
-  params.SetRemoteApplicationEntityTitle("STORESCP");
-  params.SetRemotePort(2000);
-
-  DicomStoreUserConnection assoc(params);
-  //assoc.SetCommonClassesProposed(false);
-  assoc.SetRetiredBigEndianProposed(true);
-
-  std::string s;
-  Orthanc::SystemToolbox::ReadFile(s, "/tmp/i/" + std::string(GetTransferSyntaxUid(DicomTransferSyntax_BigEndianExplicit)) +".dcm");
-
-  std::string c, i;
-  assoc.Store(c, i, s.c_str(), s.size());
-  printf("[%s] [%s]\n", c.c_str(), i.c_str());
-}
-
-
-namespace Orthanc
-{
   class IDicomTranscoder : public boost::noncopyable
   {
   public:
@@ -3167,7 +2247,21 @@
       scu.SetRetiredBigEndianProposed(true);
 
       std::string c, i;
-      IDicomTranscoder::Store(c, i, scu, transcoder, source.c_str(), source.size());
+      try
+      {
+        IDicomTranscoder::Store(c, i, scu, transcoder, source.c_str(), source.size());
+      }
+      catch (OrthancException& e)
+      {
+        if (e.GetErrorCode() == ErrorCode_NotImplemented)
+        {
+          LOG(ERROR) << "cannot transcode " << GetTransferSyntaxUid(a);
+        }
+        else
+        {
+          throw e;
+        }
+      }
     }
   }
 }