changeset 3945:0b3256c3ee14 transcoding

simplified IDicomTranscoder
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 19 May 2020 11:24:00 +0200
parents aae045f802f4
children 1f33ed7f82e6
files Core/Compression/HierarchicalZipWriter.h Core/Compression/ZipWriter.cpp Core/Compression/ZipWriter.h Core/DicomNetworking/DicomStoreUserConnection.cpp Core/DicomParsing/DcmtkTranscoder.cpp Core/DicomParsing/DcmtkTranscoder.h Core/DicomParsing/IDicomTranscoder.cpp Core/DicomParsing/IDicomTranscoder.h Core/DicomParsing/MemoryBufferTranscoder.cpp Core/DicomParsing/MemoryBufferTranscoder.h OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp OrthancServer/ServerContext.cpp OrthancServer/ServerContext.h OrthancServer/ServerJobs/ArchiveJob.cpp OrthancServer/ServerJobs/ResourceModificationJob.cpp Plugins/Engine/OrthancPlugins.cpp UnitTestsSources/FromDcmtkTests.cpp
diffstat 17 files changed, 227 insertions(+), 511 deletions(-) [+]
line wrap: on
line diff
--- a/Core/Compression/HierarchicalZipWriter.h	Tue May 19 10:17:06 2020 +0200
+++ b/Core/Compression/HierarchicalZipWriter.h	Tue May 19 11:24:00 2020 +0200
@@ -140,7 +140,7 @@
       return indexer_.GetCurrentDirectoryPath();
     }
 
-    void Write(const char* data, size_t length)
+    void Write(const void* data, size_t length)
     {
       writer_.Write(data, length);
     }
--- a/Core/Compression/ZipWriter.cpp	Tue May 19 10:17:06 2020 +0200
+++ b/Core/Compression/ZipWriter.cpp	Tue May 19 11:24:00 2020 +0200
@@ -227,7 +227,7 @@
   }
 
 
-  void ZipWriter::Write(const char* data, size_t length)
+  void ZipWriter::Write(const void* data, size_t length)
   {
     if (!hasFileInZip_)
     {
@@ -236,17 +236,19 @@
 
     const size_t maxBytesInAStep = std::numeric_limits<int32_t>::max();
 
+    const char* p = reinterpret_cast<const char*>(data);
+    
     while (length > 0)
     {
       int bytes = static_cast<int32_t>(length <= maxBytesInAStep ? length : maxBytesInAStep);
 
-      if (zipWriteInFileInZip(pimpl_->file_, data, bytes))
+      if (zipWriteInFileInZip(pimpl_->file_, p, bytes))
       {
         throw OrthancException(ErrorCode_CannotWriteFile,
                                "Cannot write data to ZIP archive: " + path_);
       }
       
-      data += bytes;
+      p += bytes;
       length -= bytes;
     }
   }
--- a/Core/Compression/ZipWriter.h	Tue May 19 10:17:06 2020 +0200
+++ b/Core/Compression/ZipWriter.h	Tue May 19 11:24:00 2020 +0200
@@ -101,7 +101,7 @@
 
     void OpenFile(const char* path);
 
-    void Write(const char* data, size_t length);
+    void Write(const void* data, size_t length);
 
     void Write(const std::string& data);
   };
--- a/Core/DicomNetworking/DicomStoreUserConnection.cpp	Tue May 19 10:17:06 2020 +0200
+++ b/Core/DicomNetworking/DicomStoreUserConnection.cpp	Tue May 19 11:24:00 2020 +0200
@@ -491,40 +491,34 @@
         uncompressedSyntaxes.insert(DicomTransferSyntax_BigEndianExplicit);
       }
 
-      std::unique_ptr<IDicomTranscoder::TranscodedDicom> transcoded(
-        transcoder.TranscodeToParsed(*dicom, buffer, size, uncompressedSyntaxes, false));
-
-      // WARNING: Below this point, "transcoded->GetDicom()" is possibly
-      // a reference to "*dicom", if the DCMTK transcoder was used
+      IDicomTranscoder::DicomImage source;
+      source.AcquireParsed(dicom.release());
+      source.SetExternalBuffer(buffer, size);
       
-      if (transcoded.get() == NULL ||
-          transcoded->GetDicom().getDataset() == NULL)
+      IDicomTranscoder::DicomImage transcoded;
+      bool hasSopInstanceUidChanged;
+      if (transcoder.Transcode(transcoded, hasSopInstanceUidChanged, source, uncompressedSyntaxes, false))
       {
-        throw OrthancException(
-          ErrorCode_NotImplemented,
-          "Cannot transcode from \"" + std::string(GetTransferSyntaxUid(inputSyntax)) +
-          "\" to an uncompressed syntax for modality: " +
-          GetParameters().GetRemoteModality().GetApplicationEntityTitle());
-      }
-      else if (transcoded->HasSopInstanceUidChanged())
-      {
-        throw OrthancException(ErrorCode_Plugin, "The transcoder has changed the SOP "
-                               "instance UID while transcoding to an uncompressed transfer syntax");
-      }
-      else
-      {
-        DicomTransferSyntax transcodedSyntax;
-
-        // Sanity check
-        if (!FromDcmtkBridge::LookupOrthancTransferSyntax(transcodedSyntax, transcoded->GetDicom()) ||
-            accepted.find(transcodedSyntax) == accepted.end())
+        if (hasSopInstanceUidChanged)
         {
-          throw OrthancException(ErrorCode_InternalError);
+          throw OrthancException(ErrorCode_Plugin, "The transcoder has changed the SOP "
+                                 "instance UID while transcoding to an uncompressed transfer syntax");
         }
         else
         {
-          Store(sopClassUid, sopInstanceUid, transcoded->GetDicom(),
-                hasMoveOriginator, moveOriginatorAET, moveOriginatorID);
+          DicomTransferSyntax transcodedSyntax;
+          
+          // Sanity check
+          if (!FromDcmtkBridge::LookupOrthancTransferSyntax(transcodedSyntax, transcoded.GetParsed()) ||
+              accepted.find(transcodedSyntax) == accepted.end())
+          {
+            throw OrthancException(ErrorCode_InternalError);
+          }
+          else
+          {
+            Store(sopClassUid, sopInstanceUid, transcoded.GetParsed(),
+                  hasMoveOriginator, moveOriginatorAET, moveOriginatorID);
+          }
         }
       }
     }
