changeset 389:7954645ed874

reimplemented jpeg-ls for older version of DCMTK
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 08 Apr 2025 14:47:47 +0200
parents a7194a77c591
children 7c787ee1f3ae
files Framework/ImageToolbox.cpp Framework/ImageToolbox.h Framework/Outputs/DicomPyramidWriter.cpp Framework/Outputs/DicomPyramidWriter.h Framework/Outputs/HierarchicalTiffWriter.cpp Framework/Outputs/HierarchicalTiffWriter.h Framework/Outputs/MultiframeDicomWriter.cpp Framework/Outputs/PyramidWriterBase.cpp Framework/Outputs/PyramidWriterBase.h
diffstat 9 files changed, 107 insertions(+), 100 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/ImageToolbox.cpp	Tue Apr 08 13:11:27 2025 +0200
+++ b/Framework/ImageToolbox.cpp	Tue Apr 08 14:47:47 2025 +0200
@@ -27,13 +27,6 @@
 #include "Jpeg2000Reader.h"
 #include "Jpeg2000Writer.h"
 
-#if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
-#  include <DicomParsing/DcmtkTranscoder.h>
-#  include <DicomParsing/ParsedDicomFile.h>
-#  include <DicomParsing/FromDcmtkBridge.h>
-#  include <dcmtk/dcmdata/dcpxitem.h>
-#endif
-
 #include <Compatibility.h>  // For std::unique_ptr
 #include <OrthancException.h>
 #include <Images/ImageProcessing.h>
@@ -176,6 +169,20 @@
     }
 
 
+    void EncodeUncompressedTile(std::string& target,
+                                const Orthanc::ImageAccessor& source)
+    {
+      unsigned int pitch = GetBytesPerPixel(source.GetFormat()) * source.GetWidth();
+      target.resize(pitch * source.GetHeight());
+
+      const unsigned int height = source.GetHeight();
+      for (unsigned int i = 0; i < height; i++)
+      {
+        memcpy(&target[i * pitch], source.GetConstRow(i), pitch);
+      }
+    }
+
+
     void EncodeTile(std::string& target,
                     const Orthanc::ImageAccessor& source,
                     ImageCompression compression,
@@ -183,22 +190,7 @@
     {
       if (compression == ImageCompression_None)
       {
-        unsigned int pitch = GetBytesPerPixel(source.GetFormat()) * source.GetWidth();
-        target.resize(pitch * source.GetHeight());
-
-        const unsigned int height = source.GetHeight();
-        for (unsigned int i = 0; i < height; i++)
-        {
-          memcpy(&target[i * pitch], source.GetConstRow(i), pitch);
-        }
-      }
-      else if (compression == ImageCompression_JpegLS)
-      {
-#if (ORTHANC_ENABLE_DCMTK_TRANSCODING == 1) && (ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS == 1)
-        CompressFrameUsingDcmtk(target, source, Orthanc::DicomTransferSyntax_JPEGLSLossless);
-#else
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "DCMTK was compiled without support for JPEG-LS");
-#endif
+        EncodeUncompressedTile(target, source);
       }
       else
       {
@@ -420,59 +412,5 @@
                 p[3] == 0xe1);
       }
     }
-
-
-#if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
-    void CompressFrameUsingDcmtk(std::string& target,
-                                 const Orthanc::ImageAccessor& frame,
-                                 Orthanc::DicomTransferSyntax syntax)
-    {
-      if (frame.GetFormat() != Orthanc::PixelFormat_RGB24)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-      }
-
-      Orthanc::ParsedDicomFile dicom(true);
-      dicom.EmbedImage(frame);
-
-      Orthanc::IDicomTranscoder::DicomImage source;
-      source.AcquireParsed(dicom);   // "dicom" is invalid below this point
-
-      Orthanc::IDicomTranscoder::DicomImage transcoded;
-
-      std::set<Orthanc::DicomTransferSyntax> s;
-      s.insert(syntax);
-
-      Orthanc::DcmtkTranscoder transcoder(1);
-
-      if (transcoder.Transcode(transcoded, source, s, true))
-      {
-        DcmPixelSequence* pixelSequence = Orthanc::FromDcmtkBridge::GetPixelSequence(*transcoded.GetParsed().getDataset());
-
-        DcmPixelItem* compressed = NULL;
-        Uint8* data = NULL;
-        if (pixelSequence != NULL &&
-            pixelSequence->card() == 2 &&
-            pixelSequence->getItem(compressed, 1).good() &&
-            compressed != NULL &&
-            compressed->getUint8Array(data).good() &&
-            data != NULL)
-        {
-          const unsigned int size = compressed->getNumberOfValues();
-
-          target.resize(size);
-          if (size != 0)
-          {
-            memcpy(&target[0], data, size);
-          }
-
-          return;  // Success
-        }
-      }
-
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "DCMTK cannot transcode to " +
-                                      std::string(Orthanc::GetTransferSyntaxUid(syntax)));
-    }
-#endif
   }
 }
