Python segmentation conversion - specifying geometry for binary labelmap

Operating system: Windows
Slicer version: 5.2.2

Hello! I’m trying to alter an existing python script which converts segmentations from an RT Struct dataset to binary labelmaps for export as stls. I’ve found that when I do this manually through the GUI, I get the best result when I use “Advanced Create” in Segmentations, choose the Planar Contour > Closed Surface > Binary Labelmap path, and click “Specify geometry” (it doesn’t matter whether I select the source geometry or not - as long as the spacing is set to 1mm on all axes).

I am trying to replicate this in my script but have had no luck primarily because I have no idea what I’m doing. The current script does this, but it gives me a large stl with a rough surface:

def convert_to_model():
log_info(“Generate Binary Label Map”)
segmentations = slicer.util.getNodesByClass(‘vtkMRMLSegmentationNode’)
segmentations[0].CreateBinaryLabelmapRepresentation()
segmentations[0].SetMasterRepresentationToBinaryLabelmap()

seg = getNode('*RTSTRUCT*')
segmentEditorWidget = slicer.qMRMLSegmentEditorWidget()
segmentEditorWidget.setMRMLScene(slicer.mrmlScene)
segmentEditorNode = slicer.vtkMRMLSegmentEditorNode()

slicer.mrmlScene.AddNode(segmentEditorNode)
segmentEditorWidget.setMRMLSegmentEditorNode(segmentEditorNode)
segmentEditorWidget.setSegmentationNode(seg)

# export segmentation to a model
shNode = slicer.mrmlScene.GetSubjectHierarchyNode()
exportFolderItemId = shNode.CreateFolderItem(shNode.GetSceneItemID(), "Segments")

log_info("Export all segments to models")
slicer.modules.segmentations.logic().ExportAllSegmentsToModels(seg, exportFolderItemId)

Would anyone be able to point me in the direction of what I might do to modify this to use the conversion path/parameters as described above? I’ve spent a long time going through documentation but I’m not quite sure what I’m looking for. I tried using this:

segmentationNode = getNode(“Segmentation”)
labelmapVolumeNode = slicer.mrmlScene.AddNewNodeByClass(“vtkMRMLLabelMapVolumeNode”)
slicer.modules.segmentations.logic().ExportAllSegmentsToLabelmapNode(segmentationNode, labelmapVolumeNode, slicer.vtkSegmentation.EXTENT_REFERENCE_GEOMETRY)

but didn’t get the same result as I did from the UI. Any help would be greatly appreciated!

If you want to skip the direct Planar contour → Closed surface conversion path so that the Planar contour → Ribbon model → Binary labelmap path is used, you can do this:

factory = slicer.vtkSegmentationConverterFactory.GetInstance()
factory.DisableConverterRule('Planar contour', 'Closed surface')

Thank you! The main issue is that I can’t find a way to specify the reference image geometry when creating the binary label map. I have noticed that when I click “specify geometry”, I don’t need to choose a segment, as long as it populates the reference image geometry with generic values like this:


then the conversion works as expected. I haven’t been able to find a way to replicate this with my python script however. I tried making a dummy volume node, adding spacing values to this and setting reference geometry of the segment I’m trying to convert, but it still doesn’t work. Do you have any suggestions for this?

This is how you can specify reference geometry from Python:

referenceGeometryMatrix = #TODO: Specify your direction matrix
extent = [0, 1, 0, 1, 0, 1]  # Arbitrary extent, the "effective extent" will be determined automatically
geometryString = slicer.vtkSegmentationConverter.SerializeImageGeometry(referenceGeometryMatrix, extent)
segmentationNode.GetSegmentation().SetConversionParameter(
  slicer.vtkSegmentationConverter.GetReferenceImageGeometryParameterName(), geometryString)

Thank you so much, I really appreciate the help! I tried using that but am still getting output that is different to what happens when clicking specify geometry on the UI:

This is what I used:
extent = [0,1,0,1,0,1]
matrix = vtk.vtkMatrix4x4()
referenceGeometryMatrix = matrix.Identity()
geometryString = slicer.vtkSegmentationConverter.SerializeImageGeometry(referenceGeometryMatrix, extent)
segmentationNode = getNode(‘RTSTRUCT’)
segmentationNode.GetSegmentation().SetConversionParameter(slicer.vtkSegmentationConverter.GetReferenceImageGeometryParameterName(), geometryString)
segmentationNode.CreateBinaryLabelmapRepresentation()

the console output was:
[VTK] CalculateOutputGeometry: No image geometry specified, default geometry is calculated (0.366345130101627;0;0;-83.49600219726562;0;0.366345130101627;0;355.9289855957031;0;0;0.366345130101627;-47.14999771118164;0;0;0;1;0;299;0;215;0;241;)

[VTK] CalculateOutputGeometry: No image geometry specified, default geometry is calculated (0.8751702253519644;0;0;-128.906005859375;0;0.8751702253519644;0;200.16799926757812;0;0;0.8751702253519644;-114.75;0;0;0;1;0;232;0;253;0;265;)

The resulting representation was still very stepped (it is smooth when specifying geometry with the button on the ui). Do you have any suggestions?

The problem is that you don’t actually set a geometry.

After the call

referenceGeometryMatrix = matrix.Identity()

referenceGeometryMatrix is None. I strongly suggest that you try to run these lines in the Python console and see the output. It is very easy but super important to see the content of the variables, and unfortunately the Identity() function has no return value.

Do you want to set an identity matrix? Then try

referenceGeometryMatrix = vtk.vtkMatrix4x4()

Oh right I see!

That worked perfectly to replicate setting the geometry from the UI (just using an identity matrix). Details for anyone experiencing the same problem:

extent = [0,1,0,1,0,1]
referenceGeometryMatrix = vtk.vtkMatrix4x4()
geometryString = slicer.vtkSegmentationConverter.SerializeImageGeometry(referenceGeometryMatrix, extent)
segmentationNode.GetSegmentation().SetConversionParameter(slicer.vtkSegmentationConverter.GetReferenceImageGeometryParameterName(), geometryString)
segmentationNode.CreateBinaryLabelmapRepresentation()

Thank you SO much for your help, I have been battling this for quite some time!

1 Like