changeset 2220:532764b7b57f deep-learning

integration mainline->deep-learning
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 22 Apr 2025 18:20:01 +0200 (3 months ago)
parents a8066ce6bacc (current diff) a10b7a6ec869 (diff)
children
files Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp
diffstat 6 files changed, 153 insertions(+), 105 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/StoneWebViewer/WebAssembly/IStoneWebViewerContext.h	Tue Apr 22 14:55:22 2025 +0200
+++ b/Applications/StoneWebViewer/WebAssembly/IStoneWebViewerContext.h	Tue Apr 22 18:20:01 2025 +0200
@@ -28,6 +28,15 @@
 #include <Images/ImageAccessor.h>
 
 
+static const int LAYER_TEXTURE = 0;
+static const int LAYER_OVERLAY = 1;
+static const int LAYER_ORIENTATION_MARKERS = 2;
+static const int LAYER_REFERENCE_LINES = 3;
+static const int LAYER_ANNOTATIONS_STONE = 5;
+static const int LAYER_ANNOTATIONS_OSIRIX = 4;
+static const int LAYER_STRUCTURED_REPORT = 6;
+
+
 #define DISPATCH_JAVASCRIPT_EVENT(name)                         \
   EM_ASM(                                                       \
     const customEvent = document.createEvent("CustomEvent");    \
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Tue Apr 22 14:55:22 2025 +0200
+++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Tue Apr 22 18:20:01 2025 +0200
@@ -73,14 +73,6 @@
 
 static const double PI = boost::math::constants::pi<double>();
 
-static const int LAYER_TEXTURE = 0;
-static const int LAYER_OVERLAY = 1;
-static const int LAYER_ORIENTATION_MARKERS = 2;
-static const int LAYER_REFERENCE_LINES = 3;
-static const int LAYER_ANNOTATIONS_STONE = 5;
-static const int LAYER_ANNOTATIONS_OSIRIX = 4;
-static const int LAYER_STRUCTURED_REPORT = 6;
-
 
 #if !defined(STONE_WEB_VIEWER_EXPORT)
 // We are not running ParseWebAssemblyExports.py, but we're compiling the wasm
--- a/OrthancStone/Sources/Toolbox/CoordinateSystem3D.cpp	Tue Apr 22 14:55:22 2025 +0200
+++ b/OrthancStone/Sources/Toolbox/CoordinateSystem3D.cpp	Tue Apr 22 18:20:01 2025 +0200
@@ -486,4 +486,24 @@
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
   }
+
+
+  bool CoordinateSystem3D::Equals(const CoordinateSystem3D& other) const
+  {
+    if (!IsValid() && !other.IsValid())
+    {
+      return true;
+    }
+    else if (IsValid() && other.IsValid())
+    {
+      // The normal is automatically computed by "CheckAndComputeNormal()" that is called in all the constructors
+      return (LinearAlgebra::IsCloseToZero(boost::numeric::ublas::norm_2(GetOrigin() - other.GetOrigin())) &&
+              LinearAlgebra::IsCloseToZero(boost::numeric::ublas::norm_2(GetAxisX() - other.GetAxisX())) &&
+              LinearAlgebra::IsCloseToZero(boost::numeric::ublas::norm_2(GetAxisY() - other.GetAxisY())));
+    }
+    else
+    {
+      return false;
+    }
+  }
 }
--- a/OrthancStone/Sources/Toolbox/CoordinateSystem3D.h	Tue Apr 22 14:55:22 2025 +0200
+++ b/OrthancStone/Sources/Toolbox/CoordinateSystem3D.h	Tue Apr 22 18:20:01 2025 +0200
@@ -165,5 +165,7 @@
                                std::string& bottom /* out */,
                                std::string& left /* out */,
                                std::string& right /* out */) const;
+
+    bool Equals(const CoordinateSystem3D& other) const;
   };
 }
--- a/OrthancStone/Sources/Toolbox/DicomStructureSet.cpp	Tue Apr 22 14:55:22 2025 +0200
+++ b/OrthancStone/Sources/Toolbox/DicomStructureSet.cpp	Tue Apr 22 18:20:01 2025 +0200
@@ -226,18 +226,6 @@
         geometry_ = geometry;
         projectionAlongNormal_ = GeometryToolbox::ProjectAlongNormal(geometry.GetOrigin(), geometry.GetNormal());
         sliceThickness_ = it->second.thickness_;
-
-        extent_.Clear();
-        
-        for (Points::const_iterator it2 = points_.begin(); it2 != points_.end(); ++it2)
-        {
-          if (IsPointOnSliceIfAny(*it2))
-          {
-            double x, y;
-            geometry.ProjectPoint2(x, y, *it2);
-            extent_.AddPoint(x, y);
-          }
-        }
         return true;
       }
     }
@@ -286,7 +274,7 @@
   void DicomStructureSet::Polygon::Project(std::list<Extent2D>& target,
                                            const CoordinateSystem3D& cuttingPlane,
                                            const Vector& estimatedNormal,
-                                           double estimatedSliceThickness) const
+                                           double estimatedSliceThickness)
   {
     CoordinateSystem3D geometry;
     double thickness = estimatedSliceThickness;
@@ -314,8 +302,11 @@
       bool found = false;
       for (size_t i = 1; i < points_.size(); i++)
       {
-        axisX = points_[1] - origin;
-        if (boost::numeric::ublas::norm_2(axisX) > 10.0 * std::numeric_limits<double>::epsilon())
+        axisX = points_[i] - origin;
+
+        bool isOpposite;  // Ignored
+        if (boost::numeric::ublas::norm_2(axisX) > 10.0 * std::numeric_limits<double>::epsilon() &&
+            !GeometryToolbox::IsParallelOrOpposite(isOpposite, axisX, estimatedNormal))
         {
           found = true;
           break;
@@ -371,14 +362,33 @@
       return;  // Should never happen
     }
 
+    if (cachedProjectedSegments_.get() == NULL ||
+        !cachedGeometry_.Equals(geometry))
+    {
+      cachedGeometry_ = geometry;
+
+      cachedProjectedSegments_.reset(new std::vector<float>());
+      cachedProjectedSegments_->resize(2 * points_.size());
+
+      for (size_t i = 0; i < points_.size(); i++)
+      {
+        double x, y;
+        geometry.ProjectPoint(x, y, points_[i]);
+        (*cachedProjectedSegments_) [2 * i] = x;
+        (*cachedProjectedSegments_) [2 * i + 1] = y;
+      }
+    }
+
     std::vector<double> intersections;
     intersections.reserve(points_.size());
 
     for (size_t i = 0; i < points_.size(); i++)
     {
-      double segmentX1, segmentY1, segmentX2, segmentY2;
-      geometry.ProjectPoint(segmentX1, segmentY1, points_[i]);
-      geometry.ProjectPoint(segmentX2, segmentY2, points_[(i + 1) % points_.size()]);
+      const size_t next = (i + 1) % points_.size();
+      const double segmentX1 = (*cachedProjectedSegments_) [2 * i];
+      const double segmentY1 = (*cachedProjectedSegments_) [2 * i + 1];
+      const double segmentX2 = (*cachedProjectedSegments_) [2 * next];
+      const double segmentY2 = (*cachedProjectedSegments_) [2 * next + 1];
 
       double x, y;
       if (GeometryToolbox::IntersectLineAndSegment(x, y, cuttingX1, cuttingY1, cuttingX2, cuttingY2,
@@ -432,14 +442,27 @@
   }
 
   
+  DicomStructureSet::Structure::~Structure()
+  {
+    for (Polygons::iterator it = polygons_.begin(); it != polygons_.end(); ++it)
+    {
+      assert(*it != NULL);
+      delete *it;
+    }
+  }
+
+
   const DicomStructureSet::Structure& DicomStructureSet::GetStructure(size_t index) const
   {
     if (index >= structures_.size())
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
     }
-
-    return structures_[index];
+    else
+    {
+      assert(structures_[index] != NULL);
+      return *structures_[index];
+    }
   }
 
 
@@ -449,8 +472,11 @@
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
     }
-
-    return structures_[index];
+    else
+    {
+      assert(structures_[index] != NULL);
+      return *structures_[index];
+    }
   }
 
   void DicomStructureSet::Setup(const IDicomDataset& tags)
@@ -495,18 +521,19 @@
 
         roiNumbersIndex[roiNumber] = i;
 
-        structures_[i].name_ = reader.GetStringValue
+        structures_[i] = new Structure();
+        structures_[i]->name_ = reader.GetStringValue
           (Orthanc::DicomPath(DICOM_TAG_STRUCTURE_SET_ROI_SEQUENCE, i, DICOM_TAG_ROI_NAME), "No name");
-        structures_[i].interpretation_ = "No interpretation";
+        structures_[i]->interpretation_ = "No interpretation";
 
-        if (structureNamesIndex_.find(structures_[i].name_) == structureNamesIndex_.end())
+        if (structureNamesIndex_.find(structures_[i]->name_) == structureNamesIndex_.end())
         {
-          structureNamesIndex_[structures_[i].name_] = i;
+          structureNamesIndex_[structures_[i]->name_] = i;
         }
         else
         {
           throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
-                                          "RT-STRUCT with twice the same name for a structure: " + structures_[i].name_);
+                                          "RT-STRUCT with twice the same name for a structure: " + structures_[i]->name_);
         }
       }
     }