--- a/Framework/ImageToolbox.h	Tue Apr 08 13:11:27 2025 +0200
+++ b/Framework/ImageToolbox.h	Tue Apr 08 14:47:47 2025 +0200
@@ -58,6 +58,9 @@
                                           unsigned int width,
                                           unsigned int height);
     
+    void EncodeUncompressedTile(std::string& target,
+                                const Orthanc::ImageAccessor& source);
+
     void EncodeTile(std::string& target,
                     const Orthanc::ImageAccessor& source,
                     ImageCompression compression,
@@ -81,11 +84,5 @@
     bool HasPngSignature(const std::string& buffer);
 
     bool HasJpegSignature(const std::string& buffer);
-
-#if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
-    void CompressFrameUsingDcmtk(std::string& target,
-                                 const Orthanc::ImageAccessor& frame,
-                                 Orthanc::DicomTransferSyntax syntax);
-#endif
   }
 }
--- a/Framework/Outputs/DicomPyramidWriter.cpp	Tue Apr 08 13:11:27 2025 +0200
+++ b/Framework/Outputs/DicomPyramidWriter.cpp	Tue Apr 08 14:47:47 2025 +0200
@@ -25,6 +25,7 @@
 #include "DicomPyramidWriter.h"
 
 #include "../DicomToolbox.h"
+#include "../ImageToolbox.h"
 
 #include <Compatibility.h>  // For std::unique_ptr
 #include <Logging.h>
@@ -198,6 +199,21 @@
   }
 
 