--- a/Core/DicomParsing/DcmtkTranscoder.cpp	Tue May 19 10:17:06 2020 +0200
+++ b/Core/DicomParsing/DcmtkTranscoder.cpp	Tue May 19 11:24:00 2020 +0200
@@ -328,78 +328,6 @@
   }
 
 
-
-  bool DcmtkTranscoder::TranscodeParsedToBuffer(
-    std::string& target /* out */,
-    bool& hasSopInstanceUidChanged /* out */,
-    DcmFileFormat& dicom /* in, possibly modified */,
-    DicomTransferSyntax targetSyntax,
-    bool allowNewSopInstanceUid)
-  {
-    if (dicom.getDataset() == NULL)
-    {
-      throw OrthancException(ErrorCode_InternalError);
-    }
-
-    std::set<DicomTransferSyntax> tmp;
-    tmp.insert(targetSyntax);
-
-    if (InplaceTranscode(hasSopInstanceUidChanged, dicom, tmp, allowNewSopInstanceUid))
-    {
-      DicomTransferSyntax targetSyntax2;
-      if (FromDcmtkBridge::LookupOrthancTransferSyntax(targetSyntax2, dicom) &&
-          targetSyntax == targetSyntax2 &&
-          dicom.getDataset() != NULL)
-      {
-        FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom.getDataset());
-        return true;
-      }
-      else
-      {
-        throw OrthancException(ErrorCode_InternalError);
-      }      
-    }
-    else
-    {
-      return false;
-    }    
-  }
-
-
-  IDicomTranscoder::TranscodedDicom* DcmtkTranscoder::TranscodeToParsed(
-    DcmFileFormat& dicom /* in, possibly modified */,
-    const void* buffer /* in, same DICOM file as "dicom" */,
-    size_t size,
-    const std::set<DicomTransferSyntax>& allowedSyntaxes,
-    bool allowNewSopInstanceUid)
-  {
-    DicomTransferSyntax sourceSyntax;
-    if (!FromDcmtkBridge::LookupOrthancTransferSyntax(sourceSyntax, dicom))
-    {
-      LOG(ERROR) << "Unsupport transfer syntax for transcoding";
-      return NULL;
-    }
-
-    bool hasSopInstanceUidChanged;
-    
-    if (allowedSyntaxes.find(sourceSyntax) != allowedSyntaxes.end())
-    {
-      // No transcoding is needed
-      return TranscodedDicom::CreateFromExternal(dicom, false /* no change in UID */);
-    }
-    else if (InplaceTranscode(hasSopInstanceUidChanged, dicom,
-                              allowedSyntaxes, allowNewSopInstanceUid))
-    {
-      return TranscodedDicom::CreateFromExternal(dicom, hasSopInstanceUidChanged);
-    }
-    else
-    {
-      // Cannot transcode
-      return NULL;
-    }
-  }
-
-
   bool DcmtkTranscoder::Transcode(DicomImage& target,
                                   bool& hasSopInstanceUidChanged /* out */,
                                   DicomImage& source /* in, "GetParsed()" possibly modified */,
--- a/Core/DicomParsing/DcmtkTranscoder.h	Tue May 19 10:17:06 2020 +0200
+++ b/Core/DicomParsing/DcmtkTranscoder.h	Tue May 19 11:24:00 2020 +0200
@@ -50,6 +50,11 @@
   private:
     unsigned int  lossyQuality_;
     
+    bool InplaceTranscode(bool& hasSopInstanceUidChanged /* out */,
+                          DcmFileFormat& dicom,
+                          const std::set<DicomTransferSyntax>& allowedSyntaxes,
+                          bool allowNewSopInstanceUid);
+    
   public:
     DcmtkTranscoder() :
       lossyQuality_(90)
@@ -63,26 +68,8 @@
       return lossyQuality_;
     }
     
-    bool InplaceTranscode(bool& hasSopInstanceUidChanged /* out */,
-                          DcmFileFormat& dicom,
-                          const std::set<DicomTransferSyntax>& allowedSyntaxes,
-                          bool allowNewSopInstanceUid);
-    
     static bool IsSupported(DicomTransferSyntax syntax);
 
