Automating thresholding of existing segments

Dear Andras, all,

Thanks again for helping me with extraction segment cross-sectional area.
I was wondering if you could help me with something else.

As I said earlier, I’m trying to find the cross sectional area of total muscle in a single cross-sectional image on CT.

I can create a segment (for instance “muscle”) and manually/rougly draw or paint the area of interest.
After that, I use the thresholding option to select only the area that has hounsfield units between -29 and + 150. In the “masking” menu I select “Inside muscle”, so that Slicer only “subtracts” the irrelevant area from what I manually delineated. I can then use the segment cross-section area option you provided to calculate the area.

I want to try to use the python scripting to do the thresholding step. I am trying to use the script over here https://gist.github.com/lassoan/5ad51c89521d3cd9c5faf65767506b37 as a basis but I can’t seem to edit it so that it does what I need. (It will only create new segmentations in the entire scan, but will not change the segmentation that I created)

Can you help me to automate this step?

Thanks so much in advance,
Justin

If you don’t want to create new segments but update existing ones then instead of using AddEmptySegment, switch to an existing segment by calling setCurrentSegmentID (see complete example here).

Dear Andras,

Thanks for your suggestion, I have started working with your suggestion and the example that you show.
Right now I have two “phases”, one creates the three segments that I will be painting by hand.

After painting the segment, the second one applies the thresholding to the existing segments using
setCurrentSegmentID. However it wil then apply the threshold to the entire scan and not just the selected segment. When I manually perform this part i select the “Masking > select editable area > inside segmentname” option separately for each segment.
Can you help me with the code that sets the masking settings within each segment?
I’m sure the code I’m using below will contain redundant steps, but at least this way I got it to work…

Best, Justin

## part 1: creating segments
# Create segmentation
segmentationNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
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")
segmentEditorWidget.setMRMLSegmentEditorNode(segmentEditorNode)
segmentEditorWidget.setSegmentationNode(segmentationNode)
segmentEditorWidget.setMasterVolumeNode(masterVolumeNode)

# Create segments by thresholding
segmentsFromHounsfieldUnits = [
    ["Pec L", -29, 150],
    ["Pec R", -29, 150],
    ["Back muscle", -29, 150] ]
for segmentName, thresholdMin, thresholdMax in segmentsFromHounsfieldUnits:
    # Create segment
    addedSegmentID = segmentationNode.GetSegmentation().AddEmptySegment(segmentName)
    segmentEditorNode.SetSelectedSegmentID(addedSegmentID)

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

## part 2: auto thresholding the hand-painted segments
# 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 = [
    ["Pec L", -29, 150],
    ["Pec R", -29, 150],
    ["Back muscle", -29, 150] ]
for segmentName, thresholdMin, thresholdMax in segmentsFromHounsfieldUnits:
    # Fill by thresholding
    segmentEditorWidget.setCurrentSegmentID(addedSegmentID)
    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)

I have looked up some more information and found that I can use
slicer.vtkMRMLSegmentEditorNode.PaintAllowedInsideSingleSegment

This segment should be defined in SetMaskSegmentID

So I have changed the script to this:

# 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)
segmentEditorNode.PaintAllowedInsideSingleSegment

# Create segments by thresholding
segmentsFromHounsfieldUnits = [
    ["Pec L", -29, 150],
    ["Pec R", -29, 150],
    ["Back muscle", -29, 150] ]
for segmentName, thresholdMin, thresholdMax in segmentsFromHounsfieldUnits:
    # Fill by thresholding
    segmentEditorWidget.setCurrentSegmentID(addedSegmentID)
    segmentEditorWidget.setActiveEffectByName("Threshold")
    segmentEditorNode.SetMaskSegmentID(addedSegmentID)
    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)

Unfortunately, this still has the same effect in that it still applies the effect on the entire scan. Do you have suggestions?

Justin

segmentEditorNode.PaintAllowedInsideSingleSegment is just a named constant. You can use this constant as an input in SetMaskMode method. If you use this mode then you also need to set a mask segment ID.

Thanks for your help Andras! I finally got the script working as it should.

Best regards, Justin

1 Like