changeset 2097:a9e23ef9ee09 dicom-sr

preparing to extract dicom-sr annotations
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 08 Nov 2023 16:31:49 +0100
parents 79e984a89a38
children 4288d635d77e
files Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp OrthancStone/Sources/Toolbox/DicomStructuredReport.h
diffstat 3 files changed, 182 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Wed Nov 08 15:15:48 2023 +0100
+++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Wed Nov 08 16:31:49 2023 +0100
@@ -226,6 +226,13 @@
                                 const OrthancStone::Vector& point,
                                 double maximumDistance) const = 0;
 
+  virtual OrthancStone::ISceneLayer* ExtractAnnotations(const std::string& sopInstanceUid,
+                                                        unsigned int frameNumber,
+                                                        double originX,
+                                                        double originY,
+                                                        double pixelSpacingX,
+                                                        double pixelSpacingY) const = 0;
+
   static OrthancStone::CoordinateSystem3D GetFrameGeometry(const IFramesCollection& frames,
                                                            size_t frameIndex)
   {
@@ -279,7 +286,17 @@
                                 double maximumDistance) const ORTHANC_OVERRIDE
   {
     return frames_->FindClosestFrame(frameIndex, point, maximumDistance);
-  };
+  }
+
+  virtual OrthancStone::ISceneLayer* ExtractAnnotations(const std::string& sopInstanceUid,
+                                                        unsigned int frameNumber,
+                                                        double originX,
+                                                        double originY,
+                                                        double pixelSpacingX,
+                                                        double pixelSpacingY) const ORTHANC_OVERRIDE
+  {
+    return NULL;
+  }
 };
 
 
@@ -321,10 +338,9 @@
       return *parameters_;
     }
   };
-  
-  std::string         studyInstanceUid_;
-  std::string         seriesInstanceUid_;
-  std::vector<Frame*> frames_;
+
+  std::unique_ptr<OrthancStone::DicomStructuredReport>  sr_;
+  std::vector<Frame*>                                   frames_;
 
   void Finalize()
   {
@@ -353,13 +369,12 @@
   }
   
 public:
-  DicomStructuredReportFrames(OrthancStone::DicomStructuredReport& sr,
+  DicomStructuredReportFrames(const OrthancStone::DicomStructuredReport& sr,
                               const OrthancStone::LoadedDicomResources& instances) :
-    studyInstanceUid_(sr.GetStudyInstanceUid()),
-    seriesInstanceUid_(sr.GetSeriesInstanceUid())
+    sr_(new OrthancStone::DicomStructuredReport(sr))
   {
     std::list<OrthancStone::DicomStructuredReport::ReferencedFrame> tmp;
-    sr.ExportReferencedFrames(tmp);
+    sr_->ExportReferencedFrames(tmp);
 
     frames_.reserve(tmp.size());
     for (std::list<OrthancStone::DicomStructuredReport::ReferencedFrame>::const_iterator
@@ -434,6 +449,61 @@
     
     return found;
   }
+
+  virtual OrthancStone::ISceneLayer* ExtractAnnotations(const std::string& sopInstanceUid,
+                                                        unsigned int frameNumber,
+                                                        double originX,
+                                                        double originY,
+                                                        double pixelSpacingX,
+                                                        double pixelSpacingY) const ORTHANC_OVERRIDE
+  {
+    size_t frameIndex;
+    if (!LookupFrame(frameIndex, sopInstanceUid, frameNumber))
+    {
+      return NULL;
+    }
+
+    const OrthancStone::DicomInstanceParameters& parameters = GetInstanceOfFrame(frameIndex);
+
+    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);
+      {
+        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);
+      }
+
+      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();
+  }
 };
 
 
@@ -1966,6 +2036,7 @@
   static const int LAYER_REFERENCE_LINES = 3;
   static const int LAYER_ANNOTATIONS_OSIRIX = 4;
   static const int LAYER_ANNOTATIONS_STONE = 5;
+  static const int LAYER_STRUCTURED_REPORT = 6;
 
   
   class ICommand : public Orthanc::IDynamicObject
@@ -2650,6 +2721,16 @@
     }
 
 
+    std::unique_ptr<OrthancStone::ISceneLayer> structuredReportAnnotations;
+
+    if (frames_.get() != NULL)
+    {
+      structuredReportAnnotations.reset(frames_->ExtractAnnotations(instance.GetSopInstanceUid(), frameIndex,
+                                                                    layer->GetOriginX(), layer->GetOriginY(),
+                                                                    layer->GetPixelSpacingX(), layer->GetPixelSpacingY()));
+    }
+
+
     {
       std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock());
 
@@ -2684,6 +2765,24 @@
         scene.DeleteLayer(LAYER_ORIENTATION_MARKERS);
       }
 
+      if (orientationMarkers.get() != NULL)
+      {
+        scene.SetLayer(LAYER_ORIENTATION_MARKERS, orientationMarkers.release());
+      }
+      else
+      {
+        scene.DeleteLayer(LAYER_ORIENTATION_MARKERS);
+      }
+
+      if (structuredReportAnnotations.get() != NULL)
+      {
+        scene.SetLayer(LAYER_STRUCTURED_REPORT, structuredReportAnnotations.release());
+      }
+      else
+      {
+        scene.DeleteLayer(LAYER_STRUCTURED_REPORT);
+      }
+
       stoneAnnotations_->Render(scene);  // Necessary for "FitContent()" to work
 
       if (fitNextContent_)
--- a/OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp	Wed Nov 08 15:15:48 2023 +0100
+++ b/OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp	Wed Nov 08 16:31:49 2023 +0100
@@ -120,14 +120,7 @@
 {
   void DicomStructuredReport::ReferencedInstance::AddFrame(unsigned int frame)
   {
-    if (frame == 0)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-    else
-    {
-      frames_.insert(frame - 1);
-    }
+    frames_.insert(frame);
   }
 
 
@@ -140,6 +133,20 @@
     bool          hasProbabilityOfCancer_;
     float         probabilityOfCancer_;
 
+  protected:
+    void Copy(const Structure& other)
+    {
+      if (other.HasFrameNumber())
+      {
+        SetFrameNumber(other.GetFrameNumber());
+      }
+
+      if (other.HasProbabilityOfCancer())
+      {
+        SetProbabilityOfCancer(other.GetProbabilityOfCancer());
+      }
+    }
+
   public:
     Structure(const std::string& sopInstanceUid) :
       sopInstanceUid_(sopInstanceUid),
@@ -152,17 +159,17 @@
     {
     }
 
+    virtual Structure* Clone() const = 0;
+
+    const std::string& GetSopInstanceUid() const
+    {
+      return sopInstanceUid_;
+    }
+
     void SetFrameNumber(unsigned int frame)
     {
-      if (frame <= 0)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-      }
-      else
-      {
-        hasFrameNumber_ = true;
-        frameNumber_ = frame - 1;
-      }
+      hasFrameNumber_ = true;
+      frameNumber_ = frame;
     }
 
     void SetProbabilityOfCancer(float probability)
@@ -229,6 +236,13 @@
     {
     }
 
+    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_;
@@ -260,6 +274,20 @@
       }
     }
 
+    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
     {
       return points_.size();
@@ -504,21 +532,22 @@
                       for (size_t m = 0; m < tokens.size(); m++)
                       {
                         uint32_t frame;
-                        if (!Orthanc::SerializationToolbox::ParseUnsignedInteger32(frame, tokens[m]))
+                        if (!Orthanc::SerializationToolbox::ParseUnsignedInteger32(frame, tokens[m]) ||
+                            frame <= 0)
                         {
                           throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
                         }
                         else
                         {
-                          AddStructure(sopInstanceUid, group, true, frame, hasProbabilityOfCancer, probabilityOfCancer);
-                          instanceInformation->second->AddFrame(frame);
+                          AddStructure(sopInstanceUid, group, true, frame - 1, hasProbabilityOfCancer, probabilityOfCancer);
+                          instanceInformation->second->AddFrame(frame - 1);
                         }
                       }
                     }
                     else
                     {
                       AddStructure(sopInstanceUid, group, false, 0, hasProbabilityOfCancer, probabilityOfCancer);
-                      instanceInformation->second->AddFrame(1);
+                      instanceInformation->second->AddFrame(0);
                     }
                   }
                 }
@@ -531,6 +560,27 @@
   }
 
 
+  DicomStructuredReport::DicomStructuredReport(const DicomStructuredReport& other) :
+    studyInstanceUid_(other.studyInstanceUid_),
+    seriesInstanceUid_(other.seriesInstanceUid_),
+    sopInstanceUid_(other.sopInstanceUid_),
+    orderedInstances_(other.orderedInstances_)
+  {
+    for (std::map<std::string, ReferencedInstance*>::const_iterator
+           it = other.instancesInformation_.begin(); it != other.instancesInformation_.end(); ++it)
+    {
+      assert(it->second != NULL);
+      instancesInformation_[it->first] = new ReferencedInstance(*it->second);
+    }
+
+    for (std::list<Structure*>::const_iterator it = other.structures_.begin(); it != other.structures_.end(); ++it)
+    {
+      assert(*it != NULL);
+      structures_.push_back((*it)->Clone());
+    }
+  }
+
+
   DicomStructuredReport::~DicomStructuredReport()
   {
     for (std::list<Structure*>::iterator it = structures_.begin(); it != structures_.end(); ++it)
--- a/OrthancStone/Sources/Toolbox/DicomStructuredReport.h	Wed Nov 08 15:15:48 2023 +0100
+++ b/OrthancStone/Sources/Toolbox/DicomStructuredReport.h	Wed Nov 08 16:31:49 2023 +0100
@@ -46,7 +46,7 @@
     class Point;
     class Polyline;
 
-    class ReferencedInstance : public boost::noncopyable
+    class ReferencedInstance
     {
     private:
       std::string  studyInstanceUid_;
@@ -154,6 +154,8 @@
 
     DicomStructuredReport(Orthanc::ParsedDicomFile& dicom);
 
+    DicomStructuredReport(const DicomStructuredReport& other);  // Copy constructor
+
     ~DicomStructuredReport();
 
     const std::string& GetStudyInstanceUid() const