changeset 1826:ac5b0b4e2434

refactoring of DicomImageDecoder
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 25 Nov 2015 16:00:57 +0100
parents f0f8a94c0858
children 4b6673e828f4
files NEWS OrthancServer/IDicomImageDecoder.h OrthancServer/Internals/DicomImageDecoder.cpp OrthancServer/Internals/DicomImageDecoder.h OrthancServer/OrthancRestApi/OrthancRestResources.cpp OrthancServer/ParsedDicomFile.cpp OrthancServer/ParsedDicomFile.h Plugins/Engine/OrthancPlugins.cpp Plugins/Engine/OrthancPlugins.h Plugins/Include/orthanc/OrthancCPlugin.h
diffstat 10 files changed, 225 insertions(+), 146 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Wed Nov 25 14:24:26 2015 +0100
+++ b/NEWS	Wed Nov 25 16:00:57 2015 +0100
@@ -37,6 +37,7 @@
 -------
 
 * New functions:
+  - "OrthancPluginRegisterDecodeImageCallback()" to replace the built-in image decoder
   - "OrthancPluginDicomInstanceToJson()" to convert DICOM to JSON
   - "OrthancPluginDicomBufferToJson()" to convert DICOM to JSON
   - "OrthancPluginRegisterErrorCode()" to declare custom error codes
--- a/OrthancServer/IDicomImageDecoder.h	Wed Nov 25 14:24:26 2015 +0100
+++ b/OrthancServer/IDicomImageDecoder.h	Wed Nov 25 16:00:57 2015 +0100
@@ -47,8 +47,7 @@
     {
     }
 
-    virtual bool Decode(ImageBuffer& target,
-                        ParsedDicomFile& dicom,
-                        unsigned int frame) = 0;
+    virtual ImageAccessor* Decode(ParsedDicomFile& dicom,
+                                  unsigned int frame) = 0;
   };
 }
--- a/OrthancServer/Internals/DicomImageDecoder.cpp	Wed Nov 25 14:24:26 2015 +0100
+++ b/OrthancServer/Internals/DicomImageDecoder.cpp	Wed Nov 25 16:00:57 2015 +0100
@@ -82,8 +82,8 @@
 
 #include "../../Core/Logging.h"
 #include "../../Core/OrthancException.h"
+#include "../../Core/Images/Image.h"
 #include "../../Core/Images/ImageProcessing.h"
-#include "../../Core/Images/PngWriter.h"  // TODO REMOVE THIS
 #include "../../Core/DicomFormat/DicomIntegerPixelAccessor.h"
 #include "../ToDcmtkBridge.h"
 #include "../FromDcmtkBridge.h"
@@ -91,6 +91,8 @@
 
 #include <boost/lexical_cast.hpp>
 
+#include <dcmtk/dcmdata/dcfilefo.h>
+
 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
 #include <dcmtk/dcmjpls/djcodecd.h>
 #include <dcmtk/dcmjpls/djcparam.h>
@@ -304,8 +306,7 @@
   };
 
 
-  void DicomImageDecoder::SetupImageBuffer(ImageBuffer& target,
-                                           DcmDataset& dataset)
+  ImageAccessor* DicomImageDecoder::CreateImage(DcmDataset& dataset)
   {
     DicomMap m;
     FromDcmtkBridge::Convert(m, dataset);
@@ -324,9 +325,7 @@
       throw OrthancException(ErrorCode_NotImplemented);
     }
 
-    target.SetHeight(info.GetHeight());
-    target.SetWidth(info.GetWidth());
-    target.SetFormat(format);
+    return new Image(format, info.GetWidth(), info.GetHeight());
   }
 
 
@@ -374,22 +373,20 @@
   }
 
 
-  void DicomImageDecoder::DecodeUncompressedImage(ImageBuffer& target,
-                                                  DcmDataset& dataset,
-                                                  unsigned int frame)
+  ImageAccessor* DicomImageDecoder::DecodeUncompressedImage(DcmDataset& dataset,
+                                                            unsigned int frame)
   {
     if (!IsUncompressedImage(dataset))
     {
       throw OrthancException(ErrorCode_BadParameterType);
     }
 
-    DecodeUncompressedImageInternal(target, dataset, frame);
+    return DecodeUncompressedImageInternal(dataset, frame);
   }
 
 
-  void DicomImageDecoder::DecodeUncompressedImageInternal(ImageBuffer& target,
-                                                          DcmDataset& dataset,
-                                                          unsigned int frame)
+  ImageAccessor* DicomImageDecoder::DecodeUncompressedImageInternal(DcmDataset& dataset,
+                                                                    unsigned int frame)
   {
     ImageSource source;
     source.Setup(dataset, frame);
@@ -399,10 +396,10 @@
      * Resize the target image.
      **/
 
-    SetupImageBuffer(target, dataset);
+    std::auto_ptr<ImageAccessor> target(CreateImage(dataset));
 
-    if (source.GetWidth() != target.GetWidth() ||
-        source.GetHeight() != target.GetHeight())
+    if (source.GetWidth() != target->GetWidth() ||
+        source.GetHeight() != target->GetHeight())
     {
       throw OrthancException(ErrorCode_InternalError);
     }
@@ -413,7 +410,6 @@
      * direct access to copy its values.
      **/
 
-    ImageAccessor targetAccessor(target.GetAccessor());
     const DicomImageInformation& info = source.GetAccessor().GetInformation();
 
     bool fastVersionSuccess = false;
@@ -435,8 +431,8 @@
                                      info.GetWidth() * GetBytesPerPixel(sourceFormat),
                                      buffer + frame * frameSize);
 
-          ImageProcessing::Convert(targetAccessor, sourceImage);
-          ImageProcessing::ShiftRight(targetAccessor, info.GetShift());
+          ImageProcessing::Convert(*target, sourceImage);
+          ImageProcessing::ShiftRight(*target, info.GetShift());
           fastVersionSuccess = true;
         }
       }
@@ -453,33 +449,34 @@
 
     if (!fastVersionSuccess)
     {
-      switch (target.GetFormat())
+      switch (target->GetFormat())
       {
         case PixelFormat_RGB24:
         case PixelFormat_RGBA32:
         case PixelFormat_Grayscale8:
-          CopyPixels<uint8_t>(targetAccessor, source.GetAccessor());
+          CopyPixels<uint8_t>(*target, source.GetAccessor());
           break;
         
         case PixelFormat_Grayscale16:
-          CopyPixels<uint16_t>(targetAccessor, source.GetAccessor());
+          CopyPixels<uint16_t>(*target, source.GetAccessor());
           break;
 
         case PixelFormat_SignedGrayscale16:
-          CopyPixels<int16_t>(targetAccessor, source.GetAccessor());
+          CopyPixels<int16_t>(*target, source.GetAccessor());
           break;
 
         default:
           throw OrthancException(ErrorCode_InternalError);
       }
     }
+
+    return target.release();
   }
 
 
 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
-  void DicomImageDecoder::DecodeJpegLossless(ImageBuffer& target,
-                                             DcmDataset& dataset,
-                                             unsigned int frame)
+  ImageAccessor* DicomImageDecoder::DecodeJpegLossless(DcmDataset& dataset,
+                                                       unsigned int frame)
   {
     if (!IsJpegLossless(dataset))
     {
@@ -500,9 +497,7 @@
       throw OrthancException(ErrorCode_BadFileFormat);
     }
 
-    SetupImageBuffer(target, dataset);
-
-    ImageAccessor targetAccessor(target.GetAccessor());
+    std::auto_ptr<ImageAccessor> target(CreateImage(dataset));
 
     /**
      * The "DJLSLosslessDecoder" and "DJLSNearLosslessDecoder" in DCMTK
@@ -518,38 +513,36 @@
     OFString decompressedColorModel;  // Out
     DJ_RPLossless representationParameter;
     OFCondition c = decoder.decodeFrame(&representationParameter, pixelSequence, &parameters, 
-                                        &dataset, frame, startFragment, targetAccessor.GetBuffer(), 
-                                        targetAccessor.GetSize(), decompressedColorModel);
+                                        &dataset, frame, startFragment, target->GetBuffer(), 
+                                        target->GetSize(), decompressedColorModel);
 
     if (!c.good())
     {
       throw OrthancException(ErrorCode_InternalError);
     }
+
+    return target.release();
   }
 #endif
 
 
 
 
-  bool DicomImageDecoder::Decode(ImageBuffer& target,
-                                 ParsedDicomFile& dicom,
-                                 unsigned int frame)
+  ImageAccessor* DicomImageDecoder::Decode(ParsedDicomFile& dicom,
+                                           unsigned int frame)
   {
     DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset();
 
     if (IsUncompressedImage(dataset))
     {
-      DecodeUncompressedImage(target, dataset, frame);
-      return true;
+      return DecodeUncompressedImage(dataset, frame);
     }
 
-
 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
     if (IsJpegLossless(dataset))
     {
       LOG(INFO) << "Decoding a JPEG-LS image";
-      DecodeJpegLossless(target, dataset, frame);
-      return true;
+      return DecodeJpegLossless(dataset, frame);
     }
 #endif
 
@@ -558,7 +551,6 @@
     // TODO Implement this part to speed up JPEG decompression
 #endif
 
-
     /**
      * This DICOM image format is not natively supported by
      * Orthanc. As a last resort, try and decode it through
@@ -575,12 +567,11 @@
 
       if (converted->canWriteXfer(EXS_LittleEndianExplicit))
       {
-        DecodeUncompressedImageInternal(target, *converted, frame);
-        return true;
+        return DecodeUncompressedImageInternal(*converted, frame);
       }
     }
 
-    return false;
+    return NULL;
   }
 
 
@@ -591,14 +582,13 @@
   }
 
 
-  bool DicomImageDecoder::TruncateDecodedImage(ImageBuffer& target,
-                                               ImageBuffer& source,
+  bool DicomImageDecoder::TruncateDecodedImage(std::auto_ptr<ImageAccessor>& image,
                                                PixelFormat format,
                                                bool allowColorConversion)
   {
     // If specified, prevent the conversion between color and
     // grayscale images
-    bool isSourceColor = IsColorImage(source.GetFormat());
+    bool isSourceColor = IsColorImage(image->GetFormat());
     bool isTargetColor = IsColorImage(format);
 
     if (!allowColorConversion)
@@ -609,34 +599,25 @@
       }
     }
 
-    if (source.GetFormat() == format)
+    if (image->GetFormat() != format)
     {
-      // No conversion is required, return the temporary image
-      target.AcquireOwnership(source);
-      return true;
+      // A conversion is required
+      std::auto_ptr<ImageAccessor> target(new Image(format, image->GetWidth(), image->GetHeight()));
+      ImageProcessing::Convert(*target, *image);
+      image = target;
     }
 
-    target.SetFormat(format);
-    target.SetWidth(source.GetWidth());
-    target.SetHeight(source.GetHeight());
-
-    ImageAccessor targetAccessor(target.GetAccessor());
-    ImageAccessor sourceAccessor(source.GetAccessor());
-    ImageProcessing::Convert(targetAccessor, sourceAccessor);
-
     return true;
   }
 
 
-  bool DicomImageDecoder::PreviewDecodedImage(ImageBuffer& target,
-                                              ImageBuffer& source)
+  bool DicomImageDecoder::PreviewDecodedImage(std::auto_ptr<ImageAccessor>& image)
   {
-    switch (source.GetFormat())
+    switch (image->GetFormat())
     {
       case PixelFormat_RGB24:
       {
-        // Directly return color images (RGB)
-        target.AcquireOwnership(source);
+        // Directly return color images without modification (RGB)
         return true;
       }
 
@@ -645,32 +626,24 @@
       case PixelFormat_SignedGrayscale16:
       {
         // Grayscale image: Stretch its dynamics to the [0,255] range
-        target.SetFormat(PixelFormat_Grayscale8);
-        target.SetWidth(source.GetWidth());
-        target.SetHeight(source.GetHeight());
+        int64_t a, b;
+        ImageProcessing::GetMinMaxValue(a, b, *image);
 
-        ImageAccessor targetAccessor(target.GetAccessor());
-        ImageAccessor sourceAccessor(source.GetAccessor());
-
-        int64_t a, b;
-        ImageProcessing::GetMinMaxValue(a, b, sourceAccessor);
-        
         if (a == b)
         {
-          ImageProcessing::Set(targetAccessor, 0);
+          ImageProcessing::Set(*image, 0);
         }
         else
         {
-          ImageProcessing::ShiftScale(sourceAccessor, static_cast<float>(-a), 255.0f / static_cast<float>(b - a));
+          ImageProcessing::ShiftScale(*image, static_cast<float>(-a), 255.0f / static_cast<float>(b - a));
+        }
 
-          if (source.GetFormat() == PixelFormat_Grayscale8)
-          {
-            target.AcquireOwnership(source);
-          }
-          else
-          {
-            ImageProcessing::Convert(targetAccessor, sourceAccessor);
-          }
+        // If the source image is not grayscale 8bpp, convert it
+        if (image->GetFormat() != PixelFormat_Grayscale8)
+        {
+          std::auto_ptr<ImageAccessor> target(new Image(PixelFormat_Grayscale8, image->GetWidth(), image->GetHeight()));
+          ImageProcessing::Convert(*target, *image);
+          image = target;
         }
 
         return true;
--- a/OrthancServer/Internals/DicomImageDecoder.h	Wed Nov 25 14:24:26 2015 +0100
+++ b/OrthancServer/Internals/DicomImageDecoder.h	Wed Nov 25 16:00:57 2015 +0100
@@ -32,10 +32,12 @@
 
 #pragma once
 
-#include <dcmtk/dcmdata/dcfilefo.h>
+#include <memory>
 
 #include "../IDicomImageDecoder.h"
 
+class DcmDataset;
+
 namespace Orthanc
 {
   class DicomImageDecoder : public IDicomImageDecoder
@@ -43,38 +45,31 @@
   private:
     class ImageSource;
 
-    static void DecodeUncompressedImageInternal(ImageBuffer& target,
-                                                DcmDataset& dataset,
-                                                unsigned int frame);
+    static ImageAccessor* DecodeUncompressedImageInternal(DcmDataset& dataset,
+                                                          unsigned int frame);
 
     static bool IsPsmctRle1(DcmDataset& dataset);
 
-    static void SetupImageBuffer(ImageBuffer& target,
-                                 DcmDataset& dataset);
+    static ImageAccessor* CreateImage(DcmDataset& dataset);
 
     static bool IsUncompressedImage(const DcmDataset& dataset);
 
-    static void DecodeUncompressedImage(ImageBuffer& target,
-                                        DcmDataset& dataset,
-                                        unsigned int frame);
+    static ImageAccessor* DecodeUncompressedImage(DcmDataset& dataset,
+                                                  unsigned int frame);
 
 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
-    static void DecodeJpegLossless(ImageBuffer& target,
-                                   DcmDataset& dataset,
-                                   unsigned int frame);
+    static ImageAccessor* DecodeJpegLossless(DcmDataset& dataset,
+                                             unsigned int frame);
 #endif
 
   public:
-    virtual bool Decode(ImageBuffer& target,
-                        ParsedDicomFile& dicom,
-                        unsigned int frame);
+    virtual ImageAccessor *Decode(ParsedDicomFile& dicom,
+                                  unsigned int frame);
 
-    static bool TruncateDecodedImage(ImageBuffer& target,
-                                     ImageBuffer& source,
+    static bool TruncateDecodedImage(std::auto_ptr<ImageAccessor>& image,
                                      PixelFormat format,
                                      bool allowColorConversion);
 
-    static bool PreviewDecodedImage(ImageBuffer& target,
-                                    ImageBuffer& source);
+    static bool PreviewDecodedImage(std::auto_ptr<ImageAccessor>& image);
   };
 }
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Wed Nov 25 14:24:26 2015 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Wed Nov 25 16:00:57 2015 +0100
@@ -410,7 +410,12 @@
 
     try
     {
-      DicomImageDecoder decoder;  // TODO plugins
+#if ORTHANC_PLUGINS_ENABLED == 1
+      IDicomImageDecoder& decoder = context.GetPlugins();
+#else
+      DicomImageDecoder decoder;  // This is Orthanc's built-in decoder
+#endif
+
       ImageToEncode image(decoder, dicom, frame, mode);
 
       HttpContentNegociation negociation;
@@ -463,15 +468,17 @@
     std::string dicomContent;
     context.ReadFile(dicomContent, publicId, FileContentType_Dicom);
 
+#if ORTHANC_PLUGINS_ENABLED == 1
+    IDicomImageDecoder& decoder = context.GetPlugins();
+#else
+    DicomImageDecoder decoder;  // This is Orthanc's built-in decoder
+#endif
+
     ParsedDicomFile dicom(dicomContent);
-    ImageBuffer buffer;
-    DicomImageDecoder decoder;  // TODO plugin
-    dicom.ExtractImage(buffer, decoder, frame);
-
-    ImageAccessor accessor(buffer.GetConstAccessor());
+    std::auto_ptr<ImageAccessor> decoded(dicom.ExtractImage(decoder, frame));
 
     std::string result;
-    accessor.ToMatlabString(result);
+    decoded->ToMatlabString(result);
 
     call.GetOutput().AnswerBuffer(result, "text/plain");
   }
--- a/OrthancServer/ParsedDicomFile.cpp	Wed Nov 25 14:24:26 2015 +0100
+++ b/OrthancServer/ParsedDicomFile.cpp	Wed Nov 25 16:00:57 2015 +0100
@@ -1048,53 +1048,59 @@
   }
 
   
-  void ParsedDicomFile::ExtractImage(ImageBuffer& result,
-                                     IDicomImageDecoder& decoder,
-                                     unsigned int frame)
+  ImageAccessor* ParsedDicomFile::ExtractImage(IDicomImageDecoder& decoder,
+                                               unsigned int frame)
   {
-    if (!decoder.Decode(result, *this, frame))
+    std::auto_ptr<ImageAccessor> decoded(decoder.Decode(*this, frame));
+
+    if (decoded.get() == NULL)
     {
+      LOG(ERROR) << "Cannot decode a DICOM image";
       throw OrthancException(ErrorCode_BadFileFormat);
     }
+    else
+    {
+      return decoded.release();
+    }
   }
 
 
-  void ParsedDicomFile::ExtractImage(ImageBuffer& result,
-                                     IDicomImageDecoder& decoder,
-                                     unsigned int frame,
-                                     ImageExtractionMode mode)
+  ImageAccessor* ParsedDicomFile::ExtractImage(IDicomImageDecoder& decoder,
+                                               unsigned int frame,
+                                               ImageExtractionMode mode)
   {
-    ImageBuffer source;
-    if (!decoder.Decode(source, *this, frame))
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
+    std::auto_ptr<ImageAccessor> decoded(ExtractImage(decoder, frame));
 
     bool ok = false;
 
     switch (mode)
     {
       case ImageExtractionMode_UInt8:
-        ok = DicomImageDecoder::TruncateDecodedImage(result, source, PixelFormat_Grayscale8, false);
+        ok = DicomImageDecoder::TruncateDecodedImage(decoded, PixelFormat_Grayscale8, false);
         break;
 
       case ImageExtractionMode_UInt16:
-        ok = DicomImageDecoder::TruncateDecodedImage(result, source, PixelFormat_Grayscale16, false);
+        ok = DicomImageDecoder::TruncateDecodedImage(decoded, PixelFormat_Grayscale16, false);
         break;
 
       case ImageExtractionMode_Int16:
-        ok = DicomImageDecoder::TruncateDecodedImage(result, source, PixelFormat_SignedGrayscale16, false);
+        ok = DicomImageDecoder::TruncateDecodedImage(decoded, PixelFormat_SignedGrayscale16, false);
         break;
 
       case ImageExtractionMode_Preview:
-        ok = DicomImageDecoder::PreviewDecodedImage(result, source);
+        ok = DicomImageDecoder::PreviewDecodedImage(decoded);
         break;
 
       default:
         throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
 
-    if (!ok)
+    if (ok)
+    {
+      assert(decoded.get() != NULL);
+      return decoded.release();
+    }
+    else
     {
       throw OrthancException(ErrorCode_NotImplemented);
     }
@@ -1106,12 +1112,11 @@
                                         unsigned int frame,
                                         ImageExtractionMode mode)
   {
-    ImageBuffer buffer;
-    ExtractImage(buffer, decoder, frame, mode);
+    std::auto_ptr<ImageAccessor> decoded(ExtractImage(decoder, frame, mode));
+    assert(decoded.get() != NULL);
 
-    ImageAccessor accessor(buffer.GetConstAccessor());
     PngWriter writer;
-    writer.WriteToMemory(result, accessor);
+    writer.WriteToMemory(result, *decoded);
   }
 
 
@@ -1127,13 +1132,12 @@
       throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
 
-    ImageBuffer buffer;
-    ExtractImage(buffer, decoder, frame, mode);
+    std::auto_ptr<ImageAccessor> decoded(ExtractImage(decoder, frame, mode));
+    assert(decoded.get() != NULL);
 
-    ImageAccessor accessor(buffer.GetConstAccessor());
     JpegWriter writer;
     writer.SetQuality(quality);
-    writer.WriteToMemory(result, accessor);
+    writer.WriteToMemory(result, *decoded);
   }
 
 
--- a/OrthancServer/ParsedDicomFile.h	Wed Nov 25 14:24:26 2015 +0100
+++ b/OrthancServer/ParsedDicomFile.h	Wed Nov 25 16:00:57 2015 +0100
@@ -126,14 +126,12 @@
     void EmbedImage(const std::string& mime,
                     const std::string& content);
 
-    void ExtractImage(ImageBuffer& result,
-                      IDicomImageDecoder& decoder,
-                      unsigned int frame);
+    ImageAccessor* ExtractImage(IDicomImageDecoder& decoder,
+                                unsigned int frame);
 
-    void ExtractImage(ImageBuffer& result,
-                      IDicomImageDecoder& decoder,
-                      unsigned int frame,
-                      ImageExtractionMode mode);
+    ImageAccessor* ExtractImage(IDicomImageDecoder& decoder,
+                                unsigned int frame,
+                                ImageExtractionMode mode);
 
     void ExtractPngImage(std::string& result,
                          IDicomImageDecoder& decoder,
--- a/Plugins/Engine/OrthancPlugins.cpp	Wed Nov 25 14:24:26 2015 +0100
+++ b/Plugins/Engine/OrthancPlugins.cpp	Wed Nov 25 16:00:57 2015 +0100
@@ -48,6 +48,7 @@
 #include "../../OrthancServer/ServerContext.h"
 #include "../../OrthancServer/ServerToolbox.h"
 #include "../../OrthancServer/Search/HierarchicalMatcher.h"
+#include "../../OrthancServer/Internals/DicomImageDecoder.h"
 #include "../../Core/Compression/ZlibCompressor.h"
 #include "../../Core/Compression/GzipCompressor.h"
 #include "../../Core/Images/Image.h"
@@ -286,11 +287,13 @@
     OnStoredCallbacks  onStoredCallbacks_;
     OnChangeCallbacks  onChangeCallbacks_;
     OrthancPluginWorklistCallback  worklistCallback_;
+    OrthancPluginDecodeImageCallback  decodeImageCallback_;
     std::auto_ptr<StorageAreaFactory>  storageArea_;
     boost::recursive_mutex restCallbackMutex_;
     boost::recursive_mutex storedCallbackMutex_;
     boost::recursive_mutex changeCallbackMutex_;
     boost::mutex worklistCallbackMutex_;
+    boost::mutex decodeImageCallbackMutex_;
     boost::recursive_mutex invokeServiceMutex_;
     Properties properties_;
     int argc_;
@@ -301,6 +304,7 @@
     PImpl() : 
       context_(NULL), 
       worklistCallback_(NULL),
+      decodeImageCallback_(NULL),
       argc_(1),
       argv_(NULL)
     {
@@ -725,12 +729,32 @@
     }
     else
     {
-      LOG(INFO) << "Plugin has registered an modality worklist callback";
+      LOG(INFO) << "Plugin has registered a callback to handle modality worklists";
       pimpl_->worklistCallback_ = p.callback;
     }
   }
 
 
+  void OrthancPlugins::RegisterDecodeImageCallback(const void* parameters)
+  {
+    const _OrthancPluginDecodeImageCallback& p = 
+      *reinterpret_cast<const _OrthancPluginDecodeImageCallback*>(parameters);
+
+    boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_);
+
+    if (pimpl_->decodeImageCallback_ != NULL)
+    {
+      LOG(ERROR) << "Can only register one plugin to handle the decompression of DICOM images";
+      throw OrthancException(ErrorCode_Plugin);
+    }
+    else
+    {
+      LOG(INFO) << "Plugin has registered a callback to decode DICOM images";
+      pimpl_->decodeImageCallback_ = p.callback;
+    }
+  }
+
+
 
 
   void OrthancPlugins::AnswerBuffer(const void* parameters)
@@ -1544,6 +1568,10 @@
         RegisterWorklistCallback(parameters);
         return true;
 
+      case _OrthancPluginService_RegisterDecodeImageCallback:
+        RegisterDecodeImageCallback(parameters);
+        return true;
+
       case _OrthancPluginService_AnswerBuffer:
         AnswerBuffer(parameters);
         return true;
@@ -2149,4 +2177,31 @@
     return pimpl_->worklistCallback_ != NULL;
   }
 
+
+  ImageAccessor*  OrthancPlugins::Decode(ParsedDicomFile& dicom, 
+                                         unsigned int frame)
+  {
+    {
+      boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_);
+      if (pimpl_->decodeImageCallback_ != NULL)
+      {
+        std::string s;
+        dicom.SaveToMemoryBuffer(s);
+
+        OrthancPluginImage* pluginImage = NULL;
+        if (pimpl_->decodeImageCallback_(&pluginImage, s.c_str(), s.size(), frame) == OrthancPluginErrorCode_Success &&
+            pluginImage != NULL)
+        {
+          return reinterpret_cast<ImageAccessor*>(pluginImage);
+        }
+        else
+        {
+          LOG(WARNING) << "The custom image decoder cannot handle an image, trying with the built-in decoder";
+        }
+      }
+    }
+
+    DicomImageDecoder defaultDecoder;
+    return defaultDecoder.Decode(dicom, frame);
+  }
 }
--- a/Plugins/Engine/OrthancPlugins.h	Wed Nov 25 14:24:26 2015 +0100
+++ b/Plugins/Engine/OrthancPlugins.h	Wed Nov 25 16:00:57 2015 +0100
@@ -50,6 +50,7 @@
 #include "../../Core/FileStorage/IStorageArea.h"
 #include "../../Core/HttpServer/IHttpHandler.h"
 #include "../../OrthancServer/IServerListener.h"
+#include "../../OrthancServer/IDicomImageDecoder.h"
 #include "../../OrthancServer/DicomProtocol/IWorklistRequestHandlerFactory.h"
 #include "OrthancPluginDatabase.h"
 #include "PluginsManager.h"
@@ -65,7 +66,8 @@
     public IHttpHandler, 
     public IPluginServiceProvider, 
     public IServerListener,
-    public IWorklistRequestHandlerFactory
+    public IWorklistRequestHandlerFactory,
+    public IDicomImageDecoder
   {
   private:
     struct PImpl;
@@ -84,6 +86,8 @@
 
     void RegisterWorklistCallback(const void* parameters);
 
+    void RegisterDecodeImageCallback(const void* parameters);
+
     void AnswerBuffer(const void* parameters);
 
     void Redirect(const void* parameters);
@@ -217,6 +221,9 @@
     bool HasWorklistHandler();
 
     virtual IWorklistRequestHandler* ConstructWorklistRequestHandler();
+
+    virtual ImageAccessor* Decode(ParsedDicomFile& dicom, 
+                                  unsigned int frame);
   };
 }
 
--- a/Plugins/Include/orthanc/OrthancCPlugin.h	Wed Nov 25 14:24:26 2015 +0100
+++ b/Plugins/Include/orthanc/OrthancCPlugin.h	Wed Nov 25 16:00:57 2015 +0100
@@ -19,6 +19,7 @@
  *    - Possibly register a custom storage area using ::OrthancPluginRegisterStorageArea().
  *    - Possibly register a custom database back-end area using OrthancPluginRegisterDatabaseBackendV2().
  *    - Possibly register a handler for C-Find SCP against DICOM worklists using OrthancPluginRegisterWorklistCallback().
+ *    - Possibly register a custom decoder for DICOM images using OrthancPluginRegisterDecodeImageCallback().
  * -# <tt>void OrthancPluginFinalize()</tt>:
  *    This function is invoked by Orthanc during its shutdown. The plugin
  *    must free all its memory.
@@ -405,6 +406,7 @@
     _OrthancPluginService_RegisterOnChangeCallback = 1003,
     _OrthancPluginService_RegisterRestCallbackNoLock = 1004,
     _OrthancPluginService_RegisterWorklistCallback = 1005,
+    _OrthancPluginService_RegisterDecodeImageCallback = 1006,
 
     /* Sending answers to REST calls */
     _OrthancPluginService_AnswerBuffer = 2000,
@@ -843,6 +845,18 @@
 
 
   /**
+   * @brief Signature of a callback function to decode a DICOM instance as an image.
+   * @ingroup Callbacks
+   **/
+  typedef OrthancPluginErrorCode (*OrthancPluginDecodeImageCallback) (
+    OrthancPluginImage** target,
+    const void* dicom,
+    const uint32_t size,
+    uint32_t frameIndex);
+
+
+
+  /**
    * @brief Signature of a function to free dynamic memory.
    **/
   typedef void (*OrthancPluginFree) (void* buffer);
@@ -4351,6 +4365,32 @@
     return context->InvokeService(context, _OrthancPluginService_DicomFromJson, &params);
   }
 
+
+  typedef struct
+  {
+    OrthancPluginDecodeImageCallback callback;
+  } _OrthancPluginDecodeImageCallback;
+
+  /**
+   * @brief Register a callback to handle the decoding of DICOM images.
+   *
+   * This function registers a custom callback to the decoding of
+   * DICOM images, replacing the built-in decoder of Orthanc.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param callback The callback.
+   * @ingroup Callbacks
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterDecodeImageCallback(
+    OrthancPluginContext*             context,
+    OrthancPluginDecodeImageCallback  callback)
+  {
+    _OrthancPluginDecodeImageCallback params;
+    params.callback = callback;
+
+    context->InvokeService(context, _OrthancPluginService_RegisterDecodeImageCallback, &params);
+  }
+  
 #ifdef  __cplusplus
 }
 #endif