changeset 2231:2161277b4586

experimental support for textual DICOM SR
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 25 Apr 2025 19:02:22 +0200
parents cbad54feb881
children ce5eaba0008d
files Applications/StoneWebViewer/NEWS Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp
diffstat 3 files changed, 51 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/StoneWebViewer/NEWS	Fri Apr 25 17:50:05 2025 +0200
+++ b/Applications/StoneWebViewer/NEWS	Fri Apr 25 19:02:22 2025 +0200
@@ -1,6 +1,7 @@
 Pending changes in the mainline
 ===============================
 
+* Experimental support for DICOM SR, display of textual reports
 * Experimental support for DICOM SR "Measurement Report" (TID 1500 - only polylines)
 * Added "Print" and "Download" buttons in the PDF viewer toolbar
 * New configuration option "ScreenshotTemplate" to define the filename
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Fri Apr 25 17:50:05 2025 +0200
+++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Fri Apr 25 19:02:22 2025 +0200
@@ -377,6 +377,7 @@
   };
 
   std::unique_ptr<OrthancStone::DicomStructuredReport>  sr_;
+  OrthancStone::DicomInstanceParameters                 parameters_;
   std::vector<Frame*>                                   frames_;
 
   void Finalize()
@@ -414,7 +415,8 @@
 public:
   DicomStructuredReportFrames(const OrthancStone::DicomStructuredReport& sr,
                               const OrthancStone::LoadedDicomResources& instances) :
-    sr_(new OrthancStone::DicomStructuredReport(sr))
+    sr_(new OrthancStone::DicomStructuredReport(sr)),
+    parameters_(sr.GetMainDicomTags())
   {
     std::list<OrthancStone::DicomStructuredReport::ReferencedFrame> tmp;
     sr_->ExportReferencedFrames(tmp);
@@ -448,23 +450,44 @@
 
   virtual size_t GetFramesCount() const ORTHANC_OVERRIDE
   {
-    return frames_.size();
+    return frames_.size() + 1;
   }
 
   virtual const OrthancStone::DicomInstanceParameters& GetInstanceOfFrame(size_t frameIndex) const ORTHANC_OVERRIDE
   {
-    return GetFrame(frameIndex).GetParameters();
+    if (frameIndex == frames_.size())
+    {
+      return parameters_;
+    }
+    else
+    {
+      return GetFrame(frameIndex).GetParameters();
+    }
   }
 
   virtual unsigned int GetFrameNumberInInstance(size_t frameIndex) const ORTHANC_OVERRIDE
   {
-    return GetFrame(frameIndex).GetInformation().GetFrameNumber();
+    if (frameIndex == frames_.size())
+    {
+      return 0;
+    }
+    else
+    {
+      return GetFrame(frameIndex).GetInformation().GetFrameNumber();
+    }
   }
 
   virtual bool LookupFrame(size_t& frameIndex,
                            const std::string& sopInstanceUid,
                            unsigned int frameNumber) const ORTHANC_OVERRIDE
   {
+    if (sopInstanceUid == sr_->GetSopInstanceUid() &&
+        frameNumber == 0)
+    {
+      frameIndex = frames_.size();
+      return true;
+    }
+
     // TODO - Could be speeded up with an additional index
     for (size_t i = 0; i < frames_.size(); i++)
     {
@@ -2692,6 +2715,10 @@
   std::list<ILayerSource*>  layerSources_;
 
 
+  // TODO - Share this with the compositor?
+  OrthancStone::FontRenderer  font_;
+
+
   void UpdateWindowing(WindowingState state,
                        const OrthancStone::Windowing& windowing)
   {
@@ -2832,8 +2859,12 @@
      * (cf. LSD-479).
      **/
     
-    bool isMonochrome1 = (instance.GetImageInformation().GetPhotometricInterpretation() ==
-                          Orthanc::PhotometricInterpretation_Monochrome1);
+    bool isMonochrome1 = false;
+    if (instance.GetSopClassUid() != OrthancStone::SopClassUid_ComprehensiveSR)
+    {
+      isMonochrome1 = (instance.GetImageInformation().GetPhotometricInterpretation() ==
+                       Orthanc::PhotometricInterpretation_Monochrome1);
+    }
       
     std::unique_ptr<OrthancStone::TextureBaseSceneLayer> layer;
 
@@ -3116,7 +3147,17 @@
       {
         try
         {
-          std::unique_ptr<Orthanc::ImageAccessor> frame(accessor->GetDicom().DecodeFrame(frameNumber));
+          std::unique_ptr<Orthanc::ImageAccessor> frame;
+          if (instance.GetSopClassUid() == OrthancStone::SopClassUid_ComprehensiveSR)
+          {
+            OrthancStone::DicomStructuredReport report(const_cast<Orthanc::ParsedDicomFile&>(accessor->GetDicom()));
+            frame.reset(report.Render(font_, OrthancStone::Color(255, 0, 0), OrthancStone::Color(0, 255, 0)));
+          }
+          else
+          {
+            frame.reset(accessor->GetDicom().DecodeFrame(frameNumber));
+          }
+
           SetFullDicomFrame::Apply(*this, accessor->GetDicom(), frame.release(), instance.GetSopInstanceUid(), frameNumber);
           return;  // Success
         }
@@ -3220,6 +3261,7 @@
       std::string ttf;
       Orthanc::EmbeddedResources::GetFileResource(ttf, Orthanc::EmbeddedResources::UBUNTU_FONT);
       lock->GetCompositor().SetFont(0, ttf, 16 /* font size */, Orthanc::Encoding_Latin1);
+      font_.LoadFont(ttf, 16 /* font size */);
     }
     
     emscripten_set_wheel_callback(viewport_->GetCanvasCssSelector().c_str(), this, true, OnWheel);
--- a/OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp	Fri Apr 25 17:50:05 2025 +0200
+++ b/OrthancStone/Sources/Toolbox/DicomStructuredReport.cpp	Fri Apr 25 19:02:22 2025 +0200
@@ -641,6 +641,7 @@
     StoneToolbox::ExtractMainDicomTags(mainDicomTags_, dicom);
     StoneToolbox::CopyDicomTag(mainDicomTags_, dicom, Orthanc::DicomTag(0x0040, 0xa491));  // "Completion Flag"
     StoneToolbox::CopyDicomTag(mainDicomTags_, dicom, Orthanc::DicomTag(0x0040, 0xa493));  // "Verification Flag"
+    StoneToolbox::CopyDicomTag(mainDicomTags_, dicom, Orthanc::DICOM_TAG_SOP_CLASS_UID);
 
     DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset();