changeset 4529:5774fe497ff2

fix decoding of images on big-endian architectures
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 24 Feb 2021 21:06:34 +0100
parents 93a51d228d80
children e40148c916b8
files OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp OrthancFramework/Sources/Images/ImageProcessing.cpp OrthancFramework/Sources/Images/ImageProcessing.h
diffstat 3 files changed, 120 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp	Wed Feb 24 15:07:54 2021 +0100
+++ b/OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp	Wed Feb 24 21:06:34 2021 +0100
@@ -91,6 +91,7 @@
 #include <dcmtk/dcmdata/dcrleccd.h>
 #include <dcmtk/dcmdata/dcrlecp.h>
 #include <dcmtk/dcmdata/dcrlerp.h>
+#include <dcmtk/dcmdata/dcswap.h>
 
 #if ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS == 1
 #  include <dcmtk/dcmjpeg/djrplol.h>
@@ -122,6 +123,7 @@
 
 namespace Orthanc
 {
+  static const Endianness ENDIANNESS = Toolbox::DetectEndianness();
   static const DicomTag DICOM_TAG_CONTENT(0x07a1, 0x100a);
   static const DicomTag DICOM_TAG_COMPRESSION_TYPE(0x07a1, 0x1011);
 
@@ -556,7 +558,25 @@
                                      info.GetWidth() * GetBytesPerPixel(sourceFormat),
                                      buffer + frame * frameSize);
 
-          ImageProcessing::Convert(*target, sourceImage);
+          switch (ENDIANNESS)
+          {
+            case Endianness_Little:
+              ImageProcessing::Convert(*target, sourceImage);
+              break;
+
+            case Endianness_Big:
+            {
+              // We cannot do byte swapping directly on the constant DcmDataset
+              std::unique_ptr<ImageAccessor> copy(Image::Clone(sourceImage));
+              ImageProcessing::SwapEndianness(*copy);
+              ImageProcessing::Convert(*target, *copy);
+              break;
+            }
+
+            default:
+              throw OrthancException(ErrorCode_InternalError);
+          }
+            
           ImageProcessing::ShiftRight(*target, info.GetShift());
           fastVersionSuccess = true;
         }
@@ -660,6 +680,29 @@
   }
 
 
+  static void UndoBigEndianSwapping(ImageAccessor& decoded)
+  {
+    if (ENDIANNESS == Endianness_Big &&
+        decoded.GetFormat() == PixelFormat_Grayscale8)
+    {
+      /**
+       * Undo the call to "swapIfNecessary()" that is done in
+       * "dcmjpeg/libsrc/djcodecd.cc" and "dcmjpls/libsrc/djcodecd.cc"
+       * if "jpeg->bytesPerSample() == 1", presumably because DCMTK
+       * plans for DICOM-to-DICOM conversion
+       **/
+      if (decoded.GetPitch() % 2 == 0)
+      {
+        swapBytes(decoded.GetBuffer(), decoded.GetPitch() * decoded.GetHeight(), sizeof(uint16_t));
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_InternalError, "Cannot swap the bytes of an image that has an odd width");
+      }
+    }
+  }
+  
+
   ImageAccessor* DicomImageDecoder::Decode(DcmDataset& dataset,
                                            unsigned int frame)
   {
@@ -710,7 +753,9 @@
           throw OrthancException(ErrorCode_InternalError);
       }
     
-      return ApplyCodec(*decoder, parameters, representationParameter, dataset, frame);
+      std::unique_ptr<ImageAccessor> result(ApplyCodec(*decoder, parameters, representationParameter, dataset, frame));
+      UndoBigEndianSwapping(*result);  // New in Orthanc 1.9.1 to decode on big-endian architectures
+      return result.release();
     }
 #endif
 
@@ -771,8 +816,10 @@
         default:
           throw OrthancException(ErrorCode_InternalError);
       }
-    
-      return ApplyCodec(*decoder, parameters, representationParameter, dataset, frame);      
+
+      std::unique_ptr<ImageAccessor> result(ApplyCodec(*decoder, parameters, representationParameter, dataset, frame));
+      UndoBigEndianSwapping(*result);  // New in Orthanc 1.9.1 to decode on big-endian architectures
+      return result.release();
     }
 #endif
 
--- a/OrthancFramework/Sources/Images/ImageProcessing.cpp	Wed Feb 24 15:07:54 2021 +0100
+++ b/OrthancFramework/Sources/Images/ImageProcessing.cpp	Wed Feb 24 21:06:34 2021 +0100
@@ -2636,4 +2636,71 @@
       }
     }
   }
+
+
+  void ImageProcessing::SwapEndianness(ImageAccessor& image /* inplace */)
+  {
+    const unsigned int width = image.GetWidth();
+    const unsigned int height = image.GetHeight();
+    
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+      case PixelFormat_RGB24:
+      case PixelFormat_RGBA32:
+      case PixelFormat_BGRA32:
+        // No swapping required
+        break;
+
+      case PixelFormat_Grayscale16:
+      case PixelFormat_SignedGrayscale16:
+        for (unsigned int y = 0; y < height; y++)
+        {
+          uint8_t* t = reinterpret_cast<uint8_t*>(image.GetRow(y));
+          for (unsigned int x = 0; x < width; x++)
+          {
+            uint8_t a = t[0];
+            t[0] = t[1];
+            t[1] = a;
+            t += 2;
+          }
+        }    
+        break;
+
+      case PixelFormat_Grayscale32:
+      case PixelFormat_Float32:
+        for (unsigned int y = 0; y < height; y++)
+        {
+          uint8_t* t = reinterpret_cast<uint8_t*>(image.GetRow(y));
+          for (unsigned int x = 0; x < width; x++)
+          {
+            uint8_t a = t[0];
+            uint8_t b = t[1];
+            t[0] = t[3];
+            t[1] = t[2];
+            t[2] = b;
+            t[3] = a;
+            t += 4;
+          }
+        }    
+        break;
+
+      case PixelFormat_RGB48:  // uint16_t per channel
+        for (unsigned int y = 0; y < height; y++)
+        {
+          uint8_t* t = reinterpret_cast<uint8_t*>(image.GetRow(y));
+          for (unsigned int x = 0; x < 3 * width; x++)
+          {
+            uint8_t a = t[0];
+            t[0] = t[1];
+            t[1] = a;
+            t += 2;
+          }
+        }    
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }          
+  }
 }
--- a/OrthancFramework/Sources/Images/ImageProcessing.h	Wed Feb 24 15:07:54 2021 +0100
+++ b/OrthancFramework/Sources/Images/ImageProcessing.h	Wed Feb 24 21:06:34 2021 +0100
@@ -200,5 +200,7 @@
 
     // https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion
     static void ConvertJpegYCbCrToRgb(ImageAccessor& image /* inplace */);
+
+    static void SwapEndianness(ImageAccessor& image /* inplace */);
   };
 }