changeset 854:ff530685e46a jpeg

fast version of image copy
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 06 Jun 2014 12:35:08 +0200
parents 839be3022203
children 6d89d5a4a723
files Core/DicomFormat/DicomImageInformation.cpp Core/DicomFormat/DicomImageInformation.h Core/DicomFormat/DicomIntegerPixelAccessor.cpp Core/DicomFormat/DicomIntegerPixelAccessor.h Core/ImageFormats/ImageProcessing.cpp Core/ImageFormats/ImageProcessing.h LinuxCompilation.txt OrthancServer/Internals/DicomImageDecoder.cpp
diffstat 8 files changed, 149 insertions(+), 91 deletions(-) [+]
line wrap: on
line diff
--- a/Core/DicomFormat/DicomImageInformation.cpp	Fri Jun 06 11:45:16 2014 +0200
+++ b/Core/DicomFormat/DicomImageInformation.cpp	Fri Jun 06 12:35:08 2014 +0200
@@ -55,11 +55,19 @@
     {
       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
       {
+        samplesPerPixel_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_SAMPLES_PER_PIXEL).AsString());
+      }
+      catch (OrthancException&)
+      {
+        samplesPerPixel_ = 1;  // Assume 1 color channel
+      }
+
+      try
+      {
         bitsStored_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_STORED).AsString());
       }
       catch (OrthancException&)
@@ -147,4 +155,39 @@
     isPlanar_ = (planarConfiguration != 0 ? true : false);
     isSigned_ = (pixelRepresentation != 0 ? true : false);
   }
+
+
+  bool DicomImageInformation::ExtractPixelFormat(PixelFormat& format) const
+  {
+    if (IsPlanar())
+    {
+      return false;
+    }
+
+    if (GetBitsStored() == 8 && GetChannelCount() == 1 && !IsSigned())
+    {
+      format = PixelFormat_Grayscale8;
+      return true;
+    }
+
+    if (GetBitsStored() == 8 && GetChannelCount() == 3 && !IsSigned())
+    {
+      format = PixelFormat_RGB24;
+      return true;
+    }
+
+    if (GetBitsStored() >= 9 && GetBitsStored() <= 16 && GetChannelCount() == 1 && !IsSigned())
+    {
+      format = PixelFormat_Grayscale16;
+      return true;
+    }
+
+    if (GetBitsStored() >= 9 && GetBitsStored() <= 16 && GetChannelCount() == 1 && IsSigned())
+    {
+      format = PixelFormat_SignedGrayscale16;
+      return true;
+    }
+
+    return false;
+  }
 }
--- a/Core/DicomFormat/DicomImageInformation.h	Fri Jun 06 11:45:16 2014 +0200
+++ b/Core/DicomFormat/DicomImageInformation.h	Fri Jun 06 12:35:08 2014 +0200
@@ -102,11 +102,6 @@
       return highBit_;
     }
 
-    unsigned int GetSamplesPerPixel() const
-    {
-      return samplesPerPixel_;
-    }
-
     bool IsPlanar() const
     {
       return isPlanar_;
@@ -116,5 +111,7 @@
     {
       return highBit_ + 1 - bitsStored_;
     }
+
+    bool ExtractPixelFormat(PixelFormat& format) const;
   };
 }
--- a/Core/DicomFormat/DicomIntegerPixelAccessor.cpp	Fri Jun 06 11:45:16 2014 +0200
+++ b/Core/DicomFormat/DicomIntegerPixelAccessor.cpp	Fri Jun 06 12:35:08 2014 +0200
@@ -55,7 +55,7 @@
   {
     frame_ = 0;
     frameOffset_ = (information_.GetHeight() * information_.GetWidth() * 
-                    information_.GetBytesPerPixel() * information_.GetSamplesPerPixel());
+                    information_.GetBytesPerPixel() * information_.GetChannelCount());
 
     if (information_.GetNumberOfFrames() * frameOffset_ > size)
     {
@@ -83,7 +83,7 @@
        * means the order of the pixel values sent shall be R1, G1, B1,
        * R2, G2, B2, ..., etc.
        **/
-      rowOffset_ = information_.GetWidth() * information_.GetBytesPerPixel() * information_.GetSamplesPerPixel();
+      rowOffset_ = information_.GetWidth() * information_.GetBytesPerPixel() * information_.GetChannelCount();
     }
     else
     {
@@ -130,7 +130,7 @@
                                               unsigned int y,
                                               unsigned int channel) const
   {
-    assert(x < information_.GetWidth() && y < information_.GetHeight() && channel < information_.GetSamplesPerPixel());
+    assert(x < information_.GetWidth() && y < information_.GetHeight() && channel < information_.GetChannelCount());
     
     const uint8_t* pixel = reinterpret_cast<const uint8_t*>(pixelData_) + 
       y * rowOffset_ + frame_ * frameOffset_;
@@ -144,7 +144,7 @@
        * means the order of the pixel values sent shall be R1, G1, B1,
        * R2, G2, B2, ..., etc.
        **/
-      pixel += channel * information_.GetBytesPerPixel() + x * information_.GetSamplesPerPixel() * information_.GetBytesPerPixel();
+      pixel += channel * information_.GetBytesPerPixel() + x * information_.GetChannelCount() * information_.GetBytesPerPixel();
     }
     else
     {
@@ -153,8 +153,8 @@
        * this means the order of the pixel values sent is R1, R2, R3,
        * ..., G1, G2, G3, ..., B1, B2, B3, etc.
        **/
-      assert(frameOffset_ % information_.GetSamplesPerPixel() == 0);
-      pixel += channel * frameOffset_ / information_.GetSamplesPerPixel() + x * information_.GetBytesPerPixel();
+      assert(frameOffset_ % information_.GetChannelCount() == 0);
+      pixel += channel * frameOffset_ / information_.GetChannelCount() + x * information_.GetBytesPerPixel();
     }
 
     uint32_t v;
--- a/Core/DicomFormat/DicomIntegerPixelAccessor.h	Fri Jun 06 11:45:16 2014 +0200
+++ b/Core/DicomFormat/DicomIntegerPixelAccessor.h	Fri Jun 06 12:35:08 2014 +0200
@@ -75,5 +75,10 @@
                           int32_t& max) const;
 
     int32_t GetValue(unsigned int x, unsigned int y, unsigned int channel = 0) const;
+
+    const void* GetPixelData() const
+    {
+      return pixelData_;
+    }
   };
 }
--- a/Core/ImageFormats/ImageProcessing.cpp	Fri Jun 06 11:45:16 2014 +0200
+++ b/Core/ImageFormats/ImageProcessing.cpp	Fri Jun 06 12:35:08 2014 +0200
@@ -60,10 +60,11 @@
 
     for (unsigned int y = 0; y < source.GetHeight(); y++)
     {
-      memcpy(target.GetRow(y), source.GetRow(y), lineSize);
+      memcpy(target.GetRow(y), source.GetConstRow(y), lineSize);
     }
   }
 
+
   void ImageProcessing::Convert(ImageAccessor& target,
                                 const ImageAccessor& source)
   {
@@ -78,5 +79,23 @@
       Copy(target, source);
       return;
     }
+
+    throw OrthancException(ErrorCode_NotImplemented);
   }
+
+
+  void ImageProcessing::ShiftRight(ImageAccessor& image,
+                                   unsigned int shift)
+  {
+    if (image.GetWidth() == 0 ||
+        image.GetHeight() == 0 ||
+        shift == 0)
+    {
+      // Nothing to do
+      return;
+    }
+
+    throw OrthancException(ErrorCode_NotImplemented);
+  }
+
 }
--- a/Core/ImageFormats/ImageProcessing.h	Fri Jun 06 11:45:16 2014 +0200
+++ b/Core/ImageFormats/ImageProcessing.h	Fri Jun 06 12:35:08 2014 +0200
@@ -44,5 +44,8 @@
 
     static void Convert(ImageAccessor& target,
                         const ImageAccessor& source);
+
+    static void ShiftRight(ImageAccessor& target,
+                           unsigned int shift);
   };
 }
--- a/LinuxCompilation.txt	Fri Jun 06 11:45:16 2014 +0200
+++ b/LinuxCompilation.txt	Fri Jun 06 12:35:08 2014 +0200
@@ -136,24 +136,25 @@
        	       	       libsqlite3-dev libssl-dev zlib1g-dev \
        	       	       libdcmtk2-dev libboost-all-dev libwrap0-dev libcharls-dev
 
-With JPEG Lossless:
+With JPEG:
 
 # cmake "-DDCMTK_LIBRARIES=CharLS;dcmjpls;wrap;oflog" \
         -DALLOW_DOWNLOADS=ON \
 	-DUSE_SYSTEM_MONGOOSE=OFF \
 	-DUSE_SYSTEM_JSONCPP=OFF \
         -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \
-        -DENABLE_JPEG_LOSSLESS=ON \
 	~/Orthanc
 
 
-Without JPEG Lossless:
+Without JPEG:
 
 # cmake "-DDCMTK_LIBRARIES=wrap;oflog" \
         -DALLOW_DOWNLOADS=ON \
 	-DUSE_SYSTEM_MONGOOSE=OFF \
 	-DUSE_SYSTEM_JSONCPP=OFF \
         -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \
+        -DENABLE_JPEG=OFF \
+        -DENABLE_JPEG_LOSSLESS=OFF \
 	~/Orthanc
 
 
--- a/OrthancServer/Internals/DicomImageDecoder.cpp	Fri Jun 06 11:45:16 2014 +0200
+++ b/OrthancServer/Internals/DicomImageDecoder.cpp	Fri Jun 06 12:35:08 2014 +0200
@@ -78,6 +78,7 @@
 
 
 #include "../../Core/OrthancException.h"
+#include "../../Core/ImageFormats/ImageProcessing.h"
 #include "../../Core/DicomFormat/DicomIntegerPixelAccessor.h"
 #include "../ToDcmtkBridge.h"
 #include "../FromDcmtkBridge.h"
@@ -180,7 +181,7 @@
       return slowAccessor_->GetInformation().GetChannelCount();
     }
 
-    const DicomIntegerPixelAccessor GetAccessor() const
+    const DicomIntegerPixelAccessor& GetAccessor() const
     {
       assert(slowAccessor_.get() != NULL);
       return *slowAccessor_;
@@ -313,67 +314,23 @@
   void DicomImageDecoder::SetupImageBuffer(ImageBuffer& target,
                                            DcmDataset& dataset)
   {
-    OFString value;
-
-    if (!dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_COLUMNS), value).good())
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-
-    unsigned int width = boost::lexical_cast<unsigned int>(value.c_str());
-
-    if (!dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_ROWS), value).good())
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-
-    unsigned int height = boost::lexical_cast<unsigned int>(value.c_str());
-
-    if (!dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_BITS_STORED), value).good())
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-
-    unsigned int bitsStored = boost::lexical_cast<unsigned int>(value.c_str());
-
-    if (!dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_PIXEL_REPRESENTATION), value).good())
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-
-    bool isSigned = (boost::lexical_cast<unsigned int>(value.c_str()) != 0);
+    DicomMap m;
+    FromDcmtkBridge::Convert(m, dataset);
 
-    unsigned int samplesPerPixel = 1; // By default
-    if (dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_SAMPLES_PER_PIXEL), value).good())
-    {
-      samplesPerPixel = boost::lexical_cast<unsigned int>(value.c_str());
-    }
-
-    target.SetHeight(height);
-    target.SetWidth(width);
-
-    if (bitsStored == 8 && samplesPerPixel == 1 && !isSigned)
-    {
-      target.SetFormat(PixelFormat_Grayscale8);
-    }
-    else if (bitsStored == 8 && samplesPerPixel == 3 && !isSigned)
+    DicomImageInformation info(m);
+    PixelFormat format;
+    
+    if (!info.ExtractPixelFormat(format))
     {
-      target.SetFormat(PixelFormat_RGB24);
-    }
-    else if (bitsStored >= 9 && bitsStored <= 16 && samplesPerPixel == 1 && !isSigned)
-    {
-      target.SetFormat(PixelFormat_Grayscale16);
-    }
-    else if (bitsStored >= 9 && bitsStored <= 16 && samplesPerPixel == 1 && isSigned)
-    {
-      target.SetFormat(PixelFormat_SignedGrayscale16);
-    }
-    else
-    {
-      LOG(WARNING) << "Unsupported DICOM image: " << bitsStored << "bpp, " 
-      << samplesPerPixel << " channels, " << (isSigned ? "signed" : "unsigned");
+      LOG(WARNING) << "Unsupported DICOM image: " << info.GetBitsStored() 
+                   << "bpp, " << info.GetChannelCount() << " channels, " 
+                   << (info.IsSigned() ? "signed" : "unsigned");
       throw OrthancException(ErrorCode_NotImplemented);
     }
+    
+    target.SetHeight(info.GetHeight());
+    target.SetWidth(info.GetWidth());
+    target.SetFormat(format);
   }
 
 
@@ -455,8 +412,8 @@
 
     SetupImageBuffer(target, dataset);
 
-    if (target.GetWidth() != target.GetWidth() ||
-        target.GetHeight() != target.GetHeight())
+    if (source.GetWidth() != target.GetWidth() ||
+        source.GetHeight() != target.GetHeight())
     {
       throw OrthancException(ErrorCode_InternalError);
     }
@@ -490,30 +447,63 @@
 
 
     /**
+     * If the format of the DICOM buffer is natively supported, use a
+     * direct access to copy its values.
+     **/
+
+    ImageAccessor targetAccessor(target.GetAccessor());
+    const DicomImageInformation& info = source.GetAccessor().GetInformation();
+
+    bool fastVersionSuccess = false;
+    PixelFormat sourceFormat;
+    if (info.ExtractPixelFormat(sourceFormat))
+    {
+      try
+      {
+        ImageAccessor sourceImage;
+        sourceImage.AssignReadOnly(sourceFormat, 
+                                   info.GetWidth(), 
+                                   info.GetHeight(),
+                                   info.GetWidth() * info.GetBytesPerPixel(),
+                                   source.GetAccessor().GetPixelData());                                   
+
+        ImageProcessing::Convert(targetAccessor, sourceImage);
+        ImageProcessing::ShiftRight(targetAccessor, info.GetShift());
+        fastVersionSuccess = true;
+      }
+      catch (OrthancException&)
+      {
+        // Unsupported conversion
+      }
+    }
+
+
+    /**
      * Loop over the DICOM buffer, storing its value into the target
      * image.
      **/
 
-    ImageAccessor accessor(target.GetAccessor());
-
-    switch (target.GetFormat())
+    if (!fastVersionSuccess)
     {
-      case PixelFormat_RGB24:
-      case PixelFormat_RGBA32:
-      case PixelFormat_Grayscale8:
-        CopyPixels<uint8_t>(accessor, source.GetAccessor());
+      switch (target.GetFormat())
+      {
+        case PixelFormat_RGB24:
+        case PixelFormat_RGBA32:
+        case PixelFormat_Grayscale8:
+        CopyPixels<uint8_t>(targetAccessor, source.GetAccessor());
         break;
 
-      case PixelFormat_Grayscale16:
-        CopyPixels<uint16_t>(accessor, source.GetAccessor());
+        case PixelFormat_Grayscale16:
+        CopyPixels<uint16_t>(targetAccessor, source.GetAccessor());
         break;
 
-      case PixelFormat_SignedGrayscale16:
-        CopyPixels<int16_t>(accessor, source.GetAccessor());
+        case PixelFormat_SignedGrayscale16:
+        CopyPixels<int16_t>(targetAccessor, source.GetAccessor());
         break;
 
-      default:
+        default:
         throw OrthancException(ErrorCode_InternalError);
+      }
     }
   }
 
@@ -544,7 +534,7 @@
 
     SetupImageBuffer(target, dataset);
 
-    ImageAccessor accessor(target.GetAccessor());
+    ImageAccessor targetAccessor(target.GetAccessor());
 
     /**
      * The "DJLSLosslessDecoder" and "DJLSNearLosslessDecoder" in DCMTK
@@ -560,8 +550,8 @@
     OFString decompressedColorModel;  // Out
     DJ_RPLossless representationParameter;
     OFCondition c = decoder.decodeFrame(&representationParameter, pixelSequence, &parameters, 
-                                        &dataset, frame, startFragment, accessor.GetBuffer(), 
-                                        accessor.GetSize(), decompressedColorModel);
+                                        &dataset, frame, startFragment, targetAccessor.GetBuffer(), 
+                                        targetAccessor.GetSize(), decompressedColorModel);
 
     if (!c.good())
     {