Cropping existing volume with ROI and shifting display via Python scripting

Hi community. I am writing a Python script to automate a pipeline that will be used in around 1000 CT scans.

Now I am able to load 1 DICOM folder and to display Volume rendering with Python

# Load DICOM files
  # https://slicer.readthedocs.io/en/latest/developer_guide/script_repository.html#dicom
dicomDataDir = "C:/Users/mario.modesto/Desktop/DICOM/W111"  # input folder with DICOM files
loadedNodeIDs = []  # this list will contain the list of all loaded node IDs

from DICOMLib import DICOMUtils
with DICOMUtils.TemporaryDICOMDatabase() as db:
  DICOMUtils.importDicom(dicomDataDir, db)
  patientUIDs = db.patients()
  for patientUID in patientUIDs:
    loadedNodeIDs.extend(DICOMUtils.loadPatientByUID(patientUID))

# Display volume rendering
# 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')
displayNode = logic.CreateVolumeRenderingDisplayNode()
displayNode.UnRegister(logic)
slicer.mrmlScene.AddNode(displayNode)
volumeNode.AddAndObserveDisplayNodeID(displayNode.GetID())
logic.UpdateDisplayNodeFromVolumeNode(displayNode, volumeNode)

However, I would like to know, in my progress, two things I am struggling right now. When volume rendering is shown, it is almost all black.

Therefore, I need to move to right the Shift option in Display to show the skull.

How han I introduce in the Python code a different shift value?

Next, as you can see in the CT, there is a platform below the skull, of approximate 2-3 cm. In all my scans this platform exists. So I would like to remove it by cropping this volume (and generating another _cropped) by using ROI.

I tried cropping in empty volume, which is a script in the Slicer documentation. But I do not know how can I apply to an existing volume and creating another.

How can it be done?

Best

Few general comments:

  1. When you are converting/processing 1000s of files, you do not want to interactively do anything, including visualization. So for your purposes visualization step is probably not necessary and will add additional time to your pipeline (but it is total fine, if you are using as a way to learn scripting).

  2. First thing I would do is to interactively create a ROI box from that will crop most of the foam out in one sample, save it to disk, and reload and test it interactively with few other samples to get a sense of how consistent the sample placement, and whether you can use this fixed size ROI for most (if not all) of your samples. If the answer is no (samples have different orientation, origins etc), you will have to find a heuristic that will get you to crop box automatically. This might involve fitting an ROI to the volume, and then reducing in the I plane a few centimeters (again if all your samples are consistently oriented).

this example here shows how to load NRRD file and fit a ROI and resample it to a lower resolution. You can modify it to fit your needs (namely modify the dimensions of the ROI box).

Thanks for the link Murat,

I adapted the code up to here:

# find the files NodeID
volumeNode = getNode('2: Facial Bones  0.75  H70h')

#create a blank Markup ROI
roiNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsROINode")

#set the new markup ROI to the dimensions of the volume
cropVolumeParameters = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLCropVolumeParametersNode")
cropVolumeParameters.SetInputVolumeNodeID(volumeNode.GetID())
cropVolumeParameters.SetROINodeID(roiNode.GetID())
slicer.modules.cropvolume.logic().SnapROIToVoxelGrid(cropVolumeParameters)  # optional (rotates the ROI to match the volume axis directions)
slicer.modules.cropvolume.logic().FitROIToInputVolume(cropVolumeParameters)
slicer.mrmlScene.RemoveNode(cropVolumeParameters)

#set the cropping parameters
cropVolumeLogic = slicer.modules.cropvolume.logic()
cropVolumeParameterNode = slicer.vtkMRMLCropVolumeParametersNode()
cropVolumeParameterNode.SetIsotropicResampling(True)

But I am struggling on how to insert cropping bounds in here. How do I specify to crop only lower bound up to 2 cm?

Answering your question, all 1000 skulls were scanned keeping the same orientation.

You have to look up the API, I don’t actually know the python call to set the ROI bounds @lassoan?

But if you are certain that orientation is consistent, then you don’t have to recreate the ROI every time and fit to the volume. I would adjust one manually, save it, and write the script in a way that will use it (load from the disk).

As @muratmaga said, a saved ROI might be the best approach.

I had a more or less similar workflow with CT angiograms and came up with a custom script. You may find some insight here to set Window/Level, load a known ROI from disk and apply cropping. Full automation should be possible the way you laid out the problem.