diff Framework/Toolbox/DicomStructureSet.cpp @ 32:517c46f527cd

sync
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 19 Dec 2016 11:00:23 +0100
parents 9aace933cb64
children a865c7992a87
line wrap: on
line diff
--- a/Framework/Toolbox/DicomStructureSet.cpp	Fri Dec 16 15:41:20 2016 +0100
+++ b/Framework/Toolbox/DicomStructureSet.cpp	Mon Dec 19 11:00:23 2016 +0100
@@ -34,6 +34,7 @@
 
 #include "../../Resources/Orthanc/Core/Logging.h"
 #include "../../Resources/Orthanc/Core/OrthancException.h"
+#include "../../Resources/Orthanc/Plugins/Samples/Common/FullOrthancDataset.h"
 #include "../Messaging/MessagingToolbox.h"
 
 #include <stdio.h>
@@ -41,27 +42,17 @@
 
 namespace OrthancStone
 {
-  static const Json::Value& GetSequence(const Json::Value& instance,
-                                        uint16_t group,
-                                        uint16_t element)
-  {
-    char buf[16];
-    sprintf(buf, "%04x,%04x", group, element);
-
-    if (instance.type() != Json::objectValue ||
-        !instance.isMember(buf) ||
-        instance[buf].type() != Json::objectValue ||
-        !instance[buf].isMember("Type") ||
-        !instance[buf].isMember("Value") ||
-        instance[buf]["Type"].type() != Json::stringValue ||
-        instance[buf]["Value"].type() != Json::arrayValue ||
-        instance[buf]["Type"].asString() != "Sequence")
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-    }
-
-    return instance[buf]["Value"];
-  }
+  static const OrthancPlugins::DicomTag DICOM_TAG_CONTOUR_GEOMETRIC_TYPE(0x3006, 0x0042);
+  static const OrthancPlugins::DicomTag DICOM_TAG_CONTOUR_IMAGE_SEQUENCE(0x3006, 0x0016);
+  static const OrthancPlugins::DicomTag DICOM_TAG_CONTOUR_SEQUENCE(0x3006, 0x0040);
+  static const OrthancPlugins::DicomTag DICOM_TAG_NUMBER_OF_CONTOUR_POINTS(0x3006, 0x0046);
+  static const OrthancPlugins::DicomTag DICOM_TAG_REFERENCED_SOP_INSTANCE_UID(0x0008, 0x1155);
+  static const OrthancPlugins::DicomTag DICOM_TAG_ROI_CONTOUR_SEQUENCE(0x3006, 0x0039);
+  static const OrthancPlugins::DicomTag DICOM_TAG_ROI_DISPLAY_COLOR(0x3006, 0x002a);
+  static const OrthancPlugins::DicomTag DICOM_TAG_ROI_NAME(0x3006, 0x0026);
+  static const OrthancPlugins::DicomTag DICOM_TAG_RT_ROI_INTERPRETED_TYPE(0x3006, 0x00a4);
+  static const OrthancPlugins::DicomTag DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE(0x3006, 0x0080);
+  static const OrthancPlugins::DicomTag DICOM_TAG_STRUCTURE_SET_ROI_SEQUENCE(0x3006, 0x0020);
 
 
   static uint8_t ConvertColor(double v)
@@ -83,17 +74,26 @@
 
   SliceGeometry DicomStructureSet::ExtractSliceGeometry(double& sliceThickness,
                                                         OrthancPlugins::IOrthancConnection& orthanc,
-                                                        const Json::Value& contour)
+                                                        const OrthancPlugins::IDicomDataset& tags,
+                                                        size_t contourIndex,
+                                                        size_t sliceIndex)
   {
-    const Json::Value& sequence = GetSequence(contour, 0x3006, 0x0016);
+    using namespace OrthancPlugins;
 
-    if (sequence.size() != 1)
+    size_t size;
+    if (!tags.GetSequenceSize(size, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, contourIndex,
+                                              DICOM_TAG_CONTOUR_SEQUENCE, sliceIndex,
+                                              DICOM_TAG_CONTOUR_IMAGE_SEQUENCE)) ||
+        size != 1)
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);          
     }
 
-    DicomDataset contourImageSequence(sequence[0]);
-    std::string parentUid = contourImageSequence.GetStringValue(std::make_pair(0x0008, 0x1155));
+    DicomDatasetReader reader(tags);
+    std::string parentUid = reader.GetMandatoryStringValue(DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, contourIndex,
+                                                                     DICOM_TAG_CONTOUR_SEQUENCE, sliceIndex,
+                                                                     DICOM_TAG_CONTOUR_IMAGE_SEQUENCE, 0,
+                                                                     DICOM_TAG_REFERENCED_SOP_INSTANCE_UID));
 
     std::string post;
     orthanc.RestApiPost(post, "/tools/lookup", parentUid);
@@ -135,32 +135,18 @@
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
     }
 
-    Json::Value parentTags;
-    MessagingToolbox::RestApiGet(parentTags, orthanc, "/instances/" + tmp[0]["ID"].asString() + "/tags?simplify");
-      
-    if (parentTags.type() != Json::objectValue ||
-        !parentTags.isMember("ImageOrientationPatient") ||
-        !parentTags.isMember("ImagePositionPatient") ||
-        parentTags["ImageOrientationPatient"].type() != Json::stringValue ||
-        parentTags["ImagePositionPatient"].type() != Json::stringValue)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);          
-    }
+    FullOrthancDataset parentTags(orthanc, "/instances/" + tmp[0]["ID"].asString() + "/tags");
+    SliceGeometry slice(parentTags);
 
-    SliceGeometry slice(parentTags["ImagePositionPatient"].asString(),
-                        parentTags["ImageOrientationPatient"].asString());
-                          
-    sliceThickness = 1;  // 1 mm by default
-
-    if (parentTags.isMember("SliceThickness") &&
-        parentTags["SliceThickness"].type() == Json::stringValue)
+    Vector v;
+    if (GeometryToolbox::ParseVector(v, parentTags, DICOM_TAG_SLICE_THICKNESS) &&
+        v.size() > 0)
     {
-      Vector tmp;
-      GeometryToolbox::ParseVector(tmp, parentTags["SliceThickness"].asString());
-      if (tmp.size() > 0)
-      {
-        sliceThickness = tmp[0];
-      }
+      sliceThickness = v[0];
+    }
+    else
+    {
+      sliceThickness = 1;  // 1 mm by default
     }
 
     if (isFirst)
@@ -201,68 +187,87 @@
   DicomStructureSet::DicomStructureSet(OrthancPlugins::IOrthancConnection& orthanc,
                                        const std::string& instanceId)
   {
-    Json::Value instance;
-    MessagingToolbox::RestApiGet(instance, orthanc, "/instances/" + instanceId + "/tags");
- 
-    Json::Value rtRoiObservationSequence = GetSequence(instance, 0x3006, 0x0080);
-    Json::Value roiContourSequence = GetSequence(instance, 0x3006, 0x0039);
-    Json::Value structureSetRoiSequence = GetSequence(instance, 0x3006, 0x0020);
+    using namespace OrthancPlugins;
 
-    Json::Value::ArrayIndex count = rtRoiObservationSequence.size();
-    if (count != roiContourSequence.size() ||
-        count != structureSetRoiSequence.size())
+    FullOrthancDataset tags(orthanc, "/instances/" + instanceId + "/tags");
+    DicomDatasetReader reader(tags);
+    
+    size_t count, tmp;
+    if (!tags.GetSequenceSize(count, DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE) ||
+        !tags.GetSequenceSize(tmp, DICOM_TAG_ROI_CONTOUR_SEQUENCE) ||
+        tmp != count ||
+        !tags.GetSequenceSize(tmp, DICOM_TAG_STRUCTURE_SET_ROI_SEQUENCE) ||
+        tmp != count)
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
     }
-      
-    structures_.resize(count);
-    for (Json::Value::ArrayIndex i = 0; i < count; i++)
-    {
-      DicomDataset observation(rtRoiObservationSequence[i]);
-      DicomDataset roi(structureSetRoiSequence[i]);
-      DicomDataset content(roiContourSequence[i]);
 
-      structures_[i].interpretation_ = observation.GetStringValue(std::make_pair(0x3006, 0x00a4), "No interpretation");
-      structures_[i].name_ = roi.GetStringValue(std::make_pair(0x3006, 0x0026), "No name");
-      structures_[i].red_ = 255;
-      structures_[i].green_ = 0;
-      structures_[i].blue_ = 0;
+    structures_.resize(count);
+    for (size_t i = 0; i < count; i++)
+    {
+      structures_[i].interpretation_ = reader.GetStringValue(DicomPath(DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE, i,
+                                                                       DICOM_TAG_RT_ROI_INTERPRETED_TYPE),
+                                                             "No interpretation");
 
-      DicomDataset::Tag tag(0x3006, 0x002a);
+      structures_[i].name_ = reader.GetStringValue(DicomPath(DICOM_TAG_STRUCTURE_SET_ROI_SEQUENCE, i,
+                                                             DICOM_TAG_ROI_NAME),
+                                                   "No interpretation");
 
       Vector color;
-      if (content.HasTag(tag))
+      if (GeometryToolbox::ParseVector(color, tags, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
+                                                              DICOM_TAG_ROI_DISPLAY_COLOR)) &&
+          color.size() == 3)
       {
-        content.GetVectorValue(color, tag);
-        if (color.size() == 3)
-        {
-          structures_[i].red_ = ConvertColor(color[0]);
-          structures_[i].green_ = ConvertColor(color[1]);
-          structures_[i].blue_ = ConvertColor(color[2]);
-        }
+        structures_[i].red_ = ConvertColor(color[0]);
+        structures_[i].green_ = ConvertColor(color[1]);
+        structures_[i].blue_ = ConvertColor(color[2]);
+      }
+      else
+      {
+        structures_[i].red_ = 255;
+        structures_[i].green_ = 0;
+        structures_[i].blue_ = 0;
       }
 
-      const Json::Value& slices = GetSequence(roiContourSequence[i], 0x3006, 0x0040);
+      size_t countSlices;
+      if (!tags.GetSequenceSize(countSlices, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
+                                                       DICOM_TAG_CONTOUR_SEQUENCE)))
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+      }
 
       LOG(WARNING) << "New RT structure: \"" << structures_[i].name_ 
                    << "\" with interpretation \"" << structures_[i].interpretation_
-                   << "\" containing " << slices.size() << " slices (color: " 
+                   << "\" containing " << countSlices << " slices (color: " 
                    << static_cast<int>(structures_[i].red_) << "," 
                    << static_cast<int>(structures_[i].green_) << ","
                    << static_cast<int>(structures_[i].blue_) << ")";
 
-      for (Json::Value::ArrayIndex j = 0; j < slices.size(); j++)
+      for (size_t j = 0; j < countSlices; j++)
       {
-        DicomDataset slice(slices[j]);
+        unsigned int countPoints;
 
-        unsigned int npoints = slice.GetUnsignedIntegerValue(std::make_pair(0x3006, 0x0046));
-        LOG(INFO) << "Parsing slice containing " << npoints << " vertices";
+        if (!reader.GetUnsignedIntegerValue(countPoints, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
+                                                                   DICOM_TAG_CONTOUR_SEQUENCE, j,
+                                                                   DICOM_TAG_NUMBER_OF_CONTOUR_POINTS)))
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+        }
+            
+        LOG(INFO) << "Parsing slice containing " << countPoints << " vertices";
 
-        if (slice.GetStringValue(std::make_pair(0x3006, 0x0042)) != "CLOSED_PLANAR")
+        std::string type = reader.GetMandatoryStringValue(DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
+                                                                    DICOM_TAG_CONTOUR_SEQUENCE, j,
+                                                                    DICOM_TAG_CONTOUR_GEOMETRIC_TYPE));
+        if (type != "CLOSED_PLANAR")
         {
+          LOG(ERROR) << "Cannot handle contour with geometry type: " << type;
           throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);          
         }
 
+        // The "CountourData" tag (3006,0050) is too large to be
+        // returned by the "/instances/{id}/tags" URI: Access it using
+        // the raw "/instances/{id}/content/{...}" endpoint
         std::string slicesData;
         orthanc.RestApiGet(slicesData, "/instances/" + instanceId + "/content/3006-0039/" +
                            boost::lexical_cast<std::string>(i) + "/3006-0040/" +
@@ -270,16 +275,16 @@
 
         Vector points;
         if (!GeometryToolbox::ParseVector(points, slicesData) ||
-            points.size() != 3 * npoints)
+            points.size() != 3 * countPoints)
         {
           throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);          
         }
 
         Polygon polygon;
-        SliceGeometry geometry = ExtractSliceGeometry(polygon.sliceThickness_, orthanc, slices[j]);
+        SliceGeometry geometry = ExtractSliceGeometry(polygon.sliceThickness_, orthanc, tags, i, j);
         polygon.projectionAlongNormal_ = geometry.ProjectAlongNormal(geometry.GetOrigin());
 
-        for (size_t k = 0; k < npoints; k++)
+        for (size_t k = 0; k < countPoints; k++)
         {
           Vector v(3);
           v[0] = points[3 * k];