changeset 3502:c160eafc42a9

new functions in ImageProcessing toolbox: FlipX/Y(), Resize(), Halve()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 26 Aug 2019 10:23:51 +0200
parents 27b53c61aa99
children 46cf170ba121
files Core/Images/ImageProcessing.cpp Core/Images/ImageProcessing.h
diffstat 2 files changed, 223 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/Core/Images/ImageProcessing.cpp	Sat Aug 24 15:40:36 2019 +0200
+++ b/Core/Images/ImageProcessing.cpp	Mon Aug 26 10:23:51 2019 +0200
@@ -34,6 +34,8 @@
 #include "../PrecompiledHeaders.h"
 #include "ImageProcessing.h"
 
+#include "Image.h"
+#include "ImageTraits.h"
 #include "PixelTraits.h"
 #include "../OrthancException.h"
 
@@ -1552,4 +1554,215 @@
         throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
     }
   }
+
+
+  template <PixelFormat Format>
+  static void ResizeInternal(ImageAccessor& target,
+                             const ImageAccessor& source)
+  {
+    assert(target.GetFormat() == source.GetFormat() &&
+           target.GetFormat() == Format);
+      
+    const unsigned int sourceWidth = source.GetWidth();
+    const unsigned int sourceHeight = source.GetHeight();
+    const unsigned int targetWidth = target.GetWidth();
+    const unsigned int targetHeight = target.GetHeight();
+
+    if (targetWidth == 0 || targetHeight == 0)
+    {
+      return;
+    }
+
+    if (sourceWidth == 0 || sourceHeight == 0)
+    {
+      // Avoids division by zero below
+      ImageProcessing::Set(target, 0);
+      return;
+    }
+      
+    const float scaleX = static_cast<float>(sourceWidth) / static_cast<float>(targetWidth);
+    const float scaleY = static_cast<float>(sourceHeight) / static_cast<float>(targetHeight);
+
+
+    /**
+     * Create two lookup tables to quickly know the (x,y) position
+     * in the source image, given the (x,y) position in the target
+     * image.
+     **/
+      
+    std::vector<unsigned int>  lookupX(targetWidth);
+      
+    for (unsigned int x = 0; x < targetWidth; x++)
+    {
+      int sourceX = std::floor((static_cast<float>(x) + 0.5f) * scaleX);
+      if (sourceX < 0)
+      {
+        sourceX = 0;  // Should never happen
+      }
+      else if (sourceX >= static_cast<int>(sourceWidth))
+      {
+        sourceX = sourceWidth - 1;
+      }
+
+      lookupX[x] = static_cast<unsigned int>(sourceX);
+    }
+      
+    std::vector<unsigned int>  lookupY(targetHeight);
+      
+    for (unsigned int y = 0; y < targetHeight; y++)
+    {
+      int sourceY = std::floor((static_cast<float>(y) + 0.5f) * scaleY);
+      if (sourceY < 0)
+      {
+        sourceY = 0;  // Should never happen
+      }
+      else if (sourceY >= static_cast<int>(sourceHeight))
+      {
+        sourceY = sourceHeight - 1;
+      }
+
+      lookupY[y] = static_cast<unsigned int>(sourceY);
+    }
+
+
+    /**
+     * Actual resizing
+     **/
+      
+    for (unsigned int targetY = 0; targetY < targetHeight; targetY++)
+    {
+      unsigned int sourceY = lookupY[targetY];
+
+      for (unsigned int targetX = 0; targetX < targetWidth; targetX++)
+      {
+        unsigned int sourceX = lookupX[targetX];
+
+        typename ImageTraits<Format>::PixelType pixel;
+        ImageTraits<Format>::GetPixel(pixel, source, sourceX, sourceY);
+        ImageTraits<Format>::SetPixel(target, pixel, targetX, targetY);
+      }
+    }            
+  }
+
+
+
+  void ImageProcessing::Resize(ImageAccessor& target,
+                               const ImageAccessor& source)
+  {
+    if (source.GetFormat() != source.GetFormat())
+    {
+      throw OrthancException(ErrorCode_IncompatibleImageFormat);
+    }
+
+    if (source.GetWidth() == target.GetWidth() &&
+        source.GetHeight() == target.GetHeight())
+    {
+      Copy(target, source);
+      return;
+    }
+      
+    switch (source.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        ResizeInternal<PixelFormat_Grayscale8>(target, source);
+        break;
+
+      case PixelFormat_RGB24:
+        ResizeInternal<PixelFormat_RGB24>(target, source);
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+
+  ImageAccessor* ImageProcessing::Halve(const ImageAccessor& source,
+                                        bool forceMinimalPitch)
+  {
+    std::auto_ptr<Image> target(new Image(source.GetFormat(), source.GetWidth() / 2,
+                                          source.GetHeight() / 2, forceMinimalPitch));
+    Resize(*target, source);
+    return target.release();
+  }
+
+    
+  template <PixelFormat Format>
+  static void FlipXInternal(ImageAccessor& image)
+  {     
+    const unsigned int height = image.GetHeight();
+    const unsigned int width = image.GetWidth();
+
+    for (unsigned int y = 0; y < height; y++)
+    {
+      for (unsigned int x1 = 0; x1 < width / 2; x1++)
+      {
+        unsigned int x2 = width - 1 - x1;
+          
+        typename ImageTraits<Format>::PixelType a, b;
+        ImageTraits<Format>::GetPixel(a, image, x1, y);
+        ImageTraits<Format>::GetPixel(b, image, x2, y);
+        ImageTraits<Format>::SetPixel(image, a, x2, y);
+        ImageTraits<Format>::SetPixel(image, b, x1, y);
+      }
+    }        
+  }
+
+    
+  void ImageProcessing::FlipX(ImageAccessor& image)
+  {
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        FlipXInternal<PixelFormat_Grayscale8>(image);
+        break;
+
+      case PixelFormat_RGB24:
+        FlipXInternal<PixelFormat_RGB24>(image);
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+    
+  template <PixelFormat Format>
+  static void FlipYInternal(ImageAccessor& image)
+  {     
+    const unsigned int height = image.GetHeight();
+    const unsigned int width = image.GetWidth();
+
+    for (unsigned int y1 = 0; y1 < height / 2; y1++)
+    {
+      unsigned int y2 = height - 1 - y1;
+        
+      for (unsigned int x = 0; x < width; x++)
+      {
+        typename ImageTraits<Format>::PixelType a, b;
+        ImageTraits<Format>::GetPixel(a, image, x, y1);
+        ImageTraits<Format>::GetPixel(b, image, x, y2);
+        ImageTraits<Format>::SetPixel(image, a, x, y2);
+        ImageTraits<Format>::SetPixel(image, b, x, y1);
+      }
+    }        
+  }
+
+    
+  void ImageProcessing::FlipY(ImageAccessor& image)
+  {
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        FlipYInternal<PixelFormat_Grayscale8>(image);
+        break;
+
+      case PixelFormat_RGB24:
+        FlipYInternal<PixelFormat_RGB24>(image);
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
 }
--- a/Core/Images/ImageProcessing.h	Sat Aug 24 15:40:36 2019 +0200
+++ b/Core/Images/ImageProcessing.h	Mon Aug 26 10:23:51 2019 +0200
@@ -138,5 +138,15 @@
     void FillPolygon(ImageAccessor& image,
                      const std::vector<ImagePoint>& points,
                      int64_t value);
+
+    void Resize(ImageAccessor& target,
+                const ImageAccessor& source);
+
+    ImageAccessor* Halve(const ImageAccessor& source,
+                         bool forceMinimalPitch);
+
+    void FlipX(ImageAccessor& image);
+
+    void FlipY(ImageAccessor& image);
   }
 }