diff Plugins/Samples/GdcmDecoder/Plugin.cpp @ 3930:b99acc213937 transcoding

transcoder plugins and GDCM transcoding are working
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 14 May 2020 19:20:40 +0200
parents 7e33516965f8
children f67b48833a4f
line wrap: on
line diff
--- a/Plugins/Samples/GdcmDecoder/Plugin.cpp	Thu May 14 14:40:13 2020 +0200
+++ b/Plugins/Samples/GdcmDecoder/Plugin.cpp	Thu May 14 19:20:40 2020 +0200
@@ -24,6 +24,13 @@
 #include "../../../Core/Toolbox.h"
 #include "GdcmDecoderCache.h"
 
+#include <gdcmImageChangeTransferSyntax.h>
+#include <gdcmImageReader.h>
+#include <gdcmImageWriter.h>
+#include <gdcmUIDGenerator.h>
+#include <gdcmAttribute.h>
+
+
 static OrthancPlugins::GdcmDecoderCache  cache_;
 static bool restrictTransferSyntaxes_ = false;
 static std::set<std::string> enabledTransferSyntaxes_;
@@ -148,6 +155,124 @@
 }
 
 
+OrthancPluginErrorCode TranscoderCallback(
+  OrthancPluginMemoryBuffer* transcoded /* out */,
+  uint8_t*                   hasSopInstanceUidChanged /* out */,
+  const void*                buffer,
+  uint64_t                   size,
+  const char* const*         allowedSyntaxes,
+  uint32_t                   countSyntaxes,
+  uint8_t                    allowNewSopInstanceUid)
+{
+  try
+  {
+    std::string dicom(reinterpret_cast<const char*>(buffer), size);
+    std::stringstream stream(dicom);
+
+    gdcm::ImageReader reader;
+    reader.SetStream(stream);
+    if (!reader.Read())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
+                                      "GDCM cannot decode the image");
+    }
+
+    // First check that transcoding is mandatory
+    for (uint32_t i = 0; i < countSyntaxes; i++)
+    {
+      gdcm::TransferSyntax syntax(gdcm::TransferSyntax::GetTSType(allowedSyntaxes[i]));
+      if (syntax.IsValid() &&
+          reader.GetImage().GetTransferSyntax() == syntax)
+      {
+        // Same transfer syntax as in the source, return a copy of the
+        // source buffer
+        OrthancPlugins::MemoryBuffer orthancBuffer(buffer, size);
+        *transcoded = orthancBuffer.Release();
+        *hasSopInstanceUidChanged = false;
+        return OrthancPluginErrorCode_Success;
+      }
+    }
+
+    for (uint32_t i = 0; i < countSyntaxes; i++)
+    {
+      gdcm::TransferSyntax syntax(gdcm::TransferSyntax::GetTSType(allowedSyntaxes[i]));
+      if (syntax.IsValid())
+      {
+        gdcm::ImageChangeTransferSyntax change;
+        change.SetTransferSyntax(syntax);
+        change.SetInput(reader.GetImage());
+
+        if (change.Change())
+        {
+          if (syntax == gdcm::TransferSyntax::JPEGBaselineProcess1 ||
+              syntax == gdcm::TransferSyntax::JPEGExtendedProcess2_4 ||
+              syntax == gdcm::TransferSyntax::JPEGLSNearLossless ||
+              syntax == gdcm::TransferSyntax::JPEG2000 ||
+              syntax == gdcm::TransferSyntax::JPEG2000Part2)
+          {
+            // In the case of a lossy compression, generate new SOP instance UID
+            gdcm::UIDGenerator generator;
+            std::string uid = generator.Generate();
+            if (uid.size() == 0)
+            {
+              throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
+                                              "GDCM cannot generate a UID");
+            }
+
+            gdcm::Attribute<0x0008,0x0018> sopInstanceUid;
+            sopInstanceUid.SetValue(uid);
+            reader.GetFile().GetDataSet().Replace(sopInstanceUid.GetAsDataElement());
+            *hasSopInstanceUidChanged = 1;
+          }
+          else
+          {
+            *hasSopInstanceUidChanged = 0;
+          }
+      
+          // GDCM was able to change the transfer syntax, serialize it
+          // to the output buffer
+          gdcm::ImageWriter writer;
+          writer.SetImage(change.GetOutput());
+          writer.SetFile(reader.GetFile());
+
+          std::stringstream ss;
+          writer.SetStream(ss);
+          if (writer.Write())
+          {
+            std::string s = ss.str();
+            OrthancPlugins::MemoryBuffer orthancBuffer(s.empty() ? NULL : s.c_str(), s.size());
+            *transcoded = orthancBuffer.Release();
+
+            return OrthancPluginErrorCode_Success;
+          }
+          else
+          {
+            throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
+                                            "GDCM cannot serialize the image");
+          }
+        }
+      }
+    }
+    
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(INFO) << "Cannot transcode image using GDCM: " << e.What();
+    return OrthancPluginErrorCode_Plugin;
+  }
+  catch (std::runtime_error& e)
+  {
+    LOG(INFO) << "Cannot transcode image using GDCM: " << e.what();
+    return OrthancPluginErrorCode_Plugin;
+  }
+  catch (...)
+  {
+    LOG(INFO) << "Native exception while decoding image using GDCM";
+    return OrthancPluginErrorCode_Plugin;
+  }
+}
+
 
 /**
  * We force the redefinition of the "ORTHANC_PLUGINS_API" macro, that
@@ -176,23 +301,18 @@
     static const char* const KEY_RESTRICT_TRANSFER_SYNTAXES = "RestrictTransferSyntaxes";
 
     OrthancPlugins::SetGlobalContext(context);
-    LOG(INFO) << "Initializing the advanced decoder of medical images using GDCM";
-
+    Orthanc::Logging::Initialize(context);
+    LOG(INFO) << "Initializing the decoder/transcoder of medical images using GDCM";
 
     /* Check the version of the Orthanc core */
-    if (OrthancPluginCheckVersion(context) == 0)
+    if (!OrthancPlugins::CheckMinimalOrthancVersion(0, 9, 5))
     {
-      char info[1024];
-      sprintf(info, "Your version of Orthanc (%s) must be above %d.%d.%d to run this plugin",
-              context->orthancVersion,
-              ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER,
-              ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER,
-              ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER);
-      OrthancPluginLogError(context, info);
+      LOG(ERROR) << "Your version of Orthanc (" << std::string(context->orthancVersion)
+                 << ") must be above 0.9.5 to run this plugin";
       return -1;
     }
 
-    OrthancPluginSetDescription(context, "Advanced decoder of medical images using GDCM.");
+    OrthancPluginSetDescription(context, "Decoder/transcoder of medical images using GDCM.");
 
     OrthancPlugins::OrthancConfiguration global;
 
@@ -220,10 +340,20 @@
     if (enabled)
     {
       OrthancPluginRegisterDecodeImageCallback(context, DecodeImageCallback);
+
+      if (OrthancPlugins::CheckMinimalOrthancVersion(1, 7, 0))
+      {
+        OrthancPluginRegisterTranscoderCallback(context, TranscoderCallback);
+      }
+      else
+      {
+        LOG(ERROR) << "Your version of Orthanc (" << std::string(context->orthancVersion)
+                   << ") must be above 1.7.0 to benefit from transcoding";
+      }
     }
     else
     {
-      LOG(WARNING) << "The advanced decoder of medical images using GDCM is disabled";
+      LOG(WARNING) << "The decoder/transcoder of medical images using GDCM is disabled";
     }
     
     return 0;
@@ -232,13 +362,13 @@
 
   ORTHANC_PLUGINS_API void OrthancPluginFinalize()
   {
-    LOG(INFO) << "Finalizing the advanced decoder of medical images using GDCM";
+    LOG(INFO) << "Finalizing the decoder/transcoder of medical images using GDCM";
   }
 
 
   ORTHANC_PLUGINS_API const char* OrthancPluginGetName()
   {
-    return "gdcm-decoder";
+    return "gdcm";
   }