@@ -544,7 +571,7 @@
             throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
           }
 
-          structures_[found->second].interpretation_ = interpretation;
+          structures_[found->second]->interpretation_ = interpretation;
         }
       }
     }
@@ -577,7 +604,7 @@
           throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
         }
 
-        Structure& target = structures_[found->second];
+        Structure& target = *structures_[found->second];
 
         Vector color;
         if (FastParseVector(color, tags, Orthanc::DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
@@ -685,8 +712,8 @@
             LOG(ERROR) << "WARNING. The following Dicom tag (Referenced SOP Instance UID) contains an empty value : // (3006,0039)[" << i << "] / (0x3006, 0x0040)[0] / (0x3006, 0x0016)[0] / (0x0008, 0x1155)";
           }
 
-          Polygon polygon(sopInstanceUid);
-          polygon.Reserve(countPoints);
+          std::unique_ptr<Polygon> polygon(new Polygon(sopInstanceUid));
+          polygon->Reserve(countPoints);
 
           for (size_t k = 0; k < countPoints; k++)
           {
@@ -694,10 +721,10 @@
             v[0] = points[3 * k];
             v[1] = points[3 * k + 1];
             v[2] = points[3 * k + 2];
-            polygon.AddPoint(v);
+            polygon->AddPoint(v);
           }
 
-          target.polygons_.push_back(polygon);
+          target.polygons_.push_back(polygon.release());
         }
       }
     }
@@ -722,29 +749,13 @@
 #endif
   
 
-  Vector DicomStructureSet::GetStructureCenter(size_t index) const
+  DicomStructureSet::~DicomStructureSet()
   {
-    const Structure& structure = GetStructure(index);
-
-    Vector center;
-    LinearAlgebra::AssignVector(center, 0, 0, 0);
-    if (structure.polygons_.empty())
+    for (size_t i = 0; i < structures_.size(); i++)
     {
-      return center;
+      assert(structures_[i] != NULL);
+      delete structures_[i];
     }
-
-    double n = static_cast<double>(structure.polygons_.size());
-
-    for (Polygons::const_iterator polygon = structure.polygons_.begin();
-         polygon != structure.polygons_.end(); ++polygon)
-    {
-      if (!polygon->GetPoints().empty())
-      {
-        center += polygon->GetPoints().front() / n;
-      }
-    }
-
-    return center;
   }
 
 
@@ -769,13 +780,14 @@
     
   void DicomStructureSet::GetReferencedInstances(std::set<std::string>& instances) const
   {
-    for (Structures::const_iterator structure = structures_.begin();
-         structure != structures_.end(); ++structure)
+    for (size_t i = 0; i < structures_.size(); i++)
     {
-      for (Polygons::const_iterator polygon = structure->polygons_.begin();
-           polygon != structure->polygons_.end(); ++polygon)
+      assert(structures_[i] != NULL);
+      for (Polygons::const_iterator polygon = structures_[i]->polygons_.begin();
+           polygon != structures_[i]->polygons_.end(); ++polygon)
       {
-        instances.insert(polygon->GetSopInstanceUid());
+        assert(*polygon != NULL);
+        instances.insert((*polygon)->GetSopInstanceUid());
       }
     }
   }
@@ -819,13 +831,15 @@
         
       referencedSlices_[sopInstanceUid] = ReferencedSlice(seriesInstanceUid, geometry, thickness);
 
-      for (Structures::iterator structure = structures_.begin();
-           structure != structures_.end(); ++structure)
+      for (size_t i = 0; i < structures_.size(); i++)
       {
-        for (Polygons::iterator polygon = structure->polygons_.begin();
-             polygon != structure->polygons_.end(); ++polygon)
+        assert(structures_[i] != NULL);
+
+        for (Polygons::iterator polygon = structures_[i]->polygons_.begin();
+             polygon != structures_[i]->polygons_.end(); ++polygon)
         {
-          polygon->UpdateReferencedSlice(referencedSlices_);
+          assert(*polygon != NULL);
+          (*polygon)->UpdateReferencedSlice(referencedSlices_);
         }
       }
     }
@@ -862,15 +876,16 @@
 
   void DicomStructureSet::CheckReferencedSlices()
   {
-    for (Structures::iterator structure = structures_.begin();
-         structure != structures_.end(); ++structure)
+    for (size_t i = 0; i < structures_.size(); i++)
     {
-      for (Polygons::iterator polygon = structure->polygons_.begin();
-           polygon != structure->polygons_.end(); ++polygon)
+      assert(structures_[i] != NULL);
+      for (Polygons::iterator polygon = structures_[i]->polygons_.begin();
+           polygon != structures_[i]->polygons_.end(); ++polygon)
       {
-        if (!polygon->UpdateReferencedSlice(referencedSlices_))
+        assert(*polygon != NULL);
+        if (!(*polygon)->UpdateReferencedSlice(referencedSlices_))
         {
-          std::string sopInstanceUid = polygon->GetSopInstanceUid();
+          std::string sopInstanceUid = (*polygon)->GetSopInstanceUid();
           if (Orthanc::Toolbox::StripSpaces(sopInstanceUid) == "")
           {
             LOG(ERROR) << "DicomStructureSet::CheckReferencedSlices(): "
@@ -924,9 +939,10 @@
       for (Polygons::const_iterator polygon = structure.polygons_.begin();
            polygon != structure.polygons_.end(); ++polygon)
       {
-        const Points& points = polygon->GetPoints();
+        assert(*polygon != NULL);
+        const Points& points = (*polygon)->GetPoints();
         
-        if (polygon->IsOnSlice(cutting, GetEstimatedNormal(), GetEstimatedSliceThickness()) &&
+        if ((*polygon)->IsOnSlice(cutting, GetEstimatedNormal(), GetEstimatedSliceThickness()) &&
             !points.empty())
         {
           chains.push_back(std::vector<ScenePoint2D>());
@@ -990,7 +1006,8 @@
       for (Polygons::const_iterator polygon = structure.polygons_.begin();
            polygon != structure.polygons_.end(); ++polygon)
       {
-        polygon->Project(rectangles, cutting, GetEstimatedNormal(), GetEstimatedSliceThickness());
+        assert(*polygon != NULL);
+        (*polygon)->Project(rectangles, cutting, GetEstimatedNormal(), GetEstimatedSliceThickness());
       }
 
       typedef std::list< std::vector<ScenePoint2D> >  Contours;
@@ -1044,12 +1061,13 @@
     // TODO - Could be optimized by adding a multimap on "Structure", mapping
     // from SOP Instance UID to polygons
     
-    for (Polygons::const_iterator it = structure.polygons_.begin();
-         it != structure.polygons_.end(); ++it)
+    for (Polygons::const_iterator polygon = structure.polygons_.begin();
+         polygon != structure.polygons_.end(); ++polygon)
     {
-      if (it->GetSopInstanceUid() == sopInstanceUid)
+      assert(*polygon != NULL);
+      if ((*polygon)->GetSopInstanceUid() == sopInstanceUid)
       {
-        target.push_back(it->GetPoints());
+        target.push_back((*polygon)->GetPoints());
       }
     }
   }
@@ -1066,13 +1084,16 @@
     unsigned int countPolygons = 0;
     for (size_t i = 0; i < structures_.size(); i++)
     {
-      const Polygons& polygons = structures_[i].polygons_;
+      assert(structures_[i] != NULL);
+      const Polygons& polygons = structures_[i]->polygons_;
 
-      for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it)
+      for (Polygons::const_iterator polygon = polygons.begin(); polygon != polygons.end(); ++polygon)
       {
+        assert(*polygon != NULL);
+
         countPolygons++;
-        
-        const Points& points = it->GetPoints();
+
+        const Points& points = (*polygon)->GetPoints();
         
         if (points.size() >= 3)
         {
@@ -1149,11 +1170,13 @@
     
     for (size_t i = 0; i < structures_.size(); i++)
     {
-      const Polygons& polygons = structures_[i].polygons_;
+      assert(structures_[i] != NULL);
+      const Polygons& polygons = structures_[i]->polygons_;
 
-      for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it)
+      for (Polygons::const_iterator polygon = polygons.begin(); polygon != polygons.end(); ++polygon)
       {
-        const Points& points = it->GetPoints();
+        assert(*polygon != NULL);
+        const Points& points = (*polygon)->GetPoints();
         polygonsProjection.push_back(GeometryToolbox::ProjectAlongNormal(points[0], estimatedNormal_));
       }
     }
--- a/OrthancStone/Sources/Toolbox/DicomStructureSet.h	Tue Apr 22 14:55:22 2025 +0200
+++ b/OrthancStone/Sources/Toolbox/DicomStructureSet.h	Tue Apr 22 18:20:01 2025 +0200
@@ -72,7 +72,7 @@
     
     typedef std::vector<Vector>  Points;
 
-    class Polygon
+    class Polygon : public boost::noncopyable
     {
     private:
       std::string         sopInstanceUid_;
@@ -81,7 +81,9 @@
       double              projectionAlongNormal_;
       double              sliceThickness_;  // In millimeters
       Points              points_;
-      Extent2D            extent_;
+
+      CoordinateSystem3D                    cachedGeometry_;
+      std::unique_ptr< std::vector<float> > cachedProjectedSegments_;
 
       bool IsPointOnSliceIfAny(const Vector& v) const;
 
@@ -130,12 +132,12 @@
       void Project(std::list<Extent2D>& target,
                    const CoordinateSystem3D& cuttingPlane,
                    const Vector& estimatedNormal,
-                   double estimatedSliceThickness) const;
+                   double estimatedSliceThickness);
     };
 
-    typedef std::list<Polygon>  Polygons;
+    typedef std::list<Polygon*>  Polygons;
 
-    struct Structure
+    struct Structure : public boost::noncopyable
     {
       std::string   name_;
       std::string   interpretation_;
@@ -143,17 +145,17 @@
       uint8_t       red_;
       uint8_t       green_;
       uint8_t       blue_;
+
+      ~Structure();
     };
 
-    typedef std::vector<Structure>         Structures;
     typedef std::map<std::string, size_t>  StructureNamesIndex;
 
-    Structures        structures_;
-    ReferencedSlices  referencedSlices_;
-    Vector            estimatedNormal_;
-    double            estimatedSliceThickness_;
-    StructureNamesIndex  structureNamesIndex_;
-    
+    std::vector<Structure*>  structures_;
+    ReferencedSlices         referencedSlices_;
+    Vector                   estimatedNormal_;
+    double                   estimatedSliceThickness_;
+    StructureNamesIndex      structureNamesIndex_;
 
     void Setup(const IDicomDataset& dataset);
     
@@ -177,13 +179,13 @@
     explicit DicomStructureSet(Orthanc::ParsedDicomFile& instance);
 #endif
 
+    ~DicomStructureSet();
+
     size_t GetStructuresCount() const
     {
       return structures_.size();
     }
 
-    Vector GetStructureCenter(size_t index) const;
-
     const std::string& GetStructureName(size_t index) const;
 
     const std::string& GetStructureInterpretation(size_t index) const;