changeset 1862:63587fdeec69

added 'SkipSeriesFromModalities' to ignore series from modality types
author Alain Mazy <am@osimis.io>
date Thu, 23 Sep 2021 14:38:42 +0200
parents 9290d2916150
children ff03eb63e847
files Applications/StoneWebViewer/BuildInstructions.txt Applications/StoneWebViewer/NEWS Applications/StoneWebViewer/WebApplication/app.js Applications/StoneWebViewer/WebApplication/configuration.json Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp OrthancStone/Sources/Loaders/LoadedDicomResources.h
diffstat 6 files changed, 79 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/StoneWebViewer/BuildInstructions.txt	Tue Sep 21 12:44:40 2021 +0200
+++ b/Applications/StoneWebViewer/BuildInstructions.txt	Thu Sep 23 14:38:42 2021 +0200
@@ -31,6 +31,8 @@
 make -j 6
 
 # build the StoneViewer WASM code
+# note: for fast link time: use -DCMAKE_BUILD_TYPE=RelWithDebInfo.  However, this produces an output that is too large to be embedded in the plugin
+# therefore, when you build the plugin, build the WASM code with -DCMAKE_BUILD_TYPE=Release
 cd ~/dev/build/wasm-stone-viewer
 cmake ../../orthanc-stone/Applications/StoneWebViewer/WebAssembly -DLIBCLANG=/usr/lib/x86_64-linux-gnu/libclang-10.so -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=${EMSDK}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DALLOW_DOWNLOADS=ON -G Ninja
 ninja install
--- a/Applications/StoneWebViewer/NEWS	Tue Sep 21 12:44:40 2021 +0200
+++ b/Applications/StoneWebViewer/NEWS	Thu Sep 23 14:38:42 2021 +0200
@@ -4,12 +4,13 @@
 * SeriesList: 
   - display the SeriesNumber tag in front of image count.
   - order series by SeriesNumber
-  - hide non displayable series (PR, SR)
+  - don't show non displayable series (see "SkipSeriesFromModalities")
 * In the top right overlay, display ContentDate/ContentTime if they are 
   available in the instance.  If not, StudyDate is displayed (previous
   behavior)
 * New configuration options:
   - "TimeFormat" to control the way Dicom Times are displayed.
+  - "SkipSeriesFromModalities" to ignore series from given modality types.
 
 
 Version 2.2 (2021-08-31)
--- a/Applications/StoneWebViewer/WebApplication/app.js	Tue Sep 21 12:44:40 2021 +0200
+++ b/Applications/StoneWebViewer/WebApplication/app.js	Thu Sep 23 14:38:42 2021 +0200
@@ -33,7 +33,6 @@
 var SERIES_DESCRIPTION = '0008,103e';
 var MODALITY = '0008,0060';
 var PATIENT_BIRTH_DATE = '0010,0030';
-var NON_DISPLAYABLE_MODALITIES = ['PR', 'SR']
 
 // Registry of the PDF series for which the instance metadata is still waiting
 var pendingSeriesPdf_ = {};
@@ -571,9 +570,6 @@
       // order series by SeriesNumber
       sourceSeries.sort((a, b) => {return a[SERIES_NUMBER] - b[SERIES_NUMBER];})
 
