diff OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp @ 4777:3b78ba359db3

Support detection of windowing and rescale in Philips multiframe images
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 30 Aug 2021 11:41:05 +0200
parents 9f207131c7f4
children 235d3a9f8dec
line wrap: on
line diff
--- a/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp	Mon Aug 30 10:25:50 2021 +0200
+++ b/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp	Mon Aug 30 11:41:05 2021 +0200
@@ -78,6 +78,7 @@
 #include "../Images/PamReader.h"
 #include "../Logging.h"
 #include "../OrthancException.h"
+#include "../SerializationToolbox.h"
 #include "../Toolbox.h"
 
 #if ORTHANC_SANDBOXED == 0
@@ -1777,14 +1778,116 @@
   }
 
 
-  bool ParsedDicomFile::LookupSubSequence(DicomMap& target,
-                                          const DicomPath& path,
-                                          size_t sequenceIndex) const
+  bool ParsedDicomFile::LookupSequenceItem(DicomMap& target,
+                                           const DicomPath& path,
+                                           size_t sequenceIndex) const
+  {
+    DcmDataset& dataset = *const_cast<ParsedDicomFile&>(*this).GetDcmtkObject().getDataset();
+    return FromDcmtkBridge::LookupSequenceItem(target, dataset, path, sequenceIndex);
+  }
+  
+
+  void ParsedDicomFile::GetDefaultWindowing(double& windowCenter,
+                                            double& windowWidth,
+                                            unsigned int frame) const
   {
     DcmDataset& dataset = *const_cast<ParsedDicomFile&>(*this).GetDcmtkObject().getDataset();
-    return FromDcmtkBridge::LookupSubSequence(target, dataset, path, sequenceIndex);
+
+    const char* wc = NULL;
+    const char* ww = NULL;
+    DcmItem *item1 = NULL;
+    DcmItem *item2 = NULL;
+
+    if (dataset.findAndGetString(DCM_WindowCenter, wc).good() &&
+        dataset.findAndGetString(DCM_WindowWidth, ww).good() &&
+        wc != NULL &&
+        ww != NULL &&
+        SerializationToolbox::ParseFirstDouble(windowCenter, wc) &&
+        SerializationToolbox::ParseFirstDouble(windowWidth, ww))
+    {
+      return;  // OK
+    }
+    else if (dataset.findAndGetSequenceItem(DCM_PerFrameFunctionalGroupsSequence, item1, frame).good() &&
+             item1 != NULL &&
+             item1->findAndGetSequenceItem(DCM_FrameVOILUTSequence, item2, 0).good() &&
+             item2 != NULL &&
+             item2->findAndGetString(DCM_WindowCenter, wc).good() &&
+             item2->findAndGetString(DCM_WindowWidth, ww).good() &&
+             wc != NULL &&
+             ww != NULL &&
+             SerializationToolbox::ParseFirstDouble(windowCenter, wc) &&
+             SerializationToolbox::ParseFirstDouble(windowWidth, ww))
+    {
+      // New in Orthanc 1.9.7, to deal with Philips multiframe images
+      // (cf. private mail from Tomas Kenda on 2021-08-17)
+      return;  // OK
+    }
+    else
+    {
+      Uint16 bitsStored = 0;
+      if (!dataset.findAndGetUint16(DCM_BitsStored, bitsStored).good() ||
+          bitsStored == 0)
+      {
+        bitsStored = 8;  // Rough assumption
+      }
+
+      windowWidth = static_cast<double>(1 << bitsStored);
+      windowCenter = windowWidth / 2.0f;
+    }
   }
+
   
+  void ParsedDicomFile::GetRescale(double& rescaleIntercept,
+                                   double& rescaleSlope,
+                                   unsigned int frame) const
+  {
+    DcmDataset& dataset = *const_cast<ParsedDicomFile&>(*this).GetDcmtkObject().getDataset();
+
+    const char* sopClassUid = NULL;
+    const char* intercept = NULL;
+    const char* slope = NULL;
+    DcmItem *item1 = NULL;
+    DcmItem *item2 = NULL;
+
+    if (dataset.findAndGetString(DCM_SOPClassUID, sopClassUid).good() &&
+        sopClassUid != NULL &&
+        std::string(sopClassUid) == std::string(UID_RTDoseStorage))
+    {
+      // We must not take the rescale value into account in the case of doses
+      rescaleIntercept = 0;
+      rescaleSlope = 1;
+    }
+    else if (dataset.findAndGetString(DCM_RescaleIntercept, intercept).good() &&
+             dataset.findAndGetString(DCM_RescaleSlope, slope).good() &&
+             intercept != NULL &&
+             slope != NULL &&
+             SerializationToolbox::ParseFirstDouble(rescaleIntercept, intercept) &&
+             SerializationToolbox::ParseFirstDouble(rescaleSlope, slope))
+    {
+      return;  // OK
+    }
+    else if (dataset.findAndGetSequenceItem(DCM_PerFrameFunctionalGroupsSequence, item1, frame).good() &&
+             item1 != NULL &&
+             item1->findAndGetSequenceItem(DCM_PixelValueTransformationSequence, item2, 0).good() &&
+             item2 != NULL &&
+             item2->findAndGetString(DCM_RescaleIntercept, intercept).good() &&
+             item2->findAndGetString(DCM_RescaleSlope, slope).good() &&
+             intercept != NULL &&
+             slope != NULL &&
+             SerializationToolbox::ParseFirstDouble(rescaleIntercept, intercept) &&
+             SerializationToolbox::ParseFirstDouble(rescaleSlope, slope))
+    {
+      // New in Orthanc 1.9.7, to deal with Philips multiframe images
+      // (cf. private mail from Tomas Kenda on 2021-08-17)
+      return;  // OK
+    }
+    else
+    {
+      rescaleIntercept = 0;
+      rescaleSlope = 1;
+    }
+  }
+
 
 #if ORTHANC_BUILDING_FRAMEWORK_LIBRARY == 1
   // Alias for binary compatibility with Orthanc Framework 1.7.2 => don't use it anymore