diff Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp @ 1849:023cce3d7844

introduction of the concept of "virtual series"
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 29 Jun 2021 12:12:46 +0200
parents 21ccc00839f7
children 932dc2265baa
line wrap: on
line diff
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Mon Jun 28 12:19:38 2021 +0200
+++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Tue Jun 29 12:12:46 2021 +0200
@@ -187,6 +187,86 @@
 static const unsigned int DEFAULT_CINE_RATE = 30;
 
 
+
+class VirtualSeries : public boost::noncopyable
+{
+private:
+  class Item
+  {
+  private:
+    std::string   seriesInstanceUid_;
+    unsigned int  numberOfFrames_;
+
+  public:
+    Item(const std::string& seriesInstanceUid,
+         unsigned int numberOfFrames) :
+      seriesInstanceUid_(seriesInstanceUid),
+      numberOfFrames_(numberOfFrames)
+    {
+      if (numberOfFrames == 0)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+      }
+    }
+
+    const std::string& GetSeriesInstanceUid() const
+    {
+      return seriesInstanceUid_;
+    }
+
+    unsigned int GetNumberOfFrames() const
+    {
+      return numberOfFrames_;
+    }
+  };
+
+  typedef std::map<std::string, Item>  Content;
+
+  Content  content_;
+
+  const Item& GetItem(const std::string& id) const
+  {
+    Content::const_iterator found = content_.find(id);
+    
+    if (found == content_.end())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+    else
+    {
+      return found->second;
+    }  
+  }
+  
+public:
+  std::string Add(const std::string& seriesInstanceUid,
+                  const std::string& sopInstanceUid,
+                  unsigned int numberOfFrames)
+  {
+    if (content_.find(sopInstanceUid) != content_.end())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+    else
+    {
+      content_.insert(std::make_pair(sopInstanceUid, Item(seriesInstanceUid, numberOfFrames)));
+      return sopInstanceUid;
+    }
+  }
+
+  const std::string& GetSeriesInstanceUid(const std::string& id) const
+  {
+    return GetItem(id).GetSeriesInstanceUid();
+  }
+
+  unsigned int GetNumberOfFrames(const std::string& id) const
+  {
+    return GetItem(id).GetNumberOfFrames();
+  }
+};
+
+
+
 class ResourcesLoader : public OrthancStone::ObserverBase<ResourcesLoader>
 {
 public:
@@ -209,8 +289,8 @@
                                        const std::string& seriesInstanceUid,
                                        const std::string& pdf) = 0;
 
-    virtual void SignalMultiframeInstanceThumbnailLoaded(const std::string& sopInstanceUid,
-                                                         const std::string& jpeg) = 0;
+    virtual void SignalVirtualSeriesThumbnailLoaded(const std::string& virtualSeriesId,
+                                                    const std::string& jpeg) = 0;
   };
   
 private:
@@ -223,7 +303,7 @@
   boost::shared_ptr<OrthancStone::DicomResourcesLoader>    resourcesLoader_;
   boost::shared_ptr<OrthancStone::SeriesThumbnailsLoader>  thumbnailsLoader_;
   boost::shared_ptr<OrthancStone::SeriesMetadataLoader>    metadataLoader_;
-  std::set<std::string>                                    scheduledMultiframeInstances_;
+  std::set<std::string>                                    scheduledVirtualSeriesThumbnails_;
 
   explicit ResourcesLoader(OrthancStone::ILoadersContext& context,
                            const OrthancStone::DicomSource& source) :
@@ -378,13 +458,14 @@
     }
   }
 
-  void FetchInstanceThumbnail(const std::string& studyInstanceUid,
-                              const std::string& seriesInstanceUid,
-                              const std::string& sopInstanceUid)
+  void FetchVirtualSeriesThumbnail(const std::string& virtualSeriesId,
+                                   const std::string& studyInstanceUid,
+                                   const std::string& seriesInstanceUid,
+                                   const std::string& sopInstanceUid)
   {
-    if (scheduledMultiframeInstances_.find(sopInstanceUid) == scheduledMultiframeInstances_.end())
+    if (scheduledVirtualSeriesThumbnails_.find(virtualSeriesId) == scheduledVirtualSeriesThumbnails_.end())
     {
-      scheduledMultiframeInstances_.insert(sopInstanceUid);
+      scheduledVirtualSeriesThumbnails_.insert(virtualSeriesId);
       
       std::map<std::string, std::string> arguments;
       std::map<std::string, std::string> headers;
@@ -400,7 +481,7 @@
         std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(context_.Lock());
         lock->Schedule(
           GetSharedObserver(), PRIORITY_LOW + 2, source_.CreateDicomWebCommand(
-            uri, arguments, headers, new Orthanc::SingleValueObject<std::string>(sopInstanceUid)));
+            uri, arguments, headers, new Orthanc::SingleValueObject<std::string>(virtualSeriesId)));
       }
     }
   }
@@ -409,10 +490,10 @@
   {
     if (observer_.get() != NULL)
     {
-      const std::string& sopInstanceUid =
+      const std::string& virtualSeriesId =
         dynamic_cast<const Orthanc::SingleValueObject<std::string>&>(
           message.GetOrigin().GetPayload()).GetValue();
-      observer_->SignalMultiframeInstanceThumbnailLoaded(sopInstanceUid, message.GetAnswer());
+      observer_->SignalVirtualSeriesThumbnailLoaded(virtualSeriesId, message.GetAnswer());
     }
   }
 
@@ -525,38 +606,44 @@
     return accessor.IsComplete();
   }
 
-  bool LookupMultiframeSeries(std::map<std::string, unsigned int>& numberOfFramesPerInstance,
-                              const std::string& seriesInstanceUid)
+  bool LookupVirtualSeries(VirtualSeries& target /* out */,
+                           std::set<std::string>& virtualSeriesIds /* out */,
+                           const std::string& seriesInstanceUid)
   {
-    numberOfFramesPerInstance.clear();
-
     OrthancStone::SeriesMetadataLoader::Accessor accessor(*metadataLoader_, seriesInstanceUid);
     if (accessor.IsComplete() &&
         accessor.GetInstancesCount() >= 2)
     {
-      bool isMultiframe = false;
+      bool hasMultiframe = false;
       
       for (size_t i = 0; i < accessor.GetInstancesCount(); i++)
       {
         OrthancStone::DicomInstanceParameters p(accessor.GetInstance(i));
-        numberOfFramesPerInstance[p.GetSopInstanceUid()] = p.GetNumberOfFrames();
 
         if (p.GetNumberOfFrames() > 1)
         {
-          isMultiframe = true;
+          hasMultiframe = true;
         }
       }
 
-      if (isMultiframe)
+      if (hasMultiframe)
       {
         for (size_t i = 0; i < accessor.GetInstancesCount(); i++)
         {
           OrthancStone::DicomInstanceParameters p(accessor.GetInstance(i));
-          FetchInstanceThumbnail(p.GetStudyInstanceUid(), p.GetSeriesInstanceUid(), p.GetSopInstanceUid());
+
+          std::string virtualSeriesId = target.Add(seriesInstanceUid, p.GetSopInstanceUid(), p.GetNumberOfFrames());
+          virtualSeriesIds.insert(virtualSeriesId);
+          
+          FetchVirtualSeriesThumbnail(virtualSeriesId, p.GetStudyInstanceUid(), p.GetSeriesInstanceUid(), p.GetSopInstanceUid());
         }
+
+        return true;
       }
-
-      return isMultiframe;
+      else
+      {
+        return false;
+      }
     }
     else
     {
@@ -3187,20 +3274,20 @@
   }
 
 
-  virtual void SignalMultiframeInstanceThumbnailLoaded(const std::string& sopInstanceUid,
-                                                       const std::string& jpeg) ORTHANC_OVERRIDE
+  virtual void SignalVirtualSeriesThumbnailLoaded(const std::string& virtualSeriesId,
+                                                  const std::string& jpeg) ORTHANC_OVERRIDE
   {
     std::string dataUriScheme;
     Orthanc::Toolbox::EncodeDataUriScheme(dataUriScheme, "image/jpeg", jpeg);    
     
     EM_ASM({
         const customEvent = document.createEvent("CustomEvent");
-        customEvent.initCustomEvent("MultiframeInstanceThumbnailLoaded", false, false,
-                                    { "sopInstanceUid" : UTF8ToString($0),
+        customEvent.initCustomEvent("VirtualSeriesThumbnailLoaded", false, false,
+                                    { "virtualSeriesId" : UTF8ToString($0),
                                         "thumbnail" : UTF8ToString($1) });
         window.dispatchEvent(customEvent);
       },
-      sopInstanceUid.c_str(),
+      virtualSeriesId.c_str(),
       dataUriScheme.c_str());
   }
 
@@ -3271,6 +3358,7 @@
 static WebViewerAction leftButtonAction_ = WebViewerAction_Windowing;
 static WebViewerAction middleButtonAction_ = WebViewerAction_Pan;
 static WebViewerAction rightButtonAction_ = WebViewerAction_Zoom;
+static VirtualSeries virtualSeries_;
 
 
 static void FormatTags(std::string& target,
@@ -3617,15 +3705,17 @@
 
 
   EMSCRIPTEN_KEEPALIVE
-  int LoadMultipartInstanceInViewport(const char* canvas,
-                                      const char* seriesInstanceUid,
-                                      const char* sopInstanceUid)
+  int LoadVirtualSeriesInViewport(const char* canvas,
+                                  const char* virtualSeriesId)
   {
     try
     {
       std::unique_ptr<OrthancStone::SortedFrames> frames(new OrthancStone::SortedFrames);
+
+      const std::string sopInstanceUid = virtualSeriesId;  // TODO
       
-      if (GetResourcesLoader().SortMultipartInstanceFrames(*frames, seriesInstanceUid, sopInstanceUid))
+      if (GetResourcesLoader().SortMultipartInstanceFrames(
+            *frames, virtualSeries_.GetSeriesInstanceUid(virtualSeriesId), sopInstanceUid))
       {
         GetViewport(canvas)->SetFrames(frames.release());
         return 1;
@@ -3957,18 +4047,21 @@
 
 
   EMSCRIPTEN_KEEPALIVE
-  int LoadMultiframeInstancesFromSeries(const char* seriesInstanceUid)
+  int LookupVirtualSeries(const char* seriesInstanceUid)
   {
     try
     {
-      std::map<std::string, unsigned int> numberOfFramesPerInstance;
-      if (GetResourcesLoader().LookupMultiframeSeries(numberOfFramesPerInstance, seriesInstanceUid))
+      std::set<std::string> virtualSeriesIds;
+      if (GetResourcesLoader().LookupVirtualSeries(virtualSeries_, virtualSeriesIds, seriesInstanceUid))
       {
-        Json::Value json = Json::objectValue;
-        for (std::map<std::string, unsigned int>::const_iterator it =
-               numberOfFramesPerInstance.begin(); it != numberOfFramesPerInstance.end(); ++it)
+        Json::Value json = Json::arrayValue;
+        for (std::set<std::string>::const_iterator it = virtualSeriesIds.begin();
+             it != virtualSeriesIds.end(); ++it)
         {
-          json[it->first] = it->second;
+          Json::Value item = Json::objectValue;
+          item["ID"] = *it;
+          item["NumberOfFrames"] = virtualSeries_.GetNumberOfFrames(*it);
+          json.append(item);
         }
 
         stringBuffer_ = json.toStyledString();