-      // discard non displayable series
-      sourceSeries = sourceSeries.filter((s) => {return NON_DISPLAYABLE_MODALITIES.indexOf(s[MODALITY]) == -1; } )
-
       for (var i = 0; i < sourceStudies.length; i++) {
         var studyInstanceUid = sourceStudies[i][STUDY_INSTANCE_UID];
         if (studyInstanceUid !== undefined) {
@@ -1147,6 +1143,10 @@
     stone.SetDicomCacheSize(app.globalConfiguration.DicomCacheSize);
   }
 
+  if ('SkipSeriesFromModalities' in app.globalConfiguration) {
+    stone.SetSkipSeriesFromModalities(JSON.stringify(app.globalConfiguration.SkipSeriesFromModalities));
+  }
+  
   // Bearer token is new in Stone Web viewer 2.0
   var token = getParameterFromUrl('token');
   if (token !== undefined)
@@ -1211,7 +1211,7 @@
 
 
 window.addEventListener('ResourcesLoaded', function() {
-  console.log('resources loaded');
+  console.log('resources loaded: ', stone.GetStudiesCount(), 'studies &', stone.GetSeriesCount(), 'series');
 
   var studies = [];
   for (var i = 0; i < stone.GetStudiesCount(); i++) {
--- a/Applications/StoneWebViewer/WebApplication/configuration.json	Tue Sep 21 12:44:40 2021 +0200
+++ b/Applications/StoneWebViewer/WebApplication/configuration.json	Thu Sep 23 14:38:42 2021 +0200
@@ -114,6 +114,11 @@
      * image, this logo will be displayed at the bottom-left of the
      * Stone Web viewer.
      **/
-    "InstitutionLogo" : ""
+    "InstitutionLogo" : "",
+
+    /**
+     * Define a list of modality type that the viewer will ignore.
+     **/
+    "SkipSeriesFromModalities": ["SR", "SEG", "PR"]
   }
 }
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Tue Sep 21 12:44:40 2021 +0200
+++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Thu Sep 23 14:38:42 2021 +0200
@@ -98,7 +98,7 @@
 #include <boost/math/special_functions/round.hpp>
 #include <boost/make_shared.hpp>
 #include <stdio.h>
-
+#include <algorithm>
 
 #if !defined(STONE_WEB_VIEWER_EXPORT)
 // We are not running ParseWebAssemblyExports.py, but we're compiling the wasm
@@ -321,6 +321,7 @@
   boost::shared_ptr<OrthancStone::SeriesMetadataLoader>    metadataLoader_;
   std::set<std::string>                                    scheduledVirtualSeriesThumbnails_;
   VirtualSeries                                            virtualSeries_;
+  std::vector<std::string>                                 skipSeriesFromModalities_;
 
   explicit ResourcesLoader(OrthancStone::ILoadersContext& context,
                            const OrthancStone::DicomSource& source) :
@@ -342,20 +343,41 @@
     LOG(INFO) << "resources loaded: " << dicom.GetSize()
               << ", " << Orthanc::EnumerationToString(payload.GetValue());
 
+    std::vector<std::string> seriesIdsToRemove;
+
     if (payload.GetValue() == Orthanc::ResourceType_Series)
     {
+      // the 'dicom' var is actually equivalent to the 'series_' member in this case
+
       for (size_t i = 0; i < dicom.GetSize(); i++)
       {
-        std::string studyInstanceUid, seriesInstanceUid;
+        std::string studyInstanceUid, seriesInstanceUid, modality;
         if (dicom.GetResource(i).LookupStringValue(
               studyInstanceUid, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, false) &&
             dicom.GetResource(i).LookupStringValue(
-              seriesInstanceUid, Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, false))
+              seriesInstanceUid, Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, false) &&
+            dicom.GetResource(i).LookupStringValue(
+              modality, Orthanc::DICOM_TAG_MODALITY, false))
         {
-          thumbnailsLoader_->ScheduleLoadThumbnail(source_, "", studyInstanceUid, seriesInstanceUid);
-          metadataLoader_->ScheduleLoadSeries(PRIORITY_LOW + 1, source_, studyInstanceUid, seriesInstanceUid);
+          // skip series that should not be displayed
+          if (std::find(skipSeriesFromModalities_.begin(), skipSeriesFromModalities_.end(), modality) == skipSeriesFromModalities_.end())
+          {
+            thumbnailsLoader_->ScheduleLoadThumbnail(source_, "", studyInstanceUid, seriesInstanceUid);
+            metadataLoader_->ScheduleLoadSeries(PRIORITY_LOW + 1, source_, studyInstanceUid, seriesInstanceUid);
+          }
+
+          else
+          {
+            seriesIdsToRemove.push_back(seriesInstanceUid);
+          }
         }
       }
+
+      for (size_t i = 0; i < seriesIdsToRemove.size(); i++)
+      {
+        LOG(INFO) << "series to hide: " << seriesIdsToRemove[i];
+        dicom.RemoveResource(seriesIdsToRemove[i]);  
+      }
     }
 
     if (pending_ == 0)
@@ -515,6 +537,11 @@
   }
 
 public:
+  void SetSkipSeriesFromModalities(const std::vector<std::string>& skipSeriesFromModalities)
+  {
+    skipSeriesFromModalities_ = skipSeriesFromModalities;
+  }
+
   static boost::shared_ptr<ResourcesLoader> Create(OrthancStone::ILoadersContext::ILock& lock,
                                                    const OrthancStone::DicomSource& source)
   {
@@ -3572,6 +3599,27 @@
   
 
   EMSCRIPTEN_KEEPALIVE
+  void SetSkipSeriesFromModalities(const char* value)
+  {
+    try
+    {
+      LOG(WARNING) << "SetSkipSeriesFromModalities " << value;
+      
+      Json::Value modalities;
+      Orthanc::Toolbox::ReadJson(modalities, value);
+      std::vector<std::string> skipSeriesFromModalities;
+
+      for (Json::Value::ArrayIndex i = 0; i < modalities.size(); i++)
+      {
+        skipSeriesFromModalities.push_back(modalities[i].asString());
+      }
+      GetResourcesLoader().SetSkipSeriesFromModalities(skipSeriesFromModalities);
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }
+
+
+  EMSCRIPTEN_KEEPALIVE
   void FetchAllStudies()
   {
     try
--- a/OrthancStone/Sources/Loaders/LoadedDicomResources.h	Tue Sep 21 12:44:40 2021 +0200
+++ b/OrthancStone/Sources/Loaders/LoadedDicomResources.h	Thu Sep 23 14:38:42 2021 +0200
@@ -109,6 +109,17 @@
       return resources_.find(id) != resources_.end();
     }
 
+    void RemoveResource(const std::string& id)
+    {
+      if (HasResource(id))
+      {
+        Resource* resource = resources_[id];
+        delete resource;
+        resources_.erase(id);
+        flattened_.clear();   // Invalidate the flattened version 
+      }
+    }
+
     void MergeResource(Orthanc::DicomMap& target,
                        const std::string& id) const;