changeset 5036:877bc3b96476

Handle Dicom sequences in ExtraMainDicomTags and save them in the 'MainDicomSequences' metadata
author Alain Mazy <am@osimis.io>
date Fri, 24 Jun 2022 15:47:10 +0200
parents 67d98fccc850
children 3444990cf295
files NEWS OrthancFramework/Sources/DicomFormat/DicomMap.cpp OrthancFramework/Sources/DicomFormat/DicomMap.h OrthancFramework/UnitTestsSources/DicomMapTests.cpp OrthancServer/Plugins/Engine/OrthancPlugins.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.h OrthancServer/Sources/DicomInstanceToStore.cpp OrthancServer/Sources/DicomInstanceToStore.h OrthancServer/Sources/OrthancConfiguration.cpp OrthancServer/Sources/OrthancConfiguration.h OrthancServer/Sources/OrthancInitialization.cpp OrthancServer/Sources/ServerContext.cpp OrthancServer/Sources/ServerEnumerations.cpp OrthancServer/Sources/ServerEnumerations.h OrthancServer/Sources/ServerIndex.cpp OrthancServer/Sources/ServerIndex.h TODO
diffstat 18 files changed, 221 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Wed Jun 22 15:37:08 2022 +0200
+++ b/NEWS	Fri Jun 24 15:47:10 2022 +0200
@@ -9,6 +9,10 @@
 * Lua: new "SetHttpTimeout" function
 * Lua: new "OnHeartBeat" callback called at regular interval provided that
        you have configured "LuaHeartBeatPeriod" > 0.
+* "ExtraMainDicomTags" configuration now accepts Dicom Sequences.  Sequences are
+  stored in a dedicated new metadata "MainDicomSequences".  This should improve
+  DicomWeb QIDO-RS and avoid warnings like "Accessing Dicom tags from storage when 
+  accessing series : 0040,0275"
 
 
 Maintenance
@@ -26,6 +30,7 @@
 * API version upgraded to 18
 * /system is now reporting "DatabaseServerIdentifier"
 * Added an Asynchronous mode to /modalities/../move.
+* "RequestedTags" option can now include DICOM sequences.
 
 Plugins
 -------
--- a/OrthancFramework/Sources/DicomFormat/DicomMap.cpp	Wed Jun 22 15:37:08 2022 +0200
+++ b/OrthancFramework/Sources/DicomFormat/DicomMap.cpp	Fri Jun 24 15:47:10 2022 +0200
@@ -34,7 +34,7 @@
 #include "../OrthancException.h"
 #include "../Toolbox.h"
 #include "DicomArray.h"
-
+#include "../DicomParsing/FromDcmtkBridge.h"
 
 namespace Orthanc
 {
@@ -612,6 +612,13 @@
     }
   }
 
+  void DicomMap::RemoveTags(const std::set<DicomTag>& tags) 
+  {
+    for (std::set<DicomTag>::const_iterator it = tags.begin(); it != tags.end(); ++it)
+    {
+      Remove(*it);
+    }
+  }
 
   static void SetupFindTemplate(DicomMap& result,
                                 const std::map<DicomTag, std::string>& mainDicomTags)
@@ -1522,7 +1529,20 @@
 
     return true;
   }
-    
+
+  void DicomMap::ExtractSequences(std::set<DicomTag>& sequences, const std::set<DicomTag>& tags)
+  {
+    sequences.clear();
+
+    for (std::set<DicomTag>::const_iterator it = tags.begin(); it != tags.end(); ++it)
+    {
+      ValueRepresentation vr = FromDcmtkBridge::LookupValueRepresentation(*it);
+      if (vr == ValueRepresentation_Sequence)
+      {
+        sequences.insert(*it);
+      }
+    }
+  }
 
   void DicomMap::Serialize(Json::Value& target) const
   {
--- a/OrthancFramework/Sources/DicomFormat/DicomMap.h	Wed Jun 22 15:37:08 2022 +0200
+++ b/OrthancFramework/Sources/DicomFormat/DicomMap.h	Fri Jun 24 15:47:10 2022 +0200
@@ -110,6 +110,8 @@
 
     void Remove(const DicomTag& tag);
 
+    void RemoveTags(const std::set<DicomTag>& tags);
+
     void ExtractPatientInformation(DicomMap& result) const;
 
     void ExtractStudyInformation(DicomMap& result) const;
@@ -147,6 +149,8 @@
 
     static bool HasComputedTags(const std::set<DicomTag>& tags);
 
+    static void ExtractSequences(std::set<DicomTag>& sequences, const std::set<DicomTag>& tags);
+
     static const std::set<DicomTag>& GetMainDicomTags(ResourceType level);
 
     // returns a string uniquely identifying the list of main dicom tags for a level
--- a/OrthancFramework/UnitTestsSources/DicomMapTests.cpp	Wed Jun 22 15:37:08 2022 +0200
+++ b/OrthancFramework/UnitTestsSources/DicomMapTests.cpp	Fri Jun 24 15:47:10 2022 +0200
@@ -169,6 +169,27 @@
 
 }
 