-    virtual bool TranscodeParsedToBuffer(std::string& target /* out */,
-                                         bool& hasSopInstanceUidChanged /* out */,
-                                         DcmFileFormat& dicom /* in, possibly modified */,
-                                         DicomTransferSyntax targetSyntax,
-                                         bool allowNewSopInstanceUid) ORTHANC_OVERRIDE;
-
-    virtual TranscodedDicom* TranscodeToParsed(
-      DcmFileFormat& dicom /* in, possibly modified */,
-      const void* buffer /* in, same DICOM file as "dicom" */,
-      size_t size,
-      const std::set<DicomTransferSyntax>& allowedSyntaxes,
-      bool allowNewSopInstanceUid) ORTHANC_OVERRIDE;
-
     virtual bool Transcode(DicomImage& target,
                            bool& hasSopInstanceUidChanged /* out */,
                            DicomImage& source /* in, "GetParsed()" possibly modified */,
--- a/Core/DicomParsing/IDicomTranscoder.cpp	Tue May 19 10:17:06 2020 +0200
+++ b/Core/DicomParsing/IDicomTranscoder.cpp	Tue May 19 11:24:00 2020 +0200
@@ -44,28 +44,50 @@
 {
   void IDicomTranscoder::DicomImage::Parse()
   {
-    if (parsed_.get() != NULL ||
-        buffer_.get() == NULL)
+    if (parsed_.get() != NULL)
     {
+      // Already parsed
       throw OrthancException(ErrorCode_BadSequenceOfCalls);
     }
-    else
+    else if (buffer_.get() != NULL)
     {
-      parsed_.reset(FromDcmtkBridge::LoadFromMemoryBuffer(
-                      buffer_->empty() ? NULL : buffer_->c_str(), buffer_->size()));
-
+      if (isExternalBuffer_)
+      {
+        throw OrthancException(ErrorCode_InternalError);
+      }
+      else
+      {
+        parsed_.reset(FromDcmtkBridge::LoadFromMemoryBuffer(
+                        buffer_->empty() ? NULL : buffer_->c_str(), buffer_->size()));
+        
+        if (parsed_.get() == NULL)
+        {
+          throw OrthancException(ErrorCode_BadFileFormat);
+        }      
+      }
+    }
+    else if (isExternalBuffer_)
+    {
+      parsed_.reset(FromDcmtkBridge::LoadFromMemoryBuffer(externalBuffer_, externalSize_));
+      
       if (parsed_.get() == NULL)
       {
         throw OrthancException(ErrorCode_BadFileFormat);
       }      
     }
+    else
+    {
+      // No buffer is available
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
   }
   
   
   void IDicomTranscoder::DicomImage::Serialize()
   {
     if (parsed_.get() == NULL ||
-        buffer_.get() != NULL)
+        buffer_.get() != NULL ||
+        isExternalBuffer_)
     {
       throw OrthancException(ErrorCode_BadSequenceOfCalls);
     }
@@ -81,10 +103,17 @@
   }
 
   
+  IDicomTranscoder::DicomImage::DicomImage() :
+    isExternalBuffer_(false)
+  {
+  }
+
+
   void IDicomTranscoder::DicomImage::Clear()
   {
     parsed_.reset(NULL);
     buffer_.reset(NULL);
+    isExternalBuffer_ = false;
   }
 
   
@@ -100,14 +129,14 @@
     {
       throw OrthancException(ErrorCode_NullPointer);
     }
+    else if (parsed->getDataset() == NULL)
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
     else if (parsed_.get() != NULL)
     {
       throw OrthancException(ErrorCode_BadSequenceOfCalls);
     }
-    else if (parsed->getDataset() == NULL)
-    {
-      throw OrthancException(ErrorCode_InternalError);
-    }
     else
     {
       parsed_.reset(parsed);
@@ -123,7 +152,8 @@
 
   void IDicomTranscoder::DicomImage::AcquireBuffer(std::string& buffer /* will be swapped */)
   {
-    if (buffer_.get() != NULL)
+    if (buffer_.get() != NULL ||
+        isExternalBuffer_)
     {
       throw OrthancException(ErrorCode_BadSequenceOfCalls);
     }
@@ -137,36 +167,69 @@
 
   void IDicomTranscoder::DicomImage::AcquireBuffer(DicomImage& other)
   {
-    if (buffer_.get() != NULL)
+    if (buffer_.get() != NULL ||
+        isExternalBuffer_)
     {
       throw OrthancException(ErrorCode_BadSequenceOfCalls);
     }
-    else if (other.buffer_.get() == NULL)
+    else if (other.isExternalBuffer_)
     {
-      buffer_.reset(NULL);
+      assert(other.buffer_.get() == NULL);
+      isExternalBuffer_ = true;
+      externalBuffer_ = other.externalBuffer_;
+      externalSize_ = other.externalSize_;
+    }
+    else if (other.buffer_.get() != NULL)
+    {
+      buffer_.reset(other.buffer_.release());
     }
     else
     {
-      buffer_.reset(other.buffer_.release());
+      buffer_.reset(NULL);
     }    
   }
 
   
+  void IDicomTranscoder::DicomImage::SetExternalBuffer(const void* buffer,
+                                                       size_t size)
+  {
+    if (buffer_.get() != NULL ||
+        isExternalBuffer_)
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      isExternalBuffer_ = true;
+      externalBuffer_ = buffer;
+      externalSize_ = size;
+    }
+  }
+
+
+  void IDicomTranscoder::DicomImage::SetExternalBuffer(const std::string& buffer)
+  {
+    SetExternalBuffer(buffer.empty() ? NULL : buffer.c_str(), buffer.size());
+  }
+
+
   DcmFileFormat& IDicomTranscoder::DicomImage::GetParsed()
   {
     if (parsed_.get() != NULL)
     {
       return *parsed_;
     }
-    else if (buffer_.get() != NULL)
+    else if (buffer_.get() != NULL ||
+             isExternalBuffer_)
     {
       Parse();
       return *parsed_;
     }
     else
     {
-      throw OrthancException(ErrorCode_BadSequenceOfCalls,
-                             "AcquireParsed() or AcquireBuffer() should have been called");
+      throw OrthancException(
+        ErrorCode_BadSequenceOfCalls,
+        "AcquireParsed(), AcquireBuffer() or SetExternalBuffer() should have been called");
     }
   }
   
@@ -178,7 +241,8 @@
       buffer_.reset(NULL);
       return parsed_.release();
     }
-    else if (buffer_.get() != NULL)
+    else if (buffer_.get() != NULL ||
+             isExternalBuffer_)
     {
       Parse();
       buffer_.reset(NULL);
@@ -186,102 +250,55 @@
     }
     else
     {
-      throw OrthancException(ErrorCode_BadSequenceOfCalls,
-                             "AcquireParsed() or AcquireBuffer() should have been called");
+      throw OrthancException(
+        ErrorCode_BadSequenceOfCalls,
+        "AcquireParsed(), AcquireBuffer() or SetExternalBuffer() should have been called");
     }
   }
 
+
+  ParsedDicomFile* IDicomTranscoder::DicomImage::ReleaseAsParsedDicomFile()
+  {
+    return ParsedDicomFile::AcquireDcmtkObject(ReleaseParsed());
+  }
+
   
   const void* IDicomTranscoder::DicomImage::GetBufferData()
   {
-    if (buffer_.get() == NULL)
+    if (isExternalBuffer_)
     {
-      Serialize();
+      assert(buffer_.get() == NULL);
+      return externalBuffer_;
     }
+    else
+    {    
+      if (buffer_.get() == NULL)
+      {
+        Serialize();
+      }
 
-    assert(buffer_.get() != NULL);
-    return buffer_->empty() ? NULL : buffer_->c_str();
+      assert(buffer_.get() != NULL);
+      return buffer_->empty() ? NULL : buffer_->c_str();
+    }
   }
 
   
   size_t IDicomTranscoder::DicomImage::GetBufferSize()
   {
-    if (buffer_.get() == NULL)
+    if (isExternalBuffer_)
     {
-      Serialize();
-    }
-
-    assert(buffer_.get() != NULL);
-    return buffer_->size();
-  }
-
-
-  IDicomTranscoder::TranscodedDicom::TranscodedDicom(bool hasSopInstanceUidChanged) :
-    external_(NULL),
-    hasSopInstanceUidChanged_(hasSopInstanceUidChanged)
-  {
-  }
-  
-
-  IDicomTranscoder::TranscodedDicom*
-  IDicomTranscoder::TranscodedDicom::CreateFromExternal(DcmFileFormat& dicom,
-                                                        bool hasSopInstanceUidChanged)
-  {
-    std::unique_ptr<TranscodedDicom> transcoded(new TranscodedDicom(hasSopInstanceUidChanged));
-    transcoded->external_ = &dicom;
-    return transcoded.release();
-  }        
-
-  
-  IDicomTranscoder::TranscodedDicom*
-  IDicomTranscoder::TranscodedDicom::CreateFromInternal(DcmFileFormat* dicom,
-                                                        bool hasSopInstanceUidChanged)
-  {
-    if (dicom == NULL)
-    {
-      throw OrthancException(ErrorCode_NullPointer);
+      assert(buffer_.get() == NULL);
+      return externalSize_;
     }
     else
-    {
-      std::unique_ptr<TranscodedDicom> transcoded(new TranscodedDicom(hasSopInstanceUidChanged));
-      transcoded->internal_.reset(dicom);
-      return transcoded.release();
+    {    
+      if (buffer_.get() == NULL)
+      {
+        Serialize();
+      }
+
+      assert(buffer_.get() != NULL);
+      return buffer_->size();
     }
   }
-
-  
-  DcmFileFormat& IDicomTranscoder::TranscodedDicom::GetDicom() const
-  {
-    if (internal_.get() != NULL)
-    {
-      return *internal_.get();
-    }
-    else if (external_ != NULL)
-    {
-      return *external_;
-    }
-    else
-    {
-      // Probably results from a call to "ReleaseDicom()"
-      throw OrthancException(ErrorCode_BadSequenceOfCalls);
-    }
-  }
-
-
-  DcmFileFormat* IDicomTranscoder::TranscodedDicom::ReleaseDicom()
-  {
-    if (internal_.get() != NULL)
-    {
-      return internal_.release();
-    }
-    else if (external_ != NULL)
-    {
-      return new DcmFileFormat(*external_);  // Clone
-    }
-    else
-    {
-      // Probably results from a call to "ReleaseDicom()"
-      throw OrthancException(ErrorCode_BadSequenceOfCalls);      
-    }        
-  }
 }
--- a/Core/DicomParsing/IDicomTranscoder.h	Tue May 19 10:17:06 2020 +0200
+++ b/Core/DicomParsing/IDicomTranscoder.h	Tue May 19 11:24:00 2020 +0200
@@ -58,6 +58,9 @@
     private:
       std::unique_ptr<DcmFileFormat>  parsed_;
       std::unique_ptr<std::string>    buffer_;
+      bool                            isExternalBuffer_;
+      const void*                     externalBuffer_;
+      size_t                          externalSize_;
 
       void Parse();
 
@@ -66,6 +69,8 @@
       DcmFileFormat* ReleaseParsed();
 
     public:
+      DicomImage();
+      
       void Clear();
       
       // Calling this method will invalidate the "ParsedDicomFile" object
@@ -79,8 +84,15 @@
 
       void AcquireBuffer(DicomImage& other);
 
+      void SetExternalBuffer(const void* buffer,
+                             size_t size);
+
+      void SetExternalBuffer(const std::string& buffer);
+
       DcmFileFormat& GetParsed();
 
+      ParsedDicomFile* ReleaseAsParsedDicomFile();
+
       const void* GetBufferData();
 
       size_t GetBufferSize();
@@ -97,54 +109,5 @@
                            DicomImage& source /* in, "GetParsed()" possibly modified */,
                            const std::set<DicomTransferSyntax>& allowedSyntaxes,
                            bool allowNewSopInstanceUid) = 0;
