Load specific DICOM Data with Python in Slicer 5.6.1

I created this script in Python to load iteratively different CTs stored in different folders under a same root folder.

In some CTs I came across something unexpected. In the example of next image I loaded Patient W-584, selected the Study date 20080521 and the Series #2.
If I check the lower right checkbox “ADVANCED” and click on the button Examine, I only want to load what is squared in red in the image.

This is the script I created to load iteratively the CTs:

import os
from DICOMLib import DICOMUtils

yourpath = r"C:/Users/mario.modesto/Desktop/test"

# walk through DICOM directory
# https://stackoverflow.com/questions/77865010/run-python-script-in-each-subfolder-automatically
for dir in os.scandir(yourpath):
    # Load DICOM files
    dicomDataDir = dir.path  # path to input folder with DICOM files
    baboon_skull  = dir.name
    loadedNodeIDs = []  # this list will contain the list of all loaded node IDs

    # 1. Load DICOM files
    # https://slicer.readthedocs.io/en/latest/developer_guide/script_repository.html#dicom
    with DICOMUtils.TemporaryDICOMDatabase() as db:
        DICOMUtils.importDicom(dicomDataDir, db)
        patientUIDs = db.patients()
        for patientUID in patientUIDs:
            loadedNodeIDs.extend(DICOMUtils.loadPatientByUID(patientUID))
    # Next two lines introduced to know number which volumes and how many are
    # This is important to "2. Load volume", the "if-else" statement
    print(loadedNodeIDs)
    print(len(loadedNodeIDs))

    # 2. Load volume
    # https://slicer.readthedocs.io/en/latest/developer_guide/script_repository.html#display-volume-using-volume-rendering
    logic = slicer.modules.volumerendering.logic()
    # This "if-else" is to select the second volume to skip the Topogram, which is the first
    # When there are two volumes, the topogram is the first and the full skull is the second
    if len(loadedNodeIDs) == 1:
      volumeNode = slicer.mrmlScene.GetNodeByID('vtkMRMLScalarVolumeNode1')
    else:
      volumeNode = slicer.mrmlScene.GetNodeByID('vtkMRMLScalarVolumeNode2')

As you can see in the #2. Load volume, I created a if/else statement because in some cases, the CTs have 2 sereies, and depending on the number, I select either the first or second.

However, how can I include in the code to load that specific DICOM data which is always named identically (2: InnerEarSeq 0.6 U75u)?

The message indicates irregular slice spacing (could be missing slices or it might have been acquired with variable table motion for some reason). It means Slicer will generate an acquisition transform to regularize the geometry and thus you get back two loaded nodes.

You can look at the loaded volume for that series and if you agree that the acquisition transform is the right way to fix it you can add something like this to your script:

            volumeNode = slicer.util.getNode(loadedNodeIDs[0])
            if len(loadedNodeIDs) == 2 and loadedNodeIDs[1].find("GridTransform") != -1:
                volumeNode.HardenTransform()

I tried this and didn’t work @pieper

I know this warning is present (btw, I am sure the correct value is 0.60), but my question is before this part, on the loading process of this DICOM CTs.

In order to replicate what I am facing loading it, the CT I am using is open source on MorphoSource. You can download the DICOM files in this link:

https://www.morphosource.org/concern/media/000058941?locale=en

When you import the DICOM, it creates a lot of volumes, as specified in the original question above, rather than one single volume with all the CTs inside.

Can you please download it and try to load the skull CT of this baboon?

Best

This data set is not a single 3D acquisition and the spacing is neither 0.5 nor 0.6. The data is acquired as 52 images, within each image the spacing is 0.6mm, but between the images the spacing is only 0.5mm. If you assume overall uniform spacing then the spacing is about 0.58mm. If the slice position fields in the DICOM header are correct then the image has varying slice spacing.

It is suspicious that maybe some inaccurate post-processing was applied on the data, because the slice positions along the third axis were specified with very low precision - just 1 digit after the decimal point - while 9 digits were used for positions along the first two axes.

Overall, Slicer handled these issues well, generally staying on the safe side (not silently ignoring inaccuracies or changing the data without asking). I agree that the user experience was not ideal, because you had to enable advanced mode and change some settings - but it was all due to trying to load the data set as a single uniformly-spaced 3D volume, while the data set is actually something else.

We introduced the DICOM Patcher module to allow fixing commonly occurring issues before a data set is imported into Slicer. If you often come across data sets that are messed up like this then we could add a new rule that could force the same acquisition number to all instances in the same series. Also, if you get confirmation from the authors of the data set or from the scanner manufacturer that the spacing was actually uniform (0.5, 0.6, or 0.58) and it was just not correctly written into the DICOM file, then you could add a rule for correcting the slice position values automatically.

If you want to automatically harden the acquisition transform on the volume after import then you can enable that in menu: Edit / Application settings / DICOM / Acquisition geometry regularization → Harden regularization transform.

Hi Andras,

I just emailed the Data Manager to know which is the spacing.

However, I still wonder how can I code if finally is 0.58 in Python. Any idea?

Best

I would then recommend to add a rule to DICOM patcher module that fixes the files. You can then import and load the image as a 3D volume normally.

Hi Andras, thanks for the tip. Can you share a link where I can learn on how to add a DICOM patcher?

The DICOM Patcher module is a simple Python script that iterates through all files in a folder and applies a set of rules on it (when it starts a processing, when it starts processing each folder, each file, etc. see details here). You can add your own rule by adding a GUI option and a new rule class as it is done here. It is possible that you need to perform some action at the very end (e.g., you may want to collect all position values, analyze them, and rewrite all in the end) - in this case you can add a new method to the rule class and call that in the end.

Hi Andras, thanks. I will check it tomorrow.

Best

Hi Andras, thank you for the suggestions. Based on looking at the examples you provided, does that mean we can use the functions from pydicom to manually set up a proper spacing like 0.58 for loading all the dataset from the same dataset? Hope my understanding is all right, though I haven’t figured out how to do it yet.

Yes, if you get confirmation from the scanner manufacturer or the technician who acquired the scan that the slice positions were incorrectly written to the file (or you consider up to 0.1mm error negligible) then you can create a rule that automatically detects such images and updates the slice positions in them.

If you are unsure if the slice positions are correct or not then probably the best is to only modify the acquisition number in the files, let Slicer create then regularization transform, and harden it on the image.

1 Like

Hi Andras, sorry for the late response. I am using this snippet to modify the Slice thickness of each dcm image into 0.6 and image pixels into 0.292969: test/test_py_dicom.py at main · chz31/test · GitHub.
I still got the same warning of 0.6 spacing was expected, 0.5 spacing was found between files.

The sample data I used is the same dataset @paleomariomm provided https://www.morphosource.org/concern/media/000058941?locale=en

Slice Thickness is characterizing how well the imaging was focused on the slice plane. It is not related the slice’s position and not used for determining the image geometry. I would recommend not to touch this field.

If you want to alter the slice spacing then you need to modify the Image Position Patient field.

1 Like

Thank you! I’ll look into it.