changeset 844:502c49adb5ad jpeg

jpeg lossless
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 05 Jun 2014 14:08:23 +0200
parents 6a5cceec04ef
children 48016722c770
files Core/ImageFormats/ImageAccessor.h Core/ImageFormats/ImageBuffer.cpp Core/ImageFormats/ImageBuffer.h Core/ImageFormats/PngWriter.h Resources/CMake/DcmtkConfiguration.cmake UnitTestsSources/JpegLossless.cpp
diffstat 6 files changed, 168 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/Core/ImageFormats/ImageAccessor.h	Wed Jun 04 17:57:59 2014 +0200
+++ b/Core/ImageFormats/ImageAccessor.h	Thu Jun 05 14:08:23 2014 +0200
@@ -77,6 +77,11 @@
       return pitch_;
     }
 
+    unsigned int GetSize() const
+    {
+      return GetHeight() * GetPitch();
+    }
+
     const void* GetConstBuffer() const
     {
       return buffer_;
--- a/Core/ImageFormats/ImageBuffer.cpp	Wed Jun 04 17:57:59 2014 +0200
+++ b/Core/ImageFormats/ImageBuffer.cpp	Thu Jun 05 14:08:23 2014 +0200
@@ -39,6 +39,13 @@
   {
     if (changed_)
     {
+      /*
+        if (forceMinimalPitch_)
+        {
+        TODO: Align pitch and memory buffer to optimal size for SIMD.
+        }
+      */
+
       pitch_ = GetBytesPerPixel() * width_;
 
       data_.resize(pitch_ * height_);
@@ -59,6 +66,7 @@
   ImageBuffer::ImageBuffer() : changed_(false)
   {
     changed_ = false;
+    forceMinimalPitch_ = true;
     format_ = PixelFormat_Grayscale8;
     width_ = 0;
     height_ = 0;
@@ -106,4 +114,11 @@
     accessor.AssignReadOnly(format_, width_, height_, pitch_, buffer_);
     return accessor;
   }
+
+
+  void ImageBuffer::SetMinimalPitchForced(bool force)
+  {
+    changed_ = true;
+    forceMinimalPitch_ = force;
+  }
 }
--- a/Core/ImageFormats/ImageBuffer.h	Wed Jun 04 17:57:59 2014 +0200
+++ b/Core/ImageFormats/ImageBuffer.h	Thu Jun 05 14:08:23 2014 +0200
@@ -45,6 +45,7 @@
     bool changed_;
     std::vector<uint8_t> data_;
 
+    bool forceMinimalPitch_;  // Currently unused
     PixelFormat format_;
     unsigned int width_;
     unsigned int height_;
@@ -85,5 +86,12 @@
     ImageAccessor GetAccessor();
 
     ImageAccessor GetConstAccessor();
+
+    bool IsMinimalPitchForced() const
+    {
+      return forceMinimalPitch_;
+    }
+
+    void SetMinimalPitchForced(bool force);
   };
 }
--- a/Core/ImageFormats/PngWriter.h	Wed Jun 04 17:57:59 2014 +0200
+++ b/Core/ImageFormats/PngWriter.h	Thu Jun 05 14:08:23 2014 +0200
@@ -32,7 +32,7 @@
 
 #pragma once
 
-#include "../Enumerations.h"
+#include "ImageAccessor.h"
 
 #include <boost/shared_ptr.hpp>
 #include <string>
@@ -74,5 +74,19 @@
                        unsigned int pitch,
                        PixelFormat format,
                        const void* buffer);
+
+    void WriteToFile(const char* filename,
+                     const ImageAccessor& accessor)
+    {
+      WriteToFile(filename, accessor.GetWidth(), accessor.GetHeight(),
+                  accessor.GetPitch(), accessor.GetFormat(), accessor.GetBuffer());
+    }
+
+    void WriteToMemory(std::string& png,
+                       const ImageAccessor& accessor)
+    {
+      WriteToMemory(png, accessor.GetWidth(), accessor.GetHeight(),
+                    accessor.GetPitch(), accessor.GetFormat(), accessor.GetBuffer());
+    }
   };
 }
--- a/Resources/CMake/DcmtkConfiguration.cmake	Wed Jun 04 17:57:59 2014 +0200
+++ b/Resources/CMake/DcmtkConfiguration.cmake	Thu Jun 05 14:08:23 2014 +0200
@@ -58,6 +58,8 @@
       )
     list(REMOVE_ITEM DCMTK_SOURCES 
       ${DCMTK_SOURCES_DIR}/dcmjpls/libsrc/djcodece.cc)
+    list(APPEND DCMTK_SOURCES 
+      ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djrplol.cc)
   endif()
 
 
--- a/UnitTestsSources/JpegLossless.cpp	Wed Jun 04 17:57:59 2014 +0200
+++ b/UnitTestsSources/JpegLossless.cpp	Thu Jun 05 14:08:23 2014 +0200
@@ -40,17 +40,29 @@
 #include <dcmtk/dcmdata/dcfilefo.h>
 
 #include <dcmtk/dcmjpls/djcodecd.h>
+#include <dcmtk/dcmjpls/djcparam.h>
+#include <dcmtk/dcmjpeg/djrplol.h>
+#include <dcmtk/dcmdata/dcstack.h>
+#include <dcmtk/dcmdata/dcpixseq.h>
 
 #include "../OrthancServer/ParsedDicomFile.h"
 #include "../OrthancServer/FromDcmtkBridge.h"
