changeset 3:0fb06c6a6c87

added route "/instances/{id}/stl"
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 18 Jul 2023 10:01:31 +0200
parents 2bdb9acb7dcf
children 5ee4448a8ff8
files Sources/Plugin.cpp
diffstat 1 files changed, 120 insertions(+), 93 deletions(-) [+]
line wrap: on
line diff
--- a/Sources/Plugin.cpp	Tue Jul 18 09:41:31 2023 +0200
+++ b/Sources/Plugin.cpp	Tue Jul 18 10:01:31 2023 +0200
@@ -119,6 +119,12 @@
                const char* url,
                const OrthancPluginHttpRequest* request)
 {
+  if (request->method != OrthancPluginHttpMethod_Get)
+  {
+    OrthancPluginSendMethodNotAllowed(OrthancPlugins::GetGlobalContext(), output, "GET");
+    return;
+  }
+
   std::string file = request->groups[0];
 
   if (file == "viewer.html")
@@ -234,9 +240,8 @@
   }
 };
 
-static void GetStringValue(std::string& value,
-                           DcmItem& item,
-                           const DcmTagKey& key)
+static std::string GetStringValue(DcmItem& item,
+                                  const DcmTagKey& key)
 {
   const char* s = NULL;
   if (!item.findAndGetString(key, s).good() ||
@@ -246,7 +251,7 @@
   }
   else
   {
-    value.assign(s);
+    return Orthanc::Toolbox::StripSpaces(s);
   }
 }
 
@@ -272,9 +277,7 @@
     }
     else
     {
-      std::string value;
-      GetStringValue(value, *item, DCM_ROIName);
-      target.insert(value);
+      target.insert(GetStringValue(*item, DCM_ROIName));
     }
   }
 }
@@ -415,22 +418,17 @@
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
     }
 
-    GetStringValue(roiName_, *structure, DCM_ROIName);
-    GetStringValue(referencedSopInstanceUid_, *referenced->getItem(0), DCM_ReferencedSOPInstanceUID);
+    roiName_ = GetStringValue(*structure, DCM_ROIName);
+    referencedSopInstanceUid_ = GetStringValue(*referenced->getItem(0), DCM_ReferencedSOPInstanceUID);
 