-                           
-
-
-    virtual bool TranscodeParsedToBuffer(std::string& target /* out */,
-                                         bool& hasSopInstanceUidChanged /* out */,
-                                         DcmFileFormat& dicom /* in, possibly modified */,
-                                         DicomTransferSyntax targetSyntax,
-                                         bool allowNewSopInstanceUid) = 0;
-
-
-    class TranscodedDicom : public boost::noncopyable
-    {
-    private:
-      std::unique_ptr<DcmFileFormat>  internal_;
-      DcmFileFormat*                  external_;
-      bool                            hasSopInstanceUidChanged_;
-
-      TranscodedDicom(bool hasSopInstanceUidChanged);
-
-    public:
-      static TranscodedDicom* CreateFromExternal(DcmFileFormat& dicom,
-                                                 bool hasSopInstanceUidChanged);
-        
-      static TranscodedDicom* CreateFromInternal(DcmFileFormat* dicom,
-                                                 bool hasSopInstanceUidChanged);
-
-      // TODO - Is this information used somewhere?
-      bool HasSopInstanceUidChanged() const
-      {
-        return hasSopInstanceUidChanged_;
-      }
-      
-      DcmFileFormat& GetDicom() const;
-
-      DcmFileFormat* ReleaseDicom();
-    };
-    
-    /**
-     * Transcoding flavor that creates a new parsed DICOM file. A
-     * "std::set<>" is used to give the possible plugin the
-     * possibility to do a single parsing for all the possible
-     * transfer syntaxes. This flavor is used by C-STORE.
-     **/
-    virtual TranscodedDicom* TranscodeToParsed(
-      DcmFileFormat& dicom /* in, possibly modified */,
-      const void* buffer /* in, same DICOM file as "dicom" */,
-      size_t size,
-      const std::set<DicomTransferSyntax>& allowedSyntaxes,
-      bool allowNewSopInstanceUid) = 0;
   };
 }
--- a/Core/DicomParsing/MemoryBufferTranscoder.cpp	Tue May 19 10:17:06 2020 +0200
+++ b/Core/DicomParsing/MemoryBufferTranscoder.cpp	Tue May 19 11:24:00 2020 +0200
@@ -66,65 +66,6 @@
   }
     
 
-  bool MemoryBufferTranscoder::TranscodeParsedToBuffer(
-    std::string& target /* out */,
-    bool& hasSopInstanceUidChanged /* out */,
-    DcmFileFormat& dicom /* in, possibly modified */,
-    DicomTransferSyntax targetSyntax,
-    bool allowNewSopInstanceUid)
-  {
-    if (dicom.getDataset() == NULL)
-    {
-      throw OrthancException(ErrorCode_InternalError);
-    }
-
-    std::string source;
-    FromDcmtkBridge::SaveToMemoryBuffer(source, *dicom.getDataset());
-
-    const void* data = source.empty() ? NULL : source.c_str();
-
-    std::set<DicomTransferSyntax> allowedSyntaxes;
-    allowedSyntaxes.insert(targetSyntax);
-
-    if (TranscodeBuffer(target, hasSopInstanceUidChanged,
-                        data, source.size(), allowedSyntaxes, allowNewSopInstanceUid))
-    {
-      CheckTargetSyntax(target, allowedSyntaxes);
-      return true;
-    }
-    else
-    {
-      return false;
-    }    
-  }
-  
-
-  IDicomTranscoder::TranscodedDicom* MemoryBufferTranscoder::TranscodeToParsed(
-    DcmFileFormat& dicom /* in, possibly modified */,
-    const void* buffer /* in, same DICOM file as "dicom" */,
-    size_t size,
-    const std::set<DicomTransferSyntax>& allowedSyntaxes,
-    bool allowNewSopInstanceUid)
-  {
-    bool hasSopInstanceUidChanged;
-    
-    std::string target;
-    if (TranscodeBuffer(target, hasSopInstanceUidChanged,
-                        buffer, size, allowedSyntaxes, allowNewSopInstanceUid))
-    {
-      CheckTargetSyntax(target, allowedSyntaxes);
-      
-      const void* data = target.empty() ? NULL : target.c_str();
-      return IDicomTranscoder::TranscodedDicom::CreateFromInternal(
-        FromDcmtkBridge::LoadFromMemoryBuffer(data, target.size()), hasSopInstanceUidChanged);
-    }
-    else
-    {
-      return NULL;
-    }
-  }
-
-
   bool MemoryBufferTranscoder::Transcode(DicomImage& target,
                                          bool& hasSopInstanceUidChanged /* out */,
                                          DicomImage& source,
--- a/Core/DicomParsing/MemoryBufferTranscoder.h	Tue May 19 10:17:06 2020 +0200
+++ b/Core/DicomParsing/MemoryBufferTranscoder.h	Tue May 19 11:24:00 2020 +0200
@@ -49,19 +49,6 @@
                                  bool allowNewSopInstanceUid) = 0;
     
   public:
-    virtual bool TranscodeParsedToBuffer(std::string& target /* out */,
-                                         bool& hasSopInstanceUidChanged /* out */,
-                                         DcmFileFormat& dicom /* in, possibly modified */,
-                                         DicomTransferSyntax targetSyntax,
-                                         bool allowNewSopInstanceUid) ORTHANC_OVERRIDE;
-
-    virtual TranscodedDicom* TranscodeToParsed(
-      DcmFileFormat& dicom /* in, possibly modified */,
-      const void* buffer /* in, same DICOM file as "dicom" */,
-      size_t size,
-      const std::set<DicomTransferSyntax>& allowedSyntaxes,
-      bool allowNewSopInstanceUid) ORTHANC_OVERRIDE;
-
     virtual bool Transcode(DicomImage& target /* out */,
                            bool& hasSopInstanceUidChanged /* out */,
                            DicomImage& source,
--- a/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Tue May 19 10:17:06 2020 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Tue May 19 11:24:00 2020 +0200
@@ -130,13 +130,19 @@
 
     if (transcode)
     {
-      std::string transcoded;
+      IDicomTranscoder::DicomImage source;
+      source.AcquireParsed(*modified);  // "modified" is invalid below this point
+      
+      IDicomTranscoder::DicomImage transcoded;
       bool hasSopInstanceUidChanged;
 
-      if (context.TranscodeParsedToBuffer(transcoded, hasSopInstanceUidChanged,
-                                          modified->GetDcmtkObject(), targetSyntax, true))
+      std::set<DicomTransferSyntax> s;
+      s.insert(targetSyntax);
+      
+      if (context.Transcode(transcoded, hasSopInstanceUidChanged, source, s, true))
       {      
-        call.GetOutput().AnswerBuffer(transcoded, MimeType_Dicom);
+        call.GetOutput().AnswerBuffer(transcoded.GetBufferData(),
+                                      transcoded.GetBufferSize(), MimeType_Dicom);
       }
       else
       {
--- a/OrthancServer/ServerContext.cpp	Tue May 19 10:17:06 2020 +0200
+++ b/OrthancServer/ServerContext.cpp	Tue May 19 11:24:00 2020 +0200
@@ -551,7 +551,7 @@
     else
     {
       // Automated transcoding of incoming DICOM files
-      
+
       DicomTransferSyntax sourceSyntax;
       if (!FromDcmtkBridge::LookupOrthancTransferSyntax(
             sourceSyntax, dicom.GetParsedDicomFile().GetDcmtkObject()) ||
@@ -565,21 +565,16 @@
         std::set<DicomTransferSyntax> syntaxes;
         syntaxes.insert(ingestTransferSyntax_);
 
-        std::unique_ptr<IDicomTranscoder::TranscodedDicom> transcoded(
-          TranscodeToParsed(dicom.GetParsedDicomFile().GetDcmtkObject(),
-                            dicom.GetBufferData(), dicom.GetBufferSize(),
-                            syntaxes, true /* allow new SOP instance UID */));
-        
-        if (transcoded.get() == NULL)
+        IDicomTranscoder::DicomImage source;
+        source.SetExternalBuffer(dicom.GetBufferData(), dicom.GetBufferSize());
+
+        IDicomTranscoder::DicomImage transcoded;
+        bool hasSopInstanceUidChanged;
+        if (Transcode(transcoded, hasSopInstanceUidChanged,
+                      source, syntaxes, true /* allow new SOP instance UID */))
         {
-          // Cannot transcode => store the original file
-          return StoreAfterTranscoding(resultPublicId, dicom, mode);
-        }
-        else
-        {
-          std::unique_ptr<ParsedDicomFile> tmp(
-            ParsedDicomFile::AcquireDcmtkObject(transcoded->ReleaseDicom()));
-      
+          std::unique_ptr<ParsedDicomFile> tmp(transcoded.ReleaseAsParsedDicomFile());
+
           DicomInstanceToStore toStore;
           toStore.SetParsedDicomFile(*tmp);
           toStore.SetOrigin(dicom.GetOrigin());
@@ -589,6 +584,11 @@
 
           return ok;
         }
+        else
+        {
+          // Cannot transcode => store the original file
+          return StoreAfterTranscoding(resultPublicId, dicom, mode);
+        }
       }
     }
   }
@@ -1322,99 +1322,6 @@
   }
 
 
-  bool ServerContext::TranscodeParsedToBuffer(std::string& target /* out */,
-                                              bool& hasSopInstanceUidChanged /* out */,
-                                              DcmFileFormat& dicom /* in, possibly modified */,
-                                              DicomTransferSyntax targetSyntax,
-                                              bool allowNewSopInstanceUid)
-  {
-    if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_Before)
-    {
-      if (dcmtkTranscoder_->TranscodeParsedToBuffer(target, hasSopInstanceUidChanged, dicom,
-                                                    targetSyntax, allowNewSopInstanceUid))
-      {
-        return true;
-      }
-    }
-    
-#if ORTHANC_ENABLE_PLUGINS == 1
-    if (HasPlugins() &&
-        GetPlugins().HasCustomTranscoder())
-    {
-      if (GetPlugins().TranscodeParsedToBuffer(target, hasSopInstanceUidChanged, dicom,
-                                               targetSyntax, allowNewSopInstanceUid))
-      {
-        return true;
-      }
-      else if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_After)
-      {
-        LOG(INFO) << "The installed transcoding plugins cannot handle an image, "
-                  << "fallback to the built-in DCMTK transcoder";
-      }
-    }
-#endif
-    
-    if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_After)
-    {
-      return dcmtkTranscoder_->TranscodeParsedToBuffer(target, hasSopInstanceUidChanged, dicom,
-                                                       targetSyntax, allowNewSopInstanceUid);
-    }
-    else
-    {
-      return false;
-    }
-  }
-      
-
-  IDicomTranscoder::TranscodedDicom*
-  ServerContext::TranscodeToParsed(DcmFileFormat& dicom,
-                                   const void* buffer,
-                                   size_t size,
-                                   const std::set<DicomTransferSyntax>& allowedSyntaxes,
-                                   bool allowNewSopInstanceUid)
-  {
-    if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_Before)
-    {
-      std::unique_ptr<IDicomTranscoder::TranscodedDicom> transcoded(
-        dcmtkTranscoder_->TranscodeToParsed(dicom, buffer, size, allowedSyntaxes,
-                                            allowNewSopInstanceUid));
-      if (transcoded.get() != NULL)
-      {
-        return transcoded.release();
-      }
-    }
-
-#if ORTHANC_ENABLE_PLUGINS == 1
-    if (HasPlugins() &&
-        GetPlugins().HasCustomTranscoder())
-    {
-      std::unique_ptr<IDicomTranscoder::TranscodedDicom> transcoded(
-        GetPlugins().TranscodeToParsed(dicom, buffer, size, allowedSyntaxes, allowNewSopInstanceUid));
-
-      if (transcoded.get() != NULL)
-      {
-        return transcoded.release();
-      }
-      else if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_After)
-      {
-        LOG(INFO) << "The installed transcoding plugins cannot handle an image, "
-                  << "fallback to the built-in DCMTK transcoder";
-      }
-    }
-#endif
-
-    if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_After)
-    {
-      return dcmtkTranscoder_->TranscodeToParsed(
-        dicom, buffer, size, allowedSyntaxes, allowNewSopInstanceUid);
-    }
-    else
-    {
-      return NULL;
-    }
-  }
-
-
   bool ServerContext::Transcode(DicomImage& target,
                                 bool& hasSopInstanceUidChanged /* out */,
                                 DicomImage& source /* in, "GetParsed()" possibly modified */,
--- a/OrthancServer/ServerContext.h	Tue May 19 10:17:06 2020 +0200
+++ b/OrthancServer/ServerContext.h	Tue May 19 11:24:00 2020 +0200
@@ -479,21 +479,6 @@
 
     // This method can be used even if the global option
     // "TranscodeDicomProtocol" is set to "false"
-    virtual bool TranscodeParsedToBuffer(std::string& target /* out */,
-                                         bool& hasSopInstanceUidChanged /* out */,
-                                         DcmFileFormat& dicom /* in, possibly modified */,
-                                         DicomTransferSyntax targetSyntax,
-                                         bool allowNewSopInstanceUid) ORTHANC_OVERRIDE;
-
-    // This method can be used even if the global option
-    // "TranscodeDicomProtocol" is set to "false"
-    virtual IDicomTranscoder::TranscodedDicom* TranscodeToParsed(
-      DcmFileFormat& dicom /* in, possibly modified */,
-      const void* buffer /* in, same DICOM file as "dicom" */,
-      size_t size,
-      const std::set<DicomTransferSyntax>& allowedSyntaxes,
-      bool allowNewSopInstanceUid) ORTHANC_OVERRIDE;
-
     virtual bool Transcode(DicomImage& target,
                            bool& hasSopInstanceUidChanged /* out */,
                            DicomImage& source /* in, "GetParsed()" possibly modified */,
--- a/OrthancServer/ServerJobs/ArchiveJob.cpp	Tue May 19 10:17:06 2020 +0200
+++ b/OrthancServer/ServerJobs/ArchiveJob.cpp	Tue May 19 11:24:00 2020 +0200
@@ -442,27 +442,19 @@
               // New in Orthanc 1.7.0
               std::set<DicomTransferSyntax> syntaxes;
               syntaxes.insert(transferSyntax);
-              
-              parsed.reset(new ParsedDicomFile(content));
-              const char* data = content.empty() ? NULL : content.c_str();
-              
-              std::unique_ptr<IDicomTranscoder::TranscodedDicom> transcodedDicom(
-                context.TranscodeToParsed(parsed->GetDcmtkObject(), data, content.size(),
-                                          syntaxes, true /* allow new SOP instance UID */));
+
+              IDicomTranscoder::DicomImage source, transcoded;
+              source.SetExternalBuffer(content);
 
-              if (transcodedDicom.get() != NULL &&
-                  transcodedDicom->GetDicom().getDataset() != NULL)
+              bool hasSopInstanceChanged;
+              if (context.Transcode(transcoded, hasSopInstanceChanged, source,
+                                    syntaxes, true /* allow new SOP instance UID */))
               {
-                std::string transcoded;
-                FromDcmtkBridge::SaveToMemoryBuffer(
-                  transcoded, *transcodedDicom->GetDicom().getDataset());
-              
-                writer.Write(transcoded);
+                writer.Write(transcoded.GetBufferData(), transcoded.GetBufferSize());
 
                 if (dicomDir != NULL)
                 {
-                  std::unique_ptr<ParsedDicomFile> tmp(
-                    ParsedDicomFile::AcquireDcmtkObject(transcodedDicom->ReleaseDicom()));
+                  std::unique_ptr<ParsedDicomFile> tmp(transcoded.ReleaseAsParsedDicomFile());
                   dicomDir->Add(dicomDirFolder, filename_, *tmp);
                 }
                 
--- a/OrthancServer/ServerJobs/ResourceModificationJob.cpp	Tue May 19 10:17:06 2020 +0200
+++ b/OrthancServer/ServerJobs/ResourceModificationJob.cpp	Tue May 19 11:24:00 2020 +0200
@@ -177,19 +177,20 @@
     {
       std::set<DicomTransferSyntax> syntaxes;
       syntaxes.insert(transferSyntax_);
+
+      IDicomTranscoder::DicomImage source;
+      source.AcquireParsed(*modified);  // "modified" is invalid below this point
       
-      std::string s;
-      modified->SaveToMemoryBuffer(s);  // TODO - AVOID THIS SERIALIZATION IF NO PLUGIN
-
-      std::unique_ptr<IDicomTranscoder::TranscodedDicom> transcoded;
-      transcoded.reset(GetContext().TranscodeToParsed(modified->GetDcmtkObject(), s.empty() ? NULL : s.c_str(), s.size(), syntaxes, true));
-      if (transcoded.get() == NULL)
+      IDicomTranscoder::DicomImage transcoded;
+      bool hasSopInstanceUidChanged;
+      if (GetContext().Transcode(transcoded, hasSopInstanceUidChanged, source, syntaxes, true))
       {
-        LOG(WARNING) << "Cannot transcode instance, keeping original transfer syntax: " << instance;
+        modified.reset(transcoded.ReleaseAsParsedDicomFile());
       }
       else
       {
-        modified.reset(ParsedDicomFile::AcquireDcmtkObject(transcoded->ReleaseDicom()));
+        LOG(WARNING) << "Cannot transcode instance, keeping original transfer syntax: " << instance;
+        modified.reset(source.ReleaseAsParsedDicomFile());
       }
     }
 
--- a/Plugins/Engine/OrthancPlugins.cpp	Tue May 19 10:17:06 2020 +0200
+++ b/Plugins/Engine/OrthancPlugins.cpp	Tue May 19 11:24:00 2020 +0200
@@ -1896,8 +1896,8 @@
     DicomInstanceToStore              instance_;
 
   public:
-    DicomInstanceFromTranscoded(IDicomTranscoder::TranscodedDicom& transcoded) :
-      parsed_(ParsedDicomFile::AcquireDcmtkObject(transcoded.ReleaseDicom()))
+    DicomInstanceFromTranscoded(IDicomTranscoder::DicomImage& transcoded) :
+      parsed_(transcoded.ReleaseAsParsedDicomFile())
     {
       instance_.SetParsedDicomFile(*parsed_);
       instance_.SetOrigin(DicomInstanceOrigin::FromPlugins());
@@ -4369,29 +4369,30 @@
         }
         else
         {
-          ParsedDicomFile dicom(p.buffer, p.size);
-
           std::set<DicomTransferSyntax> syntaxes;
           syntaxes.insert(transferSyntax);
-          
-          std::unique_ptr<IDicomTranscoder::TranscodedDicom> transcoded;
+
+          IDicomTranscoder::DicomImage source;
+          source.SetExternalBuffer(p.buffer, p.size);
+
+          IDicomTranscoder::DicomImage transcoded;
+          bool success, hasSopInstanceChanged;
           
           {
             PImpl::ServerContextLock lock(*pimpl_);
-            transcoded.reset(lock.GetContext().TranscodeToParsed(
-                               dicom.GetDcmtkObject(), p.buffer, p.size,
-                               syntaxes, true /* allow new sop */));
+            success = lock.GetContext().Transcode(transcoded, hasSopInstanceChanged, source,
+                                                  syntaxes, true /* allow new sop */);
           }
 
-          if (transcoded.get() == NULL)
+          if (success)
           {
-            throw OrthancException(ErrorCode_NotImplemented, "Cannot transcode image");
+            *(p.target) = reinterpret_cast<OrthancPluginDicomInstance*>(
+              new DicomInstanceFromTranscoded(transcoded));
+            return true;
           }
           else
           {
-            *(p.target) = reinterpret_cast<OrthancPluginDicomInstance*>(
-              new DicomInstanceFromTranscoded(*transcoded));
-            return true;
+            throw OrthancException(ErrorCode_NotImplemented, "Cannot transcode image");
           }
         }
       }
--- a/UnitTestsSources/FromDcmtkTests.cpp	Tue May 19 10:17:06 2020 +0200
+++ b/UnitTestsSources/FromDcmtkTests.cpp	Tue May 19 11:24:00 2020 +0200
@@ -1987,13 +1987,18 @@
   for (int i = 0; i <= DicomTransferSyntax_XML; i++)
   {
     DicomTransferSyntax a = (DicomTransferSyntax) i;
+    
+    std::set<DicomTransferSyntax> s;
+    s.insert(a);
 
     std::string t;
 
     bool hasSopInstanceUidChanged;
 
-    std::unique_ptr<DcmFileFormat> cloned(dynamic_cast<DcmFileFormat*>(toto->clone()));
-    if (!transcoder.TranscodeParsedToBuffer(t, hasSopInstanceUidChanged, *cloned, a, true))
+    IDicomTranscoder::DicomImage source, target;
+    source.AcquireParsed(dynamic_cast<DcmFileFormat*>(toto->clone()));
+
+    if (!transcoder.Transcode(target, hasSopInstanceUidChanged, source, s, true))
     {
       printf("**************** CANNOT: [%s] => [%s]\n",
              GetTransferSyntaxUid(sourceSyntax), GetTransferSyntaxUid(a));
@@ -2001,7 +2006,7 @@
     else
     {
       DicomTransferSyntax targetSyntax;
-      ASSERT_TRUE(FromDcmtkBridge::LookupOrthancTransferSyntax(targetSyntax, *cloned));
+      ASSERT_TRUE(FromDcmtkBridge::LookupOrthancTransferSyntax(targetSyntax, target.GetParsed()));
       
       ASSERT_EQ(targetSyntax, a);
       bool lossy = (a == DicomTransferSyntax_JPEGProcess1 ||