Create a mesh in python script

Hi all,
Is-it possible since a “head_T1.nii” to mesh only the scalp by “Segment editor tool” from Python scripts?
Thanks in advance,
Frederic

You need to be able to come up with a quasi-automatic way of doing it first. Do you know what effects would you use, in what order, and with what parameters?

Then it will be possible to automatize it from python, yes.

Once you know what effects you’ll use and how, follow these examples for running Segment Editor effects from script: https://www.slicer.org/wiki/Documentation/Nightly/ScriptRepository#How_to_run_segment_editor_effects_from_a_script

Thanks to both of you.
I would like to use 1/ threshold effect, with range from 110 to 400 and with default to other parameters and 2/ Smoothing with Gaussian with 2 mm sd.
So It would be:
segmentEditorWidget.setActiveEffectByName(“Threshold”, 110,400)
segmentEditorWidget.setActiveEffectByName(“Smmoting”,"Gaussian ", 2)

Two more question, please.
Is there in 3dslicer, a RAS to MNI coordinate conversion function?
Is there a button to trigger the shift key linked motion?

Thanks, once again.

It’s a couple of more lines (setActiveEffectByName only activates an effect). See the examples that I linked above.

RAS is anatomical coordinate system (right-anterior-superior). What is MNI coordinate system?

You can easily add a shortcut for jump to a current cursor position, just open the Python console and copy-paste these lines to make Ctrl+M jump to current slice position:

def jumpToCursor():
  ras=[0,0,0]
  slicer.mrmlScene.GetFirstNodeByClass('vtkMRMLCrosshairNode').GetCursorPositionRAS(ras)
  slicer.modules.markups.logic().JumpSlicesToLocation(ras[0], ras[1], ras[2], slicer.vtkMRMLCrosshairNode.JumpSlice)

shortcut = qt.QShortcut(slicer.util.mainWindow())
shortcut.setKey(qt.QKeySequence("Ctrl+M"))
shortcut.connect( 'activated()', jumpToCursor )

If you want this feature permanently then add it to your application startup script (edit it in menu: Edit / Application settings / Application startup script).

1 Like

