changeset 391:27001924c456

Fix issue #162 ("DICOMweb metadata resource reads all instances")
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 17 Feb 2020 16:33:32 +0100
parents 5aa7b5a1bb38
children c467391b3585
files NEWS Plugin/Configuration.cpp Plugin/Configuration.h Plugin/WadoRs.cpp
diffstat 4 files changed, 130 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Mon Feb 10 10:29:59 2020 +0100
+++ b/NEWS	Mon Feb 17 16:33:32 2020 +0100
@@ -7,6 +7,9 @@
 * Support of "window", "viewport" and "quality" parameters in "Retrieve Rendered Transaction"
 * Support of "/studies/.../series/.../rendered"
 * QIDO-RS: Allow to query against a list of multiple values separated by commas
+* WADO-RS "Retrieve Metadata": Configuration options "StudiesMetadata" and "SeriesMetadata",
+  whose value can be "Full" (read all DICOM instances from the storage area) or
+  "MainDicomTags" (only report the main DICOM tags from the Orthanc database)
 
 Maintenance
 -----------
@@ -18,6 +21,7 @@
 * Accept multiple MIME types in Accept header for WADO-RS "Retrieve Metadata"
 * Added explicit "Accept" header to avoid uncompressing DICOM files by Google cloud
   https://groups.google.com/d/msg/orthanc-users/w1Ekrsc6-U8/T2a_DoQ5CwAJ
+* Fix issue #162 ("DICOMweb metadata resource reads all instances")
 * Fix issue #164 ("JPEG YBR_422 generates a 500 in the DicomWeb plugin")
 * Upgrade to GDCM 3.0.4 in static builds
 
--- a/Plugin/Configuration.cpp	Mon Feb 10 10:29:59 2020 +0100
+++ b/Plugin/Configuration.cpp	Mon Feb 17 16:33:32 2020 +0100
@@ -307,6 +307,10 @@
       OrthancPlugins::OrthancConfiguration servers;
       configuration_->GetSection(servers, "Servers");
       OrthancPlugins::DicomWebServers::GetInstance().Load(servers.GetJson());
+
+      // Check configuration during initialization
+      GetMetadataMode(Orthanc::ResourceType_Study);
+      GetMetadataMode(Orthanc::ResourceType_Series);
     }
 
 
@@ -605,5 +609,45 @@
         return false;   // By default, return DICOM+JSON
       }
     }
+
+    
+    MetadataMode GetMetadataMode(Orthanc::ResourceType level)
+    {
+      static const std::string FULL = "Full";
+      static const std::string MAIN_DICOM_TAGS = "MainDicomTags";
+      
+      std::string key;
+      switch (level)
+      {
+        case Orthanc::ResourceType_Study:
+          key = "StudiesMetadata";
+          break;
+
+        case Orthanc::ResourceType_Series:
+          key = "SeriesMetadata";
+          break;
+
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+      }
+
+      std::string value = GetStringValue(key, FULL);
+
+      if (value == FULL)
+      {
+        return MetadataMode_Full;
+      }
+      else if (value == MAIN_DICOM_TAGS)
+      {
+        return MetadataMode_MainDicomTags;
+      }
+      else
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange,
+                                        "Bad value for option \"" + key +
+                                        "\": Should be either \"" + FULL + "\" or \"" +
+                                        MAIN_DICOM_TAGS + "\"");
+      }
+    }
   }
 }
--- a/Plugin/Configuration.h	Mon Feb 10 10:29:59 2020 +0100
+++ b/Plugin/Configuration.h	Mon Feb 17 16:33:32 2020 +0100
@@ -45,6 +45,14 @@
   static const Orthanc::DicomTag DICOM_TAG_REFERENCED_SOP_CLASS_UID(0x0008, 0x1150);
   static const Orthanc::DicomTag DICOM_TAG_REFERENCED_SOP_INSTANCE_UID(0x0008, 0x1155);
 
+  enum MetadataMode
+  {
+    MetadataMode_Full,           // Read all the DICOM instances from the storage area
+    MetadataMode_MainDicomTags,  // Only use the Orthanc database (main DICOM tags only)
+    MetadataMode_Interpolate     // Unused so far
+  };
+
+
   bool LookupHttpHeader(std::string& value,
                         const OrthancPluginHttpRequest* request,
                         const std::string& header);
@@ -115,5 +123,7 @@
 
     // TODO => REMOVE
     bool IsXmlExpected(const OrthancPluginHttpRequest* request);
+
+    MetadataMode GetMetadataMode(Orthanc::ResourceType level);
   }
 }
--- a/Plugin/WadoRs.cpp	Mon Feb 10 10:29:59 2020 +0100
+++ b/Plugin/WadoRs.cpp	Mon Feb 17 16:33:32 2020 +0100
@@ -292,6 +292,7 @@
     
     static bool ReadResource(Orthanc::DicomMap& dicom,
                              std::string& parent,
+                             OrthancPlugins::MetadataMode mode,
                              const std::string& orthancId,
                              Orthanc::ResourceType level)
     {
@@ -362,17 +363,22 @@
       }
 
 
