diff Framework/Jpeg2000Writer.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/Jpeg2000Writer.cpp	Sat Oct 22 21:48:33 2016 +0200
@@ -0,0 +1,364 @@
+/**
+ * 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 "Jpeg2000Writer.h"
+
+#include "Orthanc/Core/ChunkedBuffer.h"
+#include "Orthanc/Core/OrthancException.h"
+
+#include <openjpeg.h>
+#include <string.h>
+#include <vector>
+
+#if ORTHANC_OPENJPEG_MAJOR_VERSION == 1
+#  define OPJ_CLRSPC_GRAY CLRSPC_GRAY
+#  define OPJ_CLRSPC_SRGB CLRSPC_SRGB
+#  define OPJ_CODEC_J2K   CODEC_J2K
+typedef opj_cinfo_t opj_codec_t;
+typedef opj_cio_t opj_stream_t;
+#elif ORTHANC_OPENJPEG_MAJOR_VERSION == 2
+#else
+#error Unsupported version of OpenJpeg
+#endif
+
+namespace OrthancWSI
+{
+  namespace
+  {
+    class OpenJpegImage : public boost::noncopyable
+    {
+    private:
+      std::vector<opj_image_cmptparm_t> components_;
+      COLOR_SPACE colorspace_;
+      opj_image_t* image_;
+
+      void SetupComponents(unsigned int width,
+                           unsigned int height,
+                           Orthanc::PixelFormat format)
+      {
+        switch (format)
+        {
+          case Orthanc::PixelFormat_Grayscale8:
+            colorspace_ = OPJ_CLRSPC_GRAY;
+            components_.resize(1);
+            break;
+
+          case Orthanc::PixelFormat_RGB24:
+            colorspace_ = OPJ_CLRSPC_SRGB;
+            components_.resize(3);
+            break;
+
+          default:
+            throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+        }
+
+        for (size_t i = 0; i < components_.size(); i++)
+        {
+          memset(&components_[i], 0, sizeof(opj_image_cmptparm_t));
+          components_[i].dx = 1;
+          components_[i].dy = 1;
+          components_[i].x0 = 0;
+          components_[i].y0 = 0;
+          components_[i].w = width;
+          components_[i].h = height;
+          components_[i].prec = 8;
+          components_[i].bpp = 8;
+          components_[i].sgnd = 0;
+        }
+      }
+
+      void CopyRGB24(unsigned int width,
+                     unsigned int height,
+                     unsigned int pitch,
+                     const void* buffer)
+      {
+        int32_t* r = image_->comps[0].data;
+        int32_t* g = image_->comps[1].data;
+        int32_t* b = image_->comps[2].data;
+
+        for (unsigned int y = 0; y < height; y++)
+        {
+          const uint8_t *p = reinterpret_cast<const uint8_t*>(buffer) + y * pitch;
+
+          for (unsigned int x = 0; x < width; x++)
+          {
+            *r = p[0];
+            *g = p[1];
+            *b = p[2];
+            p += 3;
+            r++;
+            g++;
+            b++;
+          }
+        }
+      }
+
+      void CopyGrayscale8(unsigned int width,
+                          unsigned int height,
+                          unsigned int pitch,
+                          const void* buffer)
+      {
+        int32_t* q = image_->comps[0].data;
+
+        for (unsigned int y = 0; y < height; y++)
+        {
+          const uint8_t *p = reinterpret_cast<const uint8_t*>(buffer) + y * pitch;
+
+          for (unsigned int x = 0; x < width; x++)
+          {
+            *q = *p;
+            p++;
+            q++;
+          }
+        }        
+      }
+
+    public:
+      OpenJpegImage(unsigned int width,
+                    unsigned int height,
+                    unsigned int pitch,
+                    Orthanc::PixelFormat format,
+                    const void* buffer) : image_(NULL)
+      {
+        SetupComponents(width, height, format);
+
+        image_ = opj_image_create(components_.size(), &components_[0], colorspace_);
+        if (!image_)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+        }
+
+        image_->x0 = 0;
+        image_->y0 = 0;
+        image_->x1 = width;
+        image_->y1 = height;
+
+        switch (format)
+        {
+          case Orthanc::PixelFormat_Grayscale8:
+            CopyGrayscale8(width, height, pitch, buffer);
+            break;
+
+          case Orthanc::PixelFormat_RGB24:
+            CopyRGB24(width, height, pitch, buffer);
+            break;
+
+          default:
+            throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+        }
+      }
+
+
+      ~OpenJpegImage()
+      {
+        if (image_)
+        {
+          opj_image_destroy(image_);
+          image_ = NULL;
+        }
+      }
+
+
+      opj_image_t* GetObject()
+      {
+        return image_;
+      }
+    };
+
+
+    class OpenJpegEncoder : public boost::noncopyable
+    {
+    private:
+      opj_codec_t* cinfo_;
+
+    public:
+      OpenJpegEncoder(opj_cparameters_t& parameters,
+                      OpenJpegImage& image) : cinfo_(NULL)
+      {
+        cinfo_ = opj_create_compress(OPJ_CODEC_J2K);
+        if (!cinfo_)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+        }
+
+        opj_setup_encoder(cinfo_, &parameters, image.GetObject());
+      }
+
+      ~OpenJpegEncoder()
+      {
+        if (cinfo_ != NULL)
+        {
+#if ORTHANC_OPENJPEG_MAJOR_VERSION == 1
+          opj_destroy_compress(cinfo_);
+#else
+          opj_destroy_codec(cinfo_);
+#endif
+          cinfo_ = NULL;
+        }
+      } 
+
+      opj_codec_t* GetObject()
+      {
+        return cinfo_;
+      }
+    };
+
+
+    class OpenJpegOutput : public boost::noncopyable
+    {
+    private:
+      opj_stream_t* cio_;
+
+#if ORTHANC_OPENJPEG_MAJOR_VERSION == 2
+      Orthanc::ChunkedBuffer buffer_;
+
+      static void Free(void *userData)
+      {
+      }
+
+      static OPJ_SIZE_T Write(void *buffer, 
+                              OPJ_SIZE_T size,
+                              void *userData)
+      {
+        OpenJpegOutput* that = reinterpret_cast<OpenJpegOutput*>(userData);
+        that->buffer_.AddChunk(reinterpret_cast<const char*>(buffer), size);
+        return size;
+      }
+#endif
+
+    public:
+      OpenJpegOutput(OpenJpegEncoder& encoder) : cio_(NULL)
+      {
+#if ORTHANC_OPENJPEG_MAJOR_VERSION == 1
+        cio_ = opj_cio_open(reinterpret_cast<opj_common_ptr>(encoder.GetObject()), NULL, 0);
+#else
+        cio_ = opj_stream_default_create(0 /* output stream */);
+        if (!cio_)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+        }
+
+        opj_stream_set_user_data(cio_, this, Free);
+        opj_stream_set_write_function(cio_, Write);
+#endif
+      }
+
+      ~OpenJpegOutput()
+      {
+        if (cio_)
+        {
+#if ORTHANC_OPENJPEG_MAJOR_VERSION == 1
+          opj_cio_close(cio_);
+#else
+          opj_stream_destroy(cio_);
+#endif
+          cio_ = NULL;
+        }
+      }
+
+      opj_stream_t* GetObject()
+      {
+        return cio_;
+      }
+
+      void Flatten(std::string& target)
+      {
+#if ORTHANC_OPENJPEG_MAJOR_VERSION == 1
+          target.assign(reinterpret_cast<const char*>(cio_->buffer), cio_tell(cio_));
+#else
+          buffer_.Flatten(target);
+#endif
+      }
+    };
+  }
+
+
+  static void SetupParameters(opj_cparameters_t& parameters,
+                              Orthanc::PixelFormat format,
+                              bool isLossless)
+  {
+    opj_set_default_encoder_parameters(&parameters);
+    parameters.cp_disto_alloc = 1;
+
+    if (isLossless)
+    {
+      parameters.tcp_numlayers = 1;
+      parameters.tcp_rates[0] = 0;
+    }
+    else
+    {
+      parameters.tcp_numlayers = 5;
+      parameters.tcp_rates[0] = 1920;
+      parameters.tcp_rates[1] = 480;
+      parameters.tcp_rates[2] = 120;
+      parameters.tcp_rates[3] = 30;
+      parameters.tcp_rates[4] = 10;
+      parameters.irreversible = 1;
+
+      if (format == Orthanc::PixelFormat_RGB24 ||
+          format == Orthanc::PixelFormat_RGBA32)
+      {
+        // This must be set to 1 if the number of color channels is >= 3
+        parameters.tcp_mct = 1;
+      }
+    }
+
+    parameters.cp_comment = const_cast<char*>("");
+  }
+
+
+  void Jpeg2000Writer::WriteToMemoryInternal(std::string& compressed,
+                                             unsigned int width,
+                                             unsigned int height,
+                                             unsigned int pitch,
+                                             Orthanc::PixelFormat format,
+                                             const void* buffer)
+  {
+    if (format != Orthanc::PixelFormat_Grayscale8 &&
+        format != Orthanc::PixelFormat_RGB24 &&
+        format != Orthanc::PixelFormat_RGBA32)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    opj_cparameters_t parameters;
+    SetupParameters(parameters, format, isLossless_);
+
+    OpenJpegImage image(width, height, pitch, format, buffer);
+    OpenJpegEncoder encoder(parameters, image);
+    OpenJpegOutput output(encoder);
+
+#if ORTHANC_OPENJPEG_MAJOR_VERSION == 1
+    if (!opj_encode(encoder.GetObject(), output.GetObject(), image.GetObject(), parameters.index))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+#else
+    if (!opj_start_compress(encoder.GetObject(), image.GetObject(), output.GetObject()) ||
+        !opj_encode(encoder.GetObject(), output.GetObject()) ||
+        !opj_end_compress(encoder.GetObject(), output.GetObject()))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+#endif
+
+    output.Flatten(compressed);
+  }
+}