changeset 2209:e3fd5bc429a2

URI to reconstruct the main DICOM tags, the JSON summary and the metadata
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 09 Dec 2016 17:20:21 +0100
parents 90ea60bee5ff
children 7bcff7bb7cbf
files NEWS OrthancServer/DicomModification.cpp OrthancServer/DicomModification.h OrthancServer/FromDcmtkBridge.cpp OrthancServer/FromDcmtkBridge.h OrthancServer/OrthancInitialization.h OrthancServer/OrthancRestApi/OrthancRestResources.cpp OrthancServer/ParsedDicomFile.cpp OrthancServer/ParsedDicomFile.h OrthancServer/QueryRetrieveHandler.cpp OrthancServer/ServerIndex.cpp OrthancServer/ServerIndex.h OrthancServer/ServerToolbox.cpp OrthancServer/ServerToolbox.h UnitTestsSources/FromDcmtkTests.cpp
diffstat 15 files changed, 164 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Fri Dec 09 14:48:31 2016 +0100
+++ b/NEWS	Fri Dec 09 17:20:21 2016 +0100
@@ -17,15 +17,18 @@
 * "Asynchronous" flag for URIs "/modalities/{...}/store" and "/peers/{...}/store"
   to avoid waiting for the completion of image transfers
 * Possibility to DELETE "dicom-as-json" attachments to reconstruct the JSON summaries
+  (useful if "Dictionary" has changed)
 * "Keep" option for modifications to keep original DICOM identifiers (advanced feature)
 * "/tools/default-encoding" to get or temporarily change the default encoding
+* "/{resource}/{id}/reconstruct" to reconstruct the main DICOM tags, the JSON summary and
+  the metadata of a resource (useful to compute new metadata, or if using "Keep" above)
 
 Plugins
 -------
 
 * New function: "OrthancPluginRegisterPrivateDictionaryTag()" to register private tags
 * More control over client cache in the ServeFolders plugin
-* New C++ help wrappers in "Plugins/Samples/Common/" to read DICOM datasets
+* New C++ help wrappers in "Plugins/Samples/Common/" to read DICOM datasets from REST API
 
 Maintenance
 -----------
--- a/OrthancServer/DicomModification.cpp	Fri Dec 09 14:48:31 2016 +0100
+++ b/OrthancServer/DicomModification.cpp	Fri Dec 09 17:20:21 2016 +0100
@@ -149,8 +149,7 @@
     level_(ResourceType_Instance),
     allowManualIdentifiers_(true),
     keepStudyInstanceUid_(false),
-    keepSeriesInstanceUid_(false),
-    keepSopInstanceUid_(false)
+    keepSeriesInstanceUid_(false)
   {
   }
 
@@ -179,11 +178,6 @@
       keepSeriesInstanceUid_ = true;
     }
 
-    if (tag == DICOM_TAG_SOP_INSTANCE_UID)
-    {
-      keepSopInstanceUid_ = true;
-    }
-
     MarkNotOrthancAnonymization();
   }
 
@@ -510,14 +504,7 @@
     if (level_ <= ResourceType_Instance &&  // Always true
         !IsReplaced(DICOM_TAG_SOP_INSTANCE_UID))
     {
-      if (keepSopInstanceUid_)
-      {
-        LOG(WARNING) << "Modifying an instance while keeping its original SOPInstanceUID: This should be avoided!";
-      }
-      else
-      {
-        MapDicomIdentifier(toModify, ResourceType_Instance);
-      }
+      MapDicomIdentifier(toModify, ResourceType_Instance);
     }
   }
 }
--- a/OrthancServer/DicomModification.h	Fri Dec 09 14:48:31 2016 +0100
+++ b/OrthancServer/DicomModification.h	Fri Dec 09 17:20:21 2016 +0100
@@ -59,7 +59,6 @@
     bool allowManualIdentifiers_;
     bool keepStudyInstanceUid_;
     bool keepSeriesInstanceUid_;
-    bool keepSopInstanceUid_;
 
     void MapDicomIdentifier(ParsedDicomFile& dicom,
                             ResourceType level);
