changeset 853:839be3022203 jpeg

DicomImageInformation
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 06 Jun 2014 11:45:16 +0200
parents 5944b8b80842
children ff530685e46a
files CMakeLists.txt Core/DicomFormat/DicomImageInformation.cpp Core/DicomFormat/DicomImageInformation.h Core/DicomFormat/DicomIntegerPixelAccessor.cpp Core/DicomFormat/DicomIntegerPixelAccessor.h Core/Enumerations.h Core/ImageFormats/ImageBuffer.cpp Core/ImageFormats/ImageBuffer.h Core/ImageFormats/ImageProcessing.cpp Core/ImageFormats/ImageProcessing.h Core/OrthancException.cpp OrthancServer/FromDcmtkBridge.cpp OrthancServer/Internals/DicomImageDecoder.cpp OrthancServer/ParsedDicomFile.cpp UnitTestsSources/ImageProcessingTests.cpp
diffstat 15 files changed, 595 insertions(+), 167 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Fri Jun 06 10:28:02 2014 +0200
+++ b/CMakeLists.txt	Fri Jun 06 11:45:16 2014 +0200
@@ -68,6 +68,7 @@
   Core/DicomFormat/DicomArray.cpp
   Core/DicomFormat/DicomMap.cpp
   Core/DicomFormat/DicomTag.cpp
+  Core/DicomFormat/DicomImageInformation.cpp
   Core/DicomFormat/DicomIntegerPixelAccessor.cpp
   Core/DicomFormat/DicomInstanceHasher.cpp
   Core/Enumerations.cpp
@@ -94,6 +95,7 @@
   Core/MultiThreading/ThreadedCommandProcessor.cpp
   Core/ImageFormats/ImageAccessor.cpp
   Core/ImageFormats/ImageBuffer.cpp
+  Core/ImageFormats/ImageProcessing.cpp
   Core/ImageFormats/PngReader.cpp
   Core/ImageFormats/PngWriter.cpp
   Core/SQLite/Connection.cpp
@@ -164,6 +166,7 @@
   UnitTestsSources/Lua.cpp
   UnitTestsSources/MultiThreading.cpp
   UnitTestsSources/UnitTestsMain.cpp
