diff Framework/ImageToolbox.cpp @ 0:4a7a53257c7d

initial commit
author Sebastien Jodogne <s.jodogne@gmail.com>
date Sat, 22 Oct 2016 21:48:33 +0200
parents
children 7a88c614be04
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/ImageToolbox.cpp	Sat Oct 22 21:48:33 2016 +0200
@@ -0,0 +1,367 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * 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
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "ImageToolbox.h"
+
+#include "Jpeg2000Reader.h"
+#include "Jpeg2000Writer.h"
+
+#include "Orthanc/Core/OrthancException.h"
+#include "Orthanc/Core/Images/ImageProcessing.h"
+#include "Orthanc/Core/Images/PngReader.h"
+#include "Orthanc/Core/Images/PngWriter.h"
+#include "Orthanc/Core/Images/JpegReader.h"
+#include "Orthanc/Core/Images/JpegWriter.h"
+#include "Orthanc/Core/Logging.h"
+
+#include <string.h>
+#include <memory>
+
+
+namespace OrthancWSI
+{
+  namespace ImageToolbox
+  {
+    Orthanc::ImageAccessor* Allocate(Orthanc::PixelFormat format,
+                                     unsigned int width,
+                                     unsigned int height)
+    {
+      return new Orthanc::Image(format, width, height, false);
+    }
+
+
+    void Embed(Orthanc::ImageAccessor& target,
+               const Orthanc::ImageAccessor& source,
+               unsigned int x,
+               unsigned int y)
+    {
+      if (target.GetFormat() != source.GetFormat())
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
+      }
+
+      if (x >= target.GetWidth() ||
+          y >= target.GetHeight())
+      {
+        return;
+      }
+
+      unsigned int h = std::min(source.GetHeight(), target.GetHeight() - y);
+      unsigned int w = std::min(source.GetWidth(), target.GetWidth() - x);
+
+      Orthanc::ImageAccessor targetRegion = target.GetRegion(x, y, w, h);
+      Orthanc::ImageAccessor sourceRegion = source.GetRegion(0, 0, w, h);
+      Orthanc::ImageProcessing::Copy(targetRegion, sourceRegion);
+    }
+
+
+
+    void Set(Orthanc::ImageAccessor& image,
+             uint8_t r,
+             uint8_t g,
+             uint8_t b)
+    {
+      if (image.GetWidth() == 0 ||
+          image.GetHeight() == 0)
+      {
+        return;
+      }
+
+      uint8_t grayscale = (2126 * static_cast<uint16_t>(r) + 
+                           7152 * static_cast<uint16_t>(g) +
+                           0722 * static_cast<uint16_t>(b)) / 10000;
+
+      switch (image.GetFormat())
+      {
+        case Orthanc::PixelFormat_Grayscale8:
+        {
+          for (unsigned int y = 0; y < image.GetHeight(); y++)
+          {
+            memset(image.GetRow(y), grayscale, image.GetWidth());
+          }
+
+          break;
+        }
+
+        case Orthanc::PixelFormat_RGB24:
+        {
+          for (unsigned int y = 0; y < image.GetHeight(); y++)
+          {
+            uint8_t* p = reinterpret_cast<uint8_t*>(image.GetRow(y));
+            for (unsigned int x = 0; x < image.GetWidth(); x++, p += 3)
+            {
+              p[0] = r;
+              p[1] = g;
+              p[2] = b;
+            }
+          }
+
+          break;
+        }
+
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
+    }
+
+
+    Orthanc::ImageAccessor* DecodeTile(const std::string& source,
+                                       ImageCompression compression)
+    {
+      switch (compression)
+      {
+        case ImageCompression_Png:
+        {
+          std::auto_ptr<Orthanc::PngReader> reader(new Orthanc::PngReader);
+          reader->ReadFromMemory(source);
+          return reader.release();
+        }
+
+        case ImageCompression_Jpeg:
+        {
+          std::auto_ptr<Orthanc::JpegReader> reader(new Orthanc::JpegReader);
+          reader->ReadFromMemory(source);
+          return reader.release();
+        }
+
+        case ImageCompression_Jpeg2000:
+        {
+          std::auto_ptr<Jpeg2000Reader> reader(new Jpeg2000Reader);
+          reader->ReadFromMemory(source);
+          return reader.release();
+        }
+
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+      }
+    }
+
+
+    void EncodeTile(std::string& target,
+                    const Orthanc::ImageAccessor& source,
+                    ImageCompression compression,
+                    uint8_t quality)
+    {
+      if (compression == ImageCompression_None)
+      {
+        unsigned int pitch = GetBytesPerPixel(source.GetFormat()) * source.GetWidth();
+        target.resize(pitch * source.GetHeight());
+
+        for (unsigned int i = 0; i < source.GetHeight(); i++)
+        {
+          memcpy(&target[i * pitch], source.GetConstRow(i), pitch);
+        }
+      }
+      else
+      {
+        std::auto_ptr<Orthanc::IImageWriter> writer;
+
+        switch (compression)
+        {
+          case ImageCompression_Png:
+            writer.reset(new Orthanc::PngWriter);
+            break;
+
+          case ImageCompression_Jpeg:
+            writer.reset(new Orthanc::JpegWriter);
+            dynamic_cast<Orthanc::JpegWriter&>(*writer).SetQuality(quality);
+            break;
+
+          case ImageCompression_Jpeg2000:
+            writer.reset(new Jpeg2000Writer);
+            break;
+
+          default:
+            throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+        }
+
+        writer->WriteToMemory(target, source);
+      }
+    }
+
+
+    void ChangeTileCompression(std::string& target,
+                               const std::string& source,
+                               ImageCompression sourceCompression,
+                               ImageCompression targetCompression,
+                               uint8_t quality)
+    {
+      if (sourceCompression == targetCompression)
+      {
+        target = source;
+      }
+      else
+      {
+        std::auto_ptr<Orthanc::ImageAccessor> decoded(DecodeTile(source, sourceCompression));
+        EncodeTile(target, *decoded, targetCompression, quality);
+      }
+    }
+
+
+    static uint8_t GetPixelValue(const Orthanc::ImageAccessor& source,
+                                 unsigned int x,
+                                 unsigned int y,
+                                 unsigned int channel,
+                                 int offsetX,
+                                 int offsetY)
+    {
+      assert(channel < source.GetBytesPerPixel());
+      assert(source.GetFormat() == Orthanc::PixelFormat_Grayscale8 ||
+             source.GetFormat() == Orthanc::PixelFormat_RGB24 ||
+             source.GetFormat() == Orthanc::PixelFormat_RGBA32);  // 16bpp is unsupported
+
+      if (static_cast<int>(x) + offsetX < 0)
+      {
+        x = 0;
+      }
+      else
+      {
+        x += offsetX;
+        if (x >= source.GetWidth())
+        {
+          x = source.GetWidth() - 1;
+        }
+      }
+
+      if (static_cast<int>(y) + offsetY < 0)
+      {
+        y = 0;
+      }
+      else
+      {
+        y += offsetY;
+        if (y >= source.GetHeight())
+        {
+          y = source.GetHeight() - 1;
+        }
+      }
+
+      return *(reinterpret_cast<const uint8_t*>(source.GetConstBuffer()) +
+               y * source.GetPitch() + x * source.GetBytesPerPixel() + channel);
+    }
+
+
+    static uint8_t SmoothPixelValue(const Orthanc::ImageAccessor& source,
+                                    unsigned int x,
+                                    unsigned int y,
+                                    unsigned int channel)
+    {
+      static const uint32_t kernel[5] = { 1, 4, 6, 4, 1 };
+      static const uint32_t normalization = 2 * (1 + 4 + 6 + 4 + 1);
+
+      uint32_t accumulator = 0;
+
+      // Horizontal smoothing
+      for (int offset = -2; offset <= 2; offset++)
+      {
+        accumulator += kernel[offset + 2] * GetPixelValue(source, x, y, channel, offset, 0);
+      }
+
+      // Vertical smoothing
+      for (int offset = -2; offset <= 2; offset++)
+      {
+        accumulator += kernel[offset + 2] * GetPixelValue(source, x, y, channel, 0, offset);
+      }
+
+      return static_cast<uint8_t>(accumulator / normalization);
+    }
+
+
+    Orthanc::ImageAccessor* Halve(const Orthanc::ImageAccessor& source,
+                                  bool smooth)
+    {
+      if (source.GetWidth() % 2 == 1 ||
+          source.GetHeight() % 2 == 1)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);
+      }
+
+      if (source.GetFormat() != Orthanc::PixelFormat_Grayscale8 &&
+          source.GetFormat() != Orthanc::PixelFormat_RGB24 &&
+          source.GetFormat() != Orthanc::PixelFormat_RGBA32)  // 16bpp is not supported (*)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
+
+      unsigned int channelsCount = source.GetBytesPerPixel();  // OK tx (*)
+
+      std::auto_ptr<Orthanc::ImageAccessor> result(Allocate(source.GetFormat(), 
+                                                            source.GetWidth() / 2, 
+                                                            source.GetHeight() / 2));
+
+      for (unsigned int y = 0; y < source.GetHeight() / 2; y++)
+      {
+        uint8_t* q = reinterpret_cast<uint8_t*>(result->GetRow(y));
+
+        for (unsigned int x = 0; x < source.GetWidth() / 2; x++, q += 3)
+        {
+          for (unsigned int c = 0; c < channelsCount; c++)
+          {
+            if (smooth)
+            {
+              q[c] = SmoothPixelValue(source, 2 * x, 2 * y, c);
+            }
+            else
+            {
+              q[c] = GetPixelValue(source, 2 * x, 2 * y, c, 0, 0);
+            }
+          }
+        }
+      }
+
+      return result.release();
+    }
+
+
+    Orthanc::ImageAccessor* Clone(const Orthanc::ImageAccessor& accessor)
+    {
+      std::auto_ptr<Orthanc::ImageAccessor> result(Allocate(accessor.GetFormat(),
+                                                            accessor.GetWidth(),
+                                                            accessor.GetHeight()));
+      Embed(*result, accessor, 0, 0);
+
+      return result.release();
+    }
+
+
+    Orthanc::ImageAccessor* Render(ITiledPyramid& pyramid,
+                                   unsigned int level)
+    {
+      std::auto_ptr<Orthanc::ImageAccessor> result(Allocate(pyramid.GetPixelFormat(), 
+                                                            pyramid.GetLevelWidth(level),
+                                                            pyramid.GetLevelHeight(level)));
+
+      LOG(INFO) << "Rendering a tiled image of size " << result->GetWidth() << "x" << result->GetHeight();
+
+      for (unsigned int y = 0; y < result->GetHeight(); y += pyramid.GetTileHeight())
+      {
+        for (unsigned int x = 0; x < result->GetWidth(); x += pyramid.GetTileWidth())
+        {
+          std::auto_ptr<Orthanc::ImageAccessor> tile(pyramid.DecodeTile(level,
+                                                                        x / pyramid.GetTileWidth(),
+                                                                        y / pyramid.GetTileHeight()));
+          Embed(*result, *tile, x, y);
+        }
+      }
+
+      return result.release();
+    }
+  }
+}