diff Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp @ 1703:76c590a62755

start work on series with multiple multiframe instances
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 27 Nov 2020 16:36:43 +0100
parents bc40b6450261
children 902d13889ae4
line wrap: on
line diff
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Fri Nov 27 13:57:28 2020 +0100
+++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Fri Nov 27 16:36:43 2020 +0100
@@ -195,6 +195,9 @@
     virtual void SignalSeriesPdfLoaded(const std::string& studyInstanceUid,
                                        const std::string& seriesInstanceUid,
                                        const std::string& pdf) = 0;
+
+    virtual void SignalMultiframeInstanceThumbnailLoaded(const std::string& sopInstanceUid,
+                                                         const std::string& jpeg) = 0;
   };
   
 private:
@@ -207,6 +210,7 @@
   boost::shared_ptr<OrthancStone::DicomResourcesLoader>    resourcesLoader_;
   boost::shared_ptr<OrthancStone::SeriesThumbnailsLoader>  thumbnailsLoader_;
   boost::shared_ptr<OrthancStone::SeriesMetadataLoader>    metadataLoader_;
+  std::set<std::string>                                    scheduledMultiframeInstances_;
 
   explicit ResourcesLoader(OrthancStone::ILoadersContext& context,
                            const OrthancStone::DicomSource& source) :
@@ -361,6 +365,44 @@
     }
   }
 
+  void FetchInstanceThumbnail(const std::string& studyInstanceUid,
+                              const std::string& seriesInstanceUid,
+                              const std::string& sopInstanceUid)
+  {
+    if (scheduledMultiframeInstances_.find(sopInstanceUid) == scheduledMultiframeInstances_.end())
+    {
+      scheduledMultiframeInstances_.insert(sopInstanceUid);
+      
+      std::map<std::string, std::string> arguments;
+      std::map<std::string, std::string> headers;
+      arguments["viewport"] = (
+        boost::lexical_cast<std::string>(thumbnailsLoader_->GetThumbnailWidth()) + "," +
+        boost::lexical_cast<std::string>(thumbnailsLoader_->GetThumbnailHeight()));
+      headers["Accept"] = Orthanc::MIME_JPEG;
+
+      const std::string uri = ("studies/" + studyInstanceUid + "/series/" + seriesInstanceUid +
+                               "/instances/" + sopInstanceUid + "/frames/1/rendered");
+
+      {
+        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)));
+      }
+    }
+  }
+
+  void HandleInstanceThumbnail(const OrthancStone::HttpCommand::SuccessMessage& message)
+  {
+    if (observer_.get() != NULL)
+    {
+      const std::string& sopInstanceUid =
+        dynamic_cast<const Orthanc::SingleValueObject<std::string>&>(
+          message.GetOrigin().GetPayload()).GetValue();
+      observer_->SignalMultiframeInstanceThumbnailLoaded(sopInstanceUid, message.GetAnswer());
+    }
+  }
+
 public:
   static boost::shared_ptr<ResourcesLoader> Create(OrthancStone::ILoadersContext::ILock& lock,
                                                    const OrthancStone::DicomSource& source)
@@ -382,7 +424,10 @@
 
     loader->Register<OrthancStone::ParseDicomSuccessMessage>(
       lock.GetOracleObservable(), &ResourcesLoader::Handle);
-    
+
+    loader->Register<OrthancStone::HttpCommand::SuccessMessage>(
+      lock.GetOracleObservable(), &ResourcesLoader::HandleInstanceThumbnail);
+
     return loader;
   }
   
@@ -428,13 +473,13 @@
   }
 
   void GetStudy(Orthanc::DicomMap& target,
-                size_t i)
+                size_t i) const
   {
     target.Assign(studies_->GetResource(i));
   }
 
   void GetSeries(Orthanc::DicomMap& target,
-                 size_t i)
+                 size_t i) const
   {
     target.Assign(series_->GetResource(i));
 
@@ -449,26 +494,65 @@
 
   OrthancStone::SeriesThumbnailType GetSeriesThumbnail(std::string& image,
                                                        std::string& mime,
-                                                       const std::string& seriesInstanceUid)
+                                                       const std::string& seriesInstanceUid) const
   {
     return thumbnailsLoader_->GetSeriesThumbnail(image, mime, seriesInstanceUid);
   }
 
   void FetchSeriesMetadata(int priority,
                            const std::string& studyInstanceUid,
-                           const std::string& seriesInstanceUid)
+                           const std::string& seriesInstanceUid) const
   {
     metadataLoader_->ScheduleLoadSeries(priority, source_, studyInstanceUid, seriesInstanceUid);
   }
 
-  bool IsSeriesComplete(const std::string& seriesInstanceUid)
+  bool IsSeriesComplete(const std::string& seriesInstanceUid) const
   {
     OrthancStone::SeriesMetadataLoader::Accessor accessor(*metadataLoader_, seriesInstanceUid);
     return accessor.IsComplete();
   }
 
+  bool LookupMultiframeSeries(std::map<std::string, unsigned int>& numberOfFramesPerInstance,
+                              const std::string& seriesInstanceUid)
+  {
+    numberOfFramesPerInstance.clear();
+
+    OrthancStone::SeriesMetadataLoader::Accessor accessor(*metadataLoader_, seriesInstanceUid);
+    if (accessor.IsComplete() &&
+        accessor.GetInstancesCount() >= 2)
+    {
+      bool isMultiframe = 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;
+        }
+      }
+
+      if (isMultiframe)
+      {
+        for (size_t i = 0; i < accessor.GetInstancesCount(); i++)
+        {
+          OrthancStone::DicomInstanceParameters p(accessor.GetInstance(i));
+          FetchInstanceThumbnail(p.GetStudyInstanceUid(), p.GetSeriesInstanceUid(), p.GetSopInstanceUid());
+        }
+      }
+
+      return isMultiframe;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
   bool SortSeriesFrames(OrthancStone::SortedFrames& target,
-                        const std::string& seriesInstanceUid)
+                        const std::string& seriesInstanceUid) const
   {
     OrthancStone::SeriesMetadataLoader::Accessor accessor(*metadataLoader_, seriesInstanceUid);
     
@@ -2674,6 +2758,24 @@
       pdf.empty() ? 0 : reinterpret_cast<intptr_t>(pdf.c_str()),  // Explicit conversion to an integer
       pdf.size());
   }
+
+
+  virtual void SignalMultiframeInstanceThumbnailLoaded(const std::string& sopInstanceUid,
+                                                       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),
+                                        "thumbnail" : UTF8ToString($1) });
+        window.dispatchEvent(customEvent);
+      },
+      sopInstanceUid.c_str(),
+      dataUriScheme.c_str());
+  }
 };
 
 
@@ -3246,4 +3348,32 @@
     }
     EXTERN_CATCH_EXCEPTIONS;
   }
+
+
+  EMSCRIPTEN_KEEPALIVE
+  int LoadMultiframeInstancesFromSeries(const char* seriesInstanceUid)
+  {
+    try
+    {
+      std::map<std::string, unsigned int> numberOfFramesPerInstance;
+      if (GetResourcesLoader().LookupMultiframeSeries(numberOfFramesPerInstance, seriesInstanceUid))
+      {
+        Json::Value json = Json::objectValue;
+        for (std::map<std::string, unsigned int>::const_iterator it =
+               numberOfFramesPerInstance.begin(); it != numberOfFramesPerInstance.end(); ++it)
+        {
+          json[it->first] = it->second;
+        }
+
+        stringBuffer_ = json.toStyledString();
+        return true;
+      }
+      else
+      {
+        return false;
+      }
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+    return false;
+  }
 }