Thanks once again.

  1. Ok for the “setActiveEffectByName”, I will try as soon as possible.
  2. MNI coordinates is a normalized coordinates (explain here: http://neuroimage.usc.edu/brainstorm/CoordinateSystems)
  3. Thanks for this script!

From CoordinateSystems - Brainstorm website:

To be able to get “MNI coordinates” for individual brains, an extra step of normalization is required.
The method we use in Brainstorm is based on an affine co-registration with the MNI ICBM152 template from the SPM software.

This means you can easily get these coordinates in Slicer. You just have to download MNI ICBM152 image and register it to your volume in Slicer. You can either use “General registration (BRAINS)” module or install SlicerElastix extension and use “General registration (Elastix)” module. For Elastix, I think default configuration file are available for rigid or b-spline transforms, so you need to change ElastixParameterSetDatabase.xml to create a preset that uses Parameters_Affine.txt (you can copy-paste the “generic rigid” preset and change Parameters_Rigid.txt to Parameters_Affine.txt).

1 Like

Thanks Andras,
It works fine, but the registration is very rigid.

“generic rigid” preset uses rigid transform. If you need affine or warping transform then you have to modify registration parameter files as I described above.

Perfect thanks Andras.
is-it possible to make this procedure by a Python script?

Python scripting is not needed. You can edit preset files directly using a text editor.

Of course, everything is exposed to Python, so if you need to run registration from your module then you can do that. An example for that is Sequence Registration module, which uses SlicerElastix internally for registering different time points of a 4D volume to each other.

Yes, I have change manually the xml file. :wink:
My python script does no work and I do not understand why, could you help me please?

import Elastix
FixedVolume= slicer.util.loadVolume('avg152T1.nii')
MovingVolume= slicer.util.loadVolume('t0697_t1_s03.nii')
outputVolume = slicer.vtkMRMLScalarVolumeNode()
slicer.mrmlScene.AddNode(outputVolume)
outputVolume.CreateDefaultDisplayNodes()
logic = Elastix.ElastixLogic()
RegistrationPresets_ParameterFilenames = 5
parameterFilenames = logic.getRegistrationPresets()[0][RegistrationPresets_ParameterFilenames]
logic.registerVolumes(FixedVolume, MovingVolume, parameterFilenames = parameterFilenames, outputVolumeNode = outputVolume)

What happens? Do you get any error message?

The error is:

> Traceback (most recent call last):
>   File "<console>", line 1, in <module>
>   File "C:/Users/briend/AppData/Roaming/NA-MIC/Extensions-26489/SlicerElastix/lib/Slicer-4.8/qt-scripted-modules/Elastix.py", line 554, in registerVolumes
>     slicer.util.saveNode(volumeNode, filePath, {"useCompression": False})
>   File "C:\Program Files\Slicer 4.8.0\bin\Python\slicer\util.py", line 433, in saveNode
>     properties["nodeID"] = node.GetID();
> AttributeError: 'bool' object has no attribute 'GetID'

The problem is that loadVolume by default returns success=True/False. See an example for getting the loaded volume node here: https://www.slicer.org/wiki/Documentation/Nightly/ScriptRepository#Load_volume_from_file

Thanks Andras.
The correct scipt (which works well) is thus:

import Elastix
FixedVolume= slicer.util.loadVolume(‘L:/briend/Olivier_Slicer_try1/avg152T1.nii’, returnNode=True)
n=slicer.util.getNode(‘avg15*’)
MovingVolume= slicer.util.loadVolume(‘L:/briend/Olivier_Slicer_try1/t0697_t1_s03.nii’, returnNode=True)
m=slicer.util.getNode(‘t06*’)
outputVolume = slicer.vtkMRMLScalarVolumeNode()
slicer.mrmlScene.AddNode(outputVolume)
outputVolume.CreateDefaultDisplayNodes()
logic = Elastix.ElastixLogic()
RegistrationPresets_ParameterFilenames = 5
parameterFilenames = logic.getRegistrationPresets()[0][RegistrationPresets_ParameterFilenames]
logic.registerVolumes(n, m, parameterFilenames = parameterFilenames, outputVolumeNode = outputVolume)

1 Like

I am confuse to ask you all theses questions, but I do not arrive to create my mesh.
Nothing happen after my script:

import SampleData
sampleDataLogic = SampleData.SampleDataLogic()
masterVolumeNode = sampleDataLogic.downloadMRBrainTumor1()

# Create segmentation
segmentationNode = slicer.vtkMRMLSegmentationNode()
slicer.mrmlScene.AddNode(segmentationNode)
segmentationNode.CreateDefaultDisplayNodes() # only needed for display
segmentationNode.SetReferenceImageGeometryParameterFromVolumeNode(masterVolumeNode)
#segmentationNode.AddSegmentFromClosedSurfaceRepresentation(masterVolumeNode, "skin", [1,0,1]) **# I do not understand how to assign my volume here**

# 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(masterVolumeNode)

# Run segmentation
segmentEditorWidget.setActiveEffectByName("Threshold")
effect = segmentEditorWidget.activeEffect()
effect.setParameter("MinimumThreshold",35)
effect.setParameter("MaximumThreshold",695)
segmentEditorWidget.setActiveEffectByName('Smooting')
effect.setParameter('Gaussian', 2)

effect.self().onPreview()
effect.self().onApply()

# Clean up
slicer.mrmlScene.RemoveNode(segmentEditorNode)

# Make segmentation results nicely visible in 3D
segmentationDisplayNode = segmentationNode.GetDisplayNode()

@lassoan do you know why?
Thanks in advance

There were a couple of small issues. Here is a working version:

import SampleData
sampleDataLogic = SampleData.SampleDataLogic()
masterVolumeNode = sampleDataLogic.downloadMRBrainTumor1()

# Create segmentation
segmentationNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
segmentationNode.CreateDefaultDisplayNodes() # only needed for display
segmentationNode.SetReferenceImageGeometryParameterFromVolumeNode(masterVolumeNode)
addedSegmentID = segmentationNode.GetSegmentation().AddEmptySegment("skin")

# Create 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)

# Thresholding
segmentEditorWidget.setActiveEffectByName("Threshold")
effect = segmentEditorWidget.activeEffect()
effect.setParameter("MinimumThreshold","35")
effect.setParameter("MaximumThreshold","695")
effect.self().onApply()

segmentEditorWidget.setActiveEffectByName("Smoothing")
effect = segmentEditorWidget.activeEffect()
effect.setParameter("SmoothingMethod", "MEDIAN")
effect.setParameter("KernelSizeMm", 11)
effect.self().onApply()

# Clean up
segmentEditorWidget = None
slicer.mrmlScene.RemoveNode(segmentEditorNode)

# Make segmentation results visible in 3D
segmentationNode.CreateClosedSurfaceRepresentation()

# Write to STL file
surfaceMesh = segmentationNode.GetClosedSurfaceRepresentation(addedSegmentID)
writer = vtk.vtkSTLWriter()
writer.SetInputData(surfaceMesh)
writer.SetFileName("c:/tmp/something.stl")
writer.Update()

For future reference: I’ve saved this example and will maintain it on this page.

1 Like

Once again, thanks @lassoan. Your generosity makes me blush.
Little problem however, nothing happen by the smoothing parameter.

Then, how save this volume segmented (after Model Maker) in .stl format in python?
Thanks in advance!

If you use Segmentations, “model making” (and real-time updates to the generated 3D mesh) are done automatically. I’ve updated the code sample above to smooth with the correct parameter names and added STL export at the end.