changeset 993:501880d76474 plugins

improvements to GDCM plugin
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 02 Jul 2014 14:41:57 +0200
parents af014624dac1
children 79e720ca043e 40e5255e7dc5
files Core/ImageFormats/ImageProcessing.cpp Plugins/Engine/PluginsHttpHandler.cpp Plugins/Engine/PluginsHttpHandler.h Plugins/OrthancCPlugin/OrthancCPlugin.h Plugins/Samples/GdcmDecoding/OrthancContext.cpp Plugins/Samples/GdcmDecoding/OrthancContext.h Plugins/Samples/GdcmDecoding/Plugin.cpp
diffstat 7 files changed, 167 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/Core/ImageFormats/ImageProcessing.cpp	Wed Jul 02 13:53:56 2014 +0200
+++ b/Core/ImageFormats/ImageProcessing.cpp	Wed Jul 02 14:41:57 2014 +0200
@@ -75,6 +75,44 @@
   }
 
 
+  template <typename TargetType>
+  static void ConvertColorToGrayscale(ImageAccessor& target,
+                              const ImageAccessor& source)
+  {
+    assert(source.GetFormat() == PixelFormat_RGB24);
+
+    const TargetType minValue = std::numeric_limits<TargetType>::min();
+    const TargetType maxValue = std::numeric_limits<TargetType>::max();
+
+    for (unsigned int y = 0; y < source.GetHeight(); y++)
+    {
+      TargetType* t = reinterpret_cast<TargetType*>(target.GetRow(y));
+      const uint8_t* s = reinterpret_cast<const uint8_t*>(source.GetConstRow(y));
+
+      for (unsigned int x = 0; x < source.GetWidth(); x++, t++, s += 3)
+      {
+        // Y = 0.2126 R + 0.7152 G + 0.0722 B
+        int32_t v = (2126 * static_cast<int32_t>(s[0]) +
+                     7152 * static_cast<int32_t>(s[1]) +
+                     0722 * static_cast<int32_t>(s[2])) / 1000;
+        
+        if (static_cast<int32_t>(v) < static_cast<int32_t>(minValue))
+        {
+          *t = minValue;
+        }
+        else if (static_cast<int32_t>(v) > static_cast<int32_t>(maxValue))
+        {
+          *t = maxValue;
+        }
+        else
+        {
+          *t = static_cast<TargetType>(v);
+        }
+      }
+    }
+  }
+
+
   template <typename PixelType>
   static void SetInternal(ImageAccessor& image,
                           int64_t constant)
@@ -319,6 +357,27 @@
       return;
     }
 
+    if (target.GetFormat() == PixelFormat_Grayscale8 &&
+        source.GetFormat() == PixelFormat_RGB24)
+    {
+      ConvertColorToGrayscale<uint8_t>(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_Grayscale16 &&
+        source.GetFormat() == PixelFormat_RGB24)
+    {
+      ConvertColorToGrayscale<uint16_t>(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_SignedGrayscale16 &&
+        source.GetFormat() == PixelFormat_RGB24)
+    {
+      ConvertColorToGrayscale<int16_t>(target, source);
+      return;
+    }
+
     throw OrthancException(ErrorCode_NotImplemented);
   }
 
--- a/Plugins/Engine/PluginsHttpHandler.cpp	Wed Jul 02 13:53:56 2014 +0200
+++ b/Plugins/Engine/PluginsHttpHandler.cpp	Wed Jul 02 14:41:57 2014 +0200
@@ -287,6 +287,16 @@
   }
 
 
+  void PluginsHttpHandler::Redirect(const void* parameters)
+  {
+    const _OrthancPluginRedirect& p = 
+      *reinterpret_cast<const _OrthancPluginRedirect*>(parameters);
+
+    HttpOutput* translatedOutput = reinterpret_cast<HttpOutput*>(p.output);
+    translatedOutput->Redirect(p.redirection);
+  }
+
+
   void PluginsHttpHandler::CompressAndAnswerPngImage(const void* parameters)
   {
     const _OrthancPluginCompressAndAnswerPngImage& p = 
@@ -469,6 +479,10 @@
         RestApiPostPut(false, parameters);
         return true;
 
+      case _OrthancPluginService_Redirect:
+        Redirect(parameters);
+        return true;
+
       default:
         return false;
     }
--- a/Plugins/Engine/PluginsHttpHandler.h	Wed Jul 02 13:53:56 2014 +0200
+++ b/Plugins/Engine/PluginsHttpHandler.h	Wed Jul 02 14:41:57 2014 +0200
@@ -54,6 +54,8 @@
 
     void AnswerBuffer(const void* parameters);
 
+    void Redirect(const void* parameters);
+
     void CompressAndAnswerPngImage(const void* parameters);
 
     void GetDicomForInstance(const void* parameters);
--- a/Plugins/OrthancCPlugin/OrthancCPlugin.h	Wed Jul 02 13:53:56 2014 +0200
+++ b/Plugins/OrthancCPlugin/OrthancCPlugin.h	Wed Jul 02 14:41:57 2014 +0200
@@ -213,6 +213,7 @@
     /* Sending answers to REST calls */
     _OrthancPluginService_AnswerBuffer = 2000,
     _OrthancPluginService_CompressAndAnswerPngImage = 2001,
+    _OrthancPluginService_Redirect = 2002,
 
     /* Access to the Orthanc database and API */
     _OrthancPluginService_GetDicomForInstance = 3000,
@@ -664,6 +665,34 @@
   }
 
 
+  typedef struct
+  {
+    OrthancPluginRestOutput* output;
+    const char*              redirection;
+  } _OrthancPluginRedirect;
+
+  /**
+   * @brief Redirect a GET request.
+   *
+   * This function answers to a REST request by redirecting the user
+   * to another URI using HTTP status 301.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param output The HTTP connection to the client application.
+   * @param redirection Where to redirect.
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginRedirect(
+    OrthancPluginContext*    context,
+    OrthancPluginRestOutput* output,
+    const char*              redirection)
+  {
+    _OrthancPluginRedirect params;
+    params.output = output;
+    params.redirection = redirection;
+    context->InvokeService(context, _OrthancPluginService_Redirect, &params);
+  }
+
+
 #ifdef  __cplusplus
 }
 #endif
--- a/Plugins/Samples/GdcmDecoding/OrthancContext.cpp	Wed Jul 02 13:53:56 2014 +0200
+++ b/Plugins/Samples/GdcmDecoding/OrthancContext.cpp	Wed Jul 02 14:41:57 2014 +0200
@@ -156,3 +156,12 @@
   OrthancPluginCompressAndAnswerPngImage(context_, output, format, accessor.GetWidth(),
                                          accessor.GetHeight(), accessor.GetPitch(), accessor.GetConstBuffer());
 }
+
+
+
+void OrthancContext::Redirect(OrthancPluginRestOutput* output,
+                              const std::string& s)
+{
+  Check();
+  OrthancPluginRedirect(context_, output, s.c_str());
+}
--- a/Plugins/Samples/GdcmDecoding/OrthancContext.h	Wed Jul 02 13:53:56 2014 +0200
+++ b/Plugins/Samples/GdcmDecoding/OrthancContext.h	Wed Jul 02 14:41:57 2014 +0200
@@ -81,4 +81,7 @@
 
   void CompressAndAnswerPngImage(OrthancPluginRestOutput* output,
                                  const Orthanc::ImageAccessor& accessor);
+
+  void Redirect(OrthancPluginRestOutput* output,
+                const std::string& s);
 };
--- a/Plugins/Samples/GdcmDecoding/Plugin.cpp	Wed Jul 02 13:53:56 2014 +0200
+++ b/Plugins/Samples/GdcmDecoding/Plugin.cpp	Wed Jul 02 14:41:57 2014 +0200
@@ -35,6 +35,13 @@
 
 #include <gdcmReader.h>
 #include <gdcmImageReader.h>
+#include <gdcmImageChangePlanarConfiguration.h>
+
+
+static void AnswerUnsupportedImage(OrthancPluginRestOutput* output)
+{
+  OrthancContext::GetInstance().Redirect(output, "/app/images/unsupported.png");
+}
 
 
 static bool GetOrthancPixelFormat(Orthanc::PixelFormat& format,
@@ -105,23 +112,39 @@
   if (!imageReader.Read())
   {
     OrthancContext::GetInstance().LogError("GDCM cannot extract an image from this DICOM instance");
-    return -1;  // Error
+    AnswerUnsupportedImage(output);
+    return 0;
   }
 
   gdcm::Image& image = imageReader.GetImage();
 
+
   // Log information about the decoded image
   char tmp[1024];
   sprintf(tmp, "Image format: %dx%d %s with %d color channel(s)", image.GetRows(), image.GetColumns(), 
           image.GetPixelFormat().GetScalarTypeAsString(), image.GetPixelFormat().GetSamplesPerPixel());
   OrthancContext::GetInstance().LogWarning(tmp);
 
+
+  // Convert planar configuration
+  gdcm::ImageChangePlanarConfiguration planar;
+  if (image.GetPlanarConfiguration() != 0 && 
+      image.GetPixelFormat().GetSamplesPerPixel() != 1)
+  {
+    OrthancContext::GetInstance().LogWarning("Converting planar configuration to interleaved");
+    planar.SetInput(imageReader.GetImage());
+    planar.Change();
+    image = planar.GetOutput();
+  }
+
+
   // Create a read-only accessor to the bitmap decoded by GDCM
   Orthanc::PixelFormat format;
   if (!GetOrthancPixelFormat(format, image))
   {
     OrthancContext::GetInstance().LogError("This sample plugin does not support this image format");
-    return -1;  // Error
+    AnswerUnsupportedImage(output);
+    return 0;
   }
 
   Orthanc::ImageAccessor decodedImage;
@@ -166,28 +189,39 @@
       Orthanc::ImageProcessing::ShiftScale(decodedImage, offset, scaling);
     }
   }
-  else if (outputFormat == "image-uint8")
-  {
-    converted.SetFormat(Orthanc::PixelFormat_Grayscale8);
-  }
-  else if (outputFormat == "image-uint16")
-  {
-    converted.SetFormat(Orthanc::PixelFormat_Grayscale16);
-  }
-  else if (outputFormat == "image-int16")
-  {
-    converted.SetFormat(Orthanc::PixelFormat_SignedGrayscale16);
-  }
   else
   {
-    OrthancContext::GetInstance().LogError("Unknown output format: " + outputFormat);
-    return -1;
+    if (format == Orthanc::PixelFormat_RGB24 ||
+        format == Orthanc::PixelFormat_RGBA32)
+    {
+      // Do not convert color images to grayscale values (this is Orthanc convention)
+      AnswerUnsupportedImage(output);
+      return 0;
+    }
+
+    if (outputFormat == "image-uint8")
+    {
+      converted.SetFormat(Orthanc::PixelFormat_Grayscale8);
+    }
+    else if (outputFormat == "image-uint16")
+    {
+      converted.SetFormat(Orthanc::PixelFormat_Grayscale16);
+    }
+    else if (outputFormat == "image-int16")
+    {
+      converted.SetFormat(Orthanc::PixelFormat_SignedGrayscale16);
+    }
+    else
+    {
+      OrthancContext::GetInstance().LogError("Unknown output format: " + outputFormat);
+      AnswerUnsupportedImage(output);
+      return 0;
+    }
   }
 
   Orthanc::ImageAccessor convertedAccessor(converted.GetAccessor());
   Orthanc::ImageProcessing::Convert(convertedAccessor, decodedImage);
 
-
   // Compress the converted image as a PNG file
   OrthancContext::GetInstance().CompressAndAnswerPngImage(output, convertedAccessor);