--- a/OrthancServer/FromDcmtkBridge.cpp	Fri Dec 09 14:48:31 2016 +0100
+++ b/OrthancServer/FromDcmtkBridge.cpp	Fri Dec 09 17:20:21 2016 +0100
@@ -1943,4 +1943,23 @@
       }
     }
   }
+
+
+  bool FromDcmtkBridge::LookupTransferSyntax(std::string& result,
+                                             DcmFileFormat& dicom)
+  {
+    const char* value = NULL;
+
+    if (dicom.getMetaInfo() != NULL &&
+        dicom.getMetaInfo()->findAndGetString(DCM_TransferSyntaxUID, value).good() &&
+        value != NULL)
+    {
+      result.assign(value);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
 }
--- a/OrthancServer/FromDcmtkBridge.h	Fri Dec 09 14:48:31 2016 +0100
+++ b/OrthancServer/FromDcmtkBridge.h	Fri Dec 09 17:20:21 2016 +0100
@@ -199,5 +199,8 @@
 
     static void FromJson(DicomMap& values,
                          const Json::Value& result);
+
+    static bool LookupTransferSyntax(std::string& result,
+                                     DcmFileFormat& dicom);
   };
 }
--- a/OrthancServer/OrthancInitialization.h	Fri Dec 09 14:48:31 2016 +0100
+++ b/OrthancServer/OrthancInitialization.h	Fri Dec 09 17:20:21 2016 +0100
@@ -142,7 +142,6 @@
 
     static bool HasConfigurationChanged();
 
-
     static void ExtractDicomSummary(DicomMap& target, 
                                     DcmItem& dataset);
 
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Fri Dec 09 14:48:31 2016 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Fri Dec 09 17:20:21 2016 +0100
@@ -1381,6 +1381,15 @@
   }
 
 
+  template <enum ResourceType type>
+  static void ReconstructResource(RestApiPostCall& call)
+  {
+    ServerContext& context = OrthancRestApi::GetContext(call);
+    ServerToolbox::ReconstructResource(context, call.GetUriComponent("id", ""));
+    call.GetOutput().AnswerBuffer("", "text/plain");
+  }
+
+
   void OrthancRestApi::RegisterResources()
   {
     Register("/instances", ListResources<ResourceType_Instance>);
@@ -1480,5 +1489,10 @@
     Register("/instances/{id}/content/*", GetRawContent);
 
     Register("/series/{id}/ordered-slices", OrderSlices);
+
+    Register("/patients/{id}/reconstruct", ReconstructResource<ResourceType_Patient>);
+    Register("/studies/{id}/reconstruct", ReconstructResource<ResourceType_Study>);
+    Register("/series/{id}/reconstruct", ReconstructResource<ResourceType_Series>);
+    Register("/instances/{id}/reconstruct", ReconstructResource<ResourceType_Instance>);
   }
 }
--- a/OrthancServer/ParsedDicomFile.cpp	Fri Dec 09 14:48:31 2016 +0100
+++ b/OrthancServer/ParsedDicomFile.cpp	Fri Dec 09 17:20:21 2016 +0100
@@ -1300,12 +1300,6 @@
   }
 
 
-  void ParsedDicomFile::Convert(DicomMap& tags)
-  {
-    Configuration::ExtractDicomSummary(tags, *pimpl_->file_->getDataset());
-  }
-
-
   ParsedDicomFile* ParsedDicomFile::CreateFromJson(const Json::Value& json,
                                                    DicomFromJsonFlags flags)
   {
@@ -1396,4 +1390,22 @@
       FromDcmtkBridge::ChangeStringEncoding(*pimpl_->file_->getDataset(), source, target);
     }
   }
+
+
+  void ParsedDicomFile::ExtractDicomSummary(DicomMap& target) const
+  {
+    Configuration::ExtractDicomSummary(target, *pimpl_->file_->getDataset());
+  }
+
+
+  void ParsedDicomFile::ExtractDicomAsJson(Json::Value& target) const
+  {
+    Configuration::ExtractDicomAsJson(target, *pimpl_->file_->getDataset());
+  }
+
+
+  bool ParsedDicomFile::LookupTransferSyntax(std::string& result)
+  {
+    return FromDcmtkBridge::LookupTransferSyntax(result, *pimpl_->file_);
+  }
 }
