How to load one specific series of a DICOM scanning in Python

Hi everyone.

I have an issue when loading DICOM files.

I have this code, which iteratively opens different scannings of baboon skulls placed in different folders:

import os
from DICOMLib import DICOMUtils

yourpath = r"C:/Users/mario.modesto/Desktop/TEST DICOM SLICE"

# 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))
    
    # 2. Load volume
    # https://slicer.readthedocs.io/en/latest/developer_guide/script_repository.html#display-volume-using-volume-rendering
    logic = slicer.modules.volumerendering.logic()
    volumeNode = slicer.mrmlScene.GetNodeByID('vtkMRMLScalarVolumeNode1')

I have 95 baboon scans (therefore 95 subfolders), and each scan has two series:

  1. A topogram (512x512x1)
  2. Facial bones (512x512x288*) (*although 288 varies from one skull to another)

As an example, next image shows only three baboon scans already loaded

The point in here is that the series which is loaded is always the TOPOGRAM.

So my question is: how can I edit the previous code to import the FACIAL BONES when loading DICOM files rather than the TOPOGRAM?

If you could edit the previous code to adapt this requirement, I would be greatly appreciated.

The loadedNodeIDs will have a list of the ids of all nodes corresponding to that set of dicom instances (based on the default best interpretation). Instead of using the hard-coded vtkMRMLScalarVolumeNode1 as the id, you can use the loaded ids to iterate through the nodes that were loaded. You can either introspect the name of the node, which is based on the series description, or your an use the node attributes that map slices from a volume back to dicom SOPInstanceUIDs that can be used with the dicom database to get any of the instance header values, from which you can decide which series has the data you want.

Thanks for your tip @pieper .

I walked through and now I solved the problem by adding an if/else statement.

I copy here the code I used in case anyone in the future gets into this question:

import os
from DICOMLib import DICOMUtils

yourpath = r"C:/Users/mario.modesto/Desktop/TEST DICOM SLICE"
# yourpath = r"D:/Baboons/slice"

# 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))
    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
    if len(loadedNodeIDs) == 2:
      volumeNode = slicer.mrmlScene.GetNodeByID('vtkMRMLScalarVolumeNode2')
    else:
      volumeNode = slicer.mrmlScene.GetNodeByID('vtkMRMLScalarVolumeNode1')

With print(loadedNodeIDs) and print(len(loadedNodeIDs)) I was able to see which and how many volumes I was having in the scanning. I had two: vtkMRMLScalarVolumeNode1 and vtkMRMLScalarVolumeNode2.

This is why in the last part of the code I added an if/else statement.

Hope this is helpful