changeset 1746:4bedae500652

Automatically compute SliceThickness from GridFrameOffsetVector when SliceThickness is not present in the DICOM tags
author Benjamin Golinvaux <bgo@osimis.io>
date Fri, 12 Feb 2021 10:41:17 +0100
parents 04019fd631aa
children 730604db88b8
files OrthancStone/Sources/Toolbox/DicomInstanceParameters.cpp
diffstat 1 files changed, 43 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancStone/Sources/Toolbox/DicomInstanceParameters.cpp	Fri Feb 12 10:40:18 2021 +0100
+++ b/OrthancStone/Sources/Toolbox/DicomInstanceParameters.cpp	Fri Feb 12 10:41:17 2021 +0100
@@ -48,7 +48,8 @@
     {
       Orthanc::Toolbox::ToUpperCase(increment);
 
-      // This is the "Grid Frame Offset Vector" tag (DICOM_TAG_GRID_FRAME_OFFSET_VECTOR)
+      // We only support volumes where the FrameIncrementPointer (0028,0009) (required) contains 
+      // the "Grid Frame Offset Vector" tag (DICOM_TAG_GRID_FRAME_OFFSET_VECTOR)
       if (increment != "3004,000C")
       {
         LOG(WARNING) << "Bad value for the FrameIncrementPointer tags in a multiframe image";
@@ -60,7 +61,7 @@
     if (!LinearAlgebra::ParseVector(target, dicom, Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR) ||
         target.size() != numberOfFrames)
     {
-      LOG(INFO) << "The frame offset information is missing in a multiframe image";
+      LOG(ERROR) << "The frame offset information (GridFrameOffsetVector (3004,000C)) is missing in a multiframe image";
 
       // DO NOT use ".clear()" here, as the "Vector" class doesn't behave like std::vector!
       target.resize(0);
@@ -109,9 +110,13 @@
       height_ = 0;
      }
 
+
+    bool sliceThicknessPresent = true;
     if (!dicom.ParseDouble(sliceThickness_, Orthanc::DICOM_TAG_SLICE_THICKNESS))
     {
+      LOG(INFO) << "The (non-madatory) slice thickness information is missing in a multiframe image";
       sliceThickness_ = 100.0 * std::numeric_limits<double>::epsilon();
+      sliceThicknessPresent = false;
     }
 
     GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dicom);
@@ -127,6 +132,42 @@
     if (numberOfFrames_ > 1)
     {
       ExtractFrameOffsets(frameOffsets_, dicom, numberOfFrames_);
+
+      // if the slice thickness is unknown, we try to infer it from the sequence of grid frame offsets
+      // this only works if:
+      // - the first offset is 0.0 (case (a) of http://dicom.nema.org/medical/Dicom/2017c/output/chtml/part03/sect_C.8.8.3.2.html)
+      // - the offsets are all equal, to some small tolerance
+      // - the offsets is positive (increasing throughout the frames)
+      if (!sliceThicknessPresent)
+      {
+        if (frameOffsets_.size() >= 2)
+        {
+          double sliceThickness = frameOffsets_[1] - frameOffsets_[0];
+          bool sameSized = true;
+          if (sliceThickness > 0)
+          {
+            for (size_t i = 2; i < frameOffsets_.size(); ++i)
+            {
+              double currentThickness = frameOffsets_[i] - frameOffsets_[i-1];
+              if (!LinearAlgebra::IsNear(sliceThickness, currentThickness))
+              {
+                LOG(ERROR) << "Unable to extract slice thickness from GridFrameOffsetVector (3004,000C) (reason: varying spacing)";
+                sameSized = false;
+                break;
+              }
+            }
+            if (sameSized)
+            {
+              sliceThickness_ = sliceThickness;
+              LOG(INFO) << "SliceThickness was not specified in the Dicom but was inferred from GridFrameOffsetVector (3004,000C).";
+            }
+          }
+        }
+        else
+        {
+          LOG(ERROR) << "Unable to extract slice thickness from GridFrameOffsetVector (3004,000C) (reason: GridFrameOffsetVector not present or too small)";
+        }
+      }
     }
     else
     {