-    std::string s;
-    GetStringValue(s, *contour, DCM_ContourGeometricType);
-    if (s != "CLOSED_PLANAR")
+    if (GetStringValue(*contour, DCM_ContourGeometricType) != "CLOSED_PLANAR")
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
     }
 
     {
-      std::string color;
-      GetStringValue(color, *roi, DCM_ROIDisplayColor);
-
       std::vector<std::string> tokens;
-      Orthanc::Toolbox::TokenizeString(tokens, color, '\\');
+      Orthanc::Toolbox::TokenizeString(tokens, GetStringValue(*roi, DCM_ROIDisplayColor), '\\');
 
       uint32_t r, g, b;
       if (tokens.size() != 3 ||
@@ -450,12 +448,10 @@
     }
 
     {
-      GetStringValue(s, *contour, DCM_ContourData);
+      std::vector<std::string> tokens;
+      Orthanc::Toolbox::TokenizeString(tokens, GetStringValue(*contour, DCM_ContourData), '\\');
 
-      std::vector<std::string> tokens;
-      Orthanc::Toolbox::TokenizeString(tokens, s, '\\');
-
-      GetStringValue(s, *contour, DCM_NumberOfContourPoints);
+      const std::string s = GetStringValue(*contour, DCM_NumberOfContourPoints);
 
       uint32_t countPoints;
       if (!Orthanc::SerializationToolbox::ParseUnsignedInteger32(countPoints, s) ||
@@ -694,10 +690,10 @@
     maxProjectionAlongNormal_(0)
   {
     DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset();
-    GetStringValue(patientId_, dataset, DCM_PatientID);
-    GetStringValue(studyInstanceUid_, dataset, DCM_StudyInstanceUID);
-    GetStringValue(seriesInstanceUid_, dataset, DCM_SeriesInstanceUID);
-    GetStringValue(sopInstanceUid_, dataset, DCM_SOPInstanceUID);
+    patientId_ = GetStringValue(dataset, DCM_PatientID);
+    studyInstanceUid_ = GetStringValue(dataset, DCM_StudyInstanceUID);
+    seriesInstanceUid_ = GetStringValue(dataset, DCM_SeriesInstanceUID);
+    sopInstanceUid_ = GetStringValue(dataset, DCM_SOPInstanceUID);
 
     DcmSequenceOfItems* rois = NULL;
     if (!dataset.findAndGetSequence(DCM_ROIContourSequence, rois).good() ||
@@ -1111,40 +1107,47 @@
 }
 
 
+static Orthanc::ParsedDicomFile* LoadInstance(const std::string& instanceId)
+{
+  std::string dicom;
+
+  if (!OrthancPlugins::RestApiGetString(dicom, "/instances/" + instanceId + "/file", false))
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource);
+  }
+  else
+  {
+    return new Orthanc::ParsedDicomFile(dicom);
+  }
+}
+
+
 void ListStructures(OrthancPluginRestOutput* output,
                     const char* url,
                     const OrthancPluginHttpRequest* request)
 {
-  const std::string instanceId(request->groups[0]);
-
   if (request->method != OrthancPluginHttpMethod_Get)
   {
     OrthancPluginSendMethodNotAllowed(OrthancPlugins::GetGlobalContext(), output, "GET");
     return;
   }
 
-  std::string dicom;
-  if (!OrthancPlugins::RestApiGetString(dicom, "/instances/" + instanceId + "/file", false))
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource);
-  }
-  else
-  {
-    Orthanc::ParsedDicomFile parsed(dicom);
+  const std::string instanceId(request->groups[0]);
+
+  std::unique_ptr<Orthanc::ParsedDicomFile> dicom(LoadInstance(instanceId));
+
+  std::set<std::string> names;
+  ListStructuresNames(names, *dicom);
 
-    std::set<std::string> names;
-    ListStructuresNames(names, parsed);
-
-    Json::Value answer = Json::arrayValue;
+  Json::Value answer = Json::arrayValue;
 
-    for (std::set<std::string>::const_iterator it = names.begin(); it != names.end(); ++it)
-    {
-      answer.append(*it);
-    }
+  for (std::set<std::string>::const_iterator it = names.begin(); it != names.end(); ++it)
+  {
+    answer.append(*it);
+  }
 
-    std::string s = answer.toStyledString();
-    OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), Orthanc::MIME_JSON);
-  }
+  std::string s = answer.toStyledString();
+  OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), Orthanc::MIME_JSON);
 }
 
 
@@ -1181,81 +1184,104 @@
   std::set<std::string> roiNames;
   Orthanc::SerializationToolbox::ReadSetOfStrings(roiNames, body, KEY_ROI_NAMES);
 
