changeset 5742:078b724dcbf8

if frame decoding fails, transcode to LittleEndianExplicit using plugins and decode with built-in decoder
author Alain Mazy <am@orthanc.team>
date Thu, 29 Aug 2024 12:33:41 +0200
parents ead98edd5bbf
children 8bb3f2fca242
files NEWS OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp OrthancServer/Resources/Configuration.json OrthancServer/Sources/OrthancConfiguration.cpp OrthancServer/Sources/ServerContext.cpp OrthancServer/Sources/ServerEnumerations.h TODO
diffstat 7 files changed, 62 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Tue Aug 27 15:14:22 2024 +0200
+++ b/NEWS	Thu Aug 29 12:33:41 2024 +0200
@@ -23,6 +23,11 @@
   at the same time.
 * Upgraded dependencies for static builds:
   - curl 8.9.0
+* Added a new fallback when trying to decode a frame: transcode the file using the plugin
+  before decoding the frame.  This solves some issues with JP2K Lossy compression:
+  https://discourse.orthanc-server.org/t/decoding-displaying-jpeg2000-lossy-images/5117
+* Added a new warning that can be disabled in the configuration: W003_DecoderFailure
+
 
 
 Version 1.12.4 (2024-06-05)
--- a/OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp	Tue Aug 27 15:14:22 2024 +0200
+++ b/OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp	Thu Aug 29 12:33:41 2024 +0200
@@ -949,12 +949,12 @@
     {
       throw OrthancException(ErrorCode_NotImplemented,
                              "The built-in DCMTK decoder cannot decode some DICOM instance "
-                             "whose transfer syntax is: " + std::string(GetTransferSyntaxUid(s)));
+                             "whose transfer syntax is: " + std::string(GetTransferSyntaxUid(s)), false /* don't log here*/);
     }
     else
     {
       throw OrthancException(ErrorCode_NotImplemented,
-                             "The built-in DCMTK decoder cannot decode some DICOM instance");
+                             "The built-in DCMTK decoder cannot decode some DICOM instance", false /* don't log here*/);
     }
   }
 
--- a/OrthancServer/Resources/Configuration.json	Tue Aug 27 15:14:22 2024 +0200
+++ b/OrthancServer/Resources/Configuration.json	Thu Aug 29 12:33:41 2024 +0200
@@ -986,7 +986,11 @@
     // your response might be incomplete/inconsistent.
     // You should call patients|studies|series|instances/../reconstruct to rebuild
     // the DB.  You may also check for the "Housekeeper" plugin
-    "W002_InconsistentDicomTagsInDb": true
+    "W002_InconsistentDicomTagsInDb": true,
+
+    // Display a warning message when Orthanc and its plugins are unable
+    // to decode a frame (new in Orthanc 1.12.5).
+    "W003_DecoderFailure": true
   }
 
 }
--- a/OrthancServer/Sources/OrthancConfiguration.cpp	Tue Aug 27 15:14:22 2024 +0200
+++ b/OrthancServer/Sources/OrthancConfiguration.cpp	Thu Aug 29 12:33:41 2024 +0200
@@ -1157,6 +1157,10 @@
         {
           warning = Warnings_002_InconsistentDicomTagsInDb;
         }
+        else if (name == "W003_DecoderFailure")
+        {
+          warning = Warnings_003_DecoderFailure;
+        }
         else
         {
           throw OrthancException(ErrorCode_BadFileFormat, name + " is not recognized as a valid warning name");
--- a/OrthancServer/Sources/ServerContext.cpp	Tue Aug 27 15:14:22 2024 +0200
+++ b/OrthancServer/Sources/ServerContext.cpp	Thu Aug 29 12:33:41 2024 +0200
@@ -1902,13 +1902,52 @@
 
     if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_After)
     {
-      ServerContext::DicomCacheLocker locker(*this, publicId);        
-      return locker.GetDicom().DecodeFrame(frameIndex);
+      ServerContext::DicomCacheLocker locker(*this, publicId);   
+      try
+      { 
+        std::unique_ptr<ImageAccessor> decoded(locker.GetDicom().DecodeFrame(frameIndex));
+
+        if (decoded.get() != NULL)
+        {
+          return decoded.release();
+        }
+      }
+      catch (OrthancException& e)
+      {
+        LOG(INFO) << e.GetDetails();
+      }
     }
-    else
+
+    if (HasPlugins() && GetPlugins().HasCustomTranscoder())
     {
-      return NULL;  // Built-in decoder is disabled
+      // TODO: Store the raw buffer in the DicomCacheLocker
+      std::string dicomContent;
+      ReadDicom(dicomContent, publicId);
+
+      LOG(INFO) << "The plugins and built-in image decoders failed to decode a frame, "
+                << "trying to transcode the file to LittleEndianExplicit using the plugins.";
+      DicomImage explicitTemporaryImage;
+      DicomImage source;
+      std::set<DicomTransferSyntax> allowedSyntaxes;
+
+      source.AcquireBuffer(dicomContent);
+      allowedSyntaxes.insert(DicomTransferSyntax_LittleEndianExplicit);
+
+      if (Transcode(explicitTemporaryImage, source, allowedSyntaxes, true))
+      {
+        std::unique_ptr<ParsedDicomFile> file(explicitTemporaryImage.ReleaseAsParsedDicomFile());
+        return file->DecodeFrame(frameIndex);
+      }
+
     }
+
+    OrthancConfiguration::ReaderLock lock;
+    if (lock.GetConfiguration().IsWarningEnabled(Warnings_003_DecoderFailure))
+    {
+      LOG(WARNING) << "W003: Unable to decode frame " << frameIndex << " from instance " << publicId;
+    }
+
+    return NULL;
   }
 
 
--- a/OrthancServer/Sources/ServerEnumerations.h	Tue Aug 27 15:14:22 2024 +0200
+++ b/OrthancServer/Sources/ServerEnumerations.h	Thu Aug 29 12:33:41 2024 +0200
@@ -206,6 +206,7 @@
     Warnings_None,
     Warnings_001_TagsBeingReadFromStorage,
     Warnings_002_InconsistentDicomTagsInDb,
+    Warnings_003_DecoderFailure,              // new in Orthanc 1.12.5
   };
 
 
--- a/TODO	Tue Aug 27 15:14:22 2024 +0200
+++ b/TODO	Thu Aug 29 12:33:41 2024 +0200
@@ -67,7 +67,7 @@
 * Write a getting started guide (step by step) for each platform to replace
   https://orthanc.uclouvain.be/book/users/cookbook.html :
   - Ubuntu/Debian
-  - Windows
+  - Windows (done)
   - OSX
   - Docker on Linux
   Each step by step guide should contain:
@@ -149,7 +149,7 @@
   https://groups.google.com/g/orthanc-users/c/hsZ1jng5rIg/m/8xZL2C1VBgAJ
 * add an "AutoDeleteIfSuccessful": false option when creating jobs 
   https://discourse.orthanc-server.org/t/job-history-combined-with-auto-forwarding/3729/10
-* Allow skiping automatic conversion of color-space in transcoding/decoding.
+* Allow skipping automatic conversion of color-space in transcoding/decoding.
   The patch that was initialy provided was breaking the IngestTranscoding.
   This might require a DCMTK decoding plugin ?
   https://discourse.orthanc-server.org/t/orthanc-convert-ybr-to-rgb-but-does-not-change-metadata/3533/9