changeset 2487:be1dbb1dcdd6

PixelTraits and ImageTraits
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 13 Mar 2018 16:29:20 +0100
parents ad8f30fc28d1
children 345725b9350c
files Core/Enumerations.h Core/Images/ImageAccessor.h Core/Images/ImageProcessing.cpp Core/Images/ImageTraits.h Core/Images/PixelTraits.h UnitTestsSources/ImageProcessingTests.cpp
diffstat 6 files changed, 511 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/Core/Enumerations.h	Wed Mar 07 15:46:47 2018 +0100
+++ b/Core/Enumerations.h	Tue Mar 13 16:29:20 2018 +0100
@@ -35,6 +35,16 @@
 
 #include <string>
 
+
+#if defined(_MSC_VER)
+#  define ORTHANC_FORCE_INLINE __forceinline
+#elif defined(__GNUC__) || defined(__clang__) || defined(__EMSCRIPTEN__)
+#  define ORTHANC_FORCE_INLINE inline __attribute((always_inline))
+#else
+#  error Please support your compiler here
+#endif
+
+
 namespace Orthanc
 {
   enum Endianness
--- a/Core/Images/ImageAccessor.h	Wed Mar 07 15:46:47 2018 +0100
+++ b/Core/Images/ImageAccessor.h	Tue Mar 13 16:29:20 2018 +0100
@@ -43,6 +43,9 @@
   class ImageAccessor
   {
   private:
+    template <Orthanc::PixelFormat Format>
+    friend struct ImageTraits;
+    
     bool readOnly_;
     PixelFormat format_;
     unsigned int width_;
@@ -50,6 +53,23 @@
     unsigned int pitch_;
     uint8_t *buffer_;
 
+    template <typename T>
+    const T& GetPixelUnchecked(unsigned int x,
+                               unsigned int y) const
+    {
+      const uint8_t* row = reinterpret_cast<const uint8_t*>(buffer_) + y * pitch_;
+      return reinterpret_cast<const T*>(row) [x];
+    }
+
+
+    template <typename T>
+    T& GetPixelUnchecked(unsigned int x,
+                         unsigned int y)
+    {
+      uint8_t* row = reinterpret_cast<uint8_t*>(buffer_) + y * pitch_;
+      return reinterpret_cast<T*>(row) [x];
+    }
+
   public:
     ImageAccessor()
     {
--- a/Core/Images/ImageProcessing.cpp	Wed Mar 07 15:46:47 2018 +0100
+++ b/Core/Images/ImageProcessing.cpp	Tue Mar 13 16:29:20 2018 +0100
@@ -615,7 +615,7 @@
     switch (image.GetFormat())
     {
       case PixelFormat_Grayscale8:
-        SetInternal<uint8_t>(image, value);
+        memset(image.GetBuffer(), static_cast<uint8_t>(value), image.GetPitch() * image.GetHeight());
         return;
 
       case PixelFormat_Grayscale16:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/ImageTraits.h	Tue Mar 13 16:29:20 2018 +0100
@@ -0,0 +1,87 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2018 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "PixelTraits.h"
+
+
+namespace Orthanc
+{
+  template <PixelFormat Format>
+  struct ImageTraits
+  {
+    typedef ::Orthanc::PixelTraits<Format>    PixelTraits;
+    typedef typename PixelTraits::PixelType   PixelType;
+
+    static PixelFormat GetPixelFormat()
+    {
+      return Format;
+    }
+
+    static void GetPixel(PixelType& target,
+                         const ImageAccessor& image,
+                         unsigned int x,
+                         unsigned int y)
+    {
+      assert(x < image.GetWidth() && y < image.GetHeight());
+      PixelTraits::Copy(target, image.GetPixelUnchecked<PixelType>(x, y));
+    }
+
+    static void SetPixel(ImageAccessor& image,
+                         const PixelType& value,
+                         unsigned int x,
+                         unsigned int y)
+    {
+      assert(x < image.GetWidth() && y < image.GetHeight());
+      PixelTraits::Copy(image.GetPixelUnchecked<PixelType>(x, y), value);
+    }
+
+    static float GetFloatPixel(const ImageAccessor& image,
+                               unsigned int x,
+                               unsigned int y)
+    {
+      assert(x < image.GetWidth() && y < image.GetHeight());
+      return PixelTraits::PixelToFloat(image.GetPixelUnchecked<PixelType>(x, y));
+    }
+
+    static void SetFloatPixel(ImageAccessor& image,
+                              float value,
+                              unsigned int x,
+                              unsigned int y)
+    {
+      assert(x < image.GetWidth() && y < image.GetHeight());
+      PixelTraits::FloatToPixel(image.GetPixelUnchecked<PixelType>(x, y), value);
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/PixelTraits.h	Tue Mar 13 16:29:20 2018 +0100
@@ -0,0 +1,273 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2018 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Enumerations.h"
+
+
+namespace Orthanc
+{
+  template <PixelFormat Format>
+  struct PixelTraits;
+
+
+  template <>
+  struct PixelTraits<PixelFormat_Grayscale8>
+  {
+    typedef uint8_t   PixelType;
+
+    ORTHANC_FORCE_INLINE
+    static PixelFormat GetPixelFormat()
+    {
+      return PixelFormat_Grayscale8;
+    }
+
+    ORTHANC_FORCE_INLINE
+    static void SetZero(PixelType& target)
+    {
+      target = 0;
+    }
+
+    ORTHANC_FORCE_INLINE
+    static void Copy(PixelType& target,
+                     const PixelType& source)
+    {
+      target = source;
+    }
+
+    ORTHANC_FORCE_INLINE
+    static float PixelToFloat(const PixelType& source)
+    {
+      return static_cast<float>(source);
+    }
+
+    ORTHANC_FORCE_INLINE
+    static void FloatToPixel(PixelType& target,
+                             float value)
+    {
+      target = static_cast<uint8_t>(value);
+    }
+
+    ORTHANC_FORCE_INLINE
+    static bool IsEqual(const PixelType& a,
+                        const PixelType& b)
+    {
+      return a == b;
+    }
+  };
+
+
+  template <>
+  struct PixelTraits<PixelFormat_Grayscale16>
+  {
+    typedef uint16_t   PixelType;
+
+    ORTHANC_FORCE_INLINE
+    static PixelFormat GetPixelFormat()
+    {
+      return PixelFormat_Grayscale16;
+    }
+
+    ORTHANC_FORCE_INLINE
+    static void SetZero(PixelType& target)
+    {
+      target = 0;
+    }
+
+    ORTHANC_FORCE_INLINE
+    static void Copy(PixelType& target,
+                     const PixelType& source)
+    {
+      target = source;
+    }
+
+    ORTHANC_FORCE_INLINE
+    static float PixelToFloat(const PixelType& source)
+    {
+      return static_cast<float>(source);
+    }
+
+    ORTHANC_FORCE_INLINE
+    static void FloatToPixel(PixelType& target,
+                             float value)
+    {
+      target = static_cast<uint16_t>(value);
+    }
+
+    ORTHANC_FORCE_INLINE
+    static bool IsEqual(const PixelType& a,
+                        const PixelType& b)
+    {
+      return a == b;
+    }
+  };
+
+
+  template <>
+  struct PixelTraits<PixelFormat_SignedGrayscale16>
+  {
+    typedef int16_t   PixelType;
+
+    ORTHANC_FORCE_INLINE
+    static PixelFormat GetPixelFormat()
+    {
+      return PixelFormat_SignedGrayscale16;
+    }
+
+    ORTHANC_FORCE_INLINE
+    static void SetZero(PixelType& target)
+    {
+      target = 0;
+    }
+
+    ORTHANC_FORCE_INLINE
+    static void Copy(PixelType& target,
+                     const PixelType& source)
+    {
+      target = source;
+    }
+
+    ORTHANC_FORCE_INLINE
+    static float PixelToFloat(const PixelType& source)
+    {
+      return static_cast<float>(source);
+    }
+
+    ORTHANC_FORCE_INLINE
+    static void FloatToPixel(PixelType& target,
+                             float value)
+    {
+      target = static_cast<int16_t>(value);
+    }
+
+    ORTHANC_FORCE_INLINE
+    static bool IsEqual(const PixelType& a,
+                        const PixelType& b)
+    {
+      return a == b;
+    }
+  };
+
+
+  template <>
+  struct PixelTraits<PixelFormat_RGB24>
+  {
+    struct PixelType
+    {
+      uint8_t  red_;
+      uint8_t  green_;
+      uint8_t  blue_;
+    };
+
+    ORTHANC_FORCE_INLINE
+    static PixelFormat GetPixelFormat()
+    {
+      return PixelFormat_RGB24;
+    }
+
+    ORTHANC_FORCE_INLINE
+    static void SetZero(PixelType& target)
+    {
+      target.red_ = 0;
+      target.green_ = 0;
+      target.blue_ = 0;
+    }
+
+    ORTHANC_FORCE_INLINE
+    static void Copy(PixelType& target,
+                     const PixelType& source)
+    {
+      target.red_ = source.red_;
+      target.green_ = source.green_;
+      target.blue_ = source.blue_;
+    }
+
+    ORTHANC_FORCE_INLINE
+    static bool IsEqual(const PixelType& a,
+                        const PixelType& b)
+    {
+      return (a.red_ == b.red_ &&
+              a.green_ == b.green_ &&
+              a.blue_ == b.blue_);
+    }
+  };
+
+
+  template <>
+  struct PixelTraits<PixelFormat_BGRA32>
+  {
+    struct PixelType
+    {
+      uint8_t  blue_;
+      uint8_t  green_;
+      uint8_t  red_;
+      uint8_t  alpha_;
+    };
+
+    ORTHANC_FORCE_INLINE
+    static PixelFormat GetPixelFormat()
+    {
+      return PixelFormat_BGRA32;
+    }
+
+    ORTHANC_FORCE_INLINE
+    static void SetZero(PixelType& target)
+    {
+      target.blue_ = 0;
+      target.green_ = 0;
+      target.red_ = 0;
+      target.alpha_ = 0;
+    }
+
+    ORTHANC_FORCE_INLINE
+    static void Copy(PixelType& target,
+                     const PixelType& source)
+    {
+      target.blue_ = source.blue_;
+      target.green_ = source.green_;
+      target.red_ = source.red_;
+      target.alpha_ = source.alpha_;
+    }
+
+    ORTHANC_FORCE_INLINE
+    static bool IsEqual(const PixelType& a,
+                        const PixelType& b)
+    {
+      return (a.blue_ == b.blue_ &&
+              a.green_ == b.green_ &&
+              a.red_ == b.red_ &&
+              a.alpha_ == b.alpha_);
+    }
+  };
+}
--- a/UnitTestsSources/ImageProcessingTests.cpp	Wed Mar 07 15:46:47 2018 +0100
+++ b/UnitTestsSources/ImageProcessingTests.cpp	Tue Mar 13 16:29:20 2018 +0100
@@ -35,8 +35,11 @@
 #include "gtest/gtest.h"
 
 #include "../Core/DicomFormat/DicomImageInformation.h"
-#include "../Core/Images/ImageBuffer.h"
+#include "../Core/Images/Image.h"
 #include "../Core/Images/ImageProcessing.h"
+#include "../Core/Images/ImageTraits.h"
+
+#include <memory>
 
 using namespace Orthanc;
 
@@ -79,3 +82,119 @@
   ASSERT_TRUE(info.ExtractPixelFormat(format, false));
   ASSERT_EQ(PixelFormat_SignedGrayscale16, format);
 }
+
+
+
+namespace
+{
+  template <typename T>
+  class TestImageTraits : public ::testing::Test
+  {
+  private:
+    std::auto_ptr<Image>  image_;
+
+  protected:
+    virtual void SetUp() 
+    {
+      image_.reset(new Image(ImageTraits::PixelTraits::GetPixelFormat(), 7, 9, false));
+    }
+
+    virtual void TearDown()
+    {
+      image_.reset(NULL);
+    }
+
+  public:
+    typedef T ImageTraits;
+    
+    ImageAccessor& GetImage()
+    {
+      return *image_;
+    }
+  };
+
+  template <typename T>
+  class TestIntegerImageTraits : public TestImageTraits<T>
+  {
+  };
+}
+
+
+typedef ::testing::Types<
+  ImageTraits<PixelFormat_Grayscale8>,
+  ImageTraits<PixelFormat_Grayscale16>,
+  ImageTraits<PixelFormat_SignedGrayscale16>
+  > IntegerFormats;
+TYPED_TEST_CASE(TestIntegerImageTraits, IntegerFormats);
+
+typedef ::testing::Types<
+  ImageTraits<PixelFormat_Grayscale8>,
+  ImageTraits<PixelFormat_Grayscale16>,
+  ImageTraits<PixelFormat_SignedGrayscale16>,
+  ImageTraits<PixelFormat_RGB24>,
+  ImageTraits<PixelFormat_BGRA32>
+  > AllFormats;
+TYPED_TEST_CASE(TestImageTraits, AllFormats);
+
+
+TYPED_TEST(TestImageTraits, SetZero)
+{
+  ImageAccessor& image = this->GetImage();
+  
+  memset(image.GetBuffer(), 128, image.GetHeight() * image.GetWidth());
+
+  switch (image.GetFormat())
+  {
+    case PixelFormat_Grayscale8:
+    case PixelFormat_Grayscale16:
+    case PixelFormat_SignedGrayscale16:
+      ImageProcessing::Set(image, 0);
+      break;
+
+    case PixelFormat_RGB24:
+    case PixelFormat_BGRA32:
+      ImageProcessing::Set(image, 0, 0, 0, 0);
+      break;
+
+    default:
+      ASSERT_TRUE(0);
+  }
+
+  typename TestFixture::ImageTraits::PixelType zero, value;
+  TestFixture::ImageTraits::PixelTraits::SetZero(zero);
+
+  for (unsigned int y = 0; y < image.GetHeight(); y++)
+  {
+    for (unsigned int x = 0; x < image.GetWidth(); x++)
+    {
+      TestFixture::ImageTraits::GetPixel(value, image, x, y);
+      ASSERT_TRUE(TestFixture::ImageTraits::PixelTraits::IsEqual(zero, value));
+    }
+  }
+}
+
+
+TYPED_TEST(TestIntegerImageTraits, SetZeroFloat)
+{
+  ImageAccessor& image = this->GetImage();
+  
+  memset(image.GetBuffer(), 128, image.GetHeight() * image.GetWidth());
+
+  unsigned int c = 0;
+  for (unsigned int y = 0; y < image.GetHeight(); y++)
+  {
+    for (unsigned int x = 0; x < image.GetWidth(); x++, c++)
+    {
+      TestFixture::ImageTraits::SetFloatPixel(image, c, x, y);
+    }
+  }
+
+  c = 0;
+  for (unsigned int y = 0; y < image.GetHeight(); y++)
+  {
+    for (unsigned int x = 0; x < image.GetWidth(); x++, c++)
+    {
+      ASSERT_FLOAT_EQ(c, TestFixture::ImageTraits::GetFloatPixel(image, x, y));
+    }
+  }
+}