+TEST(DicomMap, ExtractSequences)
+{
+  std::set<DicomTag> allTags;
+  std::set<DicomTag> sequences;
+
+  // empty list
+  DicomMap::ExtractSequences(sequences, allTags);
+  ASSERT_EQ(0u, sequences.size());
+
+  // one tag, no sequence
+  allTags.insert(DICOM_TAG_PATIENT_NAME);
+  DicomMap::ExtractSequences(sequences, allTags);
+  ASSERT_EQ(0u, sequences.size());
+
+  // one sequence
+  allTags.insert(DICOM_TAG_REFERENCED_IMAGE_SEQUENCE);
+  DicomMap::ExtractSequences(sequences, allTags);
+  ASSERT_EQ(1u, sequences.size());
+  ASSERT_TRUE(sequences.find(DICOM_TAG_REFERENCED_IMAGE_SEQUENCE) != sequences.end());
+}
+
 TEST(DicomMap, Tags)
 {
   std::set<DicomTag> s;
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Wed Jun 22 15:37:08 2022 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Fri Jun 24 15:47:10 2022 +0200
@@ -3402,7 +3402,8 @@
       case _OrthancPluginService_GetInstanceSimplifiedJson:
       {
         Json::Value dicomAsJson;
-        instance.GetDicomAsJson(dicomAsJson);
+        std::set<DicomTag> ignoreTagLength;
+        instance.GetDicomAsJson(dicomAsJson, ignoreTagLength);
         
         std::string s;
         if (service == _OrthancPluginService_GetInstanceJson)
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Wed Jun 22 15:37:08 2022 +0200
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Fri Jun 24 15:47:10 2022 +0200
@@ -45,6 +45,51 @@
 
 namespace Orthanc
 {
+  // copy all tags from Json
+  void DicomSequencesMap::FromJson(const Json::Value& value)
+  {
+    Json::Value::Members members = value.getMemberNames();
+    for (size_t i = 0; i < members.size(); i++)
+    {
+      DicomTag tag = FromDcmtkBridge::ParseTag(members[i].c_str());
+      sequences_[tag] = value[members[i]];
+    }
+  }
+
+  // copy a subset of tags from Json
+  void DicomSequencesMap::FromDicomAsJson(const Json::Value& dicomAsJson, const std::set<DicomTag>& tags)
+  {
+    for (std::set<DicomTag>::const_iterator it = tags.begin();
+         it != tags.end(); ++it)
+    {
+      std::string tag = it->Format();
+      if (dicomAsJson.isMember(tag))
+      {
+        sequences_[*it] = dicomAsJson[tag];
+      }
+    }
+  }
+
+  void DicomSequencesMap::ToJson(Json::Value& target, DicomToJsonFormat format) const
+  {
+    // add the sequences to "target"
+    for (std::map<DicomTag, Json::Value>::const_iterator it = sequences_.begin();
+          it != sequences_.end(); ++it)
+    {
+      Json::Value sequenceForConversion = Json::objectValue;
+      sequenceForConversion[it->first.Format()] = it->second;
+
+      Json::Value requestedFormatJson;
+      Toolbox::SimplifyDicomAsJson(requestedFormatJson, sequenceForConversion, format);  
+      
+      Json::Value::Members keys = requestedFormatJson.getMemberNames();  
+      for (size_t i = 0; i < keys.size(); i++)  // there should always be only one member in this JSON
+      {
+        target[keys[i]] = requestedFormatJson[keys[i]];
+      }
+    }
+  }
+
   namespace
   {
     /**
@@ -873,6 +918,17 @@
             // read all tags from DB
             transaction.GetMainDicomTags(target.tags_, internalId);
 
+            // read all main sequences from DB
+            std::string serializedSequences;
+            if (LookupStringMetadata(serializedSequences, target.metadata_, MetadataType_MainDicomSequences))
+            {
+              Json::Value jsonMetadata;
+              Toolbox::ReadJson(jsonMetadata, serializedSequences);
+
+              assert(jsonMetadata["Version"].asInt() == 1);
+              target.sequences_.FromJson(jsonMetadata["Sequences"]);
+            }
+
             // check if we have access to all requestedTags or if we must get tags from parents
             const std::set<DicomTag>& requestedTags = tuple.get<4>();
 
@@ -882,7 +938,7 @@
               
               FromDcmtkBridge::ParseListOfTags(savedMainDicomTags, target.mainDicomTagsSignature_);
 
-              // read parent main dicom tags as long as we don't have gathered all requested tags
+              // read parent main dicom tags as long as we have not gathered all requested tags
               ResourceType currentLevel = target.type_;
               int64_t currentInternalId = internalId;
               Toolbox::GetMissingsFromSet(target.missingRequestedTags_, requestedTags, savedMainDicomTags);
@@ -2841,6 +2897,7 @@
 
   StoreStatus StatelessDatabaseOperations::Store(std::map<MetadataType, std::string>& instanceMetadata,
                                                  const DicomMap& dicomSummary,
+                                                 const std::map<DicomTag, Json::Value>& sequencesToStore,
                                                  const Attachments& attachments,
                                                  const MetadataMap& metadata,
                                                  const DicomInstanceOrigin& origin,
@@ -2859,6 +2916,7 @@
       StoreStatus                          storeStatus_;
       std::map<MetadataType, std::string>& instanceMetadata_;
       const DicomMap&                      dicomSummary_;
+      const std::map<DicomTag, Json::Value>& sequencesToStore_;
       const Attachments&                   attachments_;
       const MetadataMap&                   metadata_;
       const DicomInstanceOrigin&           origin_;
@@ -2890,6 +2948,44 @@
         instanceMetadata[metadata] = value;
       }
 
+      static void SetMainDicomSequenceMetadata(ResourcesContent& content,
+                                               int64_t resource,
+                                               const std::map<DicomTag, Json::Value>& sequencesToStore,  // all sequences for all levels !
+                                               ResourceType level)
+      {
+        if (sequencesToStore.size() > 0)
+        {
+          const std::set<DicomTag>& levelTags = DicomMap::GetMainDicomTags(level);
+          std::set<DicomTag> levelSequences;
+          DicomMap::ExtractSequences(levelSequences, levelTags);
+
+          if (levelSequences.size() == 0)
+          {
+            return;
+          }
+
+          Json::Value jsonMetadata;
+          jsonMetadata["Version"] = 1;
+          Json::Value jsonSequences = Json::objectValue;
+
+          for (std::set<DicomTag>::const_iterator it = levelSequences.begin();
+               it != levelSequences.end(); ++it)
+          {
+            std::map<DicomTag, Json::Value>::const_iterator foundSeq = sequencesToStore.find(*it);
+            if (foundSeq != sequencesToStore.end())
+            {
+              jsonSequences[it->Format()] = foundSeq->second;
+            }
+          }
+          jsonMetadata["Sequences"] = jsonSequences;
+          
+          std::string serialized;
+          Toolbox::WriteFastJson(serialized, jsonMetadata);
+
+          content.AddMetadata(resource, MetadataType_MainDicomSequences, serialized);
+        }
+
+      }
       
       static bool ComputeExpectedNumberOfInstances(int64_t& target,
                                                    const DicomMap& dicomSummary)
@@ -2948,6 +3044,7 @@
     public:
       Operations(std::map<MetadataType, std::string>& instanceMetadata,
                  const DicomMap& dicomSummary,
+                 const std::map<DicomTag, Json::Value>& sequencesToStore,
                  const Attachments& attachments,
                  const MetadataMap& metadata,
                  const DicomInstanceOrigin& origin,
@@ -2962,6 +3059,7 @@
         storeStatus_(StoreStatus_Failure),
         instanceMetadata_(instanceMetadata),
         dicomSummary_(dicomSummary),
+        sequencesToStore_(sequencesToStore),
         attachments_(attachments),
         metadata_(metadata),
         origin_(origin),
@@ -3112,23 +3210,27 @@
 
             content.AddResource(instanceId, ResourceType_Instance, dicomSummary_);
             SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Instance));  // New in Orthanc 1.11.0
+            SetMainDicomSequenceMetadata(content, instanceId, sequencesToStore_, ResourceType_Instance);   // new in Orthanc 1.11.1
 
             if (status.isNewSeries_)
             {
               content.AddResource(status.seriesId_, ResourceType_Series, dicomSummary_);
               content.AddMetadata(status.seriesId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Series));  // New in Orthanc 1.11.0
+              SetMainDicomSequenceMetadata(content, status.seriesId_, sequencesToStore_, ResourceType_Series);   // new in Orthanc 1.11.1
             }
 
             if (status.isNewStudy_)
             {
               content.AddResource(status.studyId_, ResourceType_Study, dicomSummary_);
               content.AddMetadata(status.studyId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Study));  // New in Orthanc 1.11.0
+              SetMainDicomSequenceMetadata(content, status.studyId_, sequencesToStore_, ResourceType_Study);   // new in Orthanc 1.11.1
             }
 
             if (status.isNewPatient_)
             {
               content.AddResource(status.patientId_, ResourceType_Patient, dicomSummary_);
               content.AddMetadata(status.patientId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Patient));  // New in Orthanc 1.11.0
+              SetMainDicomSequenceMetadata(content, status.patientId_, sequencesToStore_, ResourceType_Patient);   // new in Orthanc 1.11.1
             }
 
             // Attach the auto-computed metadata for the patient/study/series levels
@@ -3273,7 +3375,7 @@
     };
 
 
-    Operations operations(instanceMetadata, dicomSummary, attachments, metadata, origin,
+    Operations operations(instanceMetadata, dicomSummary, sequencesToStore, attachments, metadata, origin,
                           overwrite, hasTransferSyntax, transferSyntax, hasPixelDataOffset,
                           pixelDataOffset, maximumStorageSize, maximumPatients, isReconstruct);
     Apply(operations);
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h	Wed Jun 22 15:37:08 2022 +0200
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h	Fri Jun 24 15:47:10 2022 +0200
@@ -37,10 +37,25 @@
   class ParsedDicomFile;
   struct ServerIndexChange;
 
+  /*
+   * contains a map of dicom sequences where:
+   * the key is a DicomTag
+   * the sequence is serialized in Json "full" format
+   */
+  struct DicomSequencesMap : public boost::noncopyable
+  {
+    std::map<DicomTag, Json::Value>     sequences_;
+
+    void FromJson(const Json::Value& serialized);
+    void FromDicomAsJson(const Json::Value& dicomAsJson, const std::set<DicomTag>& tags);
+    void ToJson(Json::Value& target, DicomToJsonFormat format) const;
+  };
+
   struct ExpandedResource : public boost::noncopyable
   {
     std::string                         id_;
-    DicomMap                            tags_;  // all tags from DB
+    DicomMap                            tags_;          // all tags from DB (only leaf tags, not sequences !)
+    DicomSequencesMap                   sequences_;     // the requested sequences
     std::string                         mainDicomTagsSignature_;
     std::string                         parentId_;
     std::list<std::string>              childrenIds_;
@@ -650,6 +665,7 @@
 
     StoreStatus Store(std::map<MetadataType, std::string>& instanceMetadata,
                       const DicomMap& dicomSummary,
+                      const std::map<DicomTag, Json::Value>& sequencesToStore,
                       const Attachments& attachments,
                       const MetadataMap& metadata,
                       const DicomInstanceOrigin& origin,
--- a/OrthancServer/Sources/DicomInstanceToStore.cpp	Wed Jun 22 15:37:08 2022 +0200
+++ b/OrthancServer/Sources/DicomInstanceToStore.cpp	Fri Jun 24 15:47:10 2022 +0200
@@ -186,9 +186,9 @@
       OrthancConfiguration::DefaultExtractDicomSummary(summary, dataset_);
     }
 
-    virtual void GetDicomAsJson(Json::Value& dicomAsJson) const ORTHANC_OVERRIDE
+    virtual void GetDicomAsJson(Json::Value& dicomAsJson, const std::set<DicomTag>& ignoreTagLength) const ORTHANC_OVERRIDE
     {
-      OrthancConfiguration::DefaultDicomDatasetToJson(dicomAsJson, dataset_);
+      OrthancConfiguration::DefaultDicomDatasetToJson(dicomAsJson, dataset_, ignoreTagLength);
     }
 
     virtual void DatasetToJson(Json::Value& target, 
@@ -275,9 +275,9 @@
   }
 
   
-  void DicomInstanceToStore::GetDicomAsJson(Json::Value& dicomAsJson) const
+  void DicomInstanceToStore::GetDicomAsJson(Json::Value& dicomAsJson, const std::set<DicomTag>& ignoreTagLength) const
   {
-    OrthancConfiguration::DefaultDicomDatasetToJson(dicomAsJson, GetParsedDicomFile());
+    OrthancConfiguration::DefaultDicomDatasetToJson(dicomAsJson, GetParsedDicomFile(), ignoreTagLength);
   }
 
 
--- a/OrthancServer/Sources/DicomInstanceToStore.h	Wed Jun 22 15:37:08 2022 +0200
+++ b/OrthancServer/Sources/DicomInstanceToStore.h	Fri Jun 24 15:47:10 2022 +0200
@@ -108,7 +108,8 @@
 
     virtual void GetSummary(DicomMap& summary) const;
 
-    virtual void GetDicomAsJson(Json::Value& dicomAsJson) const;
+    virtual void GetDicomAsJson(Json::Value& dicomAsJson,
+                                const std::set<DicomTag>& ignoreTagLength) const;
 
     virtual void DatasetToJson(Json::Value& target, 
                                DicomToJsonFormat format,
--- a/OrthancServer/Sources/OrthancConfiguration.cpp	Wed Jun 22 15:37:08 2022 +0200
+++ b/OrthancServer/Sources/OrthancConfiguration.cpp	Fri Jun 24 15:47:10 2022 +0200
@@ -1126,9 +1126,9 @@
 
 
   void OrthancConfiguration::DefaultDicomDatasetToJson(Json::Value& target,
-                                                       DcmDataset& dicom)
+                                                       DcmDataset& dicom,
+                                                       const std::set<DicomTag>& ignoreTagLength)
   {
-    std::set<DicomTag> ignoreTagLength;
     FromDcmtkBridge::ExtractDicomAsJson(target, dicom, DicomToJsonFormat_Full, DicomToJsonFlags_Default, 
                                         ORTHANC_MAXIMUM_TAG_LENGTH, ignoreTagLength);    
   }    
--- a/OrthancServer/Sources/OrthancConfiguration.h	Wed Jun 22 15:37:08 2022 +0200
+++ b/OrthancServer/Sources/OrthancConfiguration.h	Fri Jun 24 15:47:10 2022 +0200
@@ -263,7 +263,8 @@
                                           const ParsedDicomFile& dicom);
     
     static void DefaultDicomDatasetToJson(Json::Value& target,
-                                          DcmDataset& dicom);
+                                          DcmDataset& dicom,
+                                          const std::set<DicomTag>& ignoreTagLength);
     
     static void DefaultDicomDatasetToJson(Json::Value& target,
                                           const ParsedDicomFile& dicom,
--- a/OrthancServer/Sources/OrthancInitialization.cpp	Wed Jun 22 15:37:08 2022 +0200
+++ b/OrthancServer/Sources/OrthancInitialization.cpp	Fri Jun 24 15:47:10 2022 +0200
@@ -262,13 +262,13 @@
             ValueRepresentation vr = FromDcmtkBridge::LookupValueRepresentation(tag);
             if (vr == ValueRepresentation_Sequence)
             {
-              LOG(WARNING) << "  - " << tagName << " can not be added in the Extra Main Dicom Tags since it is a sequence";
+              LOG(INFO) << "  - " << tagName << " (sequence)";
             }
             else
             {
-              DicomMap::AddMainDicomTag(tag, tagName, level);
               LOG(INFO) << "  - " << tagName;
             }
+            DicomMap::AddMainDicomTag(tag, tagName, level);
           }
         }
       }
--- a/OrthancServer/Sources/ServerContext.cpp	Wed Jun 22 15:37:08 2022 +0200
+++ b/OrthancServer/Sources/ServerContext.cpp	Fri Jun 24 15:47:10 2022 +0200
@@ -526,7 +526,12 @@
     bool hasTransferSyntax = dicom.LookupTransferSyntax(transferSyntax);
     
     DicomMap summary;
-    dicom.GetSummary(summary);
+    dicom.GetSummary(summary);   // -> this includes only the leaf nodes
+
+    std::set<DicomTag> allMainDicomTags = DicomMap::GetAllMainDicomTags();
+    std::set<DicomTag> mainDicomSequences;
+    DicomMap::ExtractSequences(mainDicomSequences, allMainDicomTags);
+    std::map<DicomTag, Json::Value> sequencesToStore;
 
     try
     {
@@ -536,9 +541,19 @@
       DicomInstanceHasher hasher(summary);
       resultPublicId = hasher.HashInstance();
 
-      Json::Value dicomAsJson;
-      dicom.GetDicomAsJson(dicomAsJson);
+      Json::Value dicomAsJson;    // -> this includes the sequences
+
+      dicom.GetDicomAsJson(dicomAsJson, mainDicomSequences /*ignoreTagLength*/);  // make sure that sequences that we wish to store in DB are not 'cropped'
       
+      for (std::set<DicomTag>::const_iterator it = mainDicomSequences.begin();
+           it != mainDicomSequences.end(); ++it)
+      {
+        if (dicomAsJson.isMember(it->Format()))
+        {
+          sequencesToStore[*it] = dicomAsJson[it->Format()];
+        }
+      }
+
       Json::Value simplifiedTags;
       Toolbox::SimplifyDicomAsJson(simplifiedTags, dicomAsJson, DicomToJsonFormat_Human);
 
@@ -616,7 +631,7 @@
       typedef std::map<MetadataType, std::string>  InstanceMetadata;
       InstanceMetadata  instanceMetadata;
       result.SetStatus(index_.Store(
-        instanceMetadata, summary, attachments, dicom.GetMetadata(), dicom.GetOrigin(), overwrite,
+        instanceMetadata, summary, sequencesToStore, attachments, dicom.GetMetadata(), dicom.GetOrigin(), overwrite,
         hasTransferSyntax, transferSyntax, hasPixelDataOffset, pixelDataOffset, isReconstruct));
 
       // Only keep the metadata for the "instance" level
@@ -2094,6 +2109,9 @@
 
       target[REQUESTED_TAGS] = Json::objectValue;
       FromDcmtkBridge::ToJson(target[REQUESTED_TAGS], tags, format);
+
+      // add the sequences to the requested tags
+      resource.sequences_.ToJson(target[REQUESTED_TAGS], format);
     }
 
   }
