changeset 4947:dfbe764995cf

added ParsedDicomFile::DecodeAllOverlays()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 21 Mar 2022 08:59:20 +0100
parents 51e4947aa3b3
children 5f6b13202e85
files OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h OrthancFramework/Sources/Images/ImageProcessing.cpp OrthancFramework/Sources/Images/ImageProcessing.h
diffstat 4 files changed, 138 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp	Sun Mar 20 18:03:32 2022 +0100
+++ b/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp	Mon Mar 21 08:59:20 2022 +0100
@@ -1920,7 +1920,7 @@
   }
 
 
-  void ParsedDicomFile::ListOverlays(std::set<unsigned int>& groups) const
+  void ParsedDicomFile::ListOverlays(std::set<uint16_t>& groups) const
   {
     DcmDataset& dataset = *const_cast<ParsedDicomFile&>(*this).GetDcmtkObject().getDataset();
 
@@ -1953,7 +1953,7 @@
 
   ImageAccessor* ParsedDicomFile::DecodeOverlay(int& originX,
                                                 int& originY,
-                                                unsigned int group) const
+                                                uint16_t group) const
   {
     // https://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.9.2.html
 
@@ -2014,6 +2014,67 @@
   }
 
   
+  ImageAccessor* ParsedDicomFile::DecodeAllOverlays(int& originX,
+                                                    int& originY) const
+  {
+    std::set<uint16_t> groups;
+    ListOverlays(groups);
+
+    if (groups.empty())
+    {
+      originX = 0;
+      originY = 0;
+      return new Image(PixelFormat_Grayscale8, 0, 0, false);
+    }
+    else
+    {
+      std::set<uint16_t>::const_iterator it = groups.begin();
+      assert(it != groups.end());
+      
+      std::unique_ptr<ImageAccessor> result(DecodeOverlay(originX, originY, *it));
+      assert(result.get() != NULL);
+      ++it;
+
+      int right = originX + static_cast<int>(result->GetWidth());
+      int bottom = originY + static_cast<int>(result->GetHeight());
+
+      while (it != groups.end())
+      {
+        int ox, oy;
+        std::unique_ptr<ImageAccessor> overlay(DecodeOverlay(ox, oy, *it));
+        assert(overlay.get() != NULL);
+
+        int mergedX = std::min(originX, ox);
+        int mergedY = std::min(originY, oy);
+        right = std::max(right, ox + static_cast<int>(overlay->GetWidth()));
+        bottom = std::max(bottom, oy + static_cast<int>(overlay->GetHeight()));
+
+        assert(right >= mergedX && bottom >= mergedY);
+        unsigned int width = static_cast<unsigned int>(right - mergedX);
+        unsigned int height = static_cast<unsigned int>(bottom - mergedY);
+        
+        std::unique_ptr<ImageAccessor> merged(new Image(PixelFormat_Grayscale8, width, height, false));
+        ImageProcessing::Set(*merged, 0);
+
+        ImageAccessor a;
+        merged->GetRegion(a, originX - mergedX, originY - mergedY, result->GetWidth(), result->GetHeight());
+        ImageProcessing::Maximum(a, *result);
+
+        merged->GetRegion(a, ox - mergedX, oy - mergedY, overlay->GetWidth(), overlay->GetHeight());
+        ImageProcessing::Maximum(a, *overlay);
+
+        originX = mergedX;
+        originY = mergedY;
+        result.reset(merged.release());
+        
+        ++it;
+      }
+
+      return result.release();
+    }
+  }
+
+
 #if ORTHANC_BUILDING_FRAMEWORK_LIBRARY == 1
   // Alias for binary compatibility with Orthanc Framework 1.7.2 => don't use it anymore
   void ParsedDicomFile::DatasetToJson(Json::Value& target,
--- a/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h	Sun Mar 20 18:03:32 2022 +0100
+++ b/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h	Mon Mar 21 08:59:20 2022 +0100
@@ -301,10 +301,13 @@
                     double& rescaleSlope,
                     unsigned int frame) const;
 
-    void ListOverlays(std::set<unsigned int>& groups) const;
+    void ListOverlays(std::set<uint16_t>& groups) const;
 
     ImageAccessor* DecodeOverlay(int& originX,
                                  int& originY,
-                                 unsigned int group) const;
+                                 uint16_t group) const;
+
+    ImageAccessor* DecodeAllOverlays(int& originX,
+                                     int& originY) const;
   };
 }
--- a/OrthancFramework/Sources/Images/ImageProcessing.cpp	Sun Mar 20 18:03:32 2022 +0100
+++ b/OrthancFramework/Sources/Images/ImageProcessing.cpp	Mon Mar 21 08:59:20 2022 +0100
@@ -2964,4 +2964,71 @@
         throw OrthancException(ErrorCode_NotImplemented);
     }          
   }
+
+
+  template <typename PixelType,
+            typename Functor>
+  static void ApplyImageOntoImage(Functor f,
+                                  ImageAccessor& image /* inout */,
+                                  const ImageAccessor& other)
+  {
+    const unsigned int width = image.GetWidth();
+    const unsigned int height = image.GetHeight();
+    
+    if (width != other.GetWidth() ||
+        height != other.GetHeight())
+    {
+      throw OrthancException(ErrorCode_IncompatibleImageSize);
+    }
+    else if (image.GetFormat() != other.GetFormat() ||
+             GetBytesPerPixel(image.GetFormat()) != sizeof(PixelType))
+    {
+      throw OrthancException(ErrorCode_IncompatibleImageFormat);
+    }
+    else
+    {
+      for (unsigned int y = 0; y < height; y++)
+      {
+        PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
+        const PixelType* q = reinterpret_cast<const PixelType*>(other.GetConstRow(y));
+        
+        for (unsigned int x = 0; x < width; x++, p++, q++)
+        {
+          f(*p, *q);
+        }
+      }
+    }
+  }
+
+  
+  void ImageProcessing::Maximum(ImageAccessor& image,
+                                const ImageAccessor& other)
+  {
+    struct F
+    {
+      void operator() (uint8_t& a, const uint8_t& b)
+      {
+        a = std::max(a, b);
+      }
+
+      void operator() (uint16_t& a, const uint16_t& b)
+      {
+        a = std::max(a, b);
+      }
+    };
+
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        ApplyImageOntoImage<uint8_t, F>(F(), image, other);
+        return;
+
+      case PixelFormat_Grayscale16:
+        ApplyImageOntoImage<uint16_t, F>(F(), image, other);
+        return;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
 }
--- a/OrthancFramework/Sources/Images/ImageProcessing.h	Sun Mar 20 18:03:32 2022 +0100
+++ b/OrthancFramework/Sources/Images/ImageProcessing.h	Mon Mar 21 08:59:20 2022 +0100
@@ -218,5 +218,8 @@
     static void ConvertJpegYCbCrToRgb(ImageAccessor& image /* inplace */);
 
     static void SwapEndianness(ImageAccessor& image /* inplace */);
+
+    static void Maximum(ImageAccessor& image /* inout */,
+                        const ImageAccessor& other);
   };
 }