diff Sources/Plugin.cpp @ 33:2460b376d3f7

reorganization
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 04 Apr 2024 18:50:11 +0200
parents 976da5476810
children bee2017f3088
line wrap: on
line diff
--- a/Sources/Plugin.cpp	Thu Apr 04 18:35:54 2024 +0200
+++ b/Sources/Plugin.cpp	Thu Apr 04 18:50:11 2024 +0200
@@ -22,6 +22,7 @@
  **/
 
 
+#include "StructurePolygon.h"
 #include "VTKToolbox.h"
 #include "Vector3D.h"
 #include "Toolbox.h"
@@ -40,6 +41,8 @@
 #include <SerializationToolbox.h>
 #include <SystemToolbox.h>
 
+#include <vtkNew.h>
+
 #include <boost/thread/shared_mutex.hpp>
 
 #define ORTHANC_PLUGIN_NAME  "stl"
@@ -147,235 +150,11 @@
 
 
 
-#include <dcmtk/dcmdata/dcdeftag.h>
 #include <dcmtk/dcmdata/dcfilefo.h>
-#include <dcmtk/dcmdata/dcitem.h>
 #include <dcmtk/dcmdata/dcsequen.h>
 #include <dcmtk/dcmdata/dcuid.h>
 
 
-static std::string GetStringValue(DcmItem& item,
-                                  const DcmTagKey& key)
-{
-  const char* s = NULL;
-  if (!item.findAndGetString(key, s).good() ||
-      s == NULL)
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-  }
-  else
-  {
-    return Orthanc::Toolbox::StripSpaces(s);
-  }
-}
-
-
-static void ListStructuresNames(std::set<std::string>& target,
-                                Orthanc::ParsedDicomFile& source)
-{
-  target.clear();
-
-  DcmSequenceOfItems* sequence = NULL;
-  if (!source.GetDcmtkObject().getDataset()->findAndGetSequence(DCM_StructureSetROISequence, sequence).good() ||
-      sequence == NULL)
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-  }
-
-  for (unsigned long i = 0; i < sequence->card(); i++)
-  {
-    DcmItem* item = sequence->getItem(i);
-    if (item == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-    }
-    else
-    {
-      target.insert(GetStringValue(*item, DCM_ROIName));
-    }
-  }
-}
-
-
-class StructurePolygon : public boost::noncopyable
-{
-private:
-  std::string            roiName_;
-  std::string            referencedSopInstanceUid_;
-  uint8_t                red_;
-  uint8_t                green_;
-  uint8_t                blue_;
-  std::vector<Vector3D>  points_;
-
-public:
-  StructurePolygon(Orthanc::ParsedDicomFile& dicom,
-                   unsigned long roiIndex,
-                   unsigned long contourIndex)
-  {
-    DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset();
-
-    DcmItem* structure = NULL;
-    DcmItem* roi = NULL;
-    DcmItem* contour = NULL;
-    DcmSequenceOfItems* referenced = NULL;
-
-    if (!dataset.findAndGetSequenceItem(DCM_StructureSetROISequence, structure, roiIndex).good() ||
-        structure == NULL ||
-        !dataset.findAndGetSequenceItem(DCM_ROIContourSequence, roi, roiIndex).good() ||
-        roi == NULL ||
-        !roi->findAndGetSequenceItem(DCM_ContourSequence, contour, contourIndex).good() ||
-        contour == NULL ||
-        !contour->findAndGetSequence(DCM_ContourImageSequence, referenced).good() ||
-        referenced == NULL ||
-        referenced->card() != 1)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-    }
-
-    roiName_ = GetStringValue(*structure, DCM_ROIName);
-    referencedSopInstanceUid_ = GetStringValue(*referenced->getItem(0), DCM_ReferencedSOPInstanceUID);
-
-    if (GetStringValue(*contour, DCM_ContourGeometricType) != "CLOSED_PLANAR")
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-    }
-
-    {
-      std::vector<std::string> tokens;
-      Orthanc::Toolbox::TokenizeString(tokens, GetStringValue(*roi, DCM_ROIDisplayColor), '\\');
-
-      uint32_t r, g, b;
-      if (tokens.size() != 3 ||
-          !Orthanc::SerializationToolbox::ParseFirstUnsignedInteger32(r, tokens[0]) ||
-          !Orthanc::SerializationToolbox::ParseFirstUnsignedInteger32(g, tokens[1]) ||
-          !Orthanc::SerializationToolbox::ParseFirstUnsignedInteger32(b, tokens[2]) ||
-          r > 255 ||
-          g > 255 ||
-          b > 255)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-      }
-
-      red_ = r;
-      green_ = g;
-      blue_ = b;
-    }
-
-    {
-      std::vector<std::string> tokens;
-      Orthanc::Toolbox::TokenizeString(tokens, GetStringValue(*contour, DCM_ContourData), '\\');
-
-      const std::string s = GetStringValue(*contour, DCM_NumberOfContourPoints);
-
-      uint32_t countPoints;
-      if (!Orthanc::SerializationToolbox::ParseUnsignedInteger32(countPoints, s) ||
-          tokens.size() != 3 * countPoints)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-      }
-
-      points_.reserve(countPoints);
-
-      for (size_t i = 0; i < tokens.size(); i += 3)
-      {
-        double x, y, z;
-        if (!Toolbox::MyParseDouble(x, tokens[i]) ||
-            !Toolbox::MyParseDouble(y, tokens[i + 1]) ||
-            !Toolbox::MyParseDouble(z, tokens[i + 2]))
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-        }
-
-        points_.push_back(Vector3D(x, y, z));
-      }
-
-      assert(points_.size() == countPoints);
-    }
-  }
-
-  const std::string& GetRoiName() const
-  {
-    return roiName_;
-  }
-
-  const std::string& GetReferencedSopInstanceUid() const
-  {
-    return referencedSopInstanceUid_;
-  }
-
-  size_t GetPointsCount() const
-  {
-    return points_.size();
-  }
-
-  const Vector3D& GetPoint(size_t i) const
-  {
-    if (i >= points_.size())
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-    else
-    {
-      return points_[i];
-    }
-  }
-
-  bool IsCoplanar(Vector3D& normal) const
-  {
-    if (points_.size() < 3)
-    {
-      return false;
-    }
-
-    bool hasNormal = false;
-
-    for (size_t i = 0; i < points_.size(); i++)
-    {
-      normal = Vector3D::CrossProduct(Vector3D(points_[1], points_[0]),
-                                      Vector3D(points_[2], points_[0]));
-      if (!Toolbox::IsNear(normal.ComputeNorm(), 0))
-      {
-        normal.Normalize();
-        hasNormal = true;
-      }
-    }
-
-    if (!hasNormal)
-    {
-      return false;
-    }
-
-    double a = Vector3D::DotProduct(points_[0], normal);
-
-    for (size_t i = 1; i < points_.size(); i++)
-    {
-      double b = Vector3D::DotProduct(points_[i], normal);
-      if (!Toolbox::IsNear(a, b))
-      {
-        return false;
-      }
-    }
-
-    return true;
-  }
-
-  void Add(Extent2D& extent,
-           const Vector3D& axisX,
-           const Vector3D& axisY) const
-  {
-    assert(Toolbox::IsNear(1, axisX.ComputeNorm()));
-    assert(Toolbox::IsNear(1, axisY.ComputeNorm()));
-
-    for (size_t i = 0; i < points_.size(); i++)
-    {
-      extent.Add(Vector3D::DotProduct(axisX, points_[i]),
-                 Vector3D::DotProduct(axisY, points_[i]));
-    }
-  }
-};
-
-
-
 class StructureSet : public boost::noncopyable
 {
 private:
@@ -392,10 +171,10 @@
     hasFrameOfReferenceUid_(false)
   {
     DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset();
-    patientId_ = GetStringValue(dataset, DCM_PatientID);
-    studyInstanceUid_ = GetStringValue(dataset, DCM_StudyInstanceUID);
-    seriesInstanceUid_ = GetStringValue(dataset, DCM_SeriesInstanceUID);
-    sopInstanceUid_ = GetStringValue(dataset, DCM_SOPInstanceUID);
+    patientId_ = STLToolbox::GetStringValue(dataset, DCM_PatientID);
+    studyInstanceUid_ = STLToolbox::GetStringValue(dataset, DCM_StudyInstanceUID);
+    seriesInstanceUid_ = STLToolbox::GetStringValue(dataset, DCM_SeriesInstanceUID);
+    sopInstanceUid_ = STLToolbox::GetStringValue(dataset, DCM_SOPInstanceUID);
 
     DcmSequenceOfItems* frame = NULL;
     if (!dataset.findAndGetSequence(DCM_ReferencedFrameOfReferenceSequence, frame).good() ||
@@ -523,6 +302,33 @@
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
   }
+
+  // This static method is faster than constructing the full "StructureSet" object
+  static void ListStructuresNames(std::set<std::string>& target,
+                                  Orthanc::ParsedDicomFile& source)
+  {
+    target.clear();
+
+    DcmSequenceOfItems* sequence = NULL;
+    if (!source.GetDcmtkObject().getDataset()->findAndGetSequence(DCM_StructureSetROISequence, sequence).good() ||
+        sequence == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+
+    for (unsigned long i = 0; i < sequence->card(); i++)
+    {
+      DcmItem* item = sequence->getItem(i);
+      if (item == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+      }
+      else
+      {
+        target.insert(STLToolbox::GetStringValue(*item, DCM_ROIName));
+      }
+    }
+  }
 };
 
 
@@ -555,7 +361,7 @@
 
     double d = (z - minProjectionAlongNormal_) / slicesSpacing_;
 
-    if (Toolbox::IsNear(d, round(d)))
+    if (STLToolbox::IsNear(d, round(d)))
     {
       if (d < 0.0 ||
           d > static_cast<double>(slicesCount_) - 1.0)
@@ -623,7 +429,7 @@
     // Only keep unique projections
 
     std::sort(projections.begin(), projections.end());
-    Toolbox::RemoveDuplicateValues(projections);
+    STLToolbox::RemoveDuplicateValues(projections);
     assert(!projections.empty());
 
     if (projections.size() == 1)
@@ -650,7 +456,7 @@
       }
 
       std::sort(spacings.begin(), spacings.end());
-      Toolbox::RemoveDuplicateValues(spacings);
+      STLToolbox::RemoveDuplicateValues(spacings);
 
       if (spacings.empty())
       {
@@ -687,7 +493,7 @@
       while (it != candidates.end())
       {
         double d = (projections[*it] - projections[reference]) / slicesSpacing_;
-        if (Toolbox::IsNear(d, round(d)))
+        if (STLToolbox::IsNear(d, round(d)))
         {
           countSupport ++;
         }
@@ -726,7 +532,7 @@
     for (size_t i = 0; i < projections.size(); i++)
     {
       double d = (projections[i] - bestProjection) / slicesSpacing_;
-      if (Toolbox::IsNear(d, round(d)))
+      if (STLToolbox::IsNear(d, round(d)))
       {
         minProjectionAlongNormal_ = std::min(minProjectionAlongNormal_, projections[i]);
         maxProjectionAlongNormal_ = std::max(maxProjectionAlongNormal_, projections[i]);
@@ -734,7 +540,7 @@
     }
 
     double d = (maxProjectionAlongNormal_ - minProjectionAlongNormal_) / slicesSpacing_;
-    if (Toolbox::IsNear(d, round(d)))
+    if (STLToolbox::IsNear(d, round(d)))
     {
       slicesCount_ = static_cast<size_t>(round(d)) + 1;
     }
@@ -902,12 +708,12 @@
             double x1, x2, x3, y1, y2, y3;
 
             if (items.size() == 6 &&
-                Toolbox::MyParseDouble(x1, items[0]) &&
-                Toolbox::MyParseDouble(x2, items[1]) &&
-                Toolbox::MyParseDouble(x3, items[2]) &&
-                Toolbox::MyParseDouble(y1, items[3]) &&
-                Toolbox::MyParseDouble(y2, items[4]) &&
-                Toolbox::MyParseDouble(y3, items[5]))
+                STLToolbox::MyParseDouble(x1, items[0]) &&
+                STLToolbox::MyParseDouble(x2, items[1]) &&
+                STLToolbox::MyParseDouble(x3, items[2]) &&
+                STLToolbox::MyParseDouble(y1, items[3]) &&
+                STLToolbox::MyParseDouble(y2, items[4]) &&
+                STLToolbox::MyParseDouble(y3, items[5]))
             {
               axisX = Vector3D(x1, x2, x3);
               axisY = Vector3D(y1, y2, y3);
@@ -938,7 +744,7 @@
     throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
   }
 
-  if (!Toolbox::IsNear(1, geometry.GetSlicesNormal().ComputeNorm()))
+  if (!STLToolbox::IsNear(1, geometry.GetSlicesNormal().ComputeNorm()))
   {
     throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
   }
@@ -948,8 +754,8 @@
 
   Vector3D axisZ = Vector3D::CrossProduct(axisX, axisY);
 
-  if (!Toolbox::IsNear(1, axisX.ComputeNorm()) ||
-      !Toolbox::IsNear(1, axisY.ComputeNorm()) ||
+  if (!STLToolbox::IsNear(1, axisX.ComputeNorm()) ||
+      !STLToolbox::IsNear(1, axisY.ComputeNorm()) ||
       !Vector3D::AreParallel(axisZ, geometry.GetSlicesNormal()))
   {
     throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
@@ -1037,7 +843,7 @@
   std::unique_ptr<Orthanc::ParsedDicomFile> dicom(LoadInstance(instanceId));
 
   std::set<std::string> names;
-  ListStructuresNames(names, *dicom);
+  StructureSet::ListStructuresNames(names, *dicom);
 
   Json::Value answer = Json::arrayValue;
 
@@ -1260,8 +1066,8 @@
   DcmDataset& dataset = *dicom->GetDcmtkObject().getDataset();
 
   std::string stl;
-  if (GetStringValue(dataset, DCM_MIMETypeOfEncapsulatedDocument) != Orthanc::MIME_STL ||
-      GetStringValue(dataset, DCM_SOPClassUID) != UID_EncapsulatedSTLStorage ||
+  if (STLToolbox::GetStringValue(dataset, DCM_MIMETypeOfEncapsulatedDocument) != Orthanc::MIME_STL ||
+      STLToolbox::GetStringValue(dataset, DCM_SOPClassUID) != UID_EncapsulatedSTLStorage ||
       !dicom->GetTagValue(stl, Orthanc::DICOM_TAG_ENCAPSULATED_DOCUMENT))
   {
     throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest, "DICOM instance not encapsulating a STL model: " + instanceId);