changeset 1564:1b7def486e62

creation of DICOM series
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 21 Aug 2015 15:01:21 +0200
parents 9bb416445319
children 4b23310eb7e8
files OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp OrthancServer/OrthancRestApi/OrthancRestApi.cpp OrthancServer/OrthancRestApi/OrthancRestApi.h OrthancServer/ParsedDicomFile.cpp
diffstat 4 files changed, 123 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Fri Aug 21 13:47:34 2015 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Fri Aug 21 15:01:21 2015 +0200
@@ -39,6 +39,8 @@
 #include "../ServerContext.h"
 #include "../OrthancInitialization.h"
 
+#include <boost/lexical_cast.hpp>
+
 namespace Orthanc
 {
   // Modification of DICOM instances ------------------------------------------
@@ -429,6 +431,27 @@
   }
 
 
+  static bool StoreCreatedInstance(std::string& id /* out */,
+                                   ServerContext& context,
+                                   ParsedDicomFile& dicom)
+  {
+    DicomInstanceToStore toStore;
+    toStore.SetParsedDicomFile(dicom);
+
+    StoreStatus status = context.Store(id, toStore);
+
+    if (status == StoreStatus_Failure)
+    {
+      LOG(ERROR) << "Error while storing a manually-created instance";
+      return false;
+    }
+    else
+    {
+      return true;
+    }
+  }
+
+
   static bool CreateDicomV1(ParsedDicomFile& dicom,
                             const Json::Value& request)
   {
@@ -465,16 +488,57 @@
   }
 
 
-  static bool CreateDicomV2(ParsedDicomFile& dicom,
-                            ServerContext& context,
+  static void CreateSeries(RestApiPostCall& call,
+                           ParsedDicomFile& base /* in */,
+                           const Json::Value& content)
+  {
+    assert(content.isArray());
+    assert(content.size() > 0);
+    ServerContext& context = OrthancRestApi::GetContext(call);
+
+    base.Replace(DICOM_TAG_IMAGES_IN_ACQUISITION, boost::lexical_cast<std::string>(content.size()));
+    base.Replace(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS, "1");
+
+    std::string someInstance;
+
+    for (Json::ArrayIndex i = 0; i < content.size(); i++)
+    {
+      if (content[i].type() != Json::stringValue)
+      {
+        LOG(ERROR) << "The payload of the DICOM instance must be specified according to Data URI scheme";
+        return;
+      }
+
+      std::auto_ptr<ParsedDicomFile> dicom(base.Clone());
+      dicom->EmbedContent(content[i].asString());
+      dicom->Replace(DICOM_TAG_INSTANCE_NUMBER, boost::lexical_cast<std::string>(i + 1));
+      dicom->Replace(DICOM_TAG_IMAGE_INDEX, boost::lexical_cast<std::string>(i + 1));
+
+      if (!StoreCreatedInstance(someInstance, context, *dicom))
+      {
+        LOG(ERROR) << "Error while creating the series";
+        return;
+      }
+    }
+
+    std::string series;
+    if (context.GetIndex().LookupParent(series, someInstance))
+    {
+      OrthancRestApi::GetApi(call).AnswerStoredResource(call, series, ResourceType_Series, StoreStatus_Success);
+    }
+  }
+
+
+  static void CreateDicomV2(RestApiPostCall& call,
                             const Json::Value& request)
   {
     assert(request.isObject());
+    ServerContext& context = OrthancRestApi::GetContext(call);
 
     if (!request.isMember("Tags") ||
         request["Tags"].type() != Json::objectValue)
     {
-      return false;
+      return;
     }
 
     Encoding encoding;
@@ -485,7 +549,7 @@
       if (!GetDicomEncoding(encoding, tmp))
       {
         LOG(ERROR) << "Unknown specific character set: " << tmp;
-        return false;
+        return;
       }
     }
     else
@@ -494,6 +558,7 @@
       encoding = StringToEncoding(tmp.c_str());
     }
 
+    ParsedDicomFile dicom;
     dicom.SetEncoding(encoding);
 
     ResourceType parentType = ResourceType_Instance;
@@ -505,13 +570,13 @@
       if (!context.GetIndex().LookupResourceType(parentType, parent))
       {
         LOG(ERROR) << "Trying to attach a new DICOM instance to an inexistent resource: " << parent;
-        return false;
+        return;
       }
 
       if (parentType == ResourceType_Instance)
       {
         LOG(ERROR) << "Trying to attach a new DICOM instance to an instance (must be a series, study or patient): " << parent;
-        return false;
+        return;
       }
 
       // Select one existing child instance of the parent resource, to
@@ -525,7 +590,7 @@
 
         if (siblingInstances.empty())
         {
-          return false;   // Error: No instance (should never happen)
+          return;   // Error: No instance (should never happen)
         }
 
         context.ReadJson(siblingTags, siblingInstances.front());
@@ -549,7 +614,7 @@
         std::string tmp;
         if (!context.GetIndex().LookupParent(tmp, parent))
         {
-          return false;
+          return;
         }
 
         parent = tmp;
@@ -611,7 +676,7 @@
       if (request["Tags"][name].type() != Json::stringValue)
       {
         LOG(ERROR) << "Only string values are supported when creating DICOM instances";
-        return false;
+        return;
       }
 
       std::string value = request["Tags"][name].asString();
@@ -622,13 +687,13 @@
         if (dicom.HasTag(tag))
         {
           LOG(ERROR) << "Trying to override a value inherited from a parent module";
-          return false;
+          return;
         }
 
         if (tag == DICOM_TAG_PIXEL_DATA)
         {
           LOG(ERROR) << "Use \"Content\" to inject an image into a new DICOM instance";
-          return false;
+          return;
         }
         else
         {
@@ -637,51 +702,65 @@
       }
     }
 
-
     // Inject the content (either an image, or a PDF file)
     if (request.isMember("Content"))
     {
-      if (request["Content"].type() != Json::stringValue)
+      const Json::Value& content = request["Content"];
+
+      if (content.type() == Json::stringValue)
+      {
+        dicom.EmbedContent(request["Content"].asString());
+
+      }
+      else if (content.type() == Json::arrayValue)
+      {
+        if (content.size() > 0)
+        {
+          // Let's create a series
+          CreateSeries(call, dicom, content);
+          return;
+        }
+      }
+      else
       {
         LOG(ERROR) << "The payload of the DICOM instance must be specified according to Data URI scheme";
-        return false;
+        return;
       }
-
-      dicom.EmbedContent(request["Content"].asString());
     }
 
+    std::string id;
+    if (StoreCreatedInstance(id, context, dicom))
+    {
+      OrthancRestApi::GetApi(call).AnswerStoredResource(call, id, ResourceType_Instance, StoreStatus_Success);
+    }
 
-    return true;
+    return;
   }
 
 
   static void CreateDicom(RestApiPostCall& call)
   {
-    ServerContext& context = OrthancRestApi::GetContext(call);
-
     Json::Value request;
     if (call.ParseJsonRequest(request) && 
         request.isObject())
     {
-      ParsedDicomFile dicom;
-
-      if (request.isMember("Tags") ? 
-          CreateDicomV2(dicom, context, request) :
-          CreateDicomV1(dicom, request))
+      if (request.isMember("Tags"))
+      {
+        CreateDicomV2(call, request);
+      }
+      else
       {
-        DicomInstanceToStore toStore;
-        toStore.SetParsedDicomFile(dicom);
-
-        std::string id;
-        StoreStatus status = OrthancRestApi::GetContext(call).Store(id, toStore);
-
-        if (status == StoreStatus_Failure)
+        // Compatibility with Orthanc <= 0.9.3
+        ServerContext& context = OrthancRestApi::GetContext(call);
+        ParsedDicomFile dicom;
+        if (CreateDicomV1(dicom, request))
         {
-          LOG(ERROR) << "Error while storing a manually-created instance";
-          return;
+          std::string id;
+          if (StoreCreatedInstance(id, context, dicom))
+          {
+            OrthancRestApi::GetApi(call).AnswerStoredResource(call, id, ResourceType_Instance, StoreStatus_Success);
+          }
         }
-
-        OrthancRestApi::GetApi(call).AnswerStoredInstance(call, id, status);
       }
     }
   }
--- a/OrthancServer/OrthancRestApi/OrthancRestApi.cpp	Fri Aug 21 13:47:34 2015 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestApi.cpp	Fri Aug 21 15:01:21 2015 +0200
@@ -39,8 +39,9 @@
 
 namespace Orthanc
 {
-  void OrthancRestApi::AnswerStoredInstance(RestApiPostCall& call,
+  void OrthancRestApi::AnswerStoredResource(RestApiPostCall& call,
                                             const std::string& publicId,
+                                            ResourceType resourceType,
                                             StoreStatus status) const
   {
     Json::Value result = Json::objectValue;
@@ -48,7 +49,7 @@
     if (status != StoreStatus_Failure)
     {
       result["ID"] = publicId;
-      result["Path"] = GetBasePath(ResourceType_Instance, publicId);
+      result["Path"] = GetBasePath(resourceType, publicId);
     }
 
     result["Status"] = EnumerationToString(status);
@@ -88,7 +89,7 @@
     std::string publicId;
     StoreStatus status = context.Store(publicId, toStore);
 
-    OrthancRestApi::GetApi(call).AnswerStoredInstance(call, publicId, status);
+    OrthancRestApi::GetApi(call).AnswerStoredResource(call, publicId, ResourceType_Instance, status);
   }
 
 
--- a/OrthancServer/OrthancRestApi/OrthancRestApi.h	Fri Aug 21 13:47:34 2015 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestApi.h	Fri Aug 21 15:01:21 2015 +0200
@@ -82,8 +82,9 @@
 
     static ServerIndex& GetIndex(RestApiCall& call);
 
-    void AnswerStoredInstance(RestApiPostCall& call,
+    void AnswerStoredResource(RestApiPostCall& call,
                               const std::string& publicId,
+                              ResourceType resourceType,
                               StoreStatus status) const;
 
     static bool ParseModifyRequest(DicomModification& target,
--- a/OrthancServer/ParsedDicomFile.cpp	Fri Aug 21 13:47:34 2015 +0200
+++ b/OrthancServer/ParsedDicomFile.cpp	Fri Aug 21 15:01:21 2015 +0200
@@ -1111,8 +1111,10 @@
     pimpl_(new PImpl)
   {
     pimpl_->file_.reset(dynamic_cast<DcmFileFormat*>(other.pimpl_->file_->clone()));
+    pimpl_->encoding_ = other.pimpl_->encoding_;
 
-    pimpl_->encoding_ = other.pimpl_->encoding_;
+    // Create a new instance-level identifier
+    Replace(DICOM_TAG_SOP_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Instance));
   }