--- a/OrthancServer/ParsedDicomFile.h	Fri Dec 09 14:48:31 2016 +0100
+++ b/OrthancServer/ParsedDicomFile.h	Fri Dec 09 17:20:21 2016 +0100
@@ -166,8 +166,6 @@
 
     bool ExtractPdf(std::string& pdf);
 
-    void Convert(DicomMap& tags);
-
     void GetRawFrame(std::string& target, // OUT
                      std::string& mime,   // OUT
                      unsigned int frameId);  // IN
@@ -178,5 +176,11 @@
                                            DicomFromJsonFlags flags);
 
     void ChangeEncoding(Encoding target);
+
+    void ExtractDicomSummary(DicomMap& target) const;
+
+    void ExtractDicomAsJson(Json::Value& target) const;
+
+    bool LookupTransferSyntax(std::string& result);
   };
 }
--- a/OrthancServer/QueryRetrieveHandler.cpp	Fri Dec 09 14:48:31 2016 +0100
+++ b/OrthancServer/QueryRetrieveHandler.cpp	Fri Dec 09 17:20:21 2016 +0100
@@ -100,7 +100,7 @@
                                        size_t i)
   {
     Run();
-    answers_.GetAnswer(i).Convert(target);
+    answers_.GetAnswer(i).ExtractDicomSummary(target);
   }
 
 
--- a/OrthancServer/ServerIndex.cpp	Fri Dec 09 14:48:31 2016 +0100
+++ b/OrthancServer/ServerIndex.cpp	Fri Dec 09 17:20:21 2016 +0100
@@ -40,6 +40,7 @@
 #include "ServerIndexChange.h"
 #include "EmbeddedResources.h"
 #include "OrthancInitialization.h"
+#include "ParsedDicomFile.h"
 #include "ServerToolbox.h"
 #include "../Core/Toolbox.h"
 #include "../Core/Logging.h"
@@ -2236,4 +2237,67 @@
     target = db_.GetPublicId(id);
     return true;
   }
+
+
+  void ServerIndex::ReconstructInstance(ParsedDicomFile& dicom)
+  {
+    DicomMap summary;
+    dicom.ExtractDicomSummary(summary);
+
+    DicomInstanceHasher hasher(summary);
+
+    boost::mutex::scoped_lock lock(mutex_);
+
+    try
+    {
+      Transaction t(*this);
+
+      int64_t patient = -1, study = -1, series = -1, instance = -1;
+
+      ResourceType dummy;      
+      if (!db_.LookupResource(patient, dummy, hasher.HashPatient()) ||
+          !db_.LookupResource(study, dummy, hasher.HashStudy()) ||
+          !db_.LookupResource(series, dummy, hasher.HashSeries()) ||
+          !db_.LookupResource(instance, dummy, hasher.HashInstance()) ||
+          patient == -1 ||
+          study == -1 ||
+          series == -1 ||
+          instance == -1)
+      {
+        throw OrthancException(ErrorCode_InternalError);
+      }
+
+      db_.ClearMainDicomTags(patient);
+      db_.ClearMainDicomTags(study);
+      db_.ClearMainDicomTags(series);
+      db_.ClearMainDicomTags(instance);
+
+      ServerToolbox::StoreMainDicomTags(db_, patient, ResourceType_Patient, summary);
+      ServerToolbox::StoreMainDicomTags(db_, study, ResourceType_Study, summary);
+      ServerToolbox::StoreMainDicomTags(db_, series, ResourceType_Series, summary);
+      ServerToolbox::StoreMainDicomTags(db_, instance, ResourceType_Instance, summary);
+
+      {
+        std::string s;
+        if (dicom.LookupTransferSyntax(s))
+        {
+          db_.SetMetadata(instance, MetadataType_Instance_TransferSyntax, s);
+        }
+      }
+
+      const DicomValue* value;
+      if ((value = summary.TestAndGetValue(DICOM_TAG_SOP_CLASS_UID)) != NULL &&
+          !value->IsNull() &&
+          !value->IsBinary())
+      {
+        db_.SetMetadata(instance, MetadataType_Instance_SopClassUid, value->GetContent());
+      }
+
+      t.Commit(0);  // No change in the DB size
+    }
+    catch (OrthancException& e)
+    {
+      LOG(ERROR) << "EXCEPTION [" << e.What() << "]";
+    }
+  }
 }
