changeset 3746:875308321fa8 storage-commitment

merge
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 13 Mar 2020 08:31:39 +0100
parents e5713a9acebd (current diff) 113a7b994a12 (diff)
children 4edeef72de75
files CMakeLists.txt
diffstat 11 files changed, 515 insertions(+), 93 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Wed Mar 11 14:35:32 2020 +0100
+++ b/CMakeLists.txt	Fri Mar 13 08:31:39 2020 +0100
@@ -25,6 +25,9 @@
 set(ENABLE_WEB_SERVER ON)
 set(ENABLE_ZLIB ON)
 
+# To test transcoding
+#set(ENABLE_DCMTK_TRANSCODING ON)
+
 set(HAS_EMBEDDED_RESOURCES ON)
 
 
--- a/Core/DicomParsing/FromDcmtkBridge.cpp	Wed Mar 11 14:35:32 2020 +0100
+++ b/Core/DicomParsing/FromDcmtkBridge.cpp	Fri Mar 13 08:31:39 2020 +0100
@@ -2081,7 +2081,7 @@
     // Unregister JPEG codecs
     DJDecoderRegistration::cleanup();
 # if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
-    DJDecoderRegistration::cleanup();
+    DJEncoderRegistration::cleanup();
 # endif
 #endif
   }
--- a/Core/DicomParsing/FromDcmtkBridge.h	Wed Mar 11 14:35:32 2020 +0100
+++ b/Core/DicomParsing/FromDcmtkBridge.h	Fri Mar 13 08:31:39 2020 +0100
@@ -271,7 +271,10 @@
                       ITagVisitor& visitor,
                       Encoding defaultEncoding);
 
-    static bool GetDcmtkTransferSyntax(E_TransferSyntax& target,
-                                       DicomTransferSyntax syntax);
+    static bool LookupDcmtkTransferSyntax(E_TransferSyntax& target,
+                                          DicomTransferSyntax source);
+
+    static bool LookupOrthancTransferSyntax(DicomTransferSyntax& target,
+                                            E_TransferSyntax source);
   };
 }
--- a/Core/DicomParsing/FromDcmtkBridge_TransferSyntaxes.impl.h	Wed Mar 11 14:35:32 2020 +0100
+++ b/Core/DicomParsing/FromDcmtkBridge_TransferSyntaxes.impl.h	Fri Mar 13 08:31:39 2020 +0100
@@ -34,10 +34,10 @@
 
 namespace Orthanc
 {
-  bool GetDcmtkTransferSyntax(E_TransferSyntax& target,
-                              DicomTransferSyntax syntax)
+  bool FromDcmtkBridge::LookupDcmtkTransferSyntax(E_TransferSyntax& target,
+                                                  DicomTransferSyntax source)
   {
-    switch (syntax)
+    switch (source)
     {
       case DicomTransferSyntax_LittleEndianImplicit:
         target = EXS_LittleEndianImplicit;
@@ -207,4 +207,179 @@
         return false;
     }
   }
+  
+
+  bool FromDcmtkBridge::LookupOrthancTransferSyntax(DicomTransferSyntax& target,
+                                                    E_TransferSyntax source)
+  {
+    switch (source)
+    {
+      case EXS_LittleEndianImplicit:
+        target = DicomTransferSyntax_LittleEndianImplicit;
+        return true;
+
+      case EXS_LittleEndianExplicit:
+        target = DicomTransferSyntax_LittleEndianExplicit;
+        return true;
+
+      case EXS_DeflatedLittleEndianExplicit:
+        target = DicomTransferSyntax_DeflatedLittleEndianExplicit;
+        return true;
+
+      case EXS_BigEndianExplicit:
+        target = DicomTransferSyntax_BigEndianExplicit;
+        return true;
+
+      case EXS_JPEGProcess1:
+        target = DicomTransferSyntax_JPEGProcess1;
+        return true;
+
+      case EXS_JPEGProcess2_4:
+        target = DicomTransferSyntax_JPEGProcess2_4;
+        return true;
+
+      case EXS_JPEGProcess3_5:
+        target = DicomTransferSyntax_JPEGProcess3_5;
+        return true;
+
+      case EXS_JPEGProcess6_8:
+        target = DicomTransferSyntax_JPEGProcess6_8;
+        return true;
+
+      case EXS_JPEGProcess7_9:
+        target = DicomTransferSyntax_JPEGProcess7_9;
+        return true;
+
+      case EXS_JPEGProcess10_12:
+        target = DicomTransferSyntax_JPEGProcess10_12;
+        return true;
+
+      case EXS_JPEGProcess11_13:
+        target = DicomTransferSyntax_JPEGProcess11_13;
+        return true;
+
+      case EXS_JPEGProcess14:
+        target = DicomTransferSyntax_JPEGProcess14;
+        return true;
+
+      case EXS_JPEGProcess15:
+        target = DicomTransferSyntax_JPEGProcess15;
+        return true;
+
+      case EXS_JPEGProcess16_18:
+        target = DicomTransferSyntax_JPEGProcess16_18;
+        return true;
+
+      case EXS_JPEGProcess17_19:
+        target = DicomTransferSyntax_JPEGProcess17_19;
+        return true;
+
+      case EXS_JPEGProcess20_22:
+        target = DicomTransferSyntax_JPEGProcess20_22;
+        return true;
+
+      case EXS_JPEGProcess21_23:
+        target = DicomTransferSyntax_JPEGProcess21_23;
+        return true;
+
+      case EXS_JPEGProcess24_26:
+        target = DicomTransferSyntax_JPEGProcess24_26;
+        return true;
+
+      case EXS_JPEGProcess25_27:
+        target = DicomTransferSyntax_JPEGProcess25_27;
+        return true;
+
+      case EXS_JPEGProcess28:
+        target = DicomTransferSyntax_JPEGProcess28;
+        return true;
+
+      case EXS_JPEGProcess29:
+        target = DicomTransferSyntax_JPEGProcess29;
+        return true;
+
+      case EXS_JPEGProcess14SV1:
+        target = DicomTransferSyntax_JPEGProcess14SV1;
+        return true;
+
+      case EXS_JPEGLSLossless:
+        target = DicomTransferSyntax_JPEGLSLossless;
+        return true;
+
+      case EXS_JPEGLSLossy:
+        target = DicomTransferSyntax_JPEGLSLossy;
+        return true;
+
+      case EXS_JPEG2000LosslessOnly:
+        target = DicomTransferSyntax_JPEG2000LosslessOnly;
+        return true;
+
+      case EXS_JPEG2000:
+        target = DicomTransferSyntax_JPEG2000;
+        return true;
+
+      case EXS_JPEG2000MulticomponentLosslessOnly:
+        target = DicomTransferSyntax_JPEG2000MulticomponentLosslessOnly;
+        return true;
+
+      case EXS_JPEG2000Multicomponent:
+        target = DicomTransferSyntax_JPEG2000Multicomponent;
+        return true;
+
+      case EXS_JPIPReferenced:
+        target = DicomTransferSyntax_JPIPReferenced;
+        return true;
+
+      case EXS_JPIPReferencedDeflate:
+        target = DicomTransferSyntax_JPIPReferencedDeflate;
+        return true;
+
+      case EXS_MPEG2MainProfileAtMainLevel:
+        target = DicomTransferSyntax_MPEG2MainProfileAtMainLevel;
+        return true;
+
+      case EXS_MPEG2MainProfileAtHighLevel:
+        target = DicomTransferSyntax_MPEG2MainProfileAtHighLevel;
+        return true;
+
+      case EXS_MPEG4HighProfileLevel4_1:
+        target = DicomTransferSyntax_MPEG4HighProfileLevel4_1;
+        return true;
+
+      case EXS_MPEG4BDcompatibleHighProfileLevel4_1:
+        target = DicomTransferSyntax_MPEG4BDcompatibleHighProfileLevel4_1;
+        return true;
+
+      case EXS_MPEG4HighProfileLevel4_2_For2DVideo:
+        target = DicomTransferSyntax_MPEG4HighProfileLevel4_2_For2DVideo;
+        return true;
+
+      case EXS_MPEG4HighProfileLevel4_2_For3DVideo:
+        target = DicomTransferSyntax_MPEG4HighProfileLevel4_2_For3DVideo;
+        return true;
+
+      case EXS_MPEG4StereoHighProfileLevel4_2:
+        target = DicomTransferSyntax_MPEG4StereoHighProfileLevel4_2;
+        return true;
+
+#if DCMTK_VERSION_NUMBER >= 362
+      case EXS_HEVCMainProfileLevel5_1:
+        target = DicomTransferSyntax_HEVCMainProfileLevel5_1;
+        return true;
+#endif
+
+#if DCMTK_VERSION_NUMBER >= 362
+      case EXS_HEVCMain10ProfileLevel5_1:
+        target = DicomTransferSyntax_HEVCMain10ProfileLevel5_1;
+        return true;
+#endif
+
+      case EXS_RLELossless:
+        target = DicomTransferSyntax_RLELossless;
+        return true;
+
+      default:
+        return false;
+    }
+  }
 }
--- a/Core/DicomParsing/Internals/DicomFrameIndex.cpp	Wed Mar 11 14:35:32 2020 +0100
+++ b/Core/DicomParsing/Internals/DicomFrameIndex.cpp	Fri Mar 13 08:31:39 2020 +0100
@@ -68,7 +68,10 @@
       uint32_t length = item->getLength();
       if (length == 0)
       {
-        table.clear();
+        // Degenerate case: Empty offset table means only one frame
+        // that overlaps all the fragments
+        table.resize(1);
+        table[0] = 0;
         return;
       }
 
@@ -146,7 +149,6 @@
         throw OrthancException(ErrorCode_BadFileFormat);
       }
 
-
       // Loop over the fragments (ignoring the offset table). This is
       // an alternative, faster implementation to DCMTK's
       // "DcmCodec::determineStartFragment()".
@@ -318,46 +320,10 @@
   };
 
 
-
-  bool DicomFrameIndex::IsVideo(DcmFileFormat& dicom)
+  unsigned int DicomFrameIndex::GetFramesCount(DcmDataset& dicom)
   {
-    // Retrieve the transfer syntax from the DICOM header
-    const char* value = NULL;
-    if (!dicom.getMetaInfo()->findAndGetString(DCM_TransferSyntaxUID, value).good() ||
-        value == NULL)
-    {
-      return false;
-    }
-
-    const std::string transferSyntax(value);
-
-    // Video standards supported in DICOM 2016a
-    // http://dicom.nema.org/medical/dicom/2016a/output/html/part05.html
-    if (transferSyntax == "1.2.840.10008.1.2.4.100" ||  // MPEG2 MP@ML option of ISO/IEC MPEG2
-        transferSyntax == "1.2.840.10008.1.2.4.101" ||  // MPEG2 MP@HL option of ISO/IEC MPEG2
-        transferSyntax == "1.2.840.10008.1.2.4.102" ||  // MPEG-4 AVC/H.264 High Profile / Level 4.1 of ITU-T H.264
-        transferSyntax == "1.2.840.10008.1.2.4.103" ||  // MPEG-4 AVC/H.264 BD-compat High Profile / Level 4.1 of ITU-T H.264
-        transferSyntax == "1.2.840.10008.1.2.4.104" ||  // MPEG-4 AVC/H.264 High Profile / Level 4.2 of ITU-T H.264
-        transferSyntax == "1.2.840.10008.1.2.4.105" ||  // MPEG-4 AVC/H.264 High Profile / Level 4.2 of ITU-T H.264
-        transferSyntax == "1.2.840.10008.1.2.4.106")    // MPEG-4 AVC/H.264 Stereo High Profile / Level 4.2 of the ITU-T H.264
-    {
-      return true;
-    }
-
-    return false;
-  }
-
-
-  unsigned int DicomFrameIndex::GetFramesCount(DcmFileFormat& dicom)
-  {
-    // Assume 1 frame for video transfer syntaxes
-    if (IsVideo(dicom))
-    {
-      return 1;
-    }        
-
     const char* tmp = NULL;
-    if (!dicom.getDataset()->findAndGetString(DCM_NumberOfFrames, tmp).good() ||
+    if (!dicom.findAndGetString(DCM_NumberOfFrames, tmp).good() ||
         tmp == NULL)
     {
       return 1;
@@ -378,12 +344,12 @@
     }
     else
     {
-      return count;
+      return static_cast<unsigned int>(count);
     }
   }
 
 
-  DicomFrameIndex::DicomFrameIndex(DcmFileFormat& dicom)
+  DicomFrameIndex::DicomFrameIndex(DcmDataset& dicom)
   {
     countFrames_ = GetFramesCount(dicom);
     if (countFrames_ == 0)
@@ -392,10 +358,8 @@
       return;
     }
 
-    DcmDataset& dataset = *dicom.getDataset();
-
     // Test whether this image is composed of a sequence of fragments
-    DcmPixelSequence* pixelSequence = FromDcmtkBridge::GetPixelSequence(dataset);
+    DcmPixelSequence* pixelSequence = FromDcmtkBridge::GetPixelSequence(dicom);
     if (pixelSequence != NULL)
     {
       index_.reset(new FragmentIndex(pixelSequence, countFrames_));
@@ -404,18 +368,18 @@
 
     // Extract information about the image structure
     DicomMap tags;
-    FromDcmtkBridge::ExtractDicomSummary(tags, dataset);
+    FromDcmtkBridge::ExtractDicomSummary(tags, dicom);
 
     DicomImageInformation information(tags);
 
     // Access to the raw pixel data
-    if (DicomImageDecoder::IsPsmctRle1(dataset))
+    if (DicomImageDecoder::IsPsmctRle1(dicom))
     {
-      index_.reset(new PsmctRle1Index(dataset, countFrames_, information.GetFrameSize()));
+      index_.reset(new PsmctRle1Index(dicom, countFrames_, information.GetFrameSize()));
     }
     else
     {
-      index_.reset(new UncompressedIndex(dataset, countFrames_, information.GetFrameSize()));
+      index_.reset(new UncompressedIndex(dicom, countFrames_, information.GetFrameSize()));
     }
   }
 
--- a/Core/DicomParsing/Internals/DicomFrameIndex.h	Wed Mar 11 14:35:32 2020 +0100
+++ b/Core/DicomParsing/Internals/DicomFrameIndex.h	Fri Mar 13 08:31:39 2020 +0100
@@ -67,7 +67,7 @@
     unsigned int             countFrames_;
 
   public:
-    DicomFrameIndex(DcmFileFormat& dicom);
+    DicomFrameIndex(DcmDataset& dicom);
 
     unsigned int GetFramesCount() const
     {
@@ -77,8 +77,6 @@
     void GetRawFrame(std::string& frame,
                      unsigned int index) const;
 
-    static bool IsVideo(DcmFileFormat& dicom);
-
-    static unsigned int GetFramesCount(DcmFileFormat& dicom);
+    static unsigned int GetFramesCount(DcmDataset& dicom);
   };
 }
--- a/Core/DicomParsing/Internals/DicomImageDecoder.cpp	Wed Mar 11 14:35:32 2020 +0100
+++ b/Core/DicomParsing/Internals/DicomImageDecoder.cpp	Fri Mar 13 08:31:39 2020 +0100
@@ -34,6 +34,8 @@
 #include "../../PrecompiledHeaders.h"
 #include "DicomImageDecoder.h"
 
+#include "../ParsedDicomFile.h"
+
 
 /*=========================================================================
 
@@ -84,7 +86,6 @@
 #include "../../DicomFormat/DicomIntegerPixelAccessor.h"
 #include "../ToDcmtkBridge.h"
 #include "../FromDcmtkBridge.h"
-#include "../ParsedDicomFile.h"
 
 #if ORTHANC_ENABLE_PNG == 1
 #  include "../../Images/PngWriter.h"
@@ -98,7 +99,6 @@
 #include <boost/lexical_cast.hpp>
 
 #include <dcmtk/dcmdata/dcdeftag.h>
-#include <dcmtk/dcmdata/dcfilefo.h>
 #include <dcmtk/dcmdata/dcrleccd.h>
 #include <dcmtk/dcmdata/dcrlecp.h>
 #include <dcmtk/dcmdata/dcrlerp.h>
@@ -662,7 +662,20 @@
   ImageAccessor* DicomImageDecoder::Decode(ParsedDicomFile& dicom,
                                            unsigned int frame)
   {
-    DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset();
+    if (dicom.GetDcmtkObject().getDataset() == NULL)
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+    else
+    {
+      return Decode(*dicom.GetDcmtkObject().getDataset(), frame);
+    }
+  }
+
+
+  ImageAccessor* DicomImageDecoder::Decode(DcmDataset& dataset,
+                                           unsigned int frame)
+  {
     E_TransferSyntax syntax = dataset.getOriginalXfer();
 
     /**
--- a/Core/DicomParsing/Internals/DicomImageDecoder.h	Wed Mar 11 14:35:32 2020 +0100
+++ b/Core/DicomParsing/Internals/DicomImageDecoder.h	Fri Mar 13 08:31:39 2020 +0100
@@ -34,7 +34,7 @@
 #pragma once
 
 #include "../../Compatibility.h"
-#include "../ParsedDicomFile.h"
+#include "../../Images/ImageAccessor.h"
 
 #include <memory>
 
@@ -62,6 +62,8 @@
 
 namespace Orthanc
 {
+  class ParsedDicomFile;
+  
   class DicomImageDecoder : public boost::noncopyable
   {
   private:
@@ -102,6 +104,9 @@
     static ImageAccessor *Decode(ParsedDicomFile& dicom,
                                  unsigned int frame);
 
+    static ImageAccessor *Decode(DcmDataset& dataset,
+                                 unsigned int frame);
+
     static void ExtractPamImage(std::string& result,
                                 std::unique_ptr<ImageAccessor>& image,
                                 ImageExtractionMode mode,
--- a/Core/DicomParsing/ParsedDicomFile.cpp	Wed Mar 11 14:35:32 2020 +0100
+++ b/Core/DicomParsing/ParsedDicomFile.cpp	Fri Mar 13 08:31:39 2020 +0100
@@ -1557,7 +1557,9 @@
   {
     if (pimpl_->frameIndex_.get() == NULL)
     {
-      pimpl_->frameIndex_.reset(new DicomFrameIndex(*pimpl_->file_));
+      assert(pimpl_->file_ != NULL &&
+             pimpl_->file_->getDataset() != NULL);
+      pimpl_->frameIndex_.reset(new DicomFrameIndex(*pimpl_->file_->getDataset()));
     }
 
     pimpl_->frameIndex_->GetRawFrame(target, frameId);
@@ -1589,7 +1591,9 @@
 
   unsigned int ParsedDicomFile::GetFramesCount() const
   {
-    return DicomFrameIndex::GetFramesCount(*pimpl_->file_);
+    assert(pimpl_->file_ != NULL &&
+           pimpl_->file_->getDataset() != NULL);
+    return DicomFrameIndex::GetFramesCount(*pimpl_->file_->getDataset());
   }
 
 
--- a/Resources/GenerateTransferSyntaxesDcmtk.mustache	Wed Mar 11 14:35:32 2020 +0100
+++ b/Resources/GenerateTransferSyntaxesDcmtk.mustache	Fri Mar 13 08:31:39 2020 +0100
@@ -34,10 +34,10 @@
 
 namespace Orthanc
 {
-  bool GetDcmtkTransferSyntax(E_TransferSyntax& target,
-                              DicomTransferSyntax syntax)
+  bool FromDcmtkBridge::LookupDcmtkTransferSyntax(E_TransferSyntax& target,
+                                                  DicomTransferSyntax source)
   {
-    switch (syntax)
+    switch (source)
     {
       {{#Syntaxes}}
       {{#DCMTK}}
@@ -57,4 +57,29 @@
         return false;
     }
   }
+  
+
+  bool FromDcmtkBridge::LookupOrthancTransferSyntax(DicomTransferSyntax& target,
+                                                    E_TransferSyntax source)
+  {
+    switch (source)
+    {
+      {{#Syntaxes}}
+      {{#DCMTK}}
+      {{#SinceDCMTK}}
+#if DCMTK_VERSION_NUMBER >= {{SinceDCMTK}}
+      {{/SinceDCMTK}}
+      case {{DCMTK}}:
+        target = DicomTransferSyntax_{{Value}};
+        return true;
+      {{#SinceDCMTK}}
+#endif
+      {{/SinceDCMTK}}
+
+      {{/DCMTK}}
+      {{/Syntaxes}}
+      default:
+        return false;
+    }
+  }
 }
--- a/UnitTestsSources/FromDcmtkTests.cpp	Wed Mar 11 14:35:32 2020 +0100
+++ b/UnitTestsSources/FromDcmtkTests.cpp	Fri Mar 13 08:31:39 2020 +0100
@@ -1917,7 +1917,186 @@
 
 #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
 
+#include "../Core/DicomParsing/Internals/DicomFrameIndex.h"
+
 #include <dcmtk/dcmdata/dcostrmb.h>
+#include <dcmtk/dcmdata/dcpixel.h>
+#include <dcmtk/dcmdata/dcpxitem.h>
+
+
+namespace Orthanc
+{
+  class IDicomTranscoder : public boost::noncopyable
+  {
+  public:
+    virtual ~IDicomTranscoder()
+    {
+    }
+
+    virtual DicomTransferSyntax GetTransferSyntax() = 0;
+
+    virtual std::string GetSopClassUid() = 0;
+
+    virtual std::string GetSopInstanceUid() = 0;
+
+    virtual unsigned int GetFramesCount() = 0;
+
+    virtual ImageAccessor* DecodeFrame(unsigned int frame) = 0;
+
+    virtual void GetCompressedFrame(std::string& target,
+                                    unsigned int frame) = 0;
+
+    virtual IDicomTranscoder* Transcode(std::set<DicomTransferSyntax> syntaxes,
+                                        bool allowNewSopInstanceUid) = 0;
+  };
+
+
+  class DcmtkTranscoder : public IDicomTranscoder
+  {
+  private:
+    std::unique_ptr<DcmFileFormat>    dicom_;
+    std::unique_ptr<DicomFrameIndex>  index_;
+    DicomTransferSyntax               transferSyntax_;
+    std::string                       sopClassUid_;
+    std::string                       sopInstanceUid_;
+
+    void Setup(DcmFileFormat* dicom)
+    {
+      dicom_.reset(dicom);
+      
+      if (dicom == NULL ||
+          dicom_->getDataset() == NULL)
+      {
+        throw OrthancException(ErrorCode_NullPointer);
+      }
+
+      DcmDataset& dataset = *dicom_->getDataset();
+      index_.reset(new DicomFrameIndex(dataset));
+
+      E_TransferSyntax xfer = dataset.getOriginalXfer();
+      if (xfer == EXS_Unknown)
+      {
+        dataset.updateOriginalXfer();
+        xfer = dataset.getOriginalXfer();
+        if (xfer == EXS_Unknown)
+        {
+          throw OrthancException(ErrorCode_BadFileFormat,
+                                 "Cannot determine the transfer syntax of the DICOM instance");
+        }
+      }
+
+      if (!FromDcmtkBridge::LookupOrthancTransferSyntax(transferSyntax_, xfer))
+      {
+        throw OrthancException(
+          ErrorCode_BadFileFormat,
+          "Unsupported transfer syntax: " + boost::lexical_cast<std::string>(xfer));
+      }
+
+      const char* a = NULL;
+      const char* b = NULL;
+
+      if (!dataset.findAndGetString(DCM_SOPClassUID, a).good() ||
+          !dataset.findAndGetString(DCM_SOPInstanceUID, b).good() ||
+          a == NULL ||
+          b == NULL)
+      {
+        throw OrthancException(ErrorCode_BadFileFormat,
+                               "Missing SOP class/instance UID in DICOM instance");
+      }
+
+      sopClassUid_.assign(a);
+      sopInstanceUid_.assign(b);
+    }
+    
+  public:
+    DcmtkTranscoder(DcmFileFormat* dicom)  // Takes ownership
+    {
+      Setup(dicom);
+    }
+
+    DcmtkTranscoder(const void* dicom,
+                    size_t size)
+    {
+      Setup(FromDcmtkBridge::LoadFromMemoryBuffer(dicom, size));
+    }
+
+    virtual DicomTransferSyntax GetTransferSyntax() ORTHANC_OVERRIDE
+    {
+      return transferSyntax_;
+    }
+
+    virtual std::string GetSopClassUid() ORTHANC_OVERRIDE
+    {
+      return sopClassUid_;
+    }
+    
+    virtual std::string GetSopInstanceUid() ORTHANC_OVERRIDE
+    {
+      return sopInstanceUid_;
+    }
+
+    virtual unsigned int GetFramesCount() ORTHANC_OVERRIDE
+    {
+      return index_->GetFramesCount();
+    }
+
+    virtual ImageAccessor* DecodeFrame(unsigned int frame) ORTHANC_OVERRIDE
+    {
+      assert(dicom_->getDataset() != NULL);
+      return DicomImageDecoder::Decode(*dicom_->getDataset(), frame);
+    }
+
+    virtual void GetCompressedFrame(std::string& target,
+                                    unsigned int frame) ORTHANC_OVERRIDE
+    {
+#if 1
+      index_->GetRawFrame(target, frame);
+      printf("%d: %d\n", frame, target.size());
+#endif
+
+#if 1
+      assert(dicom_->getDataset() != NULL);
+      DcmDataset& dataset = *dicom_->getDataset();
+      
+      DcmPixelSequence* pixelSequence = FromDcmtkBridge::GetPixelSequence(dataset);
+
+      if (pixelSequence != NULL &&
+          frame == 0 &&
+          pixelSequence->card() != GetFramesCount() + 1)
+      {
+        printf("COMPRESSED\n");
+        
+        // Check out "djcodecd.cc"
+        
+        printf("%d fragments\n", pixelSequence->card());
+        
+        // Skip the first fragment, that is the offset table
+        for (unsigned long i = 1; ;i++)
+        {
+          DcmPixelItem *fragment = NULL;
+          if (pixelSequence->getItem(fragment, i).good())
+          {
+            printf("fragment %d %d\n", i, fragment->getLength());
+          }
+          else
+          {
+            break;
+          }
+        }
+      }
+#endif
+    }
+
+    virtual IDicomTranscoder* Transcode(std::set<DicomTransferSyntax> syntaxes,
+                                        bool allowNewSopInstanceUid) ORTHANC_OVERRIDE
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+  };
+}
+
+
+
 
 static bool Transcode(std::string& buffer,
                       DcmDataset& dataSet,
@@ -1991,44 +2170,97 @@
 #include "dcmtk/dcmjpeg/djrploss.h"  /* for DJ_RPLossy */
 #include "dcmtk/dcmjpeg/djrplol.h"   /* for DJ_RPLossless */
 
+#include <boost/filesystem.hpp>
+
+
+static void TestFile(const std::string& path)
+{
+  printf("** %s\n", path.c_str());
+
+  std::string s;
+  SystemToolbox::ReadFile(s, path);
+
+  Orthanc::DcmtkTranscoder transcoder(s.c_str(), s.size());
+
+  printf("[%s] [%s] [%s] %d\n", GetTransferSyntaxUid(transcoder.GetTransferSyntax()),
+         transcoder.GetSopClassUid().c_str(), transcoder.GetSopInstanceUid().c_str(),
+         transcoder.GetFramesCount());
+
+  for (size_t i = 0; i < transcoder.GetFramesCount(); i++)
+  {
+    std::string f;
+    transcoder.GetCompressedFrame(f, i);
+
+    if (i == 0)
+    {
+      static unsigned int i = 0;
+      char buf[1024];
+      sprintf(buf, "/tmp/frame-%06d.dcm", i++);
+      printf(">> %s\n", buf);
+      Orthanc::SystemToolbox::WriteFile(f, buf);
+    }
+  }
+
+  printf("\n");
+}
+
 TEST(Toto, Transcode)
 {
-  OFLog::configure(OFLogger::DEBUG_LOG_LEVEL);
-  std::string s;
-  //SystemToolbox::ReadFile(s, "/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/1.2.840.10008.1.2.4.50.dcm");
-  //SystemToolbox::ReadFile(s, "/home/jodogne/DICOM/Alain.dcm");
-  SystemToolbox::ReadFile(s, "/home/jodogne/Subversion/orthanc-tests/Database/Brainix/Epi/IM-0001-0002.dcm");
+  if (0)
+  {
+    OFLog::configure(OFLogger::DEBUG_LOG_LEVEL);
 
-  std::auto_ptr<DcmFileFormat> dicom(FromDcmtkBridge::LoadFromMemoryBuffer(s.c_str(), s.size()));
+    std::string s;
+    //SystemToolbox::ReadFile(s, "/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/1.2.840.10008.1.2.4.50.dcm");
+    //SystemToolbox::ReadFile(s, "/home/jodogne/DICOM/Alain.dcm");
+    SystemToolbox::ReadFile(s, "/home/jodogne/Subversion/orthanc-tests/Database/Brainix/Epi/IM-0001-0002.dcm");
 
-  // less /home/jodogne/Downloads/dcmtk-3.6.4/dcmdata/include/dcmtk/dcmdata/dcxfer.h
-  printf(">> %d\n", dicom->getDataset()->getOriginalXfer());  // => 4 == EXS_JPEGProcess1
+    std::auto_ptr<DcmFileFormat> dicom(FromDcmtkBridge::LoadFromMemoryBuffer(s.c_str(), s.size()));
 
-  const DcmRepresentationParameter *p;
+    // less /home/jodogne/Downloads/dcmtk-3.6.4/dcmdata/include/dcmtk/dcmdata/dcxfer.h
+    printf(">> %d\n", dicom->getDataset()->getOriginalXfer());  // => 4 == EXS_JPEGProcess1
+
+    const DcmRepresentationParameter *p;
 
 #if 0
-  E_TransferSyntax target = EXS_LittleEndianExplicit;
-  p = NULL;
+    E_TransferSyntax target = EXS_LittleEndianExplicit;
+    p = NULL;
 #elif 1
-  E_TransferSyntax target = EXS_JPEGProcess14SV1;  
-  DJ_RPLossless rp_lossless(6, 0);
-  p = &rp_lossless;
+    E_TransferSyntax target = EXS_JPEGProcess14SV1;  
+    DJ_RPLossless rp_lossless(6, 0);
+    p = &rp_lossless;
 #else
-  E_TransferSyntax target = EXS_JPEGProcess1;
-  DJ_RPLossy rp_lossy(90);  // quality
-  p = &rp_lossy;
+    E_TransferSyntax target = EXS_JPEGProcess1;
+    DJ_RPLossy rp_lossy(90);  // quality
+    p = &rp_lossy;
 #endif 
   
-  //E_TransferSyntax target = EXS_LittleEndianImplicit;
-  
-  ASSERT_TRUE(dicom->getDataset()->chooseRepresentation(target, p).good());
-  ASSERT_TRUE(dicom->getDataset()->canWriteXfer(target));
+    ASSERT_TRUE(dicom->getDataset()->chooseRepresentation(target, p).good());
+    ASSERT_TRUE(dicom->getDataset()->canWriteXfer(target));
+
+    std::string t;
+    ASSERT_TRUE(Transcode(t, *dicom->getDataset(), target));
+
+    SystemToolbox::WriteFile(s, "source.dcm");
+    SystemToolbox::WriteFile(t, "target.dcm");
+  }
 
-  std::string t;
-  ASSERT_TRUE(Transcode(t, *dicom->getDataset(), target));
+  if (1)
+  {
+    const char* const PATH = "/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes";
+    
+    for (boost::filesystem::directory_iterator it(PATH);
+         it != boost::filesystem::directory_iterator(); ++it)
+    {
+      if (boost::filesystem::is_regular_file(it->status()))
+      {
+        TestFile(it->path().string());
+      }
+    }
 
-  SystemToolbox::WriteFile(s, "source.dcm");
-  SystemToolbox::WriteFile(t, "target.dcm");
+    TestFile("/home/jodogne/Subversion/orthanc-tests/Database/Multiframe.dcm");
+    TestFile("/home/jodogne/Subversion/orthanc-tests/Database/Issue44/Monochrome1-Jpeg.dcm");
+  }
 }
 
 #endif