How to interact with 3D Slicer from an external Python environment?

I have attempted to use the pyigtl and slicerio libraries, but neither was successful in displaying the three surfaces of the instrument tip in real-time within 3D Slicer. My requirement is to execute this externally, allowing modifications or displays to 3D Slicer. I can successfully achieve my needs within the Python console of 3D Slicer, but now I need to execute it externally.

I want to interact with the 3D Slicer environment from my Python project but I’m facing issues accessing the interfaces provided by 3D Slicer in an external environment. I would like to post a question on Stack Overflow regarding this issue. Below is the code that outlines my requirements:

import slicer
import vtk

# Replace with the name of the loaded volume node

volume_node = slicer.util.getNode('verse808_seg')

# Get the volume data extent

image_data = volume_node.GetImageData()
extent = image_data.GetExtent()
print("Volume extent:", extent)

# Replace with the coordinates of the point to use as a reference

point = \[1, 1, 1\]  # Your point coordinates

# Get the RASToIJKMatrix of the volume node

ras_to_ijk_matrix = vtk.vtkMatrix4x4()
volume_node.GetRASToIJKMatrix(ras_to_ijk_matrix)

# Convert the RAS coordinates of the point to IJK coordinates

ras_point = \[point\[0\], point\[1\], point\[2\], 1.0\]  # Add homogeneous coordinate
ijk_point = ras_to_ijk_matrix.MultiplyPoint(ras_point)\[:3\]  # Take the first three coordinates, ignoring the homogeneous coordinate

# Print the converted IJK coordinates for debugging

print("IJK Coordinates:", ijk_point)

# Check if the IJK coordinates are within the extent

if (extent\[0\] \<= ijk_point\[0\] \<= extent\[1\] and
extent\[2\] \<= ijk_point\[1\] \<= extent\[3\] and
extent\[4\] \<= ijk_point\[2\] \<= extent\[5\]):
print("IJK point is within the volume extent.")

    # Create a fiducial node and add control points (using fiducials instead)
    fiducial_node = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLMarkupsFiducialNode', 'F')
    fiducial_node.AddControlPoint(ras_point[0], ras_point[1], ras_point[2])
    
    # Update display
    slicer.app.processEvents()
    
    # Get slice nodes
    slice_node_red = slicer.util.getNode('vtkMRMLSliceNodeRed')
    slice_node_yellow = slicer.util.getNode('vtkMRMLSliceNodeYellow')
    slice_node_green = slicer.util.getNode('vtkMRMLSliceNodeGreen')
    
    # Get slice logic
    red_logic = slicer.app.layoutManager().sliceWidget('Red').sliceLogic()
    yellow_logic = slicer.app.layoutManager().sliceWidget('Yellow').sliceLogic()
    green_logic = slicer.app.layoutManager().sliceWidget('Green').sliceLogic()
    
    # Set slice positions and force update the display
    slice_node_red.SetSliceOffset(ras_point[2])      # Z slice
    slice_node_yellow.SetSliceOffset(ras_point[0])   # X slice
    slice_node_green.SetSliceOffset(ras_point[1])    # Y slice
    slicer.app.processEvents()
    
    # Print debugging information
    print("Red slice offset:", slice_node_red.GetSliceOffset())
    print("Yellow slice offset:", slice_node_yellow.GetSliceOffset())
    print("Green slice offset:", slice_node_green.GetSliceOffset())
    
    # Lock slice views to prevent auto-reset
    red_composite_node = red_logic.GetSliceCompositeNode()
    yellow_composite_node = yellow_logic.GetSliceCompositeNode()
    green_composite_node = green_logic.GetSliceCompositeNode()
    
    red_composite_node.SetLinkedControl(False)
    yellow_composite_node.SetLinkedControl(False)
    green_composite_node.SetLinkedControl(False)
    
    # Force refresh the view
    slicer.app.processEvents()
    
    # Add event listeners to ensure slice positions do not reset
    def onSliceNodeModified(caller, event):
        slice_node_red.SetSliceOffset(ras_point[2])      # Z slice
        slice_node_yellow.SetSliceOffset(ras_point[0])   # X slice
        slice_node_green.SetSliceOffset(ras_point[1])    # Y slice
        slicer.app.processEvents()
    
    slice_node_red.AddObserver(vtk.vtkCommand.ModifiedEvent, onSliceNodeModified)
    slice_node_yellow.AddObserver(vtk.vtkCommand.ModifiedEvent, onSliceNodeModified)
    slice_node_green.AddObserver(vtk.vtkCommand.ModifiedEvent, onSliceNodeModified)
    
    # Force refresh the view again
    slicer.app.processEvents()

else:
print("Error: Point is outside the volume extent.")

I can successfully achieve my needs within the Python console of 3D Slicer, but now I need to execute it externally.

You probably don’t need to do any programming. Instead, you can drive slices by a transform by configuring Volume Reslice Driver module (in SlicerIGT extension) and set up all other visualization options that you need, then save the scene in a single .mrb file.

To start navigation, you can start Slicer and load the scene (using slicerio python package) and send the reslicing transform via OpenIGTLink (using pyigtl python package).

Thank you very much for your professional response. I have located the Volume Reslice Driver within the IGT framework and have configured it to display the necessary faces, as shown in the figure. However, I am not clear about the phrase “end the reslicing transform via OpenIGTLink (using the pyigtl python package).” What I am trying to achieve is depicted in the figure: I have placed two surgical instruments into 3D Slicer (the real-time position matrix of the instruments is transmitted via OpenIGTLink). My goal is to display the three faces of the spine at the tip of the needle when it is placed on the spine and to continuously display the sections I need throughout the entire surgical procedure.


I was recommending pyigtl only for sending transforms to Slicer from an external Python environment. If you already have a solution streaming your transform into Slicer (e.g., using PLUS) then you are good.

Volume Reslice Driver can do this.

I don’t understand this part yet. How would the software know which slices you need? Would you manually move the slices (e.g., Shift + Mouse move)?

Thank you for your guidance. I have been using pyigtl to transmit the spatial position matrix of the surgical instruments into 3D Slicer. However, when I place the tip of the surgical instrument on the CT model of the spine, the desired sections are not displayed. Are there any additional steps I need to perform?

Regarding the continuous display of the sections I need throughout the entire surgical procedure, the slices I need will be saved in the scene, and then imported and confirmed before surgery using slicerio or pyigtl.

Additionally, you previously mentioned that the Volume Reslice Driver module can provide micromotion warnings. I would like to implement this micromotion warning during surgery. The original plan was to mark a reference object on the human skin to achieve micromotion warning during the procedure. I am not clear on how the Volume Reslice Driver module can achieve this.

Yes, you need to perform a standard tooltip calibration (typically using pivot calibration) and patient registration (usually by landmark registration). All these are described in step-by-step tutorials in SlicerIGT training page.

The whole image volume is saved in the scene, so you don’t need to save specific slices. It is sufficient if you save a surgical plan as a transform (e.g., the ToolTipToReference transform when the tooltip is at the planned position). You can then use this transform with Volume Reslice driver to show the corresponding slices.

I’m not sure what you mean exactly by micromotion warnings. If you want to visualize small motion measured by a position tracker then Slicer provides many visualization tools for this (for example, show the original and transformed tool position in 2D and 3D views), but it is not obvious how Volume Reslice Driver would be useful for this.

Thank you for your guidance and responses. I have a question regarding the 3D Slicer Volume Reslice Driver module. When displaying sections, should the ‘Driver’ be selected as ‘Transform’ or does it impact the images in some way? I have not been able to locate the ‘Transform’ option. The ‘Transform’ refers to the matrix of my surgical instrument, and I have created a ‘NeedleModel’.

The driver transform determines the position and orientation of the slice view. It does not impact any other node in any way.

This sounds good. Make sure your transform node’s class is vtkMRMLLinearTransformNode and then you can select it in the Driver combobox.

Thank you very much for your professional response. I now know what to do.