Scissors segment editor effects from a script

Hello, I am using this script example and its already working fine for the threshold step I needed to segment my data. I need also to apply the Scissors circle effect, from the axial view, using the following options:

  • operation: erase outside;
  • shape: circle
  • slice cut: unlimited

I already have the coordinates for the circle center and it’s radius.

I apologise if I am asking too much, but I am new to the software and don’t know where to look for the options names like: effect.setParameter(“MinimumThreshold”, …) that are in the example script.

Thank you in advance,

Giovanni

You can print all Segment Editor effect parameter names by typing this into the Python console:

print(slicer.mrmlScene.GetFirstNodeByClass("vtkMRMLSegmentEditorNode"))

You should get something like this:

vtkMRMLSegmentEditorNode (000001867E52D360)
  ID: vtkMRMLSegmentEditorNodeSegmentEditor
  ClassName: vtkMRMLSegmentEditorNode
  Name: SegmentEditor
  Debug: false
  MTime: 1527281
  Description: (none)
  SingletonTag: SegmentEditor
  HideFromEditors: true
  Selectable: true
  Selected: false
  UndoEnabled: false
  Attributes:
    BrushAbsoluteDiameter:25
    BrushAbsoluteMaximumDiameter:128
    BrushAbsoluteMinimumDiameter:0.5
    BrushDiameterIsRelative:1
    BrushMaximumAbsoluteDiameter:100
    BrushMinimumAbsoluteDiameter:0.01
    BrushPixelMode:0
    BrushRelativeDiameter:3
    BrushSphere:0
    EditIn3DViews:0
    Erase.ColorSmudge:0
    Erase.EraseAllSegments:0
    Fill between slices.AutoUpdate:1
    Grow from seeds.AutoUpdate:1
    Hollow.ShellMode:INSIDE_SURFACE
    Hollow.ShellThicknessMm:3
    Islands.MinimumSize:1000
    Islands.Operation:KEEP_LARGEST_ISLAND
    Logical operators.BypassMasking:1
    Logical operators.Operation:COPY
    Margin.MarginSizeMm:3
    Paint.ColorSmudge:0
    Paint.EraseAllSegments:0
    Scissors.Operation:EraseInside
    Scissors.Shape:FreeForm
    Scissors.SliceCutDepthMm:0
    Scissors.SliceCutMode:Unlimited
    Smoothing.GaussianStandardDeviationMm:3
    Smoothing.JointTaubinSmoothingFactor:0.5
    Smoothing.KernelSizeMm:3
    Smoothing.SmoothingMethod:MEDIAN
    Threshold.AutoThresholdMethod:OTSU
    Threshold.AutoThresholdMode:SET_LOWER_MAX
    Threshold.MaximumThreshold:279
    Threshold.MinimumThreshold:69.75
  Node references:
    masterVolumeRef: vtkMRMLScalarVolumeNode5
    segmentationRef: vtkMRMLSegmentationNode1
  SelectedSegmentID: Segment_1
  ActiveEffectName: 
  MaskMode: PaintAllowedEverywhere
  MaskSegmentID: 
  OverwriteMode: OverwriteAllSegments
  MasterVolumeIntensityMask: false
  MasterVolumeIntensityMaskRange: 0 0

You can find valid parameter values by looking at the source code of the effects (e.g., search for the parameter name in entire Slicer core or SegmentEditorExtraEffects extension source code).

1 Like

Thank you Andras. I still stuck on how to emulate the event of creating a circle at a specific locaion (in the red panel for example), with a specific radius. From what I understood, the processInteractionEvents() function is called after the user has drawn his segmentation, but I wanted to do it inside the script direcly, emulating this action.

You can certainly call processInteractionEvents() to simulate user interaction, but it is a very low-level API, which may not be appropriate for a module to call.

What would you like to achieve? Simulate drilling?

I did a threshold segmentation on my data, and ended up with two cylinders. One is hollow and is enclosing the other, but they still have small points of contact. The best way to separate them is to use the scissors from the axial view and since I have the diameter of the internal cylinder and it’s center, it would be nice to have a one click operation, instead of manually trying to draw the best circle.

In the end, I just want exclude the region of my segmentation beyond some radius r, centered at some (x ,y), for all slices (the cylinders are oriented in SI axis).

For this, it would be more appropriate to create a cylinder using vtkCylinderSource, import into the segmentation node, and use Logical operators effect to subtract it from the segment of interest. See first steps of this example to see how to create a segment using a VTK source and import it into a segmentation node.

1 Like

Thanks again Andras. I did what you told. So now I have:

  • a threshold segment, that selects two cylinders from my data (a hollow one enclosing a smaller one, separated by a little gap);

  • and the newly created cylinder segment from the vtkCylinderSource, that is located in between the two threshold segment cylinders;

I want to now exclude the external hollow cylinder segment, using the newly created cylinder segment located in between the two. You mentioned the use of logical operators… do you have any examples of how can I do that? Thank you very much.

Giovanni

Examples for using segmentations and segment editor from Python scripts: https://www.slicer.org/wiki/Documentation/Nightly/ScriptRepository#Segmentations . You’ll activate the Logical operators effect, set its parameters, and call its Apply method.