+  UnitTestsSources/ImageProcessingTests.cpp
   UnitTestsSources/JpegLossless.cpp
   )
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/DicomFormat/DicomImageInformation.cpp	Fri Jun 06 11:45:16 2014 +0200
@@ -0,0 +1,150 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege,
+ * 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/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+
+#include "DicomImageInformation.h"
+
+#include "../OrthancException.h"
+#include <boost/lexical_cast.hpp>
+#include <limits>
+#include <cassert>
+#include <stdio.h>
+
+namespace Orthanc
+{
+  DicomImageInformation::DicomImageInformation(const DicomMap& values)
+  {
+    unsigned int pixelRepresentation;
+    unsigned int planarConfiguration = 0;
+
+    try
+    {
+      width_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_COLUMNS).AsString());
+      height_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_ROWS).AsString());
+      samplesPerPixel_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_SAMPLES_PER_PIXEL).AsString());
+      bitsAllocated_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_ALLOCATED).AsString());
+
+      try
+      {
+        bitsStored_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_STORED).AsString());
+      }
+      catch (OrthancException&)
+      {
+        bitsStored_ = bitsAllocated_;
+      }
+
+      try
+      {
+        highBit_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_HIGH_BIT).AsString());
+      }
+      catch (OrthancException&)
+      {
+        highBit_ = bitsStored_ - 1;
+      }
+
+      try
+      {
+        pixelRepresentation = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PIXEL_REPRESENTATION).AsString());
+      }
+      catch (OrthancException&)
+      {
+        pixelRepresentation = 0;  // Assume unsigned pixels
+      }
+
+      if (samplesPerPixel_ > 1)
+      {
+        // The "Planar Configuration" is only set when "Samples per Pixels" is greater than 1
+        // https://www.dabsoft.ch/dicom/3/C.7.6.3.1.3/
+        try
+        {
+          planarConfiguration = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PLANAR_CONFIGURATION).AsString());
+        }
+        catch (OrthancException&)
+        {
+          planarConfiguration = 0;  // Assume interleaved color channels
+        }
+      }
+    }
+    catch (boost::bad_lexical_cast&)
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+    catch (OrthancException&)
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    try
+    {
+      numberOfFrames_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_NUMBER_OF_FRAMES).AsString());
+    }
+    catch (OrthancException)
+    {
+      // If the tag "NumberOfFrames" is absent, assume there is a single frame
+      numberOfFrames_ = 1;
+    }
+    catch (boost::bad_lexical_cast)
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    if ((bitsAllocated_ != 8 && bitsAllocated_ != 16 && 
+         bitsAllocated_ != 24 && bitsAllocated_ != 32) ||
+        numberOfFrames_ == 0 ||
+        (planarConfiguration != 0 && planarConfiguration != 1))
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    if (bitsAllocated_ > 32 ||
+        bitsStored_ >= 32)
+    {
+      // Not available, as the accessor internally uses int32_t values
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    if (samplesPerPixel_ == 0)
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    bytesPerPixel_ = bitsAllocated_ / 8;
+
+    isPlanar_ = (planarConfiguration != 0 ? true : false);
+    isSigned_ = (pixelRepresentation != 0 ? true : false);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/DicomFormat/DicomImageInformation.h	Fri Jun 06 11:45:16 2014 +0200
@@ -0,0 +1,120 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege,
+ * 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 "DicomMap.h"
+
+#include <stdint.h>
+
+namespace Orthanc
+{
+  class DicomImageInformation
+  {
+  private:
+    unsigned int width_;
+    unsigned int height_;
+    unsigned int samplesPerPixel_;
+    unsigned int numberOfFrames_;
+
+    bool isPlanar_;
+    bool isSigned_;
+    size_t bytesPerPixel_;
+
+    unsigned int bitsAllocated_;
+    unsigned int bitsStored_;
+    unsigned int highBit_;
+
+  public:
+    DicomImageInformation(const DicomMap& values);
+
+    unsigned int GetWidth() const
+    {
+      return width_;
+    }
+
+    unsigned int GetHeight() const
+    {
+      return height_;
+    }
+
+    unsigned int GetNumberOfFrames() const
+    {
+      return numberOfFrames_;
+    }
+
+    unsigned int GetChannelCount() const
+    {
+      return samplesPerPixel_;
+    }
+
+    unsigned int GetBitsStored() const
+    {
+      return bitsStored_;
+    }
+
+    size_t GetBytesPerPixel() const
+    {
+      return bytesPerPixel_;
+    }
+
+    bool IsSigned() const
+    {
+      return isSigned_;
+    }
+
+    unsigned int GetBitsAllocated() const
+    {
+      return bitsAllocated_;
+    }
+
+    unsigned int GetHighBit() const
+    {
+      return highBit_;
+    }
+
+    unsigned int GetSamplesPerPixel() const
+    {
+      return samplesPerPixel_;
+    }
+
+    bool IsPlanar() const
+    {
+      return isPlanar_;
+    }
+
+    unsigned int GetShift() const
+    {
+      return highBit_ + 1 - bitsStored_;
+    }
+  };
+}
--- a/Core/DicomFormat/DicomIntegerPixelAccessor.cpp	Fri Jun 06 10:28:02 2014 +0200
+++ b/Core/DicomFormat/DicomIntegerPixelAccessor.cpp	Fri Jun 06 11:45:16 2014 +0200
@@ -49,102 +49,33 @@
   DicomIntegerPixelAccessor::DicomIntegerPixelAccessor(const DicomMap& values,
                                                        const void* pixelData,
                                                        size_t size) :
+    information_(values),
     pixelData_(pixelData),
     size_(size)
   {
-    unsigned int bitsAllocated;
-    unsigned int bitsStored;
-    unsigned int highBit;
-    unsigned int pixelRepresentation;
-    planarConfiguration_ = 0;
-
-    try
-    {
-      width_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_COLUMNS).AsString());
-      height_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_ROWS).AsString());
-      samplesPerPixel_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_SAMPLES_PER_PIXEL).AsString());
-      bitsAllocated = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_ALLOCATED).AsString());
-      bitsStored = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_STORED).AsString());
-      highBit = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_HIGH_BIT).AsString());
-      pixelRepresentation = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PIXEL_REPRESENTATION).AsString());
-
-      if (samplesPerPixel_ > 1)
-      {
-        // The "Planar Configuration" is only set when "Samples per Pixels" is greater than 1
-        // https://www.dabsoft.ch/dicom/3/C.7.6.3.1.3/
-        planarConfiguration_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PLANAR_CONFIGURATION).AsString());
-      }
-    }
-    catch (boost::bad_lexical_cast)
-    {
-      throw OrthancException(ErrorCode_NotImplemented);
-    }
-    catch (OrthancException)
-    {
-      throw OrthancException(ErrorCode_NotImplemented);
-    }
+    frame_ = 0;
+    frameOffset_ = (information_.GetHeight() * information_.GetWidth() * 
+                    information_.GetBytesPerPixel() * information_.GetSamplesPerPixel());
 
-    frame_ = 0;
-    try
-    {
-      numberOfFrames_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_NUMBER_OF_FRAMES).AsString());
-    }
-    catch (OrthancException)
-    {
-      // If the tag "NumberOfFrames" is absent, assume there is a single frame
-      numberOfFrames_ = 1;
-    }
-    catch (boost::bad_lexical_cast)
-    {
-      throw OrthancException(ErrorCode_NotImplemented);
-    }
-
-    if ((bitsAllocated != 8 && bitsAllocated != 16 && 
-         bitsAllocated != 24 && bitsAllocated != 32) ||
-        numberOfFrames_ == 0 ||
-        (planarConfiguration_ != 0 && planarConfiguration_ != 1))
-    {
-      throw OrthancException(ErrorCode_NotImplemented);
-    }
-
-    if (bitsAllocated > 32 ||
-        bitsStored >= 32)
-    {
-      // Not available, as the accessor internally uses int32_t values
-      throw OrthancException(ErrorCode_NotImplemented);
-    }
-
-    if (samplesPerPixel_ == 0)
-    {
-      throw OrthancException(ErrorCode_NotImplemented);
-    }
-
-    bytesPerPixel_ = bitsAllocated / 8;
-    shift_ = highBit + 1 - bitsStored;
-    frameOffset_ = height_ * width_ * bytesPerPixel_ * samplesPerPixel_;
-
-    if (numberOfFrames_ * frameOffset_ > size)
+    if (information_.GetNumberOfFrames() * frameOffset_ > size)
     {
       throw OrthancException(ErrorCode_BadFileFormat);
     }
 
-    /*printf("%d %d %d %d %d %d %d %d\n", width_, height_, samplesPerPixel_, bitsAllocated,
-      bitsStored, highBit, pixelRepresentation, numberOfFrames_);*/
-
-    if (pixelRepresentation)
+    if (information_.IsSigned())
     {
       // Pixels are signed
-      mask_ = (1 << (bitsStored - 1)) - 1;
-      signMask_ = (1 << (bitsStored - 1));
+      mask_ = (1 << (information_.GetBitsStored() - 1)) - 1;
+      signMask_ = (1 << (information_.GetBitsStored() - 1));
     }
     else
     {
       // Pixels are unsigned
-      mask_ = (1 << bitsStored) - 1;
+      mask_ = (1 << information_.GetBitsStored()) - 1;
       signMask_ = 0;
     }
 
-    if (planarConfiguration_ == 0)
+    if (information_.IsPlanar())
     {
       /**
        * The sample values for the first pixel are followed by the
@@ -152,7 +83,7 @@
        * means the order of the pixel values sent shall be R1, G1, B1,
        * R2, G2, B2, ..., etc.
        **/
-      rowOffset_ = width_ * bytesPerPixel_ * samplesPerPixel_;
+      rowOffset_ = information_.GetWidth() * information_.GetBytesPerPixel() * information_.GetSamplesPerPixel();
     }
     else
     {
@@ -161,7 +92,7 @@
        * this means the order of the pixel values sent is R1, R2, R3,
        * ..., G1, G2, G3, ..., B1, B2, B3, etc.
        **/
-      rowOffset_ = width_ * bytesPerPixel_;
+      rowOffset_ = information_.GetWidth() * information_.GetBytesPerPixel();
     }
   }
 
@@ -169,7 +100,7 @@
   void DicomIntegerPixelAccessor::GetExtremeValues(int32_t& min, 
                                                    int32_t& max) const
   {
-    if (height_ == 0 || width_ == 0)
+    if (information_.GetHeight() == 0 || information_.GetWidth() == 0)
     {
       min = max = 0;
       return;
@@ -178,11 +109,11 @@
     min = std::numeric_limits<int32_t>::max();
     max = std::numeric_limits<int32_t>::min();
     
-    for (unsigned int y = 0; y < height_; y++)
+    for (unsigned int y = 0; y < information_.GetHeight(); y++)
     {
-      for (unsigned int x = 0; x < width_; x++)
+      for (unsigned int x = 0; x < information_.GetWidth(); x++)
       {
-        for (unsigned int c = 0; c < GetChannelCount(); c++)
+        for (unsigned int c = 0; c < information_.GetChannelCount(); c++)
         {
           int32_t v = GetValue(x, y);
           if (v < min)
@@ -199,13 +130,13 @@
                                               unsigned int y,
                                               unsigned int channel) const
   {
-    assert(x < width_ && y < height_ && channel < samplesPerPixel_);
+    assert(x < information_.GetWidth() && y < information_.GetHeight() && channel < information_.GetSamplesPerPixel());
     
     const uint8_t* pixel = reinterpret_cast<const uint8_t*>(pixelData_) + 
       y * rowOffset_ + frame_ * frameOffset_;
 
     // https://www.dabsoft.ch/dicom/3/C.7.6.3.1.3/
-    if (planarConfiguration_ == 0)
+    if (information_.IsPlanar() == 0)
     {
       /**
        * The sample values for the first pixel are followed by the
@@ -213,7 +144,7 @@
        * means the order of the pixel values sent shall be R1, G1, B1,
        * R2, G2, B2, ..., etc.
        **/
-      pixel += channel * bytesPerPixel_ + x * samplesPerPixel_ * bytesPerPixel_;
+      pixel += channel * information_.GetBytesPerPixel() + x * information_.GetSamplesPerPixel() * information_.GetBytesPerPixel();
     }
     else
     {
@@ -222,20 +153,20 @@
        * this means the order of the pixel values sent is R1, R2, R3,
        * ..., G1, G2, G3, ..., B1, B2, B3, etc.
        **/
-      assert(frameOffset_ % samplesPerPixel_ == 0);
-      pixel += channel * frameOffset_ / samplesPerPixel_ + x * bytesPerPixel_;
+      assert(frameOffset_ % information_.GetSamplesPerPixel() == 0);
+      pixel += channel * frameOffset_ / information_.GetSamplesPerPixel() + x * information_.GetBytesPerPixel();
     }
 
     uint32_t v;
     v = pixel[0];
-    if (bytesPerPixel_ >= 2)
+    if (information_.GetBytesPerPixel() >= 2)
       v = v + (static_cast<uint32_t>(pixel[1]) << 8);
-    if (bytesPerPixel_ >= 3)
+    if (information_.GetBytesPerPixel() >= 3)
       v = v + (static_cast<uint32_t>(pixel[2]) << 16);
-    if (bytesPerPixel_ >= 4)
+    if (information_.GetBytesPerPixel() >= 4)
       v = v + (static_cast<uint32_t>(pixel[3]) << 24);
 
-    v = v >> shift_;
+    v = v >> information_.GetShift();
 
     if (v & signMask_)
     {
@@ -253,7 +184,7 @@
 
   void DicomIntegerPixelAccessor::SetCurrentFrame(unsigned int frame)
   {
-    if (frame >= numberOfFrames_)
+    if (frame >= information_.GetNumberOfFrames())
     {
       throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
--- a/Core/DicomFormat/DicomIntegerPixelAccessor.h	Fri Jun 06 10:28:02 2014 +0200
+++ b/Core/DicomFormat/DicomIntegerPixelAccessor.h	Fri Jun 06 11:45:16 2014 +0200
@@ -34,6 +34,8 @@
 
 #include "DicomMap.h"
 
+#include "DicomImageInformation.h"
+
 #include <stdint.h>
 
 namespace Orthanc
@@ -41,20 +43,14 @@
   class DicomIntegerPixelAccessor
   {
   private:
-    unsigned int width_;
-    unsigned int height_;
-    unsigned int samplesPerPixel_;
-    unsigned int numberOfFrames_;
-    unsigned int planarConfiguration_;
+    DicomImageInformation information_;
+
+    uint32_t signMask_;
+    uint32_t mask_;
+
     const void* pixelData_;
     size_t size_;
-
-    uint8_t shift_;
-    uint32_t signMask_;
-    uint32_t mask_;
-    size_t bytesPerPixel_;
     unsigned int frame_;
-
     size_t frameOffset_;
     size_t rowOffset_;
 
@@ -63,19 +59,9 @@
                               const void* pixelData,
                               size_t size);
 
-    unsigned int GetWidth() const
-    {
-      return width_;
-    }
-
-    unsigned int GetHeight() const
+    const DicomImageInformation GetInformation() const
     {
-      return height_;
-    }
-
-    unsigned int GetNumberOfFrames() const
-    {
-      return numberOfFrames_;
+      return information_;
     }
 
     unsigned int GetCurrentFrame() const
@@ -88,11 +74,6 @@
     void GetExtremeValues(int32_t& min, 
                           int32_t& max) const;
 
-    unsigned int GetChannelCount() const
-    {
-      return samplesPerPixel_;
-    }
-
     int32_t GetValue(unsigned int x, unsigned int y, unsigned int channel = 0) const;
   };
 }
--- a/Core/Enumerations.h	Fri Jun 06 10:28:02 2014 +0200
+++ b/Core/Enumerations.h	Fri Jun 06 11:45:16 2014 +0200
@@ -69,7 +69,9 @@
     ErrorCode_FullStorage,
     ErrorCode_CorruptedFile,
     ErrorCode_InexistentTag,
-    ErrorCode_ReadOnly
+    ErrorCode_ReadOnly,
+    ErrorCode_IncompatibleImageFormat,
+    ErrorCode_IncompatibleImageSize
   };
 
   /**
--- a/Core/ImageFormats/ImageBuffer.cpp	Fri Jun 06 10:28:02 2014 +0200
+++ b/Core/ImageFormats/ImageBuffer.cpp	Fri Jun 06 11:45:16 2014 +0200
@@ -63,7 +63,18 @@
   }
 
 
-  ImageBuffer::ImageBuffer() : changed_(false)
+  ImageBuffer::ImageBuffer(unsigned int width,
+                           unsigned int height,
+                           PixelFormat format)
+  {
+    Initialize();
+    SetWidth(width);
+    SetHeight(height);
+    SetFormat(format);
+  }
+
+
+  void ImageBuffer::Initialize()
   {
     changed_ = false;
     forceMinimalPitch_ = true;
--- a/Core/ImageFormats/ImageBuffer.h	Fri Jun 06 10:28:02 2014 +0200
+++ b/Core/ImageFormats/ImageBuffer.h	Fri Jun 06 11:45:16 2014 +0200
@@ -51,11 +51,20 @@
     unsigned int height_;
     unsigned int pitch_;
     uint8_t *buffer_;
+
+    void Initialize();
     
     void Allocate();
 
   public:
-    ImageBuffer();
+    ImageBuffer(unsigned int width,
+                unsigned int height,
+                PixelFormat format);
+
+    ImageBuffer()
+    {
+      Initialize();
+    }
 
     PixelFormat GetFormat() const
     {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/ImageFormats/ImageProcessing.cpp	Fri Jun 06 11:45:16 2014 +0200
@@ -0,0 +1,82 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege,
+ * 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/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "ImageProcessing.h"
+
+#include "../OrthancException.h"
+
+#include <cassert>
+#include <string.h>
+
+namespace Orthanc
+{
+  void ImageProcessing::Copy(ImageAccessor& target,
+                             const ImageAccessor& source)
+  {
+    if (target.GetWidth() != source.GetWidth() ||
+        target.GetHeight() != source.GetHeight())
+    {
+      throw OrthancException(ErrorCode_IncompatibleImageSize);
+    }
+
+    if (target.GetFormat() != source.GetFormat())
+    {
+      throw OrthancException(ErrorCode_IncompatibleImageFormat);
+    }
+
+    unsigned int lineSize = GetBytesPerPixel(source.GetFormat()) * source.GetWidth();
+
+    assert(source.GetPitch() >= lineSize && target.GetPitch() >= lineSize);
+
+    for (unsigned int y = 0; y < source.GetHeight(); y++)
+    {
+      memcpy(target.GetRow(y), source.GetRow(y), lineSize);
+    }
+  }
+
+  void ImageProcessing::Convert(ImageAccessor& target,
+                                const ImageAccessor& source)
+  {
+    if (target.GetWidth() != source.GetWidth() ||
+        target.GetHeight() != source.GetHeight())
+    {
+      throw OrthancException(ErrorCode_IncompatibleImageSize);
+    }
+
+    if (source.GetFormat() == target.GetFormat())
+    {
+      Copy(target, source);
+      return;
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/ImageFormats/ImageProcessing.h	Fri Jun 06 11:45:16 2014 +0200
@@ -0,0 +1,48 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege,
+ * 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 "ImageAccessor.h"
+
+namespace Orthanc
+{
+  class ImageProcessing
+  {
+  public:
+    static void Copy(ImageAccessor& target,
+                     const ImageAccessor& source);
+
+    static void Convert(ImageAccessor& target,
+                        const ImageAccessor& source);
+  };
+}
--- a/Core/OrthancException.cpp	Fri Jun 06 10:28:02 2014 +0200
+++ b/Core/OrthancException.cpp	Fri Jun 06 11:45:16 2014 +0200
@@ -115,6 +115,12 @@
       case ErrorCode_ReadOnly:
         return "Cannot modify a read-only data structure";
 
+      case ErrorCode_IncompatibleImageSize:
+        return "Incompatible size of the images";
+
+      case ErrorCode_IncompatibleImageFormat:
+        return "Incompatible format of the images";
+
       case ErrorCode_Custom:
       default:
         return "???";
--- a/OrthancServer/FromDcmtkBridge.cpp	Fri Jun 06 10:28:02 2014 +0200
+++ b/OrthancServer/FromDcmtkBridge.cpp	Fri Jun 06 11:45:16 2014 +0200
@@ -431,15 +431,15 @@
   static void ExtractPngImageColorPreview(std::string& result,
                                           DicomIntegerPixelAccessor& accessor)
   {
-    assert(accessor.GetChannelCount() == 3);
+    assert(accessor.GetInformation().GetChannelCount() == 3);
     PngWriter w;
 
-    std::vector<uint8_t> image(accessor.GetWidth() * accessor.GetHeight() * 3, 0);
+    std::vector<uint8_t> image(accessor.GetInformation().GetWidth() * accessor.GetInformation().GetHeight() * 3, 0);
     uint8_t* pixel = &image[0];
 
-    for (unsigned int y = 0; y < accessor.GetHeight(); y++)
+    for (unsigned int y = 0; y < accessor.GetInformation().GetHeight(); y++)
     {
-      for (unsigned int x = 0; x < accessor.GetWidth(); x++)
+      for (unsigned int x = 0; x < accessor.GetInformation().GetWidth(); x++)
       {
         for (unsigned int c = 0; c < 3; c++, pixel++)
         {
@@ -454,27 +454,27 @@
       }
     }
 
-    w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(),
-                    accessor.GetWidth() * 3, PixelFormat_RGB24, &image[0]);
+    w.WriteToMemory(result, accessor.GetInformation().GetWidth(), accessor.GetInformation().GetHeight(),
+                    accessor.GetInformation().GetWidth() * 3, PixelFormat_RGB24, &image[0]);
   }
 
 
   static void ExtractPngImageGrayscalePreview(std::string& result,
                                               DicomIntegerPixelAccessor& accessor)
   {
-    assert(accessor.GetChannelCount() == 1);
+    assert(accessor.GetInformation().GetChannelCount() == 1);
     PngWriter w;
 
     int32_t min, max;
     accessor.GetExtremeValues(min, max);
 
-    std::vector<uint8_t> image(accessor.GetWidth() * accessor.GetHeight(), 0);
+    std::vector<uint8_t> image(accessor.GetInformation().GetWidth() * accessor.GetInformation().GetHeight(), 0);
     if (min != max)
     {
       uint8_t* pixel = &image[0];
-      for (unsigned int y = 0; y < accessor.GetHeight(); y++)
+      for (unsigned int y = 0; y < accessor.GetInformation().GetHeight(); y++)
       {
-        for (unsigned int x = 0; x < accessor.GetWidth(); x++, pixel++)
+        for (unsigned int x = 0; x < accessor.GetInformation().GetWidth(); x++, pixel++)
         {
           int32_t v = accessor.GetValue(x, y);
           *pixel = static_cast<uint8_t>(
@@ -484,8 +484,8 @@
       }
     }
 
-    w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(),
-                    accessor.GetWidth(), PixelFormat_Grayscale8, &image[0]);
+    w.WriteToMemory(result, accessor.GetInformation().GetWidth(), accessor.GetInformation().GetHeight(),
+                    accessor.GetInformation().GetWidth(), PixelFormat_Grayscale8, &image[0]);
   }
 
 
@@ -494,15 +494,15 @@
                                       DicomIntegerPixelAccessor& accessor,
                                       PixelFormat format)
   {
-    assert(accessor.GetChannelCount() == 1);
+    assert(accessor.GetInformation().GetChannelCount() == 1);
 
     PngWriter w;
 
-    std::vector<T> image(accessor.GetWidth() * accessor.GetHeight(), 0);
+    std::vector<T> image(accessor.GetInformation().GetWidth() * accessor.GetInformation().GetHeight(), 0);
     T* pixel = &image[0];
-    for (unsigned int y = 0; y < accessor.GetHeight(); y++)
+    for (unsigned int y = 0; y < accessor.GetInformation().GetHeight(); y++)
     {
-      for (unsigned int x = 0; x < accessor.GetWidth(); x++, pixel++)
+      for (unsigned int x = 0; x < accessor.GetInformation().GetWidth(); x++, pixel++)
       {
         int32_t v = accessor.GetValue(x, y);
         if (v < static_cast<int32_t>(std::numeric_limits<T>::min()))
@@ -514,8 +514,8 @@
       }
     }
 
-    w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(),
-                    accessor.GetWidth() * sizeof(T), format, &image[0]);
+    w.WriteToMemory(result, accessor.GetInformation().GetWidth(), accessor.GetInformation().GetHeight(),
+                    accessor.GetInformation().GetWidth() * sizeof(T), format, &image[0]);
   }
 
 
@@ -564,7 +564,7 @@
     PixelFormat format;
     bool supported = false;
 
-    if (accessor->GetChannelCount() == 1)
+    if (accessor->GetInformation().GetChannelCount() == 1)
     {
       switch (mode)
       {
@@ -593,7 +593,7 @@
           break;
       }
     }
-    else if (accessor->GetChannelCount() == 3)
+    else if (accessor->GetInformation().GetChannelCount() == 3)
     {
       switch (mode)
       {
@@ -614,8 +614,8 @@
     }   
 
     if (accessor.get() == NULL ||
-        accessor->GetWidth() == 0 ||
-        accessor->GetHeight() == 0)
+        accessor->GetInformation().GetWidth() == 0 ||
+        accessor->GetInformation().GetHeight() == 0)
     {
       PngWriter w;
       w.WriteToMemory(result, 0, 0, 0, format, NULL);
--- a/OrthancServer/Internals/DicomImageDecoder.cpp	Fri Jun 06 10:28:02 2014 +0200
+++ b/OrthancServer/Internals/DicomImageDecoder.cpp	Fri Jun 06 11:45:16 2014 +0200
@@ -99,12 +99,17 @@
   {
   private:
     std::string psmct_;
-    std::auto_ptr<DicomIntegerPixelAccessor> accessor_;
- 
+    std::auto_ptr<DicomIntegerPixelAccessor> slowAccessor_;
+    std::auto_ptr<ImageAccessor> fastAccessor_;
+
   public:
     void Setup(DcmDataset& dataset,
                unsigned int frame)
     {
+      psmct_.clear();
+      slowAccessor_.reset(NULL);
+      fastAccessor_.reset(NULL);
+
       // See also: http://support.dcmtk.org/wiki/dcmtk/howto/accessing-compressed-data
 
       DicomMap m;
@@ -121,7 +126,7 @@
         Uint8* pixData = NULL;
         if (e->getUint8Array(pixData) == EC_Normal)
         {    
-          accessor_.reset(new DicomIntegerPixelAccessor(m, pixData, e->getLength()));
+          slowAccessor_.reset(new DicomIntegerPixelAccessor(m, pixData, e->getLength()));
         }
       }
       else if (DicomImageDecoder::DecodePsmctRle1(psmct_, dataset))
@@ -133,27 +138,63 @@
           pixData = reinterpret_cast<Uint8*>(&psmct_[0]);
         }
 
-        accessor_.reset(new DicomIntegerPixelAccessor(m, pixData, psmct_.size()));
+        slowAccessor_.reset(new DicomIntegerPixelAccessor(m, pixData, psmct_.size()));
       }
     
-      if (accessor_.get() == NULL)
+      if (slowAccessor_.get() == NULL)
       {
         throw OrthancException(ErrorCode_BadFileFormat);
       }
 
-      accessor_->SetCurrentFrame(frame);
+      slowAccessor_->SetCurrentFrame(frame);
+
+
+      /**
+       * If possible, create a fast ImageAccessor to the image buffer.
+       **/
+
+      
+    }
+
+    unsigned int GetWidth() const
+    {
+      assert(slowAccessor_.get() != NULL);
+      return slowAccessor_->GetInformation().GetWidth();
+    }
+
+    unsigned int GetHeight() const
+    {
+      assert(slowAccessor_.get() != NULL);
+      return slowAccessor_->GetInformation().GetHeight();
+    }
+
+    unsigned int GetBytesPerPixel() const
+    {
+      assert(slowAccessor_.get() != NULL);
+      return slowAccessor_->GetInformation().GetBytesPerPixel();
     }
 
     unsigned int GetChannelCount() const
     {
-      assert(accessor_.get() != NULL);
-      return accessor_->GetChannelCount();
+      assert(slowAccessor_.get() != NULL);
+      return slowAccessor_->GetInformation().GetChannelCount();
     }
 
     const DicomIntegerPixelAccessor GetAccessor() const
     {
-      assert(accessor_.get() != NULL);
-      return *accessor_;
+      assert(slowAccessor_.get() != NULL);
+      return *slowAccessor_;
+    }
+
+    bool HasFastAccessor() const
+    {
+      return fastAccessor_.get() != NULL;
+    }
+
+    const ImageAccessor& GetFastAccessor() const
+    {
+      assert(HasFastAccessor());
+      return *fastAccessor_;
     }
   };
 
@@ -362,12 +403,12 @@
     const PixelType minValue = std::numeric_limits<PixelType>::min();
     const PixelType maxValue = std::numeric_limits<PixelType>::max();
 
-    for (unsigned int y = 0; y < source.GetHeight(); y++)
+    for (unsigned int y = 0; y < source.GetInformation().GetHeight(); y++)
     {
       PixelType* pixel = reinterpret_cast<PixelType*>(target.GetRow(y));
-      for (unsigned int x = 0; x < source.GetWidth(); x++)
+      for (unsigned int x = 0; x < source.GetInformation().GetWidth(); x++)
       {
-        for (unsigned int c = 0; c < source.GetChannelCount(); c++, pixel++)
+        for (unsigned int c = 0; c < source.GetInformation().GetChannelCount(); c++, pixel++)
         {
           int32_t v = source.GetValue(x, y, c);
           if (v < static_cast<int32_t>(minValue))
--- a/OrthancServer/ParsedDicomFile.cpp	Fri Jun 06 10:28:02 2014 +0200
+++ b/OrthancServer/ParsedDicomFile.cpp	Fri Jun 06 11:45:16 2014 +0200
@@ -999,15 +999,15 @@
                                       DicomIntegerPixelAccessor& accessor,
                                       PixelFormat format)
   {
-    assert(accessor.GetChannelCount() == 1);
+    assert(accessor.GetInformation().GetChannelCount() == 1);
 
     PngWriter w;
 
-    std::vector<T> image(accessor.GetWidth() * accessor.GetHeight(), 0);
+    std::vector<T> image(accessor.GetInformation().GetWidth() * accessor.GetInformation().GetHeight(), 0);
     T* pixel = &image[0];
-    for (unsigned int y = 0; y < accessor.GetHeight(); y++)
+    for (unsigned int y = 0; y < accessor.GetInformation().GetHeight(); y++)
     {
-      for (unsigned int x = 0; x < accessor.GetWidth(); x++, pixel++)
+      for (unsigned int x = 0; x < accessor.GetInformation().GetWidth(); x++, pixel++)
       {
         int32_t v = accessor.GetValue(x, y);
         if (v < static_cast<int32_t>(std::numeric_limits<T>::min()))
@@ -1019,8 +1019,8 @@
       }
     }
 
-    w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(),
-                    accessor.GetWidth() * sizeof(T), format, &image[0]);
+    w.WriteToMemory(result, accessor.GetInformation().GetWidth(), accessor.GetInformation().GetHeight(),
+                    accessor.GetInformation().GetWidth() * sizeof(T), format, &image[0]);
   }
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UnitTestsSources/ImageProcessingTests.cpp	Fri Jun 06 11:45:16 2014 +0200
@@ -0,0 +1,44 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege,
+ * 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/>.
+ **/
+
+
+#include "PrecompiledHeadersUnitTests.h"
+#include "gtest/gtest.h"
+
+#include "../Core/ImageFormats/ImageBuffer.h"
+#include "../Core/ImageFormats/ImageProcessing.h"
+
+using namespace Orthanc;
+
+
+TEST(ImageProcessing, Copy)
+{
+}