@@ -2405,6 +2423,9 @@
       // possibly merge missing requested tags from dicom-as-json
       if (!resource.missingRequestedTags_.empty() && !DicomMap::HasOnlyComputedTags(resource.missingRequestedTags_))
       {
+        std::set<DicomTag> missingSequences;
+        DicomMap::ExtractSequences(missingSequences, resource.missingRequestedTags_);
+
         OrthancConfiguration::ReaderLock lock;
         if (lock.GetConfiguration().IsWarningEnabled(Warnings_001_TagsBeingReadFromStorage))
         {
@@ -2449,12 +2470,14 @@
           }
   
           Json::Value tmpDicomAsJson;
-          ReadDicomAsJson(tmpDicomAsJson, instanceId_);
+          ReadDicomAsJson(tmpDicomAsJson, instanceId_, resource.missingRequestedTags_ /* ignoreTagLength */);  // read all tags from DICOM and avoid cropping requested tags
           tagsFromJson.FromDicomAsJson(tmpDicomAsJson);
+          resource.sequences_.FromDicomAsJson(tmpDicomAsJson, missingSequences);
         }
         else
         {
           tagsFromJson.FromDicomAsJson(*dicomAsJson);
+          resource.sequences_.FromDicomAsJson(*dicomAsJson, missingSequences);
         }
 
         resource.tags_.Merge(tagsFromJson);
--- a/OrthancServer/Sources/ServerEnumerations.cpp	Wed Jun 22 15:37:08 2022 +0200
+++ b/OrthancServer/Sources/ServerEnumerations.cpp	Fri Jun 24 15:47:10 2022 +0200
@@ -61,6 +61,7 @@
     dictMetadataType_.Add(MetadataType_Instance_HttpUsername, "HttpUsername");
     dictMetadataType_.Add(MetadataType_Instance_PixelDataOffset, "PixelDataOffset");
     dictMetadataType_.Add(MetadataType_MainDicomTagsSignature, "MainDicomTagsSignature");
+    dictMetadataType_.Add(MetadataType_MainDicomSequences, "MainDicomSequences");
 
     dictContentType_.Add(FileContentType_Dicom, "dicom");
     dictContentType_.Add(FileContentType_DicomAsJson, "dicom-as-json");
--- a/OrthancServer/Sources/ServerEnumerations.h	Wed Jun 22 15:37:08 2022 +0200
+++ b/OrthancServer/Sources/ServerEnumerations.h	Fri Jun 24 15:47:10 2022 +0200
@@ -152,6 +152,7 @@
     MetadataType_Instance_HttpUsername = 13,     // New in Orthanc 1.4.0
     MetadataType_Instance_PixelDataOffset = 14,  // New in Orthanc 1.9.0
     MetadataType_MainDicomTagsSignature = 15,    // New in Orthanc 1.11.0
