Masking settings from a script on a segment editor effect in python

Hi,

I am stuck with this for several days now.

Would like to evaluate regions in a CT via a python script.
Created a module, works ok.

Four regions should be evaluated in my script, which principally works due to the excellent source code example of Andras Lasso, but if I try to mask this work by using a “Right lung mask” or a “Left lung mask” (which I have priorly segmented and which are stored under “Segmentation”), the threshold algorithm is always using the complete data set and does not work within the mask.

Here is the relevant part I added for the right lung (the right lung mask is the first one stored under “Segmentation”).

segmentationNode2 = slicer.util.getNode('Segmentation') segmentEditorNode.SetMaskSegmentID(segmentationNode2.GetSegmentation().GetNthSegmentID(0)) segmentEditorNode.SetOverwriteMode(slicer.vtkMRMLSegmentEditorNode.OverwriteNone) segmentEditorNode.SetMaskMode(slicer.vtkMRMLSegmentEditorNode.PaintAllowedInsideSingleSegment)

Here is the complete source:

def process(self, inputVolume):

if not inputVolume:
  raise ValueError("Input volume is invalid")

import time
startTime = time.time()
logging.info('Processing started')

# Compute 
masterVolumeNode = inputVolume
# Create segmentation
segmentationNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
segmentationNode.CreateDefaultDisplayNodes() # only needed for display
segmentationNode.SetReferenceImageGeometryParameterFromVolumeNode(masterVolumeNode)
#segmentationNode2 = slicer.util.getNode('Segmentation')
#slicer.util.messageBox("Seg Node:" + segmentationNode2.GetSegmentation().GetNthSegmentID(0), dontShowAgainSettingsKey = "MainWindow/DontShowSomeMessage")

# Create temporary segment editor to get access to effects
segmentEditorWidget = slicer.qMRMLSegmentEditorWidget()
segmentEditorWidget.setMRMLScene(slicer.mrmlScene)
segmentEditorNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentEditorNode")
segmentEditorWidget.setMRMLSegmentEditorNode(segmentEditorNode)
segmentEditorWidget.setSegmentationNode(segmentationNode)
segmentEditorWidget.setMasterVolumeNode(masterVolumeNode)  

# Create segments by thresholding
segmentsFromHounsfieldUnits = [
    ["Emphysema right", -1000, -900],
    ["Ventilated right", -900, -600],
    ["Infiltration right", -600, -50],
    ["Collapsed right", -50, 3000] ]
for segmentName, thresholdMin, thresholdMax in segmentsFromHounsfieldUnits:
    # Create segment
    addedSegmentID = segmentationNode.GetSegmentation().AddEmptySegment(segmentName)
    segmentEditorNode.SetSelectedSegmentID(addedSegmentID) 
    # slicer.util.messageBox("Some message", dontShowAgainSettingsKey = "MainWindow/DontShowSomeMessage")
    segmentationNode2 = slicer.util.getNode('Segmentation')
    segmentEditorNode.SetMaskSegmentID(segmentationNode2.GetSegmentation().GetNthSegmentID(0))
    segmentEditorNode.SetOverwriteMode(slicer.vtkMRMLSegmentEditorNode.OverwriteNone)
    segmentEditorNode.SetMaskMode(slicer.vtkMRMLSegmentEditorNode.PaintAllowedInsideSingleSegment)
    # Fill by thresholding
    segmentEditorWidget.setActiveEffectByName("Threshold")
    effect = segmentEditorWidget.activeEffect()
    effect.setParameter("MinimumThreshold",str(thresholdMin))
    effect.setParameter("MaximumThreshold",str(thresholdMax))        
    effect.self().onApply()

# Delete temporary segment editor
segmentEditorWidget = None
slicer.mrmlScene.RemoveNode(segmentEditorNode)

# Compute segment volumes
resultsTableNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLTableNode')
import SegmentStatistics
segStatLogic = SegmentStatistics.SegmentStatisticsLogic()
segStatLogic.getParameterNode().SetParameter("Segmentation", segmentationNode.GetID())
segStatLogic.getParameterNode().SetParameter("ScalarVolume", masterVolumeNode.GetID())
segStatLogic.getParameterNode().SetParameter("LabelmapSegmentStatisticsPlugin.enabled","True")
segStatLogic.getParameterNode().SetParameter("ScalarVolumeSegmentStatisticsPlugin.voxel_count.enabled","False")
segStatLogic.getParameterNode().SetParameter("ScalarVolumeSegmentStatisticsPlugin.volume_mm3.enabled","False")
segStatLogic.computeStatistics()
segStatLogic.exportToTable(resultsTableNode)
segStatLogic.showTable(resultsTableNode)

# Export segmentation to a labelmap
#labelmapVolumeNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLLabelMapVolumeNode')
#slicer.modules.segmentations.logic().ExportVisibleSegmentsToLabelmapNode(segmentationNode, labelmapVolumeNode, masterVolumeNode)
#slicer.util.saveNode(labelmapVolumeNode, "c:/tmp/BodyComposition-label.nrrd")
stopTime = time.time()
logging.info('Processing completed in {0:.2f} seconds'.format(stopTime-startTime))

Thank you for any help.
Best regards
rudolf

You can only edit one segmentation node at a time, so you cannot use mask segment from SegmentationNodeA while editing SegmentationNodeB.

Probably the easiest is to not create a new segmentation node but just add new segments to `segmentationNode2’.

I resolved this in part by using a masked volume (lung only) as the starting volume.

Also thought about better adding segmentations to an existing node, but for some reason

segmentationNode = slicer.mrmlScene.GetFirstNodeByClass("vtkMRMLSegmentationNode")

worked without error message, but the subsequent

   effect.self().onApply()

hangs forever …

Thanks anyway, I am a big slicer fan :slight_smile:

1 Like

Thanks for the update. If you can provide a short script that reproduces the hang then we can investigate.

I tried to reproduce the hang, but its gone. Thank you.

However, I still can not figure out why threshold is not working together with the masking effect in the code above …

The problem with the script above was that you tried to use mask segment from another segmentation node. The mask segment must be in the currently edited segmentation node.

Probably true for the code above, but even if I change (your code)

segmentationNode = slicer.mrmlScene.AddNewNodeByClass(“vtkMRMLSegmentationNode”)

to

segmentationNode = slicer.util.getNode(‘Segmentation’)

Two segmentations (right lung mask, left lung mask) are defined in “Segmentation” beforehand …
I do get the new segmentations in the same node now, but do not get a masked output from “Theshold”

Would really like to make that work.
Here is the adopted code:

def process(self, inputVolume, showResult=True):
“”"
Run the processing algorithm.
Can be used without GUI widget.
:param inputVolume: volume to be thresholded
“”"

if not inputVolume:
  raise ValueError("Input volume is invalid")

import time
startTime = time.time()
logging.info('Processing started')

# Compute 
masterVolumeNode = inputVolume
# Create segmentation
#segmentationNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
#segmentationNode = slicer.mrmlScene.GetFirstNodeByClass("vtkMRMLSegmentationNode")
segmentationNode = slicer.util.getNode('Segmentation')
segmentationNode.CreateDefaultDisplayNodes() # only needed for display
segmentationNode.SetReferenceImageGeometryParameterFromVolumeNode(masterVolumeNode)

# Create temporary segment editor to get access to effects
segmentEditorWidget = slicer.qMRMLSegmentEditorWidget()
segmentEditorWidget.setMRMLScene(slicer.mrmlScene)
segmentEditorNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentEditorNode")

# Do masking
segmentEditorNode.SetMaskSegmentID(segmentationNode.GetSegmentation().GetNthSegmentID(0))
segmentEditorNode.SetOverwriteMode(slicer.vtkMRMLSegmentEditorNode.OverwriteAllSegments)
segmentEditorNode.SetMaskMode(slicer.vtkMRMLSegmentEditorNode.PaintAllowedInsideSingleSegment)

segmentEditorWidget.setMRMLSegmentEditorNode(segmentEditorNode)
segmentEditorWidget.setSegmentationNode(segmentationNode)
segmentEditorWidget.setMasterVolumeNode(masterVolumeNode)  
segmentationDisplayNode=segmentationNode.GetDisplayNode()
segmentation=segmentationNode.GetSegmentation()

# Create segments by thresholding
segmentsFromHounsfieldUnits = [
    ["Emphysema right", -1000, -900,0.0,0.0,0.0],
    ["Ventilated right", -900, -600,0,0.5,1.0],
    ["Infiltration right", -600, -50,1.0,0.5,0],
    ["Collapsed right", -50, 3000,1.0,0,1.0] ]
for segmentName, thresholdMin, thresholdMax, r, g, b in segmentsFromHounsfieldUnits:
    # Create segment
    logging.info('Creating segment.')
    addedSegmentID = segmentationNode.GetSegmentation().AddEmptySegment(segmentName)
    segmentEditorNode.SetSelectedSegmentID(addedSegmentID)
    # Set color
    logging.info('Setting segment color.')
    segmentId = segmentation.GetSegmentIdBySegmentName(segmentName)
    segmentationDisplayNode.SetSegmentOpacity3D(segmentId,0.2)
    segmentation.GetSegment(segmentId).SetColor(r,g,b)  # color should be set in segmentation node
    # Fill by thresholding
    logging.info('Thresholding.')
    segmentEditorWidget.setActiveEffectByName("Threshold")
    effect = segmentEditorWidget.activeEffect()
    effect.setParameter("MinimumThreshold",str(thresholdMin))
    effect.setParameter("MaximumThreshold",str(thresholdMax))        
    effect.self().onApply()



# Change overall segmentation display properties
# segmentationDisplayNode.SetOpacity3D(0.5)


# Delete temporary segment editor
segmentEditorWidget = None
slicer.mrmlScene.RemoveNode(segmentEditorNode)

logging.info('Creating statistics.')
# Compute segment volumes
resultsTableNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLTableNode')
import SegmentStatistics
segStatLogic = SegmentStatistics.SegmentStatisticsLogic()
segStatLogic.getParameterNode().SetParameter("Segmentation", segmentationNode.GetID())
segStatLogic.getParameterNode().SetParameter("ScalarVolume", masterVolumeNode.GetID())
segStatLogic.getParameterNode().SetParameter("LabelmapSegmentStatisticsPlugin.enabled","True")
segStatLogic.getParameterNode().SetParameter("ScalarVolumeSegmentStatisticsPlugin.voxel_count.enabled","False")
segStatLogic.getParameterNode().SetParameter("ScalarVolumeSegmentStatisticsPlugin.volume_mm3.enabled","False")
segStatLogic.computeStatistics()
segStatLogic.exportToTable(resultsTableNode)
segStatLogic.showTable(resultsTableNode)

# Export segmentation to a labelmap
#labelmapVolumeNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLLabelMapVolumeNode')
#slicer.modules.segmentations.logic().ExportVisibleSegmentsToLabelmapNode(segmentationNode, labelmapVolumeNode, masterVolumeNode)
#slicer.util.saveNode(labelmapVolumeNode, "c:/tmp/BodyComposition-label.nrrd")
stopTime = time.time()
logging.info('Processing completed in {0:.2f} seconds'.format(stopTime-startTime))

CTLungAnalyzerTest