I noticed that the cylinder I createad using CreateClosedSurfaceRepresentation following your hint is showing up in the 3D view, but it is not applied as a segmentation. How can I use it to segment everything inside? I tried tu use as a mask also, but no success. The last step in the code was:

    segmentationNode.CreateClosedSurfaceRepresentation()

If you create a model node and you want to use it as a segment then then you need to import it into the segmentation.

If you create a vtkPolyData using a VTK source then you can add it as a segment as shown in this example: https://gist.github.com/lassoan/2d5a5b73645f65a5eb6f8d5f97abf31b#file-segmentgrowcutsimple-py-L16-L21

I did exactly that, but it does’t segment nothing, it just shows the cylinder in the 3D view (because I also called segmentationNode.CreateClosedSurfaceRepresentation()), with no segmentation in the slice panels.

Here is my code:

    # Cylinder for separation of the core from its casing
    segmentationNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
    segmentationNode.CreateDefaultDisplayNodes()
    segmentationNode.SetReferenceImageGeometryParameterFromVolumeNode(inputVolume)
    segmentationCylinder = vtk.vtkCylinderSource()
    segmentationCylinder.SetRadius(coreDiameter / 2)
    segmentationCylinder.SetHeight(len(slicer.util.arrayFromVolume(inputVolume)) * self.imageSpacing[2] * 1.5)
    segmentationCylinder.SetResolution(100)
    segmentationNode.AddSegmentFromClosedSurfaceRepresentation(segmentationCylinder.GetOutput(), "Core", [0.0, 1.0, 0.0])
    segmentationCylinder.Update()

Ok. Now it is working. I was calling Update() after adding it. The correct way is:

    segmentationCylinder.Update()
    segmentationNode.AddSegmentFromClosedSurfaceRepresentation(segmentationCylinder.GetOutput(), "Core", [0.0, 1.0, 0.0])

Thank you Andras.

1 Like

Andras, what is the proper way to set this parameter: MasterVolumeIntensityMaskRange

I tried:

    effect.setParameter("MasterVolumeIntensityMaskRange", "(-1000, 0)")

or

    effect.setParameter("MasterVolumeIntensityMaskRange", "-1000 0")

but no success.

I want to use with the Mask Volume effect.

Masking parameters are not effect parameters. They are stored directly in the segment editor node and you can change them like this: getNode('SegmentEditor').SetMasterVolumeIntensityMaskRange(15, 80)

Still not being able to make it take effect:

    segmentEditorNode.SetMaskSegmentID(segmentationNode.GetSegmentation().GetNthSegmentID(0))
    segmentEditorNode.SetMasterVolumeIntensityMask(True)
    segmentEditorNode.SetMasterVolumeIntensityMaskRange(-3000, -2800)

Is there anything else I have to set up?

Probably you don’t want to use both a segment as a mask and also an intensity range. What would you like to do? Blank out parts of the original volume?

Also note that you may have several segment editor nodes in the scene. One is created by Segment Editor module but you may have created several others.

I would like to apply both with the same segment, yes. I can do it manually, using the segmentation editor, but in the code I still can’t replicate the intensity range effect. I will post here the sequence of commands I am currently running (I have only one segmentation):

    # Create segment editor to get access to effects
    segmentEditorWidget = slicer.qMRMLSegmentEditorWidget()
    segmentEditorWidget.setMRMLScene(slicer.mrmlScene)
    segmentEditorNode = slicer.vtkMRMLSegmentEditorNode()
    slicer.mrmlScene.AddNode(segmentEditorNode)
    segmentEditorWidget.setMRMLSegmentEditorNode(segmentEditorNode)
    segmentEditorWidget.setSegmentationNode(segmentationNode)
    segmentEditorWidget.setMasterVolumeNode(inputVolume)

    segmentEditorWidget.setCurrentSegmentID(segmentationNode.GetSegmentation().GetNthSegmentID(0))
    # Set up masking parameters
    segmentEditorWidget.setActiveEffectByName("Mask volume")
    effect = segmentEditorWidget.activeEffect()
    segmentEditorNode.SetMaskSegmentID(segmentationNode.GetSegmentation().GetNthSegmentID(0))
    segmentEditorNode.SetMasterVolumeIntensityMask(True)
    segmentEditorNode.SetMasterVolumeIntensityMaskRange(6900, 7000)
    effect.setParameter("Operation", "FILL_OUTSIDE")
    effect.setParameter("FillValue", str(inputVolume.GetImageData().GetScalarRange()[0]))
    maskedVolume = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLScalarVolumeNode", "Temporary masked volume")
    effect.self().outputVolumeSelector.setCurrentNode(maskedVolume)
    effect.self().onApply()

The FillValue parameter is working.

You can debug the issue by showing the segmentEditorWidget (segmentEditorWidget.show()) and see what parameter is not set as you expected.

I tried that, but unfortunately it closes soon after opening, before it can display anything. Maybe has something to do with the version I am currently running? Will try with the latest nightly.

The widget is hidden when the variable is deleted. To prevent this, you can run the code from the console or store a reference to the widget a variable that is not deleted (e.g., add this line slicer.savedWidget = segmentEditorWidget).