changeset 2098:4288d635d77e dicom-sr

first rendering of dicom-sr
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 08 Nov 2023 17:23:31 +0100
parents a9e23ef9ee09
children 129cb165ea8d
files Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp OrthancStone/Sources/Toolbox/DicomStructuredReport.h
diffstat 3 files changed, 291 insertions(+), 200 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Wed Nov 08 16:31:49 2023 +0100
+++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Wed Nov 08 17:23:31 2023 +0100
@@ -457,49 +457,64 @@
                                                         double pixelSpacingX,
                                                         double pixelSpacingY) const ORTHANC_OVERRIDE
   {
-    size_t frameIndex;
-    if (!LookupFrame(frameIndex, sopInstanceUid, frameNumber))
-    {
-      return NULL;
-    }
-
-    const OrthancStone::DicomInstanceParameters& parameters = GetInstanceOfFrame(frameIndex);
+    std::unique_ptr<OrthancStone::MacroSceneLayer> layer(new OrthancStone::MacroSceneLayer);
 
     const double x = originX - pixelSpacingX / 2.0;
     const double y = originY - pixelSpacingY / 2.0;
-    const double w = parameters.GetWidth() * pixelSpacingX;
-    const double h = parameters.GetHeight() * pixelSpacingY;
-
-    std::unique_ptr<OrthancStone::MacroSceneLayer> layer(new OrthancStone::MacroSceneLayer);
-
-    {
-      std::unique_ptr<OrthancStone::PolylineSceneLayer> polyline(new OrthancStone::PolylineSceneLayer);
+
+    for (size_t i = 0; i < sr_->GetStructuresCount(); i++)
+    {
+      const OrthancStone::DicomStructuredReport::Structure& structure = sr_->GetStructure(i);
+      if (structure.GetSopInstanceUid() == sopInstanceUid &&
+          (!structure.HasFrameNumber() ||
+           structure.GetFrameNumber() == frameNumber))
       {
-        OrthancStone::PolylineSceneLayer::Chain chain;
-        chain.push_back(OrthancStone::ScenePoint2D(x, y));
-        chain.push_back(OrthancStone::ScenePoint2D(x + pixelSpacingX, y));
-        chain.push_back(OrthancStone::ScenePoint2D(x + pixelSpacingX, y + pixelSpacingY));
-        chain.push_back(OrthancStone::ScenePoint2D(x, y + pixelSpacingY));
-
-        polyline->AddChain(chain, true, 255, 0, 0);
+        OrthancStone::Color color(0, 0, 255);
+
+        if (structure.HasProbabilityOfCancer())
+        {
+          if (structure.GetProbabilityOfCancer() > 50.0f)
+          {
+            color = OrthancStone::Color(255, 0, 0);
+          }
+          else
+          {
+            color = OrthancStone::Color(0, 255, 0);
+          }
+        }
+
+        switch (structure.GetType())
+        {
+          case OrthancStone::DicomStructuredReport::StructureType_Point:
+            // TODO
+            break;
+
+          case OrthancStone::DicomStructuredReport::StructureType_Polyline:
+          {
+            const OrthancStone::DicomStructuredReport::Polyline& source = dynamic_cast<const OrthancStone::DicomStructuredReport::Polyline&>(structure);
+
+            if (source.GetSize() > 1)
+            {
+              std::unique_ptr<OrthancStone::PolylineSceneLayer> target(new OrthancStone::PolylineSceneLayer);
+
+              OrthancStone::PolylineSceneLayer::Chain chain;
+              chain.resize(source.GetSize());
+              for (size_t i = 0; i < source.GetSize(); i++)
+              {
+                chain[i] = OrthancStone::ScenePoint2D(x + source.GetPoint(i).GetX() * pixelSpacingX,
+                                                      y + source.GetPoint(i).GetY() * pixelSpacingY);
+              }
+
+              target->AddChain(chain, false, color.GetRed(), color.GetGreen(), color.GetBlue());
+              layer->AddLayer(target.release());
+            }
+            break;
+          }
+
+          default:
+            break;
+        }
       }
-
-      layer->AddLayer(polyline.release());
-    }
-
-    {
-      std::unique_ptr<OrthancStone::PolylineSceneLayer> polyline(new OrthancStone::PolylineSceneLayer);
-      {
-        OrthancStone::PolylineSceneLayer::Chain chain;
-        chain.push_back(OrthancStone::ScenePoint2D(x, y));
-        chain.push_back(OrthancStone::ScenePoint2D(x + w, y));
-        chain.push_back(OrthancStone::ScenePoint2D(x + w, y + h));
-        chain.push_back(OrthancStone::ScenePoint2D(x, y + h));
-
-        polyline->AddChain(chain, true, 255, 0, 0);
-      }
-
-      layer->AddLayer(polyline.release());
     }
 
     return layer.release();
--- a/OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp	Wed Nov 08 16:31:49 2023 +0100
+++ b/OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp	Wed Nov 08 17:23:31 2023 +0100
@@ -118,193 +118,138 @@
 
 namespace OrthancStone
 {
-  void DicomStructuredReport::ReferencedInstance::AddFrame(unsigned int frame)
+  void DicomStructuredReport::Structure::Copy(const Structure& other)
+  {
+    if (other.HasFrameNumber())
+    {
+      SetFrameNumber(other.GetFrameNumber());
+    }
+
+    if (other.HasProbabilityOfCancer())
+    {
+      SetProbabilityOfCancer(other.GetProbabilityOfCancer());
+    }
+  }
+
+
+  DicomStructuredReport::Structure::Structure(const std::string& sopInstanceUid) :
+    sopInstanceUid_(sopInstanceUid),
+    hasFrameNumber_(false),
+    hasProbabilityOfCancer_(false)
   {
-    frames_.insert(frame);
+  }
+
+
+  void DicomStructuredReport::Structure::SetFrameNumber(unsigned int frame)
+  {
+    hasFrameNumber_ = true;
+    frameNumber_ = frame;
+  }
+
+
+  void DicomStructuredReport::Structure::SetProbabilityOfCancer(float probability)
+  {
+    if (probability < 0 ||
+        probability > 100)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+    else
+    {
+      hasProbabilityOfCancer_ = true;
+      probabilityOfCancer_ = probability;
+    }
   }
 
 
-  class DicomStructuredReport::Structure : public boost::noncopyable
+  unsigned int DicomStructuredReport::Structure::GetFrameNumber() const
   {
-  private:
-    std::string   sopInstanceUid_;
-    bool          hasFrameNumber_;
-    unsigned int  frameNumber_;
-    bool          hasProbabilityOfCancer_;
-    float         probabilityOfCancer_;
-
-  protected:
-    void Copy(const Structure& other)
+    if (hasFrameNumber_)
     {
-      if (other.HasFrameNumber())
-      {
-        SetFrameNumber(other.GetFrameNumber());
-      }
+      return frameNumber_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
 
-      if (other.HasProbabilityOfCancer())
-      {
-        SetProbabilityOfCancer(other.GetProbabilityOfCancer());
-      }
-    }
-
-  public:
-    Structure(const std::string& sopInstanceUid) :
-      sopInstanceUid_(sopInstanceUid),
-      hasFrameNumber_(false),
-      hasProbabilityOfCancer_(false)
+  float DicomStructuredReport::Structure::GetProbabilityOfCancer() const
+  {
+    if (hasProbabilityOfCancer_)
     {
-    }
-
-    virtual ~Structure()
-    {
+      return probabilityOfCancer_;
     }
-
-    virtual Structure* Clone() const = 0;
-
-    const std::string& GetSopInstanceUid() const
+    else
     {
-      return sopInstanceUid_;
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
+  }
 
-    void SetFrameNumber(unsigned int frame)
-    {
-      hasFrameNumber_ = true;
-      frameNumber_ = frame;
-    }
 
-    void SetProbabilityOfCancer(float probability)
-    {
-      if (probability < 0 ||
-          probability > 100)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-      }
-      else
-      {
-        hasProbabilityOfCancer_ = true;
-        probabilityOfCancer_ = probability;
-      }
-    }
+  DicomStructuredReport::Point::Point(const std::string& sopInstanceUid,
+                                      double x,
+                                      double y) :
+    Structure(sopInstanceUid),
+    point_(x, y)
+  {
+  }
+
 
-    bool HasFrameNumber() const
-    {
-      return hasFrameNumber_;
-    }
+  DicomStructuredReport::Structure* DicomStructuredReport::Point::Clone() const
+  {
+    std::unique_ptr<Point> cloned(new Point(GetSopInstanceUid(), point_.GetX(), point_.GetY()));
+    cloned->Copy(*this);
+    return cloned.release();
+  }
 
-    bool HasProbabilityOfCancer() const
-    {
-      return hasProbabilityOfCancer_;
-    }
 
-    unsigned int GetFrameNumber() const
+  DicomStructuredReport::Polyline::Polyline(const std::string& sopInstanceUid,
+                                            const float* points,
+                                            unsigned long pointsCount) :
+    Structure(sopInstanceUid)
+  {
+    if (pointsCount % 2 != 0)
     {
-      if (hasFrameNumber_)
-      {
-        return frameNumber_;
-      }
-      else
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
     }
 
-    float GetProbabilityOfCancer() const
+    points_.reserve(pointsCount / 2);
+
+    for (unsigned long i = 0; i < pointsCount; i += 2)
     {
-      if (hasProbabilityOfCancer_)
-      {
-        return probabilityOfCancer_;
-      }
-      else
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
+      points_.push_back(ScenePoint2D(points[i], points[i + 1]));
     }
-  };
+  }
 
 
-  class DicomStructuredReport::Point : public Structure
+  DicomStructuredReport::Polyline::Polyline(const std::string& sopInstanceUid,
+                                            const std::vector<ScenePoint2D>& points) :
+    Structure(sopInstanceUid),
+    points_(points)
   {
-  private:
-    ScenePoint2D  point_;
-
-  public:
-    Point(const std::string& sopInstanceUid,
-          double x,
-          double y) :
-      Structure(sopInstanceUid),
-      point_(x, y)
-    {
-    }
-
-    virtual Structure* Clone() const
-    {
-      std::unique_ptr<Point> cloned(new Point(GetSopInstanceUid(), point_.GetX(), point_.GetY()));
-      cloned->Copy(*this);
-      return cloned.release();
-    }
-
-    const ScenePoint2D& GetPoint() const
-    {
-      return point_;
-    }
-  };
+  }
 
 
-  class DicomStructuredReport::Polyline : public Structure
+  DicomStructuredReport::Structure* DicomStructuredReport::Polyline::Clone() const
   {
-  private:
-    std::vector<ScenePoint2D>  points_;
+    std::unique_ptr<Polyline> cloned(new Polyline(GetSopInstanceUid(), points_));
+    cloned->Copy(*this);
+    return cloned.release();
+  }
 
-  public:
-    Polyline(const std::string& sopInstanceUid,
-             const float* points,
-             unsigned long pointsCount) :
-      Structure(sopInstanceUid)
-    {
-      if (pointsCount % 2 != 0)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-      }
-
-      points_.reserve(pointsCount / 2);
-
-      for (unsigned long i = 0; i < pointsCount; i += 2)
-      {
-        points_.push_back(ScenePoint2D(points[i], points[i + 1]));
-      }
-    }
 
-    Polyline(const std::string& sopInstanceUid,
-             const std::vector<ScenePoint2D>& points) :
-      Structure(sopInstanceUid),
-      points_(points)
-    {
-    }
-
-    virtual Structure* Clone() const
-    {
-      std::unique_ptr<Polyline> cloned(new Polyline(GetSopInstanceUid(), points_));
-      cloned->Copy(*this);
-      return cloned.release();
-    }
-
-    size_t GetSize() const
+  const ScenePoint2D& DicomStructuredReport::Polyline::GetPoint(size_t i) const
+  {
+    if (i >= points_.size())
     {
-      return points_.size();
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
     }
-
-    const ScenePoint2D& GetPoint(size_t i) const
+    else
     {
-      if (i >= points_.size())
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-      }
-      else
-      {
-        return points_[i];
-      }
+      return points_[i];
     }
-  };
+  }
 
 
   void DicomStructuredReport::AddStructure(const std::string& sopInstanceUid,
@@ -573,7 +518,7 @@
       instancesInformation_[it->first] = new ReferencedInstance(*it->second);
     }
 