-  std::string dicom;
-  if (!OrthancPlugins::RestApiGetString(dicom, "/instances/" + instanceId + "/file", false))
+  std::unique_ptr<Orthanc::ParsedDicomFile> dicom(LoadInstance(instanceId));
+
+  StructureSet structureSet(*dicom);
+
+  std::string stl;
+  if (!EncodeStructureSetMesh(stl, structureSet, roiNames, resolution, smooth))
   {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource);
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Cannot encode STL");
   }
   else
   {
-    Orthanc::ParsedDicomFile parsed(dicom);
-    StructureSet structureSet(parsed);
+    std::string content;
+    Orthanc::Toolbox::EncodeDataUriScheme(content, "model/stl", stl);
 
-    std::string stl;
-    if (!EncodeStructureSetMesh(stl, structureSet, roiNames, resolution, smooth))
+    Json::Value create;
+    create["Content"] = content;
+    create["Parent"] = structureSet.HashStudy();
+
+    if (body.isMember(KEY_TAGS))
     {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Cannot encode STL");
+      create[KEY_TAGS] = body[KEY_TAGS];
     }
     else
     {
-      std::string content;
-      Orthanc::Toolbox::EncodeDataUriScheme(content, "model/stl", stl);
+      std::string description;
 
-      Json::Value create;
-      create["Content"] = content;
-      create["Parent"] = structureSet.HashStudy();
-
-      if (body.isMember(KEY_TAGS))
+      if (dicom->GetTagValue(description, Orthanc::DICOM_TAG_SERIES_DESCRIPTION))
       {
-        create[KEY_TAGS] = body[KEY_TAGS];
+        description += ": ";
       }
       else
       {
-        std::string description;
+        description.clear();
+      }
 
-        if (parsed.GetTagValue(description, Orthanc::DICOM_TAG_SERIES_DESCRIPTION))
+      bool first = true;
+      for (std::set<std::string>::const_iterator it = roiNames.begin(); it != roiNames.end(); ++it)
+      {
+        if (first)
         {
-          description += ": ";
+          first = false;
         }
         else
         {
-          description.clear();
+          description += ", ";
         }
 
-        bool first = true;
-        for (std::set<std::string>::const_iterator it = roiNames.begin(); it != roiNames.end(); ++it)
-        {
-          if (first)
-          {
-            first = false;
-          }
-          else
-          {
-            description += ", ";
-          }
-
-          description += *it;
-        }
-
-        create[KEY_TAGS] = Json::objectValue;
-        create[KEY_TAGS]["SeriesDescription"] = description;
+        description += *it;
       }
 
-      Json::Value answer;
-      if (OrthancPlugins::RestApiPost(answer, "/tools/create-dicom", create.toStyledString(), false))
-      {
-        std::string s = answer.toStyledString();
-        OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), Orthanc::MIME_JSON);
-      }
-      else
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest, "Cannot create DICOM from STL");
-      }
+      create[KEY_TAGS] = Json::objectValue;
+      create[KEY_TAGS]["SeriesDescription"] = description;
+    }
+
+    Json::Value answer;
+    if (OrthancPlugins::RestApiPost(answer, "/tools/create-dicom", create.toStyledString(), false))
+    {
+      std::string s = answer.toStyledString();
+      OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), Orthanc::MIME_JSON);
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest, "Cannot create DICOM from STL");
     }
   }
 }
 
 
+void ExtractStl(OrthancPluginRestOutput* output,
+                const char* url,
+                const OrthancPluginHttpRequest* request)
+{
+  if (request->method != OrthancPluginHttpMethod_Get)
+  {
+    OrthancPluginSendMethodNotAllowed(OrthancPlugins::GetGlobalContext(), output, "GET");
+    return;
+  }
+
+  const std::string instanceId(request->groups[0]);
+
+  std::unique_ptr<Orthanc::ParsedDicomFile> dicom(LoadInstance(instanceId));
+  DcmDataset& dataset = *dicom->GetDcmtkObject().getDataset();
+
+  std::string stl;
+  if (GetStringValue(dataset, DCM_MIMETypeOfEncapsulatedDocument) != Orthanc::MIME_STL ||
+      GetStringValue(dataset, DCM_SOPClassUID) != "1.2.840.10008.5.1.4.1.1.104.3" ||
+      !dicom->GetTagValue(stl, Orthanc::DICOM_TAG_ENCAPSULATED_DOCUMENT))
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest, "DICOM instance not encapsulating a STL model: " + instanceId);
+  }
+  else
+  {
+    OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output,
+                              stl.empty() ? NULL : stl.c_str(), stl.size(), Orthanc::MIME_STL);
+  }
+}
+
+
 extern "C"
 {
   ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context)
@@ -1294,6 +1320,7 @@
     OrthancPluginSetDescription(context, "STL plugin for Orthanc.");
 
     OrthancPlugins::RegisterRestCallback<ServeFile>("/stl/app/(.*)", true);
+    OrthancPlugins::RegisterRestCallback<ExtractStl>("/instances/([0-9a-f-]+)/stl", true);
     OrthancPlugins::RegisterRestCallback<ListStructures>("/stl/rt-struct/([0-9a-f-]+)", true);
 
     if (hasCreateDicomStl_)