+    MetadataType_MainDicomSequences = 16,        // New in Orthanc 1.11.1
     
     // Make sure that the value "65535" can be stored into this enumeration
     MetadataType_StartUser = 1024,
--- a/OrthancServer/Sources/ServerIndex.cpp	Wed Jun 22 15:37:08 2022 +0200
+++ b/OrthancServer/Sources/ServerIndex.cpp	Fri Jun 24 15:47:10 2022 +0200
@@ -511,6 +511,7 @@
 
   StoreStatus ServerIndex::Store(std::map<MetadataType, std::string>& instanceMetadata,
                                  const DicomMap& dicomSummary,
+                                 const std::map<DicomTag, Json::Value>& sequencesToStore,
                                  const ServerIndex::Attachments& attachments,
                                  const ServerIndex::MetadataMap& metadata,
                                  const DicomInstanceOrigin& origin,
@@ -531,7 +532,7 @@
     }
 
     return StatelessDatabaseOperations::Store(
-      instanceMetadata, dicomSummary, attachments, metadata, origin, overwrite, hasTransferSyntax,
+      instanceMetadata, dicomSummary, sequencesToStore, attachments, metadata, origin, overwrite, hasTransferSyntax,
       transferSyntax, hasPixelDataOffset, pixelDataOffset, maximumStorageSize, maximumPatients, isReconstruct);
   }
 
--- a/OrthancServer/Sources/ServerIndex.h	Wed Jun 22 15:37:08 2022 +0200
+++ b/OrthancServer/Sources/ServerIndex.h	Fri Jun 24 15:47:10 2022 +0200
@@ -77,6 +77,7 @@
 
     StoreStatus Store(std::map<MetadataType, std::string>& instanceMetadata,
                       const DicomMap& dicomSummary,
+                      const std::map<DicomTag, Json::Value>& sequencesToStore,
                       const Attachments& attachments,
                       const MetadataMap& metadata,
                       const DicomInstanceOrigin& origin,
--- a/TODO	Wed Jun 22 15:37:08 2022 +0200
+++ b/TODO	Fri Jun 24 15:47:10 2022 +0200
@@ -39,7 +39,7 @@
 * Add configurations to enable/disable warnings:
   - Modifying an instance while keeping its original SOPInstanceUID: This should be avoided!
   - Modifying a study while keeping its original StudyInstanceUID: This should be avoided!
-* Allow storing sequences in DB to speed up Dicom-web: https://groups.google.com/g/orthanc-users/c/6CTlfa8UGM4/m/V1D0xuKQAAAJ
+
 
 ============================
 Documentation (Orthanc Book)