diff Applications/Samples/SingleFrameEditorApplication.h @ 338:b3b3fa0e3689 am-2

BitmapStack
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 19 Oct 2018 12:50:38 +0200
parents c4d4213f095c
children 5a7915b23138
line wrap: on
line diff
--- a/Applications/Samples/SingleFrameEditorApplication.h	Thu Oct 18 20:07:09 2018 +0200
+++ b/Applications/Samples/SingleFrameEditorApplication.h	Fri Oct 19 12:50:38 2018 +0200
@@ -25,22 +25,215 @@
 
 #include "../../Framework/Layers/OrthancFrameLayerSource.h"
 
+#include <Core/DicomFormat/DicomArray.h>
+#include <Core/Images/PamReader.h>
 #include <Core/Logging.h>
+#include <Core/Toolbox.h>
+#include <Plugins/Samples/Common/FullOrthancDataset.h>
 
 namespace OrthancStone
 {
-
-  class GrayscaleBitmapStack :
-    public WorldSceneWidget,
+  class BitmapStack :
     public IObserver,
     public IObservable
   {
   public:
-    typedef OriginMessage<MessageType_Widget_GeometryChanged, GrayscaleBitmapStack> GeometryChangedMessage;
-    typedef OriginMessage<MessageType_Widget_ContentChanged, GrayscaleBitmapStack> ContentChangedMessage;
+    typedef OriginMessage<MessageType_Widget_GeometryChanged, BitmapStack> GeometryChangedMessage;
+    typedef OriginMessage<MessageType_Widget_ContentChanged, BitmapStack> ContentChangedMessage;
 
   private:
+    class Bitmap : public boost::noncopyable
+    {
+    private:
+      std::string                            uuid_;   // TODO is this necessary?
+      std::auto_ptr<Orthanc::ImageAccessor>  source_;
+      std::auto_ptr<Orthanc::ImageAccessor>  converted_;  // Float32 or RGB24
+      std::auto_ptr<Orthanc::Image>          alpha_;  // Grayscale8 (if any)
+      std::auto_ptr<DicomFrameConverter>     converter_;
+
+      void ApplyConverter()
+      {
+        if (source_.get() != NULL &&
+            converter_.get() != NULL)
+        {
+          printf("CONVERTED!\n");
+          converted_.reset(converter_->ConvertFrame(*source_));
+        }
+      }
+      
+    public:
+      Bitmap(const std::string& uuid) :
+        uuid_(uuid)
+      {
+      }
+      
+      void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset)
+      {
+        converter_.reset(new DicomFrameConverter);
+        converter_->ReadParameters(dataset);
+        ApplyConverter();
+      }
+
+      void SetSourceImage(Orthanc::ImageAccessor* image)   // Takes ownership
+      {
+        source_.reset(image);
+        ApplyConverter();
+      }
+
+      bool GetDefaultWindowing(float& center,
+                               float& width) const
+      {
+        if (converter_.get() != NULL &&
+            converter_->HasDefaultWindow())
+        {
+          center = static_cast<float>(converter_->GetDefaultWindowCenter());
+          width = static_cast<float>(converter_->GetDefaultWindowWidth());
+        }
+      }
+    }; 
+
+
+    typedef std::map<std::string, Bitmap*>  Bitmaps;
+        
     OrthancApiClient&    orthanc_;
+    bool                 hasWindowing_;
+    float                windowingCenter_;
+    float                windowingWidth_;
+    Bitmaps              bitmaps_;
+
+  public:
+    BitmapStack(MessageBroker& broker,
+                OrthancApiClient& orthanc) :
+      IObserver(broker),
+      IObservable(broker),
+      orthanc_(orthanc),
+      hasWindowing_(false),
+      windowingCenter_(0),  // Dummy initialization
+      windowingWidth_(0)    // Dummy initialization
+    {
+    }
+
+    
+    virtual ~BitmapStack()
+    {
+      for (Bitmaps::iterator it = bitmaps_.begin(); it != bitmaps_.end(); it++)
+      {
+        assert(it->second != NULL);
+        delete it->second;
+      }
+    }
+    
+
+    std::string LoadFrame(const std::string& instance,
+                          unsigned int frame,
+                          bool httpCompression)
+    {
+      std::string uuid;
+      
+      for (;;)
+      {
+        uuid = Orthanc::Toolbox::GenerateUuid();
+        if (bitmaps_.find(uuid) == bitmaps_.end())
+        {
+          break;
+        }
+      }
+
+      bitmaps_[uuid] = new Bitmap(uuid);
+      
+
+      {
+        IWebService::Headers headers;
+        std::string uri = "/instances/" + instance + "/tags";
+        orthanc_.GetBinaryAsync(uri, headers,
+                                new Callable<BitmapStack, OrthancApiClient::BinaryResponseReadyMessage>
+                                (*this, &BitmapStack::OnTagsReceived), NULL,
+                                new Orthanc::SingleValueObject<std::string>(uuid));
+      }
+
+      {
+        IWebService::Headers headers;
+        headers["Accept"] = "image/x-portable-arbitrarymap";
+
+        if (httpCompression)
+        {
+          headers["Accept-Encoding"] = "gzip";
+        }
+        
+        std::string uri = "/instances/" + instance + "/frames/" + boost::lexical_cast<std::string>(frame) + "/image-uint16";
+        orthanc_.GetBinaryAsync(uri, headers,
+                                new Callable<BitmapStack, OrthancApiClient::BinaryResponseReadyMessage>
+                                (*this, &BitmapStack::OnFrameReceived), NULL,
+                                new Orthanc::SingleValueObject<std::string>(uuid));
+      }
+
+      return uuid;
+    }
+
+    
+    void OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message)
+    {
+      const std::string& uuid = dynamic_cast<Orthanc::SingleValueObject<std::string>*>(message.Payload)->GetValue();
+      
+      printf("JSON received: [%s] (%d bytes) for bitmap %s\n",
+             message.Uri.c_str(), message.AnswerSize, uuid.c_str());
+
+      Bitmaps::iterator bitmap = bitmaps_.find(uuid);
+      if (bitmap != bitmaps_.end())
+      {
+        assert(bitmap->second != NULL);
+        
+        OrthancPlugins::FullOrthancDataset dicom(message.Answer, message.AnswerSize);
+        bitmap->second->SetDicomTags(dicom);
+
+        float c, w;
+        if (!hasWindowing_ &&
+            bitmap->second->GetDefaultWindowing(c, w))
+        {
+          hasWindowing_ = true;
+          windowingCenter_ = c;
+          windowingWidth_ = w;
+        }
+
+        EmitMessage(GeometryChangedMessage(*this));
+      }
+    }
+    
+
+    void OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message)
+    {
+      const std::string& uuid = dynamic_cast<Orthanc::SingleValueObject<std::string>*>(message.Payload)->GetValue();
+
+      printf("Frame received: [%s] (%d bytes) for bitmap %s\n", message.Uri.c_str(), message.AnswerSize, uuid.c_str());
+      
+      Bitmaps::iterator bitmap = bitmaps_.find(uuid);
+      if (bitmap != bitmaps_.end())
+      {
+        assert(bitmap->second != NULL);
+
+        std::string content;
+        if (message.AnswerSize > 0)
+        {
+          content.assign(reinterpret_cast<const char*>(message.Answer), message.AnswerSize);
+        }
+        
+        std::auto_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader);
+        reader->ReadFromMemory(content);
+        bitmap->second->SetSourceImage(reader.release());
+
+        EmitMessage(ContentChangedMessage(*this));
+      }
+    }
+  };
+
+  
+  class BitmapStackWidget :
+    public WorldSceneWidget,
+    public IObservable,
+    public IObserver
+  {
+  private:
+    BitmapStack&   stack_;
 
   protected:
     virtual Extent2D GetSceneExtent()
@@ -55,27 +248,29 @@
     }
 
   public:
-    GrayscaleBitmapStack(MessageBroker& broker,
-                         OrthancApiClient& orthanc,
-                         const std::string& name) :
+    BitmapStackWidget(MessageBroker& broker,
+                      BitmapStack& stack,
+                      const std::string& name) :
       WorldSceneWidget(name),
+      IObservable(broker),
       IObserver(broker),
-      IObservable(broker),
-      orthanc_(orthanc)
+      stack_(stack)
     {
+      stack.RegisterObserverCallback(new Callable<BitmapStackWidget, BitmapStack::GeometryChangedMessage>(*this, &BitmapStackWidget::OnGeometryChanged));
+      stack.RegisterObserverCallback(new Callable<BitmapStackWidget, BitmapStack::ContentChangedMessage>(*this, &BitmapStackWidget::OnContentChanged));
     }
 
-    void LoadDicom(const std::string& dicom)
+    void OnGeometryChanged(const BitmapStack::GeometryChangedMessage& message)
     {
-      orthanc_.GetBinaryAsync("/instances/" + dicom + "/file", "application/dicom",
-                              new Callable<GrayscaleBitmapStack, OrthancApiClient::BinaryResponseReadyMessage>(*this, &GrayscaleBitmapStack::OnDicomReceived));
-    }
-  
-    void OnDicomReceived(const OrthancApiClient::BinaryResponseReadyMessage& message)
-    {
-      printf("DICOM received: [%s] (%d bytes)\n", message.Uri.c_str(), message.AnswerSize);
+      printf("Geometry has changed\n");
+      FitContent();
     }
 
+    void OnContentChanged(const BitmapStack::ContentChangedMessage& message)
+    {
+      printf("Content has changed\n");
+      NotifyContentChanged();
+    }
   };
 
   
@@ -197,13 +392,9 @@
         }
       };
 
-      void OnGeometryChanged(const GrayscaleBitmapStack::GeometryChangedMessage& message)
-      {
-        mainWidget_->FitContent();
-      }
-      
       std::auto_ptr<Interactor>        mainWidgetInteractor_;
       std::auto_ptr<OrthancApiClient>  orthancApiClient_;
+      std::auto_ptr<BitmapStack>       stack_;
       Tools                            currentTool_;
       const OrthancFrameLayerSource*   source_;
       unsigned int                     slice_;
@@ -251,15 +442,11 @@
 
         orthancApiClient_.reset(new OrthancApiClient(IObserver::broker_, context_->GetWebService()));
 
+        stack_.reset(new BitmapStack(IObserver::broker_, *orthancApiClient_));
+        stack_->LoadFrame(instance, frame, false);
+        stack_->LoadFrame(instance, frame, false);
         
-        mainWidget_ = new GrayscaleBitmapStack(broker_, *orthancApiClient_, "main-widget");
-        dynamic_cast<GrayscaleBitmapStack*>(mainWidget_)->LoadDicom(instance);
-
-        dynamic_cast<GrayscaleBitmapStack*>(mainWidget_)->RegisterObserverCallback(
-          new Callable<SingleFrameEditorApplication,
-          GrayscaleBitmapStack::GeometryChangedMessage>
-          (*this, &SingleFrameEditorApplication::OnGeometryChanged));
-
+        mainWidget_ = new BitmapStackWidget(IObserver::broker_, *stack_, "main-widget");
         mainWidget_->SetTransmitMouseOver(true);
 
         mainWidgetInteractor_.reset(new Interactor(*this));