# HG changeset patch # User Sebastien Jodogne # Date 1584025688 -3600 # Node ID 33c19a6643e11cdd89069cc47f62aa76d43cb2bd # Parent c1550e710410792b1d0c44339affa46a900151b0 creating IDicomTranscoder abstraction diff -r c1550e710410 -r 33c19a6643e1 CMakeLists.txt --- 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) diff -r c1550e710410 -r 33c19a6643e1 Core/DicomParsing/FromDcmtkBridge.cpp --- 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 } diff -r c1550e710410 -r 33c19a6643e1 Core/DicomParsing/FromDcmtkBridge.h --- 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); }; } diff -r c1550e710410 -r 33c19a6643e1 Core/DicomParsing/FromDcmtkBridge_TransferSyntaxes.impl.h --- 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; + } + } } diff -r c1550e710410 -r 33c19a6643e1 Resources/GenerateTransferSyntaxesDcmtk.mustache --- 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; + } + } } diff -r c1550e710410 -r 33c19a6643e1 UnitTestsSources/FromDcmtkTests.cpp --- 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 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 dicom_; + DicomTransferSyntax transferSyntax_; + std::string sopClassUid_; + std::string sopInstanceUid_; + DicomMap tags_; + std::unique_ptr 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(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 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 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 + + +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 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 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