changeset 863:3c0d0836f704 jpeg

refactoring
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 10 Jun 2014 17:20:33 +0200
parents 5a125d587810
children 2c545bb20dd3
files Core/DicomFormat/DicomImageInformation.cpp Core/Enumerations.h Core/ImageFormats/ImageBuffer.cpp Core/ImageFormats/ImageBuffer.h Core/ImageFormats/ImageProcessing.cpp Core/ImageFormats/ImageProcessing.h OrthancServer/FromDcmtkBridge.cpp OrthancServer/Internals/DicomImageDecoder.cpp OrthancServer/Internals/DicomImageDecoder.h
diffstat 9 files changed, 561 insertions(+), 155 deletions(-) [+]
line wrap: on
line diff
--- a/Core/DicomFormat/DicomImageInformation.cpp	Sat Jun 07 10:51:28 2014 +0200
+++ b/Core/DicomFormat/DicomImageInformation.cpp	Tue Jun 10 17:20:33 2014 +0200
@@ -159,11 +159,6 @@
 
   bool DicomImageInformation::ExtractPixelFormat(PixelFormat& format) const
   {
-    if (IsPlanar())
-    {
-      return false;
-    }
-
     if (GetBitsStored() == 8 && GetChannelCount() == 1 && !IsSigned())
     {
       format = PixelFormat_Grayscale8;
--- a/Core/Enumerations.h	Sat Jun 07 10:51:28 2014 +0200
+++ b/Core/Enumerations.h	Tue Jun 10 17:20:33 2014 +0200
@@ -82,34 +82,34 @@
     /**
      * {summary}{Color image in RGB24 format.}
      * {description}{This format describes a color image. The pixels are stored in 3
-     * consecutive bytes. The memory layout is RGB.
+     * consecutive bytes. The memory layout is RGB.}
      **/
-    PixelFormat_RGB24,
+    PixelFormat_RGB24 = 1,
 
     /**
      * {summary}{Color image in RGBA32 format.}
      * {description}{This format describes a color image. The pixels are stored in 4
-     * consecutive bytes. The memory layout is RGBA.
+     * consecutive bytes. The memory layout is RGBA.}
      **/
-    PixelFormat_RGBA32,
+    PixelFormat_RGBA32 = 2,
 
     /**
      * {summary}{Graylevel 8bpp image.}
      * {description}{The image is graylevel. Each pixel is unsigned and stored in one byte.}
      **/
-    PixelFormat_Grayscale8,
+    PixelFormat_Grayscale8 = 3,
       
     /**
      * {summary}{Graylevel, unsigned 16bpp image.}
      * {description}{The image is graylevel. Each pixel is unsigned and stored in two bytes.}
      **/
-    PixelFormat_Grayscale16,
+    PixelFormat_Grayscale16 = 4,
       
     /**
      * {summary}{Graylevel, signed 16bpp image.}
      * {description}{The image is graylevel. Each pixel is signed and stored in two bytes.}
      **/
-    PixelFormat_SignedGrayscale16
+    PixelFormat_SignedGrayscale16 = 5
   };
 
 
@@ -122,22 +122,22 @@
      * {summary}{Rescaled to 8bpp.}
      * {description}{The minimum value of the image is set to 0, and its maximum value is set to 255.}
      **/
-    ImageExtractionMode_Preview,
+    ImageExtractionMode_Preview = 1,
 
     /**
      * {summary}{Truncation to the [0, 255] range.}
      **/
-    ImageExtractionMode_UInt8,
+    ImageExtractionMode_UInt8 = 2,
 
     /**
      * {summary}{Truncation to the [0, 65535] range.}
      **/
-    ImageExtractionMode_UInt16,
+    ImageExtractionMode_UInt16 = 3,
 
     /**
      * {summary}{Truncation to the [-32768, 32767] range.}
      **/
-    ImageExtractionMode_Int16
+    ImageExtractionMode_Int16 = 4
   };
 
 
--- a/Core/ImageFormats/ImageBuffer.cpp	Sat Jun 07 10:51:28 2014 +0200
+++ b/Core/ImageFormats/ImageBuffer.cpp	Tue Jun 10 17:20:33 2014 +0200
@@ -33,7 +33,10 @@
 #include "../PrecompiledHeaders.h"
 #include "ImageBuffer.h"
 
+#include "../OrthancException.h"
+
 #include <stdio.h>
+#include <stdlib.h>
 
 namespace Orthanc
 {
@@ -41,6 +44,8 @@
   {
     if (changed_)
     {
+      Deallocate();
+
       /*
         if (forceMinimalPitch_)
         {
@@ -49,15 +54,19 @@
       */
 
       pitch_ = GetBytesPerPixel() * width_;
+      size_t size = pitch_ * height_;
 
-      data_.resize(pitch_ * height_);
-      if (data_.size() > 0)
+      if (size == 0)
       {
-        buffer_ = &data_[0];
+        buffer_ = NULL;
       }
       else
       {
-        buffer_ = 0;
+        buffer_ = malloc(size);
+        if (buffer_ == NULL)
+        {
+          throw OrthancException(ErrorCode_NotEnoughMemory);
+        }
       }
 
       changed_ = false;
@@ -65,6 +74,17 @@
   }
 
 
+  void ImageBuffer::Deallocate()
+  {
+    if (buffer_ != NULL)
+    {
+      free(buffer_);
+      buffer_ = NULL;
+      changed_ = true;
+    }
+  }
+
+
   ImageBuffer::ImageBuffer(unsigned int width,
                            unsigned int height,
                            PixelFormat format)
@@ -90,22 +110,31 @@
 
   void ImageBuffer::SetFormat(PixelFormat format)
   {
-    changed_ = true;
-    format_ = format;
+    if (format != format_)
+    {
+      changed_ = true;
+      format_ = format;
+    }
   }
 
 
   void ImageBuffer::SetWidth(unsigned int width)
   {
-    changed_ = true;
-    width_ = width;     
+    if (width != width_)
+    {
+      changed_ = true;
+      width_ = width;     
+    }
   }
 
 
   void ImageBuffer::SetHeight(unsigned int height)
   {
-    changed_ = true;
-    height_ = height;     
+    if (height != height_)
+    {
+      changed_ = true;
+      height_ = height;     
+    }
   }
 
 
@@ -131,7 +160,33 @@
 
   void ImageBuffer::SetMinimalPitchForced(bool force)
   {
-    changed_ = true;
-    forceMinimalPitch_ = force;
+    if (force != forceMinimalPitch_)
+    {
+      changed_ = true;
+      forceMinimalPitch_ = force;
+    }
+  }
+
+
+  void ImageBuffer::AcquireOwnership(ImageBuffer& other)
+  {
+    // Remove the content of the current image
+    Deallocate();
+
+    // Force the allocation of the other image (if not already
+    // allocated)
+    other.Allocate();
+
+    // Transfer the content of the other image
+    changed_ = false;
+    forceMinimalPitch_ = other.forceMinimalPitch_;
+    format_ = other.format_;
+    width_ = other.width_;
+    height_ = other.height_;
+    pitch_ = other.pitch_;
+    buffer_ = other.buffer_;
+
+    // Force the reinitialization of the other image
+    other.Initialize();
   }
 }
--- a/Core/ImageFormats/ImageBuffer.h	Sat Jun 07 10:51:28 2014 +0200
+++ b/Core/ImageFormats/ImageBuffer.h	Tue Jun 10 17:20:33 2014 +0200
@@ -36,26 +36,28 @@
 
 #include <vector>
 #include <stdint.h>
+#include <boost/noncopyable.hpp>
 
 namespace Orthanc
 {
-  class ImageBuffer
+  class ImageBuffer : public boost::noncopyable
   {
   private:
     bool changed_;
-    std::vector<uint8_t> data_;
 
     bool forceMinimalPitch_;  // Currently unused
     PixelFormat format_;
     unsigned int width_;
     unsigned int height_;
     unsigned int pitch_;
-    uint8_t *buffer_;
+    void *buffer_;
 
     void Initialize();
     
     void Allocate();
 
+    void Deallocate();
+
   public:
     ImageBuffer(unsigned int width,
                 unsigned int height,
@@ -66,6 +68,11 @@
       Initialize();
     }
 
+    ~ImageBuffer()
+    {
+      Deallocate();
+    }
+
     PixelFormat GetFormat() const
     {
       return format_;
@@ -102,5 +109,7 @@
     }
 
     void SetMinimalPitchForced(bool force);
+
+    void AcquireOwnership(ImageBuffer& other);
   };
 }
--- a/Core/ImageFormats/ImageProcessing.cpp	Sat Jun 07 10:51:28 2014 +0200
+++ b/Core/ImageFormats/ImageProcessing.cpp	Tue Jun 10 17:20:33 2014 +0200
@@ -35,13 +35,13 @@
 
 #include "../OrthancException.h"
 
+#include <boost/math/special_functions/round.hpp>
+
 #include <cassert>
 #include <string.h>
 #include <limits>
 #include <stdint.h>
 
-#include <stdio.h>
-
 namespace Orthanc
 {
   template <typename TargetType, typename SourceType>
@@ -75,6 +75,167 @@
   }
 
 
+  template <typename PixelType>
+  static void SetInternal(ImageAccessor& image,
+                          int64_t constant)
+  {
+    for (unsigned int y = 0; y < image.GetHeight(); y++)
+    {
+      PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
+
+      for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
+      {
+        *p = static_cast<PixelType>(constant);
+      }
+    }
+  }
+
+
+  template <typename PixelType>
+  static void GetMinMaxValueInternal(PixelType& minValue,
+                                     PixelType& maxValue,
+                                     const ImageAccessor& source)
+  {
+    // Deal with the special case of empty image
+    if (source.GetWidth() == 0 ||
+        source.GetHeight() == 0)
+    {
+      minValue = 0;
+      maxValue = 0;
+      return;
+    }
+
+    minValue = std::numeric_limits<PixelType>::max();
+    maxValue = std::numeric_limits<PixelType>::min();
+
+    for (unsigned int y = 0; y < source.GetHeight(); y++)
+    {
+      const PixelType* p = reinterpret_cast<const PixelType*>(source.GetConstRow(y));
+
+      for (unsigned int x = 0; x < source.GetWidth(); x++, p++)
+      {
+        if (*p < minValue)
+        {
+          minValue = *p;
+        }
+
+        if (*p > maxValue)
+        {
+          maxValue = *p;
+        }
+      }
+    }
+  }
+
+
+
+  template <typename PixelType>
+  static void AddConstantInternal(ImageAccessor& image,
+                                  int64_t constant)
+  {
+    if (constant == 0)
+    {
+      return;
+    }
+
+    const int64_t minValue = std::numeric_limits<PixelType>::min();
+    const int64_t maxValue = std::numeric_limits<PixelType>::max();
+
+    for (unsigned int y = 0; y < image.GetHeight(); y++)
+    {
+      PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
+
+      for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
+      {
+        int64_t v = static_cast<int64_t>(*p) + constant;
+
+        if (v > maxValue)
+        {
+          *p = maxValue;
+        }
+        else if (v < minValue)
+        {
+          *p = minValue;
+        }
+        else
+        {
+          *p = static_cast<PixelType>(v);
+        }
+      }
+    }
+  }
+
+
+
+  template <typename PixelType>
+  void MultiplyConstantInternal(ImageAccessor& image,
+                                float factor)
+  {
+    if (abs(factor - 1.0f) <= std::numeric_limits<float>::epsilon())
+    {
+      return;
+    }
+
+    const int64_t minValue = std::numeric_limits<PixelType>::min();
+    const int64_t maxValue = std::numeric_limits<PixelType>::max();
+
+    for (unsigned int y = 0; y < image.GetHeight(); y++)
+    {
+      PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
+
+      for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
+      {
+        int64_t v = boost::math::llround(static_cast<float>(*p) * factor);
+
+        if (v > maxValue)
+        {
+          *p = maxValue;
+        }
+        else if (v < minValue)
+        {
+          *p = minValue;
+        }
+        else
+        {
+          *p = static_cast<PixelType>(v);
+        }
+      }
+    }
+  }
+
+
+  template <typename PixelType>
+  void ShiftScaleInternal(ImageAccessor& image,
+                          float offset,
+                          float scaling)
+  {
+    const float minValue = static_cast<float>(std::numeric_limits<PixelType>::min());
+    const float maxValue = static_cast<float>(std::numeric_limits<PixelType>::max());
+
+    for (unsigned int y = 0; y < image.GetHeight(); y++)
+    {
+      PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
+
+      for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
+      {
+        float v = (static_cast<float>(*p) + offset) * scaling;
+
+        if (v > maxValue)
+        {
+          *p = std::numeric_limits<PixelType>::max();
+        }
+        else if (v < minValue)
+        {
+          *p = std::numeric_limits<PixelType>::min();
+        }
+        else
+        {
+          *p = static_cast<PixelType>(boost::math::iround(v));
+        }
+      }
+    }
+  }
+
 
   void ImageProcessing::Copy(ImageAccessor& target,
                              const ImageAccessor& source)
@@ -133,31 +294,7 @@
     if (target.GetFormat() == PixelFormat_Grayscale8 &&
         source.GetFormat() == PixelFormat_Grayscale16)
     {
-      printf("ICI2\n");
       ConvertInternal<uint8_t, uint16_t>(target, source);
-
-      /*for (unsigned int y = 0; y < source.GetHeight(); y++)
-      {
-        uint8_t* t = reinterpret_cast<uint8_t*>(target.GetRow(y));
-        const uint16_t* s = reinterpret_cast<const uint16_t*>(source.GetConstRow(y));
-
-        for (unsigned int x = 0; x < source.GetWidth(); x++, t++, s++)
-        {
-          if (*s < 0)
-          {
-            *t = 0;
-          }
-          else if (*s > 255)
-          {
-            *t = 255;
-          }
-          else
-          {
-            *t = static_cast<uint8_t>(*s);
-          }
-        }
-        }*/
-        
       return;
     }
 
@@ -186,6 +323,30 @@
   }
 
 
+
+  void ImageProcessing::Set(ImageAccessor& image,
+                            int64_t value)
+  {
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        SetInternal<uint8_t>(image, value);
+        return;
+
+      case PixelFormat_Grayscale16:
+        SetInternal<uint16_t>(image, value);
+        return;
+
+      case PixelFormat_SignedGrayscale16:
+        SetInternal<int16_t>(image, value);
+        return;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+
   void ImageProcessing::ShiftRight(ImageAccessor& image,
                                    unsigned int shift)
   {
@@ -200,4 +361,113 @@
     throw OrthancException(ErrorCode_NotImplemented);
   }
 
+
+  void ImageProcessing::GetMinMaxValue(int64_t& minValue,
+                                       int64_t& maxValue,
+                                       const ImageAccessor& image)
+  {
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+      {
+        uint8_t a, b;
+        GetMinMaxValueInternal<uint8_t>(a, b, image);
+        minValue = a;
+        maxValue = b;
+        break;
+      }
+
+      case PixelFormat_Grayscale16:
+      {
+        uint16_t a, b;
+        GetMinMaxValueInternal<uint16_t>(a, b, image);
+        minValue = a;
+        maxValue = b;
+        break;
+      }
+
+      case PixelFormat_SignedGrayscale16:
+      {
+        int16_t a, b;
+        GetMinMaxValueInternal<int16_t>(a, b, image);
+        minValue = a;
+        maxValue = b;
+        break;
+      }
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+
+
+  void ImageProcessing::AddConstant(ImageAccessor& image,
+                                    int64_t value)
+  {
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        AddConstantInternal<uint8_t>(image, value);
+        return;
+
+      case PixelFormat_Grayscale16:
+        AddConstantInternal<uint16_t>(image, value);
+        return;
+
+      case PixelFormat_SignedGrayscale16:
+        AddConstantInternal<int16_t>(image, value);
+        return;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+
+  void ImageProcessing::MultiplyConstant(ImageAccessor& image,
+                                         float factor)
+  {
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        MultiplyConstantInternal<uint8_t>(image, factor);
+        return;
+
+      case PixelFormat_Grayscale16:
+        MultiplyConstantInternal<uint16_t>(image, factor);
+        return;
+
+      case PixelFormat_SignedGrayscale16:
+        MultiplyConstantInternal<int16_t>(image, factor);
+        return;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+
+  void ImageProcessing::ShiftScale(ImageAccessor& image,
+                                   float offset,
+                                   float scaling)
+  {
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        ShiftScaleInternal<uint8_t>(image, offset, scaling);
+        return;
+
+      case PixelFormat_Grayscale16:
+        ShiftScaleInternal<uint16_t>(image, offset, scaling);
+        return;
+
+      case PixelFormat_SignedGrayscale16:
+        ShiftScaleInternal<int16_t>(image, offset, scaling);
+        return;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
 }
--- a/Core/ImageFormats/ImageProcessing.h	Sat Jun 07 10:51:28 2014 +0200
+++ b/Core/ImageFormats/ImageProcessing.h	Tue Jun 10 17:20:33 2014 +0200
@@ -34,6 +34,8 @@
 
 #include "ImageAccessor.h"
 
+#include <stdint.h>
+
 namespace Orthanc
 {
   class ImageProcessing
@@ -45,7 +47,24 @@
     static void Convert(ImageAccessor& target,
                         const ImageAccessor& source);
 
+    static void Set(ImageAccessor& image,
+                    int64_t value);
+
     static void ShiftRight(ImageAccessor& target,
                            unsigned int shift);
+
+    static void GetMinMaxValue(int64_t& minValue,
+                               int64_t& maxValue,
+                               const ImageAccessor& image);
+
+    static void AddConstant(ImageAccessor& image,
+                            int64_t value);
+
+    static void MultiplyConstant(ImageAccessor& image,
+                                 float factor);
+
+    static void ShiftScale(ImageAccessor& image,
+                           float offset,
+                           float scaling);
   };
 }
--- a/OrthancServer/FromDcmtkBridge.cpp	Sat Jun 07 10:51:28 2014 +0200
+++ b/OrthancServer/FromDcmtkBridge.cpp	Tue Jun 10 17:20:33 2014 +0200
@@ -527,21 +527,45 @@
                                         ImageExtractionMode mode)
   {
     // TODO CONTINUE THIS
-    if (mode == ImageExtractionMode_UInt8)
+
+    ImageBuffer tmp;
+    bool ok = false;
+
+    switch (mode)
     {
-      printf(">>>>>>>>\n");
-      ImageBuffer tmp;
-      if (DicomImageDecoder::Decode(tmp, dataset, frame, PixelFormat_Grayscale8, DicomImageDecoder::Mode_Truncate))
-      {
-        ImageAccessor accessor(tmp.GetAccessor());
-        PngWriter writer;
-        writer.WriteToMemory(result, accessor);
-        printf("<<<<<<<< OK\n");
-        return;
-      }
-      printf("<<<<<<<< FAILURE\n");
+      case ImageExtractionMode_UInt8:
+        ok = DicomImageDecoder::DecodeAndTruncate(tmp, dataset, frame, PixelFormat_Grayscale8);
+        break;
+
+      case ImageExtractionMode_UInt16:
+        ok = DicomImageDecoder::DecodeAndTruncate(tmp, dataset, frame, PixelFormat_Grayscale16);
+        break;
+
+      case ImageExtractionMode_Int16:
+        ok = DicomImageDecoder::DecodeAndTruncate(tmp, dataset, frame, PixelFormat_SignedGrayscale16);
+        break;
+
+      case ImageExtractionMode_Preview:
+        ok = DicomImageDecoder::DecodePreview(tmp, dataset, frame);
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
 
+    if (ok)
+    {
+      ImageAccessor accessor(tmp.GetAccessor());
+      PngWriter writer;
+      writer.WriteToMemory(result, accessor);
+      return;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+
     // See also: http://support.dcmtk.org/wiki/dcmtk/howto/accessing-compressed-data
 
     std::auto_ptr<DicomIntegerPixelAccessor> accessor;
--- a/OrthancServer/Internals/DicomImageDecoder.cpp	Sat Jun 07 10:51:28 2014 +0200
+++ b/OrthancServer/Internals/DicomImageDecoder.cpp	Tue Jun 10 17:20:33 2014 +0200
@@ -42,38 +42,38 @@
   Program: GDCM (Grassroots DICOM). A DICOM library
   Module:  http://gdcm.sourceforge.net/Copyright.html
 
-Copyright (c) 2006-2011 Mathieu Malaterre
-Copyright (c) 1993-2005 CREATIS
-(CREATIS = Centre de Recherche et d'Applications en Traitement de l'Image)
-All rights reserved.
+  Copyright (c) 2006-2011 Mathieu Malaterre
+  Copyright (c) 1993-2005 CREATIS
+  (CREATIS = Centre de Recherche et d'Applications en Traitement de l'Image)
+  All rights reserved.
 
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
 
- * Redistributions of source code must retain the above copyright notice,
-   this list of conditions and the following disclaimer.
+  * Redistributions of source code must retain the above copyright notice,
+  this list of conditions and the following disclaimer.
 
- * Redistributions in binary form must reproduce the above copyright notice,
-   this list of conditions and the following disclaimer in the documentation
-   and/or other materials provided with the distribution.
+  * Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
 
- * Neither name of Mathieu Malaterre, or CREATIS, nor the names of any
-   contributors (CNRS, INSERM, UCB, Universite Lyon I), may be used to
-   endorse or promote products derived from this software without specific
-   prior written permission.
+  * Neither name of Mathieu Malaterre, or CREATIS, nor the names of any
+  contributors (CNRS, INSERM, UCB, Universite Lyon I), may be used to
+  endorse or promote products derived from this software without specific
+  prior written permission.
 
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
+  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-=========================================================================*/
+  =========================================================================*/
 
 
 
@@ -426,7 +426,8 @@
 
     bool fastVersionSuccess = false;
     PixelFormat sourceFormat;
-    if (info.ExtractPixelFormat(sourceFormat))
+    if (!info.IsPlanar() &&
+        info.ExtractPixelFormat(sourceFormat))
     {
       try
       {
@@ -460,19 +461,19 @@
         case PixelFormat_RGB24:
         case PixelFormat_RGBA32:
         case PixelFormat_Grayscale8:
-        CopyPixels<uint8_t>(targetAccessor, source.GetAccessor());
-        break;
+          CopyPixels<uint8_t>(targetAccessor, source.GetAccessor());
+          break;
         
         case PixelFormat_Grayscale16:
-        CopyPixels<uint16_t>(targetAccessor, source.GetAccessor());
-        break;
+          CopyPixels<uint16_t>(targetAccessor, source.GetAccessor());
+          break;
 
         case PixelFormat_SignedGrayscale16:
-        CopyPixels<int16_t>(targetAccessor, source.GetAccessor());
-        break;
+          CopyPixels<int16_t>(targetAccessor, source.GetAccessor());
+          break;
 
         default:
-        throw OrthancException(ErrorCode_InternalError);
+          throw OrthancException(ErrorCode_InternalError);
       }
     }
   }
@@ -584,53 +585,97 @@
   }
 
 
-  bool DicomImageDecoder::Decode(ImageBuffer& target,
-                                 DcmDataset& dataset,
-                                 unsigned int frame,
-                                 PixelFormat format,
-                                 Mode mode)
+  bool DicomImageDecoder::DecodeAndTruncate(ImageBuffer& target,
+                                            DcmDataset& dataset,
+                                            unsigned int frame,
+                                            PixelFormat format)
   {
-    // TODO OPTIMIZE THIS (avoid unnecessary image copies) !!!
+    // TODO Special case for uncompressed images
+    
+    ImageBuffer source;
+    if (!Decode(source, dataset, frame))
+    {
+      return false;
+    }
+
+    if (source.GetFormat() == format)
+    {
+      // No conversion is required, return the temporary image
+      target.AcquireOwnership(source);
+      return true;
+    }
 
-    ImageBuffer tmp;
-    if (!Decode(tmp, dataset, frame))
+    target.SetFormat(format);
+    target.SetWidth(source.GetWidth());
+    target.SetHeight(source.GetHeight());
+
+    ImageAccessor targetAccessor(target.GetAccessor());
+    ImageAccessor sourceAccessor(source.GetAccessor());
+    ImageProcessing::Convert(targetAccessor, sourceAccessor);
+
+    return true;
+  }
+
+
+  bool DicomImageDecoder::DecodePreview(ImageBuffer& target,
+                                        DcmDataset& dataset,
+                                        unsigned int frame)
+  {
+    // TODO Special case for uncompressed images
+    
+    ImageBuffer source;
+    if (!Decode(source, dataset, frame))
     {
       return false;
     }
 
-    if (!IsUncompressedImage(dataset) && !IsJpegLossless(dataset))
+    switch (source.GetFormat())
     {
-      printf("ICI\n");
-      PngWriter w;
-      ImageAccessor b(tmp.GetConstAccessor());
-      w.WriteToFile("toto.png", b);
-    }
-
-    target.SetFormat(format);
-    target.SetWidth(tmp.GetWidth());
-    target.SetHeight(tmp.GetHeight());
-
-    switch (mode)
-    {
-      case Mode_Truncate:
+      case PixelFormat_RGB24:
       {
-        if (!IsUncompressedImage(dataset) && !IsJpegLossless(dataset))
-        {
-          printf("%d => %d\n", tmp.GetFormat(), target.GetFormat());
-        }
-        ImageAccessor a(target.GetAccessor());
-        ImageAccessor b(tmp.GetConstAccessor());
-        ImageProcessing::Convert(a, b);
+        // Directly return color images (RGB)
+        target.AcquireOwnership(source);
         return true;
       }
 
+      case PixelFormat_Grayscale8:
+      case PixelFormat_Grayscale16:
+      case PixelFormat_SignedGrayscale16:
+      {
+        // Grayscale image: Stretch its dynamics to the [0,255] range
+        target.SetFormat(PixelFormat_Grayscale8);
+        target.SetWidth(source.GetWidth());
+        target.SetHeight(source.GetHeight());
+
+        ImageAccessor targetAccessor(target.GetAccessor());
+        ImageAccessor sourceAccessor(source.GetAccessor());
+
+        int64_t a, b;
+        ImageProcessing::GetMinMaxValue(a, b, sourceAccessor);
+        
+        if (a == b)
+        {
+          ImageProcessing::Set(targetAccessor, 0);
+        }
+        else
+        {
+          ImageProcessing::ShiftScale(sourceAccessor, -a, 255.0f / static_cast<float>(b - a));
+
+          if (source.GetFormat() == PixelFormat_Grayscale8)
+          {
+            target.AcquireOwnership(source);
+          }
+          else
+          {
+            ImageProcessing::Convert(targetAccessor, sourceAccessor);
+          }
+        }
+
+        return true;
+      }
+      
       default:
         throw OrthancException(ErrorCode_NotImplemented);
     }
-
-    return false;
   }
-
-
-
 }
--- a/OrthancServer/Internals/DicomImageDecoder.h	Sat Jun 07 10:51:28 2014 +0200
+++ b/OrthancServer/Internals/DicomImageDecoder.h	Tue Jun 10 17:20:33 2014 +0200
@@ -40,13 +40,6 @@
 {
   class DicomImageDecoder
   {
-  public:
-    enum Mode
-    {
-      Mode_Truncate,
-      Mode_Stretch
-    };
-
   private:
     class ImageSource;
 
@@ -72,12 +65,6 @@
                                         DcmDataset& dataset,
                                         unsigned int frame);
 
-    static void DecodeUncompressedImage(ImageBuffer& target,
-                                        DcmDataset& dataset,
-                                        unsigned int frame,
-                                        PixelFormat format,
-                                        Mode mode);
-
 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
     static void DecodeJpegLossless(ImageBuffer& target,
                                    DcmDataset& dataset,
@@ -88,11 +75,13 @@
                        DcmDataset& dataset,
                        unsigned int frame);
 
-    static bool Decode(ImageBuffer& target,
-                       DcmDataset& dataset,
-                       unsigned int frame,
-                       PixelFormat format,
-                       Mode mode);
+    static bool DecodeAndTruncate(ImageBuffer& target,
+                                  DcmDataset& dataset,
+                                  unsigned int frame,
+                                  PixelFormat format);
 
+    static bool DecodePreview(ImageBuffer& target,
+                              DcmDataset& dataset,
+                              unsigned int frame);
   };
 }