-    for (std::list<Structure*>::const_iterator it = other.structures_.begin(); it != other.structures_.end(); ++it)
+    for (std::deque<Structure*>::const_iterator it = other.structures_.begin(); it != other.structures_.end(); ++it)
     {
       assert(*it != NULL);
       structures_.push_back((*it)->Clone());
@@ -583,7 +528,7 @@
 
   DicomStructuredReport::~DicomStructuredReport()
   {
-    for (std::list<Structure*>::iterator it = structures_.begin(); it != structures_.end(); ++it)
+    for (std::deque<Structure*>::iterator it = structures_.begin(); it != structures_.end(); ++it)
     {
       assert(*it != NULL);
       delete *it;
@@ -648,4 +593,18 @@
       }
     }
   }
+
+
+  const DicomStructuredReport::Structure& DicomStructuredReport::GetStructure(size_t index) const
+  {
+    if (index >= structures_.size())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+    else
+    {
+      assert(structures_[index] != NULL);
+      return *structures_[index];
+    }
+  }
 }
--- a/OrthancStone/Sources/Toolbox/DicomStructuredReport.h	Wed Nov 08 16:31:49 2023 +0100
+++ b/OrthancStone/Sources/Toolbox/DicomStructuredReport.h	Wed Nov 08 17:23:31 2023 +0100
@@ -31,9 +31,12 @@
 #  error Support for DCMTK must be enabled
 #endif
 
+#include "../Scene2D/ScenePoint2D.h"
+
 #include <DicomParsing/ParsedDicomFile.h>
 
 #include <dcmtk/dcmdata/dcitem.h>
+#include <deque>
 #include <list>
 #include <set>
 
@@ -41,11 +44,115 @@
 {
   class DicomStructuredReport : public boost::noncopyable
   {
+  public:
+    enum StructureType
+    {
+      StructureType_Point,
+      StructureType_Polyline
+    };
+
+    class Structure : public boost::noncopyable
+    {
+    private:
+      std::string   sopInstanceUid_;
+      bool          hasFrameNumber_;
+      unsigned int  frameNumber_;
+      bool          hasProbabilityOfCancer_;
+      float         probabilityOfCancer_;
+
+    protected:
+      void Copy(const Structure& other);
+
+    public:
+      Structure(const std::string& sopInstanceUid);
+
+      virtual ~Structure()
+      {
+      }
+
+      virtual Structure* Clone() const = 0;
+
+      virtual StructureType GetType() const = 0;
+
+      const std::string& GetSopInstanceUid() const
+      {
+        return sopInstanceUid_;
+      }
+
+      void SetFrameNumber(unsigned int frame);
+
+      void SetProbabilityOfCancer(float probability);
+
+      bool HasFrameNumber() const
+      {
+        return hasFrameNumber_;
+      }
+
+      bool HasProbabilityOfCancer() const
+      {
+        return hasProbabilityOfCancer_;
+      }
+
+      unsigned int GetFrameNumber() const;
+
+      float GetProbabilityOfCancer() const;
+    };
+
+
+    class Point : public Structure
+    {
+    private:
+      ScenePoint2D  point_;
+
+    public:
+      Point(const std::string& sopInstanceUid,
+            double x,
+            double y);
+
+      virtual Structure* Clone() const ORTHANC_OVERRIDE;
+
+      virtual StructureType GetType() const ORTHANC_OVERRIDE
+      {
+        return StructureType_Point;
+      }
+
+      const ScenePoint2D& GetPoint() const
+      {
+        return point_;
+      }
+    };
+
+
+    class Polyline : public Structure
+    {
+    private:
+      std::vector<ScenePoint2D>  points_;
+
+    public:
+      Polyline(const std::string& sopInstanceUid,
+               const float* points,
+               unsigned long pointsCount);
+
+      Polyline(const std::string& sopInstanceUid,
+               const std::vector<ScenePoint2D>& points);
+
+      virtual Structure* Clone() const ORTHANC_OVERRIDE;
+
+      virtual StructureType GetType() const ORTHANC_OVERRIDE
+      {
+        return StructureType_Polyline;
+      }
+
+      size_t GetSize() const
+      {
+        return points_.size();
+      }
+
+      const ScenePoint2D& GetPoint(size_t i) const;
+    };
+
+
   private:
-    class Structure;
-    class Point;
-    class Polyline;
-
     class ReferencedInstance
     {
     private:
@@ -79,7 +186,10 @@
         return sopClassUid_;
       }
 
-      void AddFrame(unsigned int frame);
+      void AddFrame(unsigned int frame)
+      {
+        frames_.insert(frame);
+      }
 
       const std::set<unsigned int>& GetFrames() const
       {
@@ -100,7 +210,7 @@
     std::string                                 sopInstanceUid_;
     std::map<std::string, ReferencedInstance*>  instancesInformation_;
     std::vector<std::string>                    orderedInstances_;
-    std::list<Structure*>                       structures_;
+    std::deque<Structure*>                      structures_;
 
   public:
     class ReferencedFrame
@@ -185,5 +295,12 @@
                                size_t i) const;
 
     void ExportReferencedFrames(std::list<ReferencedFrame>& frames) const;
+
+    size_t GetStructuresCount() const
+    {
+      return structures_.size();
+    }
+
+    const Structure& GetStructure(size_t index) const;
   };
 }