+#include "../OrthancServer/ToDcmtkBridge.h"
+#include "../Core/OrthancException.h"
+#include "../Core/ImageFormats/ImageBuffer.h"
+#include "../Core/ImageFormats/PngWriter.h"
+
+#include <boost/lexical_cast.hpp>
 
 using namespace Orthanc;
 
 TEST(JpegLossless, Basic)
 {
-  DJLSDecoderRegistration::registerCodecs( EJLSUC_default, EJLSPC_restore,OFFalse );
+  //DJLSDecoderRegistration::registerCodecs( EJLSUC_default, EJLSPC_restore,OFFalse );
 
 #if 0
+  // Fallback
+
   std::string s;
   Toolbox::ReadFile(s, "IM-0001-1001-0001.dcm");
 
@@ -67,41 +79,130 @@
 
     parsed.SaveToFile("tutu.dcm");
 
-    FromDcmtkBridge::ExtractPngImage(s, *dataset, 1, ImageExtractionMode_Preview);
-    //fileformat.saveFile("test_decompressed.dcm", EXS_LittleEndianExplicit);
-  }
-#else
-  DcmFileFormat fileformat;
-  if (fileformat.loadFile("IM-0001-1001-0001.dcm").good())
-  {
-    DcmDataset *dataset = fileformat.getDataset();
-
     // decompress data set if compressed
     dataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
 
     DcmXfer original_xfer(dataset->getOriginalXfer());
     std::cout << original_xfer.getXferName() << std::endl;
 
-    printf("OK1\n");
+    FromDcmtkBridge::ExtractPngImage(s, *dataset, 1, ImageExtractionMode_Preview);
+    //fileformat.saveFile("test_decompressed.dcm", EXS_LittleEndianExplicit);
+  }
+#else
+  DcmFileFormat fileformat;
+  //if (fileformat.loadFile("IM-0001-1001-0001.dcm").good())
+  if (fileformat.loadFile("tata.dcm").good())
+  {
+    DcmDataset *dataset = fileformat.getDataset();
+
+    // <data-set xfer="1.2.840.10008.1.2.4.80" name="JPEG-LS Lossless">
+
+    DcmTag k(DICOM_TAG_PIXEL_DATA.GetGroup(),
+             DICOM_TAG_PIXEL_DATA.GetElement());
+
+    DcmElement *element = NULL;
+    if (dataset->findAndGetElement(k, element).good())
+    {
+      DcmPixelData& pixelData = dynamic_cast<DcmPixelData&>(*element);
+      DcmPixelSequence* pixelSequence = NULL;
+      if (pixelData.getEncapsulatedRepresentation
+          (dataset->getOriginalXfer(), NULL, pixelSequence).good())
+      {
+        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);
 
-    // check if everything went well
-    if (1) //dataset->canWriteXfer(EXS_LittleEndianExplicit))
-    {
-      printf("OK2\n");
+        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());
+        }
+
+        ImageBuffer buffer;
+        buffer.SetHeight(height);
+        buffer.SetWidth(width);
 
-      fileformat.saveFile("tutu.dcm", EXS_LittleEndianExplicit);
+        if (bitsStored == 8 && samplesPerPixel == 1 && !isSigned)
+        {
+          buffer.SetFormat(PixelFormat_Grayscale8);
+        }
+        else if (bitsStored == 8 && samplesPerPixel == 3 && !isSigned)
+        {
+          buffer.SetFormat(PixelFormat_RGB24);
+        }
+        else if (bitsStored == 16 && samplesPerPixel == 1 && !isSigned)
+        {
+          buffer.SetFormat(PixelFormat_Grayscale16);
+        }
+        else if (bitsStored == 16 && samplesPerPixel == 1 && isSigned)
+        {
+          buffer.SetFormat(PixelFormat_SignedGrayscale16);
+        }
+        else
+        {
+          throw OrthancException(ErrorCode_NotImplemented);
+        }
+
+        ImageAccessor accessor(buffer.GetAccessor());
+
+        // http://support.dcmtk.org/docs/classDJLSLosslessDecoder.html
+        DJLSLosslessDecoder bb; DJLSCodecParameter cp;
+        //DJLSNearLosslessDecoder bb; DJLSCodecParameter cp;
+
+        Uint32 startFragment = 0;  // Default 
+        OFString decompressedColorModel;  // Out
+        DJ_RPLossless rp;
+        OFCondition c = bb.decodeFrame(&rp, pixelSequence, &cp, dataset, 0, startFragment, 
+                                       accessor.GetBuffer(), accessor.GetSize(), decompressedColorModel);
+
+
+
+        for (unsigned int y = 0; y < accessor.GetHeight(); y++)
+        {
+          int16_t *p = reinterpret_cast<int16_t*>(accessor.GetRow(y));
+          for (unsigned int x = 0; x < accessor.GetWidth(); x++, p ++)
+          {
+            if (*p < 0)
+              *p = 0;
+          }
+        }
+
+        PngWriter w;
+        w.WriteToFile("tata.png", accessor);
+      }
     }
   }
 
-
 #endif
 
 
-  // http://support.dcmtk.org/docs/classDJLSLosslessDecoder.html
-  //DJLSDecoderBase b;
-  DJLSLosslessDecoder bb;
-
-  DJLSDecoderRegistration::cleanup();
+  //DJLSDecoderRegistration::cleanup();
 }