--- a/OrthancServer/ServerIndex.h	Fri Dec 09 14:48:31 2016 +0100
+++ b/OrthancServer/ServerIndex.h	Fri Dec 09 17:20:21 2016 +0100
@@ -47,6 +47,7 @@
   class LookupResource;
   class ServerContext;
   class DicomInstanceToStore;
+  class ParsedDicomFile;
 
   class ServerIndex : public boost::noncopyable
   {
@@ -273,5 +274,7 @@
     bool LookupParent(std::string& target,
                       const std::string& publicId,
                       ResourceType parentType);
+
+    void ReconstructInstance(ParsedDicomFile& dicom);
   };
 }
--- a/OrthancServer/ServerToolbox.cpp	Fri Dec 09 14:48:31 2016 +0100
+++ b/OrthancServer/ServerToolbox.cpp	Fri Dec 09 17:20:21 2016 +0100
@@ -37,7 +37,6 @@
 #include "../Core/FileStorage/StorageAccessor.h"
 #include "../Core/Logging.h"
 #include "../Core/OrthancException.h"
-#include "ParsedDicomFile.h"
 
 #include <cassert>
 
@@ -406,7 +405,7 @@
 
           // Update the tags of this resource
           DicomMap dicomSummary;
-          dicom.Convert(dicomSummary);
+          dicom.ExtractDicomSummary(dicomSummary);
 
           database.ClearMainDicomTags(resource);
           StoreMainDicomTags(database, resource, level, dicomSummary);
@@ -497,5 +496,29 @@
 
       return false;
     }
+
+    
+    void ReconstructResource(ServerContext& context,
+                             const std::string& resource)
+    {
+      LOG(WARNING) << "Reconstructing resource " << resource;
+      
+      std::list<std::string> instances;
+      context.GetIndex().GetChildInstances(instances, resource);
+
+      for (std::list<std::string>::const_iterator 
+             it = instances.begin(); it != instances.end(); ++it)
+      {
+        ServerContext::DicomCacheLocker locker(context, *it);
+
+        Json::Value dicomAsJson;
+        locker.GetDicom().ExtractDicomAsJson(dicomAsJson);
+
+        std::string s = dicomAsJson.toStyledString();
+        context.AddAttachment(*it, FileContentType_DicomAsJson, s.c_str(), s.size());
+
+        context.GetIndex().ReconstructInstance(locker.GetDicom());
+      }
+    }
   }
 }
--- a/OrthancServer/ServerToolbox.h	Fri Dec 09 14:48:31 2016 +0100
+++ b/OrthancServer/ServerToolbox.h	Fri Dec 09 17:20:21 2016 +0100
@@ -32,8 +32,7 @@
 
 #pragma once
 
-#include "../Core/DicomFormat/DicomMap.h"
-#include "IDatabaseWrapper.h"
+#include "ServerContext.h"
 
 #include <json/json.h>
 
@@ -69,5 +68,8 @@
                       ResourceType level);
 
     std::string NormalizeIdentifier(const std::string& value);
+
+    void ReconstructResource(ServerContext& context,
+                             const std::string& resource);
   }
 }
--- a/UnitTestsSources/FromDcmtkTests.cpp	Fri Dec 09 14:48:31 2016 +0100
+++ b/UnitTestsSources/FromDcmtkTests.cpp	Fri Dec 09 17:20:21 2016 +0100
@@ -469,6 +469,7 @@
   f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "Tata");  // (**)
 
   std::string s;
+  ASSERT_FALSE(f.LookupTransferSyntax(s));
 
   ASSERT_THROW(f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession"),
                          false, DicomReplaceMode_ThrowIfAbsent), OrthancException);