diff Framework/Scene2D/LookupTableTextureSceneLayer.cpp @ 1179:177e7d431cd1 broker

log scale in textures, remove redundant code for LUTs
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 20 Nov 2019 15:24:20 +0100
parents 640feb146fa8
children 96d4f4fee5bb
line wrap: on
line diff
--- a/Framework/Scene2D/LookupTableTextureSceneLayer.cpp	Wed Nov 20 14:12:11 2019 +0100
+++ b/Framework/Scene2D/LookupTableTextureSceneLayer.cpp	Wed Nov 20 15:24:20 2019 +0100
@@ -132,6 +132,12 @@
     }
   }
 
+  void LookupTableTextureSceneLayer::SetApplyLog(bool apply)
+  {
+    applyLog_ = apply;
+    IncrementRevision();
+  }
+
   void LookupTableTextureSceneLayer::FitRange()
   {
     Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue_, maxValue_, GetTexture());
@@ -158,4 +164,147 @@
 
     return cloned.release();
   }
+
+
+  // Templatized function to speed up computations, by avoiding
+  // testing conditions on each pixel
+  template <bool IsApplyLog,
+            Orthanc::PixelFormat TargetFormat>
+  static void RenderInternal(Orthanc::ImageAccessor& target,
+                             const Orthanc::ImageAccessor& source,
+                             float minValue,
+                             float slope,
+                             const std::vector<uint8_t>& lut)
+  {
+    static const float LOG_NORMALIZATION = 255.0f / log(1.0f + 255.0f);
+
+    const unsigned int width = source.GetWidth();
+    const unsigned int height = source.GetHeight();
+    
+    for (unsigned int y = 0; y < height; y++)
+    {
+      const float* p = reinterpret_cast<const float*>(source.GetConstRow(y));
+      uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
+
+      for (unsigned int x = 0; x < width; x++)
+      {
+        float v = (*p - minValue) * slope;
+        if (v <= 0)
+        {
+          v = 0;
+        }
+        else if (v >= 255)
+        {
+          v = 255;
+        }
+
+        if (IsApplyLog)
+        {
+          // https://theailearner.com/2019/01/01/log-transformation/
+          v = LOG_NORMALIZATION * log(1.0f + static_cast<float>(v));
+        }
+
+        assert(v >= 0.0f && v <= 255.0f);
+
+        uint8_t vv = static_cast<uint8_t>(v);
+
+        switch (TargetFormat)
+        {
+          case Orthanc::PixelFormat_BGRA32:
+            // For Cairo surfaces
+            q[0] = lut[4 * vv + 2];  // B
+            q[1] = lut[4 * vv + 1];  // G
+            q[2] = lut[4 * vv + 0];  // R
+            q[3] = lut[4 * vv + 3];  // A
+            break;
+
+          case Orthanc::PixelFormat_RGBA32:
+            // For OpenGL
+            q[0] = lut[4 * vv + 0];  // R
+            q[1] = lut[4 * vv + 1];  // G
+            q[2] = lut[4 * vv + 2];  // B
+            q[3] = lut[4 * vv + 3];  // A
+            break;
+
+          default:
+            assert(0);
+        }
+            
+        p++;
+        q += 4;
+      }
+    }
+  }
+  
+
+  void LookupTableTextureSceneLayer::Render(Orthanc::ImageAccessor& target) const
+  {
+    assert(sizeof(float) == 4);
+
+    if (!HasTexture())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+
+    const Orthanc::ImageAccessor& source = GetTexture();
+    
+    if (source.GetFormat() != Orthanc::PixelFormat_Float32 ||
+        (target.GetFormat() != Orthanc::PixelFormat_RGBA32 &&
+         target.GetFormat() != Orthanc::PixelFormat_BGRA32))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
+    }
+
+    if (source.GetWidth() != target.GetWidth() ||
+        source.GetHeight() != target.GetHeight())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);
+    }
+
+    const float minValue = GetMinValue();
+    float slope;
+
+    if (GetMinValue() >= GetMaxValue())
+    {
+      slope = 0;
+    }
+    else
+    {
+      slope = 256.0f / (GetMaxValue() - GetMinValue());
+    }
+
+    const std::vector<uint8_t>& lut = GetLookupTable();
+    if (lut.size() != 4 * 256)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+
+    switch (target.GetFormat())
+    {
+      case Orthanc::PixelFormat_RGBA32:
+        if (applyLog_)
+        {
+          RenderInternal<true, Orthanc::PixelFormat_RGBA32>(target, source, minValue, slope, lut);
+        }
+        else
+        {
+          RenderInternal<false, Orthanc::PixelFormat_RGBA32>(target, source, minValue, slope, lut);
+        }
+        break;
+
+      case Orthanc::PixelFormat_BGRA32:
+        if (applyLog_)
+        {
+          RenderInternal<true, Orthanc::PixelFormat_BGRA32>(target, source, minValue, slope, lut);
+        }
+        else
+        {
+          RenderInternal<false, Orthanc::PixelFormat_BGRA32>(target, source, minValue, slope, lut);
+        }
+        break;
+
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+  }
 }