changeset 3743:33c19a6643e1

creating IDicomTranscoder abstraction
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 12 Mar 2020 16:08:08 +0100
parents c1550e710410
children accf1b60b108
files CMakeLists.txt Core/DicomParsing/FromDcmtkBridge.cpp Core/DicomParsing/FromDcmtkBridge.h Core/DicomParsing/FromDcmtkBridge_TransferSyntaxes.impl.h Resources/GenerateTransferSyntaxesDcmtk.mustache UnitTestsSources/FromDcmtkTests.cpp
diffstat 6 files changed, 416 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Tue Mar 10 10:34:09 2020 +0100
+++ b/CMakeLists.txt	Thu Mar 12 16:08:08 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	Tue Mar 10 10:34:09 2020 +0100
+++ b/Core/DicomParsing/FromDcmtkBridge.cpp	Thu Mar 12 16:08:08 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	Tue Mar 10 10:34:09 2020 +0100
+++ b/Core/DicomParsing/FromDcmtkBridge.h	Thu Mar 12 16:08:08 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	Tue Mar 10 10:34:09 2020 +0100
+++ b/Core/DicomParsing/FromDcmtkBridge_TransferSyntaxes.impl.h	Thu Mar 12 16:08:08 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/Resources/GenerateTransferSyntaxesDcmtk.mustache	Tue Mar 10 10:34:09 2020 +0100
+++ b/Resources/GenerateTransferSyntaxesDcmtk.mustache	Thu Mar 12 16:08:08 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	Tue Mar 10 10:34:09 2020 +0100
+++ b/UnitTestsSources/FromDcmtkTests.cpp	Thu Mar 12 16:08:08 2020 +0100
@@ -1917,6 +1917,147 @@
 
 #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
 
+#include "../Core/DicomFormat/DicomImageInformation.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 IDicomTranscoder* Transcode(std::set<DicomTransferSyntax> syntaxes,
+                                        bool allowNewSopInstanceUid) = 0;
+
+    virtual ImageAccessor* DecodeFrame(unsigned int frame) = 0;
+
+    virtual void GetCompressedFrame(std::string& target,
+                                    unsigned int frame) = 0;
+  };
+
+
+  class DcmtkTranscoder : public IDicomTranscoder
+  {
+  private:
+    std::unique_ptr<DcmFileFormat>          dicom_;
+    DicomTransferSyntax                     transferSyntax_;
+    std::string                             sopClassUid_;
+    std::string                             sopInstanceUid_;
+    DicomMap                                tags_;
+    std::unique_ptr<DicomImageInformation>  info_;
+
+    void Setup(DcmFileFormat* dicom)
+    {
+      dicom_.reset(dicom);
+      
+      if (dicom == NULL ||
+          dicom_->getDataset() == NULL)
+      {
+        throw OrthancException(ErrorCode_NullPointer);
+      }
+
+      DcmDataset& dataset = *dicom_->getDataset();
+
+      tags_.Clear();
+      FromDcmtkBridge::ExtractDicomSummary(tags_, dataset);
+
+      info_.reset(new DicomImageInformation(tags_));
+      
+      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));
+      }
+
+      if (!tags_.LookupStringValue(sopClassUid_, Orthanc::DICOM_TAG_SOP_CLASS_UID, false) ||
+          !tags_.LookupStringValue(sopInstanceUid_, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false))
+      {
+        throw OrthancException(ErrorCode_BadFileFormat,
+                               "Missing SOP class/instance UID in DICOM instance");
+      }
+    }
+    
+  public:
+    DcmtkTranscoder(DcmFileFormat* dicom)  // Takes ownership
+    {
+      Setup(dicom);
+    }
+
+    DcmtkTranscoder(const void* dicom,
+                    size_t size)
+    {
+      Setup(FromDcmtkBridge::LoadFromMemoryBuffer(dicom, size));
+    }
+
+    DcmtkTranscoder(const ParsedDicomFile& dicom)
+    {
+      Setup(new DcmFileFormat(dicom.GetDcmtkObject()));
+    }
+
+    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 info_->GetNumberOfFrames();
+    }
+
+    virtual IDicomTranscoder* Transcode(std::set<DicomTransferSyntax> syntaxes,
+                                        bool allowNewSopInstanceUid) ORTHANC_OVERRIDE
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    virtual ImageAccessor* DecodeFrame(unsigned int frame) ORTHANC_OVERRIDE
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    virtual void GetCompressedFrame(std::string& target,
+                                    unsigned int frame) ORTHANC_OVERRIDE
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+  };
+}
+
+
+
 #include <dcmtk/dcmdata/dcostrmb.h>
 
 static bool Transcode(std::string& buffer,
@@ -1991,44 +2132,79 @@
 #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\n", GetTransferSyntaxUid(transcoder.GetTransferSyntax()),
+         transcoder.GetSopClassUid().c_str(), transcoder.GetSopInstanceUid().c_str(),
+         transcoder.GetFramesCount());
+}
+
 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");
+  }
 }
 
 #endif