-      /**
-       * Complete the series-level tags, with instance-level tags that
-       * are not considered as "main DICOM tags" in Orthanc, but that
-       * are necessary for Web viewers, and that should be constant
-       * throughout all the instances of the series. To this end, we
-       * read 1 DICOM instance of this series from disk, and extract
-       * the subset of tags of interest. Obviously, this is an
-       * approximation to improve performance.
-       **/
-      if (level == Orthanc::ResourceType_Series)
+      if (mode == OrthancPlugins::MetadataMode_Interpolate &&
+          level == Orthanc::ResourceType_Series)
       {
+        /**
+         * Complete the series-level tags, with instance-level tags
+         * that are not considered as "main DICOM tags" in Orthanc,
+         * but that are necessary for Web viewers, and that should be
+         * constant throughout all the instances of the series. To
+         * this end, we read 1 DICOM instance of this series from
+         * disk, and extract the subset of tags of
+         * interest. Obviously, this is an approximation to improve
+         * performance.
+         *
+         * TODO - Decide which tags are safe (i.e. what is supposed to
+         * be constant?)
+         **/
         if (!value.isMember(INSTANCES) ||
             value[INSTANCES].type() != Json::arrayValue ||
             value[INSTANCES].size() == 0 ||
@@ -407,14 +413,14 @@
             CopyTagIfMissing(dicom, instance, Orthanc::DICOM_TAG_SOP_CLASS_UID);
             CopyTagIfMissing(dicom, instance, Orthanc::DICOM_TAG_WINDOW_CENTER);
             CopyTagIfMissing(dicom, instance, Orthanc::DICOM_TAG_WINDOW_WIDTH);
-            CopyTagIfMissing(dicom, instance, Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR);
-            CopyTagIfMissing(dicom, instance, Orthanc::DICOM_TAG_FRAME_INCREMENT_POINTER);
+            CopyTagIfMissing(dicom, instance, Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR);  // TODO => probably unsafe!
+            CopyTagIfMissing(dicom, instance, Orthanc::DICOM_TAG_FRAME_INCREMENT_POINTER);  // TODO => probably unsafe!
             CopyTagIfMissing(dicom, instance, Orthanc::DICOM_TAG_SLICE_THICKNESS);
             //CopyTagIfMissing(dicom, instance, Orthanc::DICOM_TAG_IMAGE_POSITION_PATIENT);  // => Already in main DICOM tags
             CopyTagIfMissing(dicom, instance, Orthanc::DICOM_TAG_IMAGE_ORIENTATION_PATIENT);
             CopyTagIfMissing(dicom, instance, Orthanc::DICOM_TAG_RESCALE_INTERCEPT);
             CopyTagIfMissing(dicom, instance, Orthanc::DICOM_TAG_RESCALE_SLOPE);
-            CopyTagIfMissing(dicom, instance, Orthanc::DICOM_TAG_DOSE_GRID_SCALING);
+            CopyTagIfMissing(dicom, instance, Orthanc::DICOM_TAG_DOSE_GRID_SCALING);  // TODO => probably unsafe!
 
             // SeriesMetadataLoader
             //CopyTagIfMissing(dicom, instance, Orthanc::DICOM_TAG_SOP_INSTANCE_UID);  // => Already in main DICOM tags
@@ -444,6 +450,7 @@
 
     bool Lookup(Orthanc::DicomMap& dicom,
                 std::string& parent,
+                OrthancPlugins::MetadataMode mode,
                 const std::string& orthancId,
                 Orthanc::ResourceType level)
     {
@@ -452,7 +459,7 @@
       if (found == content_.end())
       {
         std::auto_ptr<Info> info(new Info);
-        if (!ReadResource(info->dicom_, info->parent_, orthancId, level))
+        if (!ReadResource(info->dicom_, info->parent_, mode, orthancId, level))
         {
           return false;
         }
@@ -481,13 +488,14 @@
 
 
     bool GetInstance(Orthanc::DicomMap& dicom,
+                     OrthancPlugins::MetadataMode mode,
                      const std::string& orthancId)
     {
       std::string seriesId, studyId, nope;
       
-      return (ReadResource(dicom, seriesId, orthancId, Orthanc::ResourceType_Instance) &&
-              Lookup(dicom, studyId, seriesId, Orthanc::ResourceType_Series) &&
-              Lookup(dicom, nope, studyId, Orthanc::ResourceType_Study));
+      return (ReadResource(dicom, seriesId, mode, orthancId, Orthanc::ResourceType_Instance) &&
+              Lookup(dicom, studyId, mode, seriesId, Orthanc::ResourceType_Series) &&
+              Lookup(dicom, nope /* patient id is unused */, mode, studyId, Orthanc::ResourceType_Study));
     }
   };
 }
@@ -495,6 +503,7 @@
 
 
 static void WriteInstanceMetadata(OrthancPlugins::DicomWebFormatter::HttpWriter& writer,
+                                  OrthancPlugins::MetadataMode mode,
                                   MainDicomTagsCache& cache,
                                   const std::string& orthancId,
                                   const std::string& studyInstanceUid,
@@ -513,23 +522,42 @@
                                 "/series/" + seriesInstanceUid + 
                                 "/instances/" + sopInstanceUid + "/bulk");
 
-#if 0
-  Orthanc::DicomMap dicom;
-  if (cache.GetInstance(dicom, orthancId))
+  switch (mode)
   {
-    writer.AddOrthancMap(dicom);
-  }  
+    case OrthancPlugins::MetadataMode_MainDicomTags:
+    case OrthancPlugins::MetadataMode_Interpolate:
+    {
+      Orthanc::DicomMap dicom;
+      if (cache.GetInstance(dicom, mode, orthancId))
+      {
+        writer.AddOrthancMap(dicom);
+      }
+
+      break;
+    }
+
+    case OrthancPlugins::MetadataMode_Full:
+    {
+      // On a SSD drive, this version is twice slower than if using
+      // cache (see below)
+    
+      OrthancPlugins::MemoryBuffer dicom;
+      if (dicom.RestApiGet("/instances/" + orthancId + "/file", false))
+      {
+        writer.AddDicom(dicom.GetData(), dicom.GetSize(), bulkRoot);
+      }
+
+      break;
+    }
+
+    default:
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+  }
+    
   
-#elif 1
-  // On a SSD drive, this version is twice slower than if using
-  // cache (see below)
-    
-  OrthancPlugins::MemoryBuffer dicom;
-  if (dicom.RestApiGet("/instances/" + orthancId + "/file", false))
-  {
-    writer.AddDicom(dicom.GetData(), dicom.GetSize(), bulkRoot);
-  }
-#else
+#if 0
+    /**
+     **/
 
   // TODO - Have a global setting to enable/disable caching of DICOMweb
 
@@ -896,6 +924,9 @@
   }
   else
   {
+    const OrthancPlugins::MetadataMode mode =
+      OrthancPlugins::Configuration::GetMetadataMode(Orthanc::ResourceType_Study);
+    
     MainDicomTagsCache cache;
 
     std::string studyOrthancId, studyInstanceUid;
@@ -913,8 +944,8 @@
 
         for (std::list<Identifier>::const_iterator b = instances.begin(); b != instances.end(); ++b)
         {
-          WriteInstanceMetadata(writer, cache, b->GetOrthancId(), studyInstanceUid, a->GetDicomUid(), b->GetDicomUid(),
-                                OrthancPlugins::Configuration::GetBaseUrl(request));
+          WriteInstanceMetadata(writer, mode, cache, b->GetOrthancId(), studyInstanceUid, a->GetDicomUid(),
+                                b->GetDicomUid(), OrthancPlugins::Configuration::GetBaseUrl(request));
         }
       }
 
