Help on 3D Slicer crash when volume node's image data is invalid

Hi Andras, I have tried on UnstructuredGridToVolume and refer to the code in below link: SlicerNotebookDemos/UnstructuredGridToVolume.ipynb at master · lassoan/SlicerNotebookDemos · GitHub
I believe the code is exactly the same except the input unstructuredGrid vtk file is generated by myself. Because in my case, I need convert ploydata to unstructuredGrid.

step 1: Generate the UnstructuredGrid vtk file in 3D Slicer python console, the code as blow:

v = slicer.util.getNode('View1')
v.SetBoxVisible(False)

# Sphere
sphereSource = vtk.vtkSphereSource()
sphereSource.SetCenter(0,0,0)
sphereSource.SetRadius(5.0)
sphereSource.Update()
sphereSource = sphereSource.GetOutput() # vtkPolyData()

# Create a model with the sphere and add it to scene
modelNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLModelNode')
modelNode.SetAndObservePolyData(sphereSource)
modelNode.CreateDefaultDisplayNodes()
modelNode.SetName('My sphere')

# Append the meshes
appendFilter = vtk.vtkAppendPolyData()
appendFilter.AddInputData(sphereSource)
appendFilter.Update()

ugrid = vtk.vtkUnstructuredGrid()
ugrid.DeepCopy(appendFilter.GetOutput())

writer = vtk.vtkUnstructuredGridWriter()
writer.SetFileTypeToASCII()
writer.SetFileName('unstructuredGrid.vtu')
writer.SetInputData(ugrid)
writer.Write()

after the above step, I got the generated unstructuredGrid file with the name ‘unstructuredGrid.vtu’

step 2: Use the generated UnstructuredGrid file as the input and converting it to volume. The code as below:

import math
# Load sample unstructured grid

def calculate_dimensions(bounds, spacing):
    # compute dimensions
    dim = [0]*3
    for i in range(3):
        dim[i] = int(math.ceil((bounds[i * 2 + 1] - bounds[i * 2]) / spacing[i])) + 1
        if dim[i] < 1:
            dim[i] = 1
    return dim

sampleModelFile = 'unstructuredGrid.vtu'
modelNode = slicer.util.loadModel(sampleModelFile)

# Set output volume properties
volumeNodeName = "MyNewVolume"
voxelType = vtk.VTK_FLOAT

# Create an empty output volume
bounds=[0,0,0,0,0,0]
modelNode.GetBounds(bounds)
spacing = [0]*3 # desired volume spacing
spacing[0] = 0.5
spacing[1] = 0.5
spacing[2] = 0.5

imageSize = calculate_dimensions(bounds, spacing)
imageOrigin = [bounds[0], bounds[2], bounds[4]]
imageSpacing = [(bounds[1]-bounds[0])/imageSize[0], (bounds[3]-bounds[2])/imageSize[1], (bounds[5]-bounds[4])/imageSize[2]]
imageDirections = [[1,0,0], [0,1,0], [0,0,1]]
imageData = vtk.vtkImageData()

imageData.SetDimensions(imageSize)
imageData.AllocateScalars(voxelType, 1)
# Create volume node
volumeNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLScalarVolumeNode", volumeNodeName)
volumeNode.SetOrigin(imageOrigin)
volumeNode.SetSpacing(imageSpacing)
volumeNode.SetIJKToRASDirections(imageDirections)
volumeNode.SetAndObserveImageData(imageData)
volumeNode.CreateDefaultDisplayNodes()

# Sample the mesh at each voxel of the image volume

# Set up matrices to put model points into ijk space of volume
# This assumes points are in RAS space of volume (i.e. RAS==world)
transformer = vtk.vtkTransformFilter()
transformer.SetInputConnection(modelNode.GetMeshConnection())
matrixRasToIjk = vtk.vtkMatrix4x4()
volumeNode.GetRASToIJKMatrix(matrixRasToIjk)
transformRasToIjk = vtk.vtkTransform()
transformRasToIjk.SetMatrix(matrixRasToIjk)
transformer.SetTransform(transformRasToIjk)

probe = vtk.vtkProbeFilter()
probe.SetSourceConnection(transformer.GetOutputPort())
probe.SetInputConnection(volumeNode.GetImageDataConnection())
probe.Update()
volumeNode.SetAndObserveImageData(probe.GetOutput())

# Set colormap for volume display
volumeNode.GetDisplayNode().SetAndObserveColorNodeID(getNode('Inferno').GetID())
# Show all sides of the mesh
modelNode.GetDisplayNode().BackfaceCullingOff()
# Show volume in slice viewers
slicer.util.setSliceViewerLayers(background=volumeNode)
sliceLogics = slicer.app.layoutManager().mrmlSliceLogics()
for i in range(sliceLogics.GetNumberOfItems()):
    sliceLogics.GetItemAsObject(i).FitSliceToAll()

After step 2, 3D Slicer crashed without any prompt. And I cannot find any clue from the logs.
is there any wrong with the code? Thanks a lot for your great help!

Polydata is a surface mesh, it is not an unstructured grid. Don’t attempt to write it using an unstructured grid writer. Slicer should not crash when you read an email invalid file, so I will fix that.

You can convert polydata to volume using segmentation.

See example of creating segment from sphere source here. Around there you can find examples that show how to export segmentation to volume.

1 Like

Thank you so much for the quick reply! I will try it.

I’ve checked and latest Slicer Stable and Preview Releases properly report that the file is invalid and do not crash. Can you reproduce the crash with a latest release? If you execute the code line by line, which line causes the crash?

Yes, it is crashed on both 4.11 and 4.13 version, I don’t know if the version 4.13 is the latest one, it is a build version serveral weeks ago. After step 2, I have copied the code line by line in 3D Slicer python console, when execute the below line:

Show volume in slice viewers

slicer.util.setSliceViewerLayers(background=volumeNode)

The 3D Slicer crashed and quit. I have also tried the version 4.13 in Visual Studio 2019 debug mode. And it reported exception:

in vtkReslicePermuteExecute captured scalar is nullptr.

If anything needed please feel free to tell me.

Thanks a lot for your help!

I see. I don’t think there is a chance we can capture such low level errors. You can always find execution paths that are not protected from invalid inputs. In this case the volume node’s input is invalid in a very strange way. Using the volume node’s image data both as input and output of vtkProbeFilter does not look good; and due to the not loaded model, the probe filter’s source connection contains invalid input, too.

OK. I got it. Thank you!