changeset 3913:6ddad3e0b569 transcoding

transcoding ZIP archive and media
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 08 May 2020 19:15:28 +0200
parents 7610af1532c3
children e14b5a7a0f75
files NEWS OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp OrthancServer/OrthancRestApi/OrthancRestArchive.cpp OrthancServer/ServerContext.cpp OrthancServer/ServerContext.h OrthancServer/ServerJobs/ArchiveJob.cpp OrthancServer/ServerJobs/ArchiveJob.h
diffstat 7 files changed, 180 insertions(+), 53 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Fri May 08 13:43:50 2020 +0200
+++ b/NEWS	Fri May 08 19:15:28 2020 +0200
@@ -12,7 +12,8 @@
   - "/queries/.../answers/../retrieve": "TargetAet" not mandatory anymore
     (defaults to the local AET)
 * Changes:
-  - "/instances/.../modify": New option "Transcode"
+  - "/instances/.../modify", ".../archive", ".../media",
+    "/tools/create-media" and "/tools/create-archive": New option "Transcode"
   - "/ordered-slices": reverted the change introduced in 1.5.8 and go-back 
     to 1.5.7 behaviour.
 
--- a/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Fri May 08 13:43:50 2020 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Fri May 08 19:15:28 2020 +0200
@@ -134,8 +134,9 @@
       DicomTransferSyntax sourceSyntax;
       bool hasSopInstanceUidChanged;
 
-      if (context.Transcode(transcoded, sourceSyntax, hasSopInstanceUidChanged,
-                            *modified, targetSyntax, true))
+      if (context.GetTranscoder().TranscodeParsedToBuffer(
+            transcoded, sourceSyntax, hasSopInstanceUidChanged,
+            modified->GetDcmtkObject(), targetSyntax, true))
       {      
         call.GetOutput().AnswerBuffer(transcoded, MimeType_Dicom);
       }
--- a/OrthancServer/OrthancRestApi/OrthancRestArchive.cpp	Fri May 08 13:43:50 2020 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestArchive.cpp	Fri May 08 19:15:28 2020 +0200
@@ -46,6 +46,7 @@
 {
   static const char* const KEY_RESOURCES = "Resources";
   static const char* const KEY_EXTENDED = "Extended";
+  static const char* const KEY_TRANSCODE = "Transcode";
   
   static void AddResourcesOfInterestFromArray(ArchiveJob& job,
                                               const Json::Value& resources)
@@ -98,11 +99,28 @@
   }
 
 
-  static void GetJobParameters(bool& synchronous,         /* out */
-                               bool& extended,            /* out */
-                               int& priority,             /* out */
-                               const Json::Value& body,   /* in */
-                               const bool defaultExtended /* in */)
+  static DicomTransferSyntax GetTransferSyntax(const std::string& value)
+  {
+    DicomTransferSyntax syntax;
+    if (LookupTransferSyntax(syntax, value))
+    {
+      return syntax;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange,
+                             "Unknown transfer syntax: " + value);
+    }
+  }
+  
+  
+  static void GetJobParameters(bool& synchronous,            /* out */
+                               bool& extended,               /* out */
+                               bool& transcode,              /* out */
+                               DicomTransferSyntax& syntax,  /* out */
+                               int& priority,                /* out */
+                               const Json::Value& body,      /* in */
+                               const bool defaultExtended    /* in */)
   {
     synchronous = OrthancRestApi::IsSynchronousJobRequest
       (true /* synchronous by default */, body);
@@ -118,6 +136,17 @@
     {
       extended = defaultExtended;
     }
+
+    if (body.type() == Json::objectValue &&
+        body.isMember(KEY_TRANSCODE))
+    {
+      transcode = true;
+      syntax = GetTransferSyntax(SerializationToolbox::ReadString(body, KEY_TRANSCODE));
+    }
+    else
+    {
+      transcode = false;
+    }
   }
 
 
@@ -175,12 +204,20 @@
     Json::Value body;
     if (call.ParseJsonRequest(body))
     {
-      bool synchronous, extended;
+      bool synchronous, extended, transcode;
+      DicomTransferSyntax transferSyntax;
       int priority;
-      GetJobParameters(synchronous, extended, priority, body, DEFAULT_IS_EXTENDED);
+      GetJobParameters(synchronous, extended, transcode, transferSyntax,
+                       priority, body, DEFAULT_IS_EXTENDED);
       
       std::unique_ptr<ArchiveJob> job(new ArchiveJob(context, IS_MEDIA, extended));
       AddResourcesOfInterest(*job, body);
+
+      if (transcode)
+      {
+        job->SetTranscode(transferSyntax);
+      }
+      
       SubmitJob(call.GetOutput(), context, job, priority, synchronous, "Archive.zip");
     }
     else
@@ -208,10 +245,16 @@
     {
       extended = false;
     }
-    
+
     std::unique_ptr<ArchiveJob> job(new ArchiveJob(context, IS_MEDIA, extended));
     job->AddResource(id);
 
+    static const char* const TRANSCODE = "transcode";
+    if (call.HasArgument(TRANSCODE))
+    {
+      job->SetTranscode(GetTransferSyntax(call.GetArgument(TRANSCODE, "")));
+    }
+
     SubmitJob(call.GetOutput(), context, job, 0 /* priority */,
               true /* synchronous */, id + ".zip");
   }
@@ -228,12 +271,20 @@
     Json::Value body;
     if (call.ParseJsonRequest(body))
     {
-      bool synchronous, extended;
+      bool synchronous, extended, transcode;
+      DicomTransferSyntax transferSyntax;
       int priority;
-      GetJobParameters(synchronous, extended, priority, body, DEFAULT_IS_EXTENDED);
+      GetJobParameters(synchronous, extended, transcode, transferSyntax,
+                       priority, body, DEFAULT_IS_EXTENDED);
       
       std::unique_ptr<ArchiveJob> job(new ArchiveJob(context, IS_MEDIA, extended));
       job->AddResource(id);
+
+      if (transcode)
+      {
+        job->SetTranscode(transferSyntax);
+      }
+
       SubmitJob(call.GetOutput(), context, job, priority, synchronous, id + ".zip");
     }
     else
--- a/OrthancServer/ServerContext.cpp	Fri May 08 13:43:50 2020 +0200
+++ b/OrthancServer/ServerContext.cpp	Fri May 08 19:15:28 2020 +0200
@@ -506,7 +506,8 @@
                                    DicomInstanceToStore& dicom,
                                    StoreInstanceMode mode)
   {
-    const DicomTransferSyntax option = DicomTransferSyntax_JPEGProcess1;
+    //const DicomTransferSyntax option = DicomTransferSyntax_JPEGProcess1;
+    const DicomTransferSyntax option = DicomTransferSyntax_LittleEndianExplicit;
     
     if (1)
     {
@@ -1214,17 +1215,4 @@
                            hasMoveOriginator, moveOriginatorAet, moveOriginatorId);
     }
   }
-
-
-  bool ServerContext::Transcode(std::string& target /* out */,
-                                DicomTransferSyntax& sourceSyntax /* out */,
-                                bool& hasSopInstanceUidChanged /* out */,
-                                ParsedDicomFile& dicom, // Possibly modified
-                                DicomTransferSyntax targetSyntax,
-                                bool allowNewSopInstanceUid)
-  {
-    return GetTranscoder().TranscodeParsedToBuffer(
-      target, sourceSyntax, hasSopInstanceUidChanged,
-      dicom.GetDcmtkObject(), targetSyntax, allowNewSopInstanceUid);
-  }
 }
--- a/OrthancServer/ServerContext.h	Fri May 08 13:43:50 2020 +0200
+++ b/OrthancServer/ServerContext.h	Fri May 08 19:15:28 2020 +0200
@@ -229,8 +229,6 @@
     bool transcodeDicomProtocol_;
     std::unique_ptr<IDicomTranscoder>  dcmtkTranscoder_;
 
-    IDicomTranscoder& GetTranscoder();
-    
     StoreStatus StoreAfterTranscoding(std::string& resultPublicId,
                                       DicomInstanceToStore& dicom,
                                       StoreInstanceMode mode);
@@ -469,13 +467,8 @@
                               const std::string& moveOriginatorAet,
                               uint16_t moveOriginatorId);
 
-    // This method can be used even if the global option
+    // This accessor can be used even if the global option
     // "TranscodeDicomProtocol" is set to "false"
-    bool Transcode(std::string& target /* out */,
-                   DicomTransferSyntax& sourceSyntax /* out */,
-                   bool& hasSopInstanceUidChanged /* out */,
-                   ParsedDicomFile& dicom, // Possibly modified
-                   DicomTransferSyntax targetSyntax,
-                   bool allowNewSopInstanceUid);
+    IDicomTranscoder& GetTranscoder();
   };
 }
--- a/OrthancServer/ServerJobs/ArchiveJob.cpp	Fri May 08 13:43:50 2020 +0200
+++ b/OrthancServer/ServerJobs/ArchiveJob.cpp	Fri May 08 19:15:28 2020 +0200
@@ -37,6 +37,7 @@
 #include "../../Core/Cache/SharedArchive.h"
 #include "../../Core/Compression/HierarchicalZipWriter.h"
 #include "../../Core/DicomParsing/DicomDirWriter.h"
+#include "../../Core/DicomParsing/FromDcmtkBridge.h"
 #include "../../Core/Logging.h"
 #include "../../Core/OrthancException.h"
 #include "../OrthancConfiguration.h"
@@ -55,6 +56,7 @@
 static const char* const KEY_DESCRIPTION = "Description";
 static const char* const KEY_INSTANCES_COUNT = "InstancesCount";
 static const char* const KEY_UNCOMPRESSED_SIZE_MB = "UncompressedSizeMB";
+static const char* const KEY_TRANSCODE = "Transcode";
 
 
 namespace Orthanc
@@ -399,7 +401,9 @@
       void Apply(HierarchicalZipWriter& writer,
                  ServerContext& context,
                  DicomDirWriter* dicomDir,
-                 const std::string& dicomDirFolder) const
+                 const std::string& dicomDirFolder,
+                 bool transcode,
+                 DicomTransferSyntax transferSyntax) const
       {
         switch (type_)
         {
@@ -426,14 +430,65 @@
             }
 
             //boost::this_thread::sleep(boost::posix_time::milliseconds(300));
-            
+
             writer.OpenFile(filename_.c_str());
-            writer.Write(content);
+
+            bool transcodeSuccess = false;
+
+            std::unique_ptr<ParsedDicomFile> parsed;
+            
+            if (transcode)
+            {
+              // 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.GetTranscoder().TranscodeToParsed(
+                  parsed->GetDcmtkObject(), data, content.size(),
+                  syntaxes, true /* allow new SOP instance UID */));
+
+              if (transcodedDicom.get() != NULL &&
+                  transcodedDicom->GetDicom().getDataset() != NULL)
+              {
+                std::string transcoded;
+                FromDcmtkBridge::SaveToMemoryBuffer(
+                  transcoded, *transcodedDicom->GetDicom().getDataset());
+              
+                writer.Write(transcoded);
 
-            if (dicomDir != NULL)
+                if (dicomDir != NULL)
+                {
+                  std::unique_ptr<ParsedDicomFile> tmp(
+                    ParsedDicomFile::AcquireDcmtkObject(transcodedDicom->ReleaseDicom()));
+                  dicomDir->Add(dicomDirFolder, filename_, *tmp);
+                }
+                
+                transcodeSuccess = true;
+              }
+              else
+              {
+                LOG(INFO) << "Cannot transcode instance " << instanceId_
+                          << " to transfer syntax: " << GetTransferSyntaxUid(transferSyntax);
+              }
+            }
+
+            if (!transcodeSuccess)
             {
-              ParsedDicomFile parsed(content);
-              dicomDir->Add(dicomDirFolder, filename_, parsed);
+              writer.Write(content);
+
+              if (dicomDir != NULL)
+              {
+                if (parsed.get() == NULL)
+                {
+                  parsed.reset(new ParsedDicomFile(content));
+                }
+
+                dicomDir->Add(dicomDirFolder, filename_, *parsed);
+              }
             }
               
             break;
@@ -454,14 +509,16 @@
                        ServerContext& context,
                        size_t index,
                        DicomDirWriter* dicomDir,
-                       const std::string& dicomDirFolder) const
+                       const std::string& dicomDirFolder,
+                       bool transcode,
+                       DicomTransferSyntax transferSyntax) const
     {
       if (index >= commands_.size())
       {
         throw OrthancException(ErrorCode_ParameterOutOfRange);
       }
 
-      commands_[index]->Apply(writer, context, dicomDir, dicomDirFolder);
+      commands_[index]->Apply(writer, context, dicomDir, dicomDirFolder, transcode, transferSyntax);
     }
       
   public:
@@ -496,20 +553,26 @@
       return uncompressedSize_;
     }
 
+    // "media" flavor (with DICOMDIR)
     void Apply(HierarchicalZipWriter& writer,
                ServerContext& context,
                size_t index,
                DicomDirWriter& dicomDir,
-               const std::string& dicomDirFolder) const
+               const std::string& dicomDirFolder,
+               bool transcode,
+               DicomTransferSyntax transferSyntax) const
     {
-      ApplyInternal(writer, context, index, &dicomDir, dicomDirFolder);
+      ApplyInternal(writer, context, index, &dicomDir, dicomDirFolder, transcode, transferSyntax);
     }
 
+    // "archive" flavor (without DICOMDIR)
     void Apply(HierarchicalZipWriter& writer,
                ServerContext& context,
-               size_t index) const
+               size_t index,
+               bool transcode,
+               DicomTransferSyntax transferSyntax) const
     {
-      ApplyInternal(writer, context, index, NULL, "");
+      ApplyInternal(writer, context, index, NULL, "", transcode, transferSyntax);
     }
       
     void AddOpenDirectory(const std::string& filename)
@@ -740,7 +803,9 @@
       return commands_.GetSize() + 1;
     }
 
-    void RunStep(size_t index)
+    void RunStep(size_t index,
+                 bool transcode,
+                 DicomTransferSyntax transferSyntax)
     {
       if (index > commands_.GetSize())
       {
@@ -764,12 +829,13 @@
         if (isMedia_)
         {
           assert(dicomDir_.get() != NULL);
-          commands_.Apply(*zip_, context_, index, *dicomDir_, MEDIA_IMAGES_FOLDER);
+          commands_.Apply(*zip_, context_, index, *dicomDir_,
+                          MEDIA_IMAGES_FOLDER, transcode, transferSyntax);
         }
         else
         {
           assert(dicomDir_.get() == NULL);
-          commands_.Apply(*zip_, context_, index);
+          commands_.Apply(*zip_, context_, index, transcode, transferSyntax);
         }
       }
     }
@@ -795,7 +861,9 @@
     enableExtendedSopClass_(enableExtendedSopClass),
     currentStep_(0),
     instancesCount_(0),
-    uncompressedSize_(0)
+    uncompressedSize_(0),
+    transcode_(false),
+    transferSyntax_(DicomTransferSyntax_LittleEndianImplicit)
   {
   }
 
@@ -854,6 +922,20 @@
     }
   }
 
+
+  void ArchiveJob::SetTranscode(DicomTransferSyntax transferSyntax)
+  {
+    if (writer_.get() != NULL)   // Already started
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      transcode_ = true;
+      transferSyntax_ = transferSyntax;
+    }
+  }
+
   
   void ArchiveJob::Reset()
   {
@@ -954,7 +1036,7 @@
     }
     else
     {
-      writer_->RunStep(currentStep_);
+      writer_->RunStep(currentStep_, transcode_, transferSyntax_);
 
       currentStep_ ++;
 
@@ -1006,6 +1088,11 @@
     value[KEY_INSTANCES_COUNT] = instancesCount_;
     value[KEY_UNCOMPRESSED_SIZE_MB] =
       static_cast<unsigned int>(uncompressedSize_ / MEGA_BYTES);
+
+    if (transcode_)
+    {
+      value[KEY_TRANSCODE] = GetTransferSyntaxUid(transferSyntax_);
+    }
   }
 
 
--- a/OrthancServer/ServerJobs/ArchiveJob.h	Fri May 08 13:43:50 2020 +0200
+++ b/OrthancServer/ServerJobs/ArchiveJob.h	Fri May 08 19:15:28 2020 +0200
@@ -69,6 +69,10 @@
     uint64_t                              uncompressedSize_;
     std::string                           mediaArchiveId_;
 
+    // New in Orthanc 1.7.0
+    bool                 transcode_;
+    DicomTransferSyntax  transferSyntax_;
+    
     void FinalizeTarget();
     
   public:
@@ -89,6 +93,8 @@
 
     void AddResource(const std::string& publicId);
 
+    void SetTranscode(DicomTransferSyntax transferSyntax);
+
     virtual void Reset() ORTHANC_OVERRIDE;
 
     virtual void Start() ORTHANC_OVERRIDE;