@@ -937,6 +968,9 @@
   }
   else
   {
+    const OrthancPlugins::MetadataMode mode =
+      OrthancPlugins::Configuration::GetMetadataMode(Orthanc::ResourceType_Series);
+    
     MainDicomTagsCache cache;
 
     std::string seriesOrthancId, studyInstanceUid, seriesInstanceUid;
@@ -949,8 +983,8 @@
 
       for (std::list<Identifier>::const_iterator a = instances.begin(); a != instances.end(); ++a)
       {
-        WriteInstanceMetadata(writer, cache, a->GetOrthancId(), studyInstanceUid, seriesInstanceUid, a->GetDicomUid(),
-                              OrthancPlugins::Configuration::GetBaseUrl(request));
+        WriteInstanceMetadata(writer, mode, cache, a->GetOrthancId(), studyInstanceUid, seriesInstanceUid,
+                              a->GetDicomUid(), OrthancPlugins::Configuration::GetBaseUrl(request));
       }
 
       writer.Send();
@@ -976,8 +1010,8 @@
     if (LocateInstance(output, orthancId, studyInstanceUid, seriesInstanceUid, sopInstanceUid, request))
     {
       OrthancPlugins::DicomWebFormatter::HttpWriter writer(output, isXml);
-      WriteInstanceMetadata(writer, cache, orthancId, studyInstanceUid, seriesInstanceUid, sopInstanceUid,
-                            OrthancPlugins::Configuration::GetBaseUrl(request));
+      WriteInstanceMetadata(writer, OrthancPlugins::MetadataMode_Full, cache, orthancId, studyInstanceUid,
+                            seriesInstanceUid, sopInstanceUid, OrthancPlugins::Configuration::GetBaseUrl(request));
       writer.Send();      
     }
   }