changeset 432:4eb96c6b4e96 am-vsol-upgrade

improved handling of MONOCHROME1, background and invertion
author am@osimis.io
date Mon, 03 Dec 2018 13:53:29 +0100
parents 26b90b110719
children 8999823db8b8
files Framework/Radiography/RadiographyAlphaLayer.cpp Framework/Radiography/RadiographyDicomLayer.cpp Framework/Radiography/RadiographyLayer.cpp Framework/Radiography/RadiographyLayer.h Framework/Radiography/RadiographyScene.cpp Framework/Radiography/RadiographyScene.h Framework/Radiography/RadiographyWidget.cpp Framework/Radiography/RadiographyWidget.h Framework/StoneEnumerations.h
diffstat 9 files changed, 113 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Radiography/RadiographyAlphaLayer.cpp	Thu Nov 29 19:25:15 2018 +0100
+++ b/Framework/Radiography/RadiographyAlphaLayer.cpp	Mon Dec 03 13:53:29 2018 +0100
@@ -86,7 +86,7 @@
       float center, width;
       if (scene_.GetWindowing(center, width))
       {
-        value = center + width / 2.0f;  // TODO: shouldn't it be center alone ?
+        value = center + width / 2.0f;  // set it to the maximum pixel value of the image
       }
     }
 
--- a/Framework/Radiography/RadiographyDicomLayer.cpp	Thu Nov 29 19:25:15 2018 +0100
+++ b/Framework/Radiography/RadiographyDicomLayer.cpp	Mon Dec 03 13:53:29 2018 +0100
@@ -63,8 +63,6 @@
       SetPixelSpacing(pixelSpacing[0], pixelSpacing[1]);
     }
 
-    //SetPan(-0.5 * GetPixelSpacingX(), -0.5 * GetPixelSpacingY());
-
     OrthancPlugins::DicomDatasetReader reader(dataset);
 
     unsigned int width, height;
@@ -77,6 +75,18 @@
     {
       SetSize(width, height);
     }
+
+    if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION)))
+    {
+      if (tmp == "MONOCHROME1")
+      {
+        SetPreferredPhotomotricDisplayMode(PhotometricDisplayMode_Monochrome1);
+      }
+      else if (tmp == "MONOCHROME2")
+      {
+        SetPreferredPhotomotricDisplayMode(PhotometricDisplayMode_Monochrome2);
+      }
+    }
   }
 
   void RadiographyDicomLayer::SetSourceImage(Orthanc::ImageAccessor* image)   // Takes ownership
--- a/Framework/Radiography/RadiographyLayer.cpp	Thu Nov 29 19:25:15 2018 +0100
+++ b/Framework/Radiography/RadiographyLayer.cpp	Mon Dec 03 13:53:29 2018 +0100
@@ -185,7 +185,8 @@
     index_(0),
     hasSize_(false),
     width_(0),
-    height_(0)
+    height_(0),
+    prefferedPhotometricDisplayMode_(PhotometricDisplayMode_Default)
   {
     UpdateTransform();
   }
--- a/Framework/Radiography/RadiographyLayer.h	Thu Nov 29 19:25:15 2018 +0100
+++ b/Framework/Radiography/RadiographyLayer.h	Mon Dec 03 13:53:29 2018 +0100
@@ -30,7 +30,7 @@
   class RadiographyLayer : public boost::noncopyable
   {
     friend class RadiographyScene;
-      
+
   public:
     class Geometry
     {
@@ -139,6 +139,8 @@
     AffineTransform2D  transform_;
     AffineTransform2D  transformInverse_;
     Geometry           geometry_;
+    PhotometricDisplayMode  prefferedPhotometricDisplayMode_;
+
 
   protected:
     const AffineTransform2D& GetTransform() const
@@ -146,6 +148,11 @@
       return transform_;
     }
 
+    void SetPreferredPhotomotricDisplayMode(PhotometricDisplayMode  prefferedPhotometricDisplayMode)
+    {
+      prefferedPhotometricDisplayMode_ = prefferedPhotometricDisplayMode;
+    }
+
   private:
     void UpdateTransform();
       
@@ -257,6 +264,11 @@
     virtual bool GetDefaultWindowing(float& center,
                                      float& width) const = 0;
 
+    PhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const
+    {
+      return prefferedPhotometricDisplayMode_;
+    }
+
     virtual void Render(Orthanc::ImageAccessor& buffer,
                         const AffineTransform2D& viewTransform,
                         ImageInterpolation interpolation) const = 0;
--- a/Framework/Radiography/RadiographyScene.cpp	Thu Nov 29 19:25:15 2018 +0100
+++ b/Framework/Radiography/RadiographyScene.cpp	Mon Dec 03 13:53:29 2018 +0100
@@ -166,6 +166,21 @@
     }
   }
 
+  PhotometricDisplayMode RadiographyScene::GetPreferredPhotomotricDisplayMode() const
+  {
+    // return the mode of the first layer who "cares" about its display mode (normaly, the one and only layer that is a DicomLayer)
+    for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++)
+    {
+      if (it->second->GetPreferredPhotomotricDisplayMode() != PhotometricDisplayMode_Default)
+      {
+        return it->second->GetPreferredPhotomotricDisplayMode();
+      }
+    }
+
+    return PhotometricDisplayMode_Default;
+  }
+
+
   void RadiographyScene::GetLayersIndexes(std::vector<size_t>& output) const
   {
     for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++)
@@ -423,8 +438,6 @@
                                 const AffineTransform2D& viewTransform,
                                 ImageInterpolation interpolation) const
   {
-    Orthanc::ImageProcessing::Set(buffer, 0);
-
     // Render layers in the background-to-foreground order
     for (size_t index = 0; index < countLayers_; index++)
     {
@@ -571,6 +584,9 @@
           AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY),
           AffineTransform2D::CreateOffset(-extent.GetX1(), -extent.GetY1()));
 
+    // wipe background before rendering
+    Orthanc::ImageProcessing::Set(layers, 0);
+
     Render(layers, view, interpolation);
 
     Orthanc::Image rendered(Orthanc::PixelFormat_Grayscale16,
--- a/Framework/Radiography/RadiographyScene.h	Thu Nov 29 19:25:15 2018 +0100
+++ b/Framework/Radiography/RadiographyScene.h	Mon Dec 03 13:53:29 2018 +0100
@@ -23,7 +23,7 @@
 
 #include "RadiographyLayer.h"
 #include "../Toolbox/OrthancApiClient.h"
-
+#include "Framework/StoneEnumerations.h"
 
 namespace OrthancStone
 {
@@ -139,6 +139,8 @@
     void SetWindowing(float center,
                       float width);
 
+    PhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const;
+
     RadiographyLayer& LoadText(const Orthanc::Font& font,
                                const std::string& utf8,
                                RadiographyLayer::Geometry* geometry);
--- a/Framework/Radiography/RadiographyWidget.cpp	Thu Nov 29 19:25:15 2018 +0100
+++ b/Framework/Radiography/RadiographyWidget.cpp	Mon Dec 03 13:53:29 2018 +0100
@@ -21,18 +21,53 @@
 
 #include "RadiographyWidget.h"
 
+#include <Core/OrthancException.h>
 #include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
 
 
 namespace OrthancStone
 {
+
+  bool RadiographyWidget::IsInvertedInternal() const
+  {
+    return (scene_->GetPreferredPhotomotricDisplayMode() == PhotometricDisplayMode_Monochrome1) ^ invert_; // MONOCHROME1 images must be inverted and the user can invert the image too -> XOR the two
+  }
+
+  void RadiographyWidget::RenderBackground(Orthanc::ImageAccessor& image, float minValue, float maxValue)
+  {
+    // wipe background before rendering
+    float backgroundValue = minValue;
+
+    switch (scene_->GetPreferredPhotomotricDisplayMode())
+    {
+    case PhotometricDisplayMode_Monochrome1:
+    case PhotometricDisplayMode_Default:
+      if (IsInvertedInternal())
+        backgroundValue = maxValue;
+      else
+        backgroundValue = minValue;
+      break;
+    case PhotometricDisplayMode_Monochrome2:
+      if (IsInvertedInternal())
+        backgroundValue = minValue;
+      else
+        backgroundValue = maxValue;
+      break;
+    default:
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+    }
+
+    Orthanc::ImageProcessing::Set(image, backgroundValue);
+  }
+
   bool RadiographyWidget::RenderInternal(unsigned int width,
                                          unsigned int height,
                                          ImageInterpolation interpolation)
   {
     float windowCenter, windowWidth;
     scene_->GetWindowingWithDefault(windowCenter, windowWidth);
-      
+
     float x0 = windowCenter - windowWidth / 2.0f;
     float x1 = windowCenter + windowWidth / 2.0f;
 
@@ -56,8 +91,10 @@
         cairoBuffer_.reset(new CairoSurface(width, height));
       }
 
+      RenderBackground(*floatBuffer_, x0, x1);
+
       scene_->Render(*floatBuffer_, GetView().GetMatrix(), interpolation);
-        
+
       // Conversion from Float32 to BGRA32 (cairo). Very similar to
       // GrayscaleFrameRenderer => TODO MERGE?
 
@@ -65,7 +102,9 @@
       cairoBuffer_->GetWriteableAccessor(target);
 
       float scaling = 255.0f / (x1 - x0);
-        
+
+      bool invert = IsInvertedInternal();
+
       for (unsigned int y = 0; y < height; y++)
       {
         const float* p = reinterpret_cast<const float*>(floatBuffer_->GetConstRow(y));
@@ -88,7 +127,7 @@
             v = static_cast<uint8_t>(scaling * (*p - x0));  // (*)
           }
 
-          if (invert_)
+          if (invert)
           {
             v = 255 - v;
           }
@@ -172,14 +211,15 @@
   
   void RadiographyWidget::OnGeometryChanged(const RadiographyScene::GeometryChangedMessage& message)
   {
-    LOG(INFO) << "Geometry has changed";
+    LOG(INFO) << "Scene geometry has changed";
+
     FitContent();
   }
 
   
   void RadiographyWidget::OnContentChanged(const RadiographyScene::ContentChangedMessage& message)
   {
-    LOG(INFO) << "Content has changed";
+    LOG(INFO) << "Scene content has changed";
     NotifyContentChanged();
   }
 
@@ -220,12 +260,14 @@
     scene_ = scene;
 
     scene_->RegisterObserverCallback(
-      new Callable<RadiographyWidget, RadiographyScene::GeometryChangedMessage>
-      (*this, &RadiographyWidget::OnGeometryChanged));
+          new Callable<RadiographyWidget, RadiographyScene::GeometryChangedMessage>
+          (*this, &RadiographyWidget::OnGeometryChanged));
 
     scene_->RegisterObserverCallback(
-      new Callable<RadiographyWidget, RadiographyScene::ContentChangedMessage>
-      (*this, &RadiographyWidget::OnContentChanged));
+          new Callable<RadiographyWidget, RadiographyScene::ContentChangedMessage>
+          (*this, &RadiographyWidget::OnContentChanged));
+
+    NotifyContentChanged();
 
     // force redraw
     FitContent();
--- a/Framework/Radiography/RadiographyWidget.h	Thu Nov 29 19:25:15 2018 +0100
+++ b/Framework/Radiography/RadiographyWidget.h	Mon Dec 03 13:53:29 2018 +0100
@@ -53,6 +53,10 @@
     virtual bool RenderScene(CairoContext& context,
                              const ViewportGeometry& view);
 
+    virtual void RenderBackground(Orthanc::ImageAccessor& image, float minValue, float maxValue);
+
+    bool IsInvertedInternal() const;
+
   public:
     RadiographyWidget(MessageBroker& broker,
                       boost::shared_ptr<RadiographyScene> scene,  // TODO: check how we can avoid boost::shared_ptr here since we don't want them in the public API (app is keeping a boost::shared_ptr to this right now)
--- a/Framework/StoneEnumerations.h	Thu Nov 29 19:25:15 2018 +0100
+++ b/Framework/StoneEnumerations.h	Mon Dec 03 13:53:29 2018 +0100
@@ -180,6 +180,14 @@
     Corner_BottomRight
   };
 
+  enum PhotometricDisplayMode
+  {
+    PhotometricDisplayMode_Default,
+
+    PhotometricDisplayMode_Monochrome1,
+    PhotometricDisplayMode_Monochrome2
+  };
+
   
   bool StringToSopClassUid(SopClassUid& result,
                            const std::string& source);