changeset 3928:4cdc875510d1 transcoding

ServerContext::DecodeDicomFrame()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 14 May 2020 13:31:05 +0200
parents 2910b0d30fe0
children 7dc5e7e0045d
files OrthancServer/DefaultDicomImageDecoder.h OrthancServer/OrthancRestApi/OrthancRestResources.cpp OrthancServer/ServerContext.cpp OrthancServer/ServerContext.h Plugins/Engine/OrthancPlugins.cpp Plugins/Engine/OrthancPlugins.h
diffstat 6 files changed, 110 insertions(+), 145 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancServer/DefaultDicomImageDecoder.h	Thu May 14 07:37:44 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "IDicomImageDecoder.h"
-#include "../Core/DicomParsing/ParsedDicomFile.h"
-#include "../Core/DicomParsing/Internals/DicomImageDecoder.h"
-
-namespace Orthanc
-{
-  class DefaultDicomImageDecoder : public IDicomImageDecoder
-  {
-  public:
-    virtual ImageAccessor* Decode(const void* dicom,
-                                  size_t size,
-                                  unsigned int frame)
-    {
-      ParsedDicomFile parsed(dicom, size);
-      return DicomImageDecoder::Decode(parsed, frame);
-    }
-  };
-}
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Thu May 14 07:37:44 2020 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Thu May 14 13:31:05 2020 +0200
@@ -44,7 +44,6 @@
 #include "../../Core/Images/ImageProcessing.h"
 #include "../../Core/Logging.h"
 #include "../../Core/MultiThreading/Semaphore.h"
-#include "../DefaultDicomImageDecoder.h"
 #include "../OrthancConfiguration.h"
 #include "../Search/DatabaseLookup.h"
 #include "../ServerContext.h"
@@ -556,44 +555,23 @@
         {
           std::string publicId = call.GetUriComponent("id", "");
 
-#if ORTHANC_ENABLE_PLUGINS == 1
-          if (context.GetPlugins().HasCustomImageDecoder())
-          {
-            // TODO create a cache of file
-            std::string dicomContent;
-            context.ReadDicom(dicomContent, publicId);
-            decoded.reset(context.GetPlugins().DecodeUnsafe(dicomContent.c_str(), dicomContent.size(), frame));
-
-            /**
-             * Note that we call "DecodeUnsafe()": We do not fallback to
-             * the builtin decoder if no installed decoder plugin is able
-             * to decode the image. This allows us to take advantage of
-             * the cache below.
-             **/
-
-            if (handler.RequiresDicomTags() &&
-                decoded.get() != NULL)
-            {
-              // TODO Optimize this lookup for photometric interpretation:
-              // It should be implemented by the plugin to avoid parsing
-              // twice the DICOM file
-              ParsedDicomFile parsed(dicomContent);
-              parsed.ExtractDicomSummary(dicom);
-            }
-          }
-#endif
+          decoded.reset(context.DecodeDicomFrame(publicId, frame));
 
           if (decoded.get() == NULL)
           {
-            // Use Orthanc's built-in decoder, using the cache to speed-up
-            // things on multi-frame images
-            ServerContext::DicomCacheLocker locker(context, publicId);        
-            decoded.reset(DicomImageDecoder::Decode(locker.GetDicom(), frame));
-
-            if (handler.RequiresDicomTags())
-            {
-              locker.GetDicom().ExtractDicomSummary(dicom);
-            }
+            throw OrthancException(ErrorCode_NotImplemented,
+                                   "Cannot decode DICOM instance with ID: " + publicId);
+          }
+          
+          if (handler.RequiresDicomTags())
+          {
+            /**
+             * Retrieve a summary of the DICOM tags, which is
+             * necessary to deal with MONOCHROME1 photometric
+             * interpretation, and with windowing parameters.
+             **/ 
+            ServerContext::DicomCacheLocker locker(context, publicId);
+            locker.GetDicom().ExtractDicomSummary(dicom);
           }
         }
         catch (OrthancException& e)
@@ -982,21 +960,19 @@
     }
 
     std::string publicId = call.GetUriComponent("id", "");
-    std::string dicomContent;
-    context.ReadDicom(dicomContent, publicId);
+    std::unique_ptr<ImageAccessor> decoded(context.DecodeDicomFrame(publicId, frame));
 
-#if ORTHANC_ENABLE_PLUGINS == 1
-    IDicomImageDecoder& decoder = context.GetPlugins();
-#else
-    DefaultDicomImageDecoder decoder;  // This is Orthanc's built-in decoder
-#endif
-
-    std::unique_ptr<ImageAccessor> decoded(decoder.Decode(dicomContent.c_str(), dicomContent.size(), frame));
-
-    std::string result;
-    decoded->ToMatlabString(result);
-
-    call.GetOutput().AnswerBuffer(result, MimeType_PlainText);
+    if (decoded.get() == NULL)
+    {
+      throw OrthancException(ErrorCode_NotImplemented,
+                             "Cannot decode DICOM instance with ID: " + publicId);
+    }
+    else
+    {
+      std::string result;
+      decoded->ToMatlabString(result);
+      call.GetOutput().AnswerBuffer(result, MimeType_PlainText);
+    }
   }
 
 
--- a/OrthancServer/ServerContext.cpp	Thu May 14 07:37:44 2020 +0200
+++ b/OrthancServer/ServerContext.cpp	Thu May 14 13:31:05 2020 +0200
@@ -34,6 +34,7 @@
 #include "PrecompiledHeadersServer.h"
 #include "ServerContext.h"
 
+#include "../Core/DicomParsing/Internals/DicomImageDecoder.h"
 #include "../Core/Cache/SharedArchive.h"
 #include "../Core/DicomParsing/DcmtkTranscoder.h"
 #include "../Core/DicomParsing/FromDcmtkBridge.h"
@@ -1193,6 +1194,69 @@
   }   
 
 
+  ImageAccessor* ServerContext::DecodeDicomFrame(const std::string& publicId,
+                                                 unsigned int frameIndex)
+  {
+    // TODO => Reorder given the global parameter
+
+#if ORTHANC_ENABLE_PLUGINS == 1
+    if (HasPlugins() &&
+        GetPlugins().HasCustomImageDecoder())
+    {
+      // TODO: Store the raw buffer in the DicomCacheLocker
+      std::string dicomContent;
+      ReadDicom(dicomContent, publicId);
+
+      std::unique_ptr<ImageAccessor> decoded(
+        GetPlugins().Decode(dicomContent.c_str(), dicomContent.size(), frameIndex));
+      if (decoded.get() != NULL)
+      {
+        return decoded.release();
+      }
+      else
+      {
+        LOG(INFO) << "The installed image decoding plugins cannot handle an image, "
+                  << "fallback to the built-in decoder";
+      }
+    }
+#endif
+
+    {
+      // Use Orthanc's built-in decoder, using the cache to speed-up
+      // things on multi-frame images
+      ServerContext::DicomCacheLocker locker(*this, publicId);        
+      return DicomImageDecoder::Decode(locker.GetDicom(), frameIndex);
+    }
+  }
+
+
+  ImageAccessor* ServerContext::DecodeDicomFrame(const DicomInstanceToStore& dicom,
+                                                 unsigned int frameIndex)
+  {
+    // TODO => Reorder given the global parameter
+
+#if ORTHANC_ENABLE_PLUGINS == 1
+    if (HasPlugins() &&
+        GetPlugins().HasCustomImageDecoder())
+    {
+      std::unique_ptr<ImageAccessor> decoded(
+        GetPlugins().Decode(dicom.GetBufferData(), dicom.GetBufferSize(), frameIndex));
+      if (decoded.get() != NULL)
+      {
+        return decoded.release();
+      }
+      else
+      {
+        LOG(INFO) << "The installed image decoding plugins cannot handle an image, "
+                  << "fallback to the built-in decoder";
+      }
+    }
+#endif
+
+    return DicomImageDecoder::Decode(dicom.GetParsedDicomFile(), frameIndex);
+  }
+
+
   void ServerContext::StoreWithTranscoding(std::string& sopClassUid,
                                            std::string& sopInstanceUid,
                                            DicomStoreUserConnection& connection,
--- a/OrthancServer/ServerContext.h	Thu May 14 07:37:44 2020 +0200
+++ b/OrthancServer/ServerContext.h	Thu May 14 13:31:05 2020 +0200
@@ -459,6 +459,12 @@
       return *storageCommitmentReports_;
     }
 
+    ImageAccessor* DecodeDicomFrame(const std::string& publicId,
+                                    unsigned int frameIndex);
+
+    ImageAccessor* DecodeDicomFrame(const DicomInstanceToStore& dicom,
+                                    unsigned int frameIndex);
+
     void StoreWithTranscoding(std::string& sopClassUid,
                               std::string& sopInstanceUid,
                               DicomStoreUserConnection& connection,
--- a/Plugins/Engine/OrthancPlugins.cpp	Thu May 14 07:37:44 2020 +0200
+++ b/Plugins/Engine/OrthancPlugins.cpp	Thu May 14 13:31:05 2020 +0200
@@ -62,7 +62,6 @@
 #include "../../Core/OrthancException.h"
 #include "../../Core/SerializationToolbox.h"
 #include "../../Core/Toolbox.h"
-#include "../../OrthancServer/DefaultDicomImageDecoder.h"
 #include "../../OrthancServer/OrthancConfiguration.h"
 #include "../../OrthancServer/OrthancFindRequestHandler.h"
 #include "../../OrthancServer/Search/HierarchicalMatcher.h"
@@ -2757,6 +2756,11 @@
     // Images returned to plugins are assumed to be writeable. If the
     // input image is read-only, we return a copy so that it can be modified.
 
+    if (image.get() == NULL)
+    {
+      throw OrthancException(ErrorCode_NullPointer);
+    }
+    
     if (image->IsReadOnly())
     {
       std::unique_ptr<Image> copy(new Image(image->GetFormat(), image->GetWidth(), image->GetHeight(), false));
@@ -2810,22 +2814,17 @@
         
       case _OrthancPluginService_GetInstanceDecodedFrame:
       {
-        std::unique_ptr<ImageAccessor> decoded;
         if (p.targetImage == NULL)
         {
           throw OrthancException(ErrorCode_NullPointer);
         }
-        else if (HasCustomImageDecoder())
+
+        std::unique_ptr<ImageAccessor> decoded;
         {
-          // TODO - This call could be speeded up the future, if a
-          // "decoding context" gets introduced in the decoder plugins          
-          decoded.reset(Decode(instance.GetBufferData(), instance.GetBufferSize(), p.frameIndex));
+          PImpl::ServerContextLock lock(*pimpl_);
+          decoded.reset(lock.GetContext().DecodeDicomFrame(instance, p.frameIndex));
         }
-        else
-        {
-          decoded.reset(DicomImageDecoder::Decode(instance.GetParsedDicomFile(), p.frameIndex));
-        }
-
+        
         *(p.targetImage) = ReturnImage(decoded);
         return;
       }
@@ -2859,8 +2858,7 @@
           static_cast<DicomToJsonFlags>(p.flags), p.maxStringLength);
 
         Json::FastWriter writer;
-        *p.targetStringToFree = CopyString(writer.write(json));
-        
+        *p.targetStringToFree = CopyString(writer.write(json));        
         return;
       }
       
@@ -4825,9 +4823,9 @@
   }
 
 
-  ImageAccessor*  OrthancPlugins::DecodeUnsafe(const void* dicom,
-                                               size_t size,
-                                               unsigned int frame)
+  ImageAccessor* OrthancPlugins::Decode(const void* dicom,
+                                        size_t size,
+                                        unsigned int frame)
   {
     boost::shared_lock<boost::shared_mutex> lock(pimpl_->decodeImageCallbackMutex_);
 
@@ -4846,25 +4844,6 @@
     return NULL;
   }
 
-
-  ImageAccessor* OrthancPlugins::Decode(const void* dicom,
-                                        size_t size,
-                                        unsigned int frame)
-  {
-    ImageAccessor* result = DecodeUnsafe(dicom, size, frame);
-
-    if (result != NULL)
-    {
-      return result;
-    }
-    else
-    {
-      LOG(INFO) << "The installed image decoding plugins cannot handle an image, fallback to the built-in decoder";
-      DefaultDicomImageDecoder defaultDecoder;
-      return defaultDecoder.Decode(dicom, size, frame); 
-    }
-  }
-
   
   bool OrthancPlugins::IsAllowed(HttpMethod method,
                                  const char* uri,
--- a/Plugins/Engine/OrthancPlugins.h	Thu May 14 07:37:44 2020 +0200
+++ b/Plugins/Engine/OrthancPlugins.h	Thu May 14 13:31:05 2020 +0200
@@ -328,13 +328,6 @@
 
     bool HasCustomImageDecoder();
 
-    // Contrarily to "Decode()", this method does not fallback to the
-    // builtin image decoder, if no installed custom decoder can
-    // handle the image (it returns NULL in this case).
-    ImageAccessor* DecodeUnsafe(const void* dicom,
-                                size_t size,
-                                unsigned int frame);
-
     virtual ImageAccessor* Decode(const void* dicom,
                                   size_t size,
                                   unsigned int frame) ORTHANC_OVERRIDE;