+  void DicomPyramidWriter::EncodeTileInternal(std::string& encoded,
+                                              const Orthanc::ImageAccessor& tile)
+  {
+    if (GetImageCompression() == ImageCompression_JpegLS)
+    {
+      // For JPEG-LS, images are first store uncompressed, then transcoded in "MultiframeDicomWriter"
+      ImageToolbox::EncodeUncompressedTile(encoded, tile);
+    }
+    else
+    {
+      ImageToolbox::EncodeTile(encoded, tile, GetImageCompression(), GetJpegQuality());
+    }
+  }
+
+
   DicomPyramidWriter::DicomPyramidWriter(IFileTarget&  target,
                                          const DcmDataset& dataset,
                                          Orthanc::PixelFormat pixelFormat,
--- a/Framework/Outputs/DicomPyramidWriter.h	Tue Apr 08 13:11:27 2025 +0200
+++ b/Framework/Outputs/DicomPyramidWriter.h	Tue Apr 08 14:47:47 2025 +0200
@@ -65,6 +65,9 @@
     {
     }
 
+    virtual void EncodeTileInternal(std::string& encoded,
+                                    const Orthanc::ImageAccessor& tile) ORTHANC_OVERRIDE;
+
   public:
     DicomPyramidWriter(IFileTarget&  target,
                        const DcmDataset& dataset,
--- a/Framework/Outputs/HierarchicalTiffWriter.cpp	Tue Apr 08 13:11:27 2025 +0200
+++ b/Framework/Outputs/HierarchicalTiffWriter.cpp	Tue Apr 08 14:47:47 2025 +0200
@@ -24,6 +24,8 @@
 #include "../PrecompiledHeadersWSI.h"
 #include "HierarchicalTiffWriter.h"
 
+#include "../ImageToolbox.h"
+
 #include <Logging.h>
 #include <OrthancException.h>
 #include <TemporaryFile.h>
@@ -415,6 +417,13 @@
   }
 
 
+  void HierarchicalTiffWriter::EncodeTileInternal(std::string& encoded,
+                                                  const Orthanc::ImageAccessor& tile)
+  {
+    ImageToolbox::EncodeTile(encoded, tile, GetImageCompression(), GetJpegQuality());
+  }
+
+
   HierarchicalTiffWriter::HierarchicalTiffWriter(const std::string& path,
                                                  Orthanc::PixelFormat pixelFormat, 
                                                  ImageCompression compression,
--- a/Framework/Outputs/HierarchicalTiffWriter.h	Tue Apr 08 13:11:27 2025 +0200
+++ b/Framework/Outputs/HierarchicalTiffWriter.h	Tue Apr 08 14:47:47 2025 +0200
@@ -75,6 +75,8 @@
         
     virtual void AddLevelInternal(const Level& level) ORTHANC_OVERRIDE;
 
+    virtual void EncodeTileInternal(std::string& encoded,
+                                    const Orthanc::ImageAccessor& tile) ORTHANC_OVERRIDE;
 
   public:
     HierarchicalTiffWriter(const std::string& path,
--- a/Framework/Outputs/MultiframeDicomWriter.cpp	Tue Apr 08 13:11:27 2025 +0200
+++ b/Framework/Outputs/MultiframeDicomWriter.cpp	Tue Apr 08 14:47:47 2025 +0200
@@ -31,6 +31,10 @@
 #include <Logging.h>
 #include <OrthancException.h>
 
+#if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
+#  include <DicomParsing/DcmtkTranscoder.h>
+#endif
+
 #include <dcmtk/dcmdata/dcuid.h>
 #include <dcmtk/dcmdata/dcdeftag.h>
 #include <dcmtk/dcmdata/dcostrmb.h>
@@ -255,7 +259,8 @@
     // Free the functional group on error
     std::unique_ptr<DcmItem> functionalGroupRaii(functionalGroup);
 
-    if (compression_ == ImageCompression_None)
+    if (compression_ == ImageCompression_None ||
+        compression_ == ImageCompression_JpegLS)
     {
       if (frame.size() != uncompressedFrameSize_)
       {
@@ -317,25 +322,58 @@
       DicomToolbox::SetUint16Tag(*dicom->getDataset(), DCM_InConcatenationNumber, countInstances_);
     }
 
-    switch (compression_)
+    if (compression_ == ImageCompression_JpegLS)
     {
-      case ImageCompression_None:
-        InjectUncompressedPixelData(*dicom);
-        break;
+#if (ORTHANC_ENABLE_DCMTK_TRANSCODING == 1) && (ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS == 1)
+      const Orthanc::DicomTransferSyntax syntax = Orthanc::DicomTransferSyntax_JPEGLSLossless;
+
+      InjectUncompressedPixelData(*dicom);
+
+      Orthanc::IDicomTranscoder::DicomImage source;
+      source.AcquireParsed(dicom.release());  // "dicom" is invalid below this point
+
+      std::set<Orthanc::DicomTransferSyntax> s;
+      s.insert(syntax);
+
+      Orthanc::IDicomTranscoder::DicomImage transcoded;
 
-      case ImageCompression_Jpeg:
-      case ImageCompression_Jpeg2000:
-      case ImageCompression_JpegLS:
-        offsetTable_->createOffsetTable(*offsetList_);
-        dicom->getDataset()->insert(compressedPixelSequence_.release());
-        break;
+      Orthanc::DcmtkTranscoder transcoder(1);
+      if (transcoder.Transcode(transcoded, source, s, true))
+      {
+        ResetImage();
+        SaveDicomToMemory(target, transcoded.GetParsed(), transferSyntax_);
+        return;  // Success
+      }
+      else
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "DCMTK cannot transcode to " +
+                                        std::string(Orthanc::GetTransferSyntaxUid(syntax)));
+      }
+#else
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "DCMTK was compiled without support for JPEG-LS");
+#endif
+    }
+    else
+    {
+      switch (compression_)
+      {
+        case ImageCompression_None:
+          InjectUncompressedPixelData(*dicom);
+          break;
 
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
+        case ImageCompression_Jpeg:
+        case ImageCompression_Jpeg2000:
+          offsetTable_->createOffsetTable(*offsetList_);
+          dicom->getDataset()->insert(compressedPixelSequence_.release());
+          break;
 
-    ResetImage();
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+      }
 
-    SaveDicomToMemory(target, *dicom, transferSyntax_);
+      ResetImage();
+
+      SaveDicomToMemory(target, *dicom, transferSyntax_);
+    }
   }
 }
--- a/Framework/Outputs/PyramidWriterBase.cpp	Tue Apr 08 13:11:27 2025 +0200
+++ b/Framework/Outputs/PyramidWriterBase.cpp	Tue Apr 08 14:47:47 2025 +0200
@@ -148,7 +148,8 @@
     const Level level = GetLevel(z);
 
     std::string raw;
-    ImageToolbox::EncodeTile(raw, tile, compression_, jpegQuality_);
+    EncodeTileInternal(raw, tile);
+
     WriteRawTileInternal(raw, level, x, y);
   }
 }
--- a/Framework/Outputs/PyramidWriterBase.h	Tue Apr 08 13:11:27 2025 +0200
+++ b/Framework/Outputs/PyramidWriterBase.h	Tue Apr 08 14:47:47 2025 +0200
@@ -51,6 +51,9 @@
     // This function is invoked before any call to WriteRawTileInternal()
     virtual void AddLevelInternal(const Level& level) = 0;
 
+    virtual void EncodeTileInternal(std::string& encoded,
+                                    const Orthanc::ImageAccessor& tile) = 0;
+
   private:
     boost::mutex          mutex_;   // This mutex protects access to the levels
     Orthanc::PixelFormat  pixelFormat_;