Difference between revisions of "Slicer3:Execution Model Documentation:Python"

From NAMIC Wiki
Jump to: navigation, search
m (Text replacement - "http://www.slicer.org/slicerWiki/index.php/" to "https://www.slicer.org/wiki/")
 
(23 intermediate revisions by 2 users not shown)
Line 1: Line 1:
A [[Slicer3:Python | Python]] interpreter has been integrated into Slicer3.  More details of the Python language can be found at [http://www.python.org Python.org].  This interpreter may be used to execute Python script plugins.  The modules have a few specific requirements to be correctly found and used as plugins.  Like the standard executable and shared library plugins, Python plugins need to be self describing.  To do this, the script must have a top level variable called '''XML''' or provide a '''toXML''' procedure.  For example:
+
<big>'''Note:''' We are migrating this content to the slicer.org domain - <font color="orange">The newer page is [https://www.slicer.org/wiki/Slicer3:Execution_Model_Documentation:Python  here]</font></big>a
 
 
XML = """<?xml version="1.0" encoding="utf-8"?>
 
<executable>
 
  <category>Filtering.Denoising</category>
 
  ...
 
 
def toXML():
 
  return XML;
 
 
 
 
 
Details of the XML format are found in the main [[Slicer3:Execution Model Documentation | Execution Model documentation]].  Rather than construct a command line to pass into Python, Slicer3 directly calls an '''Execute''' procedure.  It is assumed that the '''Execute''' function expects positional arguments first, and any optional arguments are passed in as keyword arguments.  For instance, the GradientAnisotropicDiffusion.py module provides an '''Execute''':
 
 
 
def Execute ( inputVolume, outputVolume, conductance=1.0, timeStep=0.0625, iterations=1 ):
 
    print "Executing Python Demo Application!"
 
    Slicer = __import__ ( "Slicer" );
 
    slicer = Slicer.Slicer()
 
    in = slicer.MRMLScene.GetNodeByID ( inputVolume );
 
    out = slicer.MRMLScene.GetNodeByID ( outputVolume );
 
 
    filter = slicer.vtkITKGradientAnisotropicDiffusionImageFilter.New()
 
    filter.SetConductanceParameter ( conductance )
 
    filter.SetTimeStep ( timeStep )
 
    filter.SetNumberOfIterations ( iterations )
 
    filter.SetInput ( in.GetImageData() )
 
    filter.Update()
 
    out.SetAndObserveImageData(filter.GetOutput())
 
    return
 
 
 
 
 
The function first constructs a '''Slicer''' object by importing the Slicer module.  The '''Slicer''' object is the main interface into Slicer3 as a whole.  The first two arguments, '''inputVolume''' and '''outputVolume''' are not proper MRMLVolumes, and must be looked up using the '''Slicer''' object (that is, the arguments are unique IDs rather than pointers to the objects). The filter is constructed through the '''Slicer''' object, and the parameters are set.  After the filter is updated, the output image is put using the '''SetAndObserveImageData''' method on the output volume.
 
 
 
===== ToDo =====
 
* Progress functionality
 
* Casting image arguments to proper MRML volume objects before calling '''Execute'''
 
 
 
==Python Module collection==
 
 
 
* [[Slicer3:Execution Model Documentation:Python#PythonScript.py | PythonScript.py]]
 
* [[Slicer3:Execution Model Documentation:Python#SurfaceConnectivity.py | SurfaceConnectivity.py]]
 
* [[Slicer3:Execution Model Documentation:Python#SurfaceICPRegistration.py | SurfaceICPRegistration.py]]
 
* [[Slicer3:Execution Model Documentation:Python#SurfaceToolbox.py | SurfaceToolbox.py]]
 
 
 
 
 
===PythonScript.py===
 
 
 
This module allows to run external Python code saved in a .py file on an image or surface (or both), and have the results loaded back in Slicer. It doesn't really do anything itself, but it's handy for:
 
* writing Python modules, since one can store the actual module code in a file without having to copy it to Slicer3-build/lib/Slicer3/Plugins
 
* writing quick one-shot filtering operations that are not elegible to become full-fledged modules
 
* experimenting with Python in Slicer.
 
 
 
XML = """<?xml version="1.0" encoding="utf-8"?>
 
<executable>
 
 
  <category>Python Modules</category>
 
  <title>Python Script</title>
 
  <description>Run external Python code on a surface or image.</description>
 
  <version>1.0</version>
 
  <documentation-url></documentation-url>
 
  <license></license>
 
  <contributor>Luca Antiga and Daniel Blezek</contributor>
 
 
  <parameters>
 
    <label>Python Script Parameters</label>
 
    <description>Parameters for Python script</description>
 
    <file>
 
      <name>scriptFileName</name>
 
      <longflag>scriptFileName</longflag>
 
      <description>File containing Python code to run</description>
 
      <label>Script file</label>
 
    </file>
 
  </parameters>
 
 
  <parameters>
 
    <label>IO</label>
 
    <description>Input/output parameters</description>
 
 
    <geometry>
 
      <name>inputSurface</name>
 
      <longflag>inputSurface</longflag>
 
      <label>Input Surface</label>
 
      <channel>input</channel>
 
      <description>Input surface to be filtered</description>
 
    </geometry>
 
 
    <image>
 
      <name>inputVolume</name>
 
      <longflag>inputVolume</longflag>
 
      <label>Input Volume</label>
 
      <channel>input</channel>
 
      <description>Input image to be filtered</description>
 
    </image>
 
 
    <geometry>
 
      <name>outputSurface</name>
 
      <longflag>outputSurface</longflag>
 
      <label>Output Surface</label>
 
      <channel>output</channel>
 
      <description>Output filtered surface</description>
 
    </geometry>
 
 
    <image>
 
      <name>outputVolume</name>
 
      <longflag>outputVolume</longflag>
 
      <label>Output Volume</label>
 
      <channel>output</channel>
 
      <description>Output filtered volume</description>
 
    </image>
 
 
 
  </parameters>
 
 
</executable>
 
"""
 
 
 
def Execute (scriptFileName="", inputSurface="", inputVolume="", outputSurface="", outputVolume=""):
 
 
    Slicer = __import__("Slicer")
 
    slicer = Slicer.Slicer()
 
    scene = slicer.MRMLScene
 
 
    if inputSurface:
 
        inputSurface = scene.GetNodeByID(inputSurface)
 
 
    if inputVolume:
 
        inputVolume = scene.GetNodeByID(inputVolume)
 
 
    if outputSurface:
 
        outputSurface = scene.GetNodeByID(outputSurface)
 
 
    if outputVolume:
 
        outputVolume = scene.GetNodeByID(outputVolume)
 
 
    try:
 
        execfile(scriptFileName)
 
    except Exception, error:
 
        slicer.Application.ErrorMessage("Python script error: %s" % error)
 
 
    return
 
 
 
Note how the try...except block intercepts Python exceptions generated while running the external script and prints messages out in Slicer's message console.
 
 
 
Now let's see what an external script code could look like. Say we want to take the input surface and decimate it.
 
 
 
inputPolyData = inputSurface.GetPolyData()
 
 
decimation = slicer.vtkDecimatePro.New()
 
decimation.SetInput(inputSurface.GetPolyData())
 
decimation.SetTargetReduction(0.99)
 
decimation.Update()
 
 
outputSurface.SetAndObservePolyData(decimation.GetOutput())
 
 
 
In the script (which we can save in a .py file), we don't have to start by importing Slicer module, since it's already done in the PythonScript.py module. We have to assume that inputSurface, outputSurface, inputVolume and outputVolume are already available as MRML nodes (vtkMRMLModelNode and vtkMRMLVolumeNode, respectively), and that the slicer module is also already imported.
 
 
 
Setting of the filtered poly data to the outputSurface is left to the user (see last line).
 
 
 
 
 
===SurfaceConnectivity.py===
 
 
 
  XML = """<?xml version="1.0" encoding="utf-8"?>
 
<executable>
 
 
  <category>Python Modules</category>
 
  <title>Python Surface Connectivity</title>
 
  <description>
 
Identify connected regions of a surface.
 
</description>
 
  <version>1.0</version>
 
  <documentation-url></documentation-url>
 
  <license></license>
 
  <contributor>Luca Antiga and Daniel Blezek</contributor>
 
 
  <parameters>
 
    <label>Surface Connectivity Parameters</label>
 
    <description>Parameters for surface connectivity</description>
 
 
    <string-enumeration>
 
      <name>connectivityMode</name>
 
      <longflag>connectivityMode</longflag>
 
      <description>Mode of operation of connectivity filter</description>
 
      <label>Connectivity mode</label>
 
      <default>AllRegions</default>
 
      <element>AllRegions</element>
 
      <element>ClosestToSeed</element>
 
    </string-enumeration>
 
 
    <boolean>
 
      <name>enableOutputFiducials</name>
 
      <longflag>enableOutputFiducials</longflag>
 
      <description>Toggle generation of labeled fiducials corresponding to each extracted region</description>
 
      <label>Enable output fiducials</label>
 
      <default>false</default>
 
    </boolean>
 
 
    <point multiple="false">
 
      <name>seedPoint</name>
 
      <longflag>seedPoint</longflag>
 
      <label>Seed Point</label>
 
      <description>Fiducial to extract closest surface</description>
 
    </point>
 
  </parameters>
 
 
  <parameters>
 
    <label>IO</label>
 
    <description>Input/output parameters</description>
 
 
    <geometry>
 
      <name>inputSurface</name>
 
      <label>Input Surface</label>
 
      <channel>input</channel>
 
      <index>0</index>
 
      <description>Input surface to be filtered</description>
 
    </geometry>
 
 
    <geometry>
 
      <name>outputSurface</name>
 
      <label>Output Surface</label>
 
      <channel>output</channel>
 
      <index>1</index>
 
      <description>Output filtered surface</description>
 
    </geometry>
 
  </parameters>
 
 
</executable>
 
"""
 
 
 
def Execute (inputSurface, outputSurface, connectivityMode="AllRegions", enableOutputFiducials=False, seedPoint=[0.0,0.0,0.0]):
 
 
    Slicer = __import__("Slicer")
 
    slicer = Slicer.Slicer()
 
    scene = slicer.MRMLScene
 
    inputSurface = scene.GetNodeByID(inputSurface)
 
    outputSurface = scene.GetNodeByID(outputSurface)
 
 
    connectivityFilter = slicer.vtkPolyDataConnectivityFilter.New()
 
    connectivityFilter.SetInput(inputSurface.GetPolyData())
 
    if connectivityMode == "AllRegions":
 
        connectivityFilter.SetExtractionModeToAllRegions()
 
        connectivityFilter.ColorRegionsOn()
 
    elif connectivityMode == "ClosestToSeed":
 
        connectivityFilter.SetExtractionModeToClosestPointRegion()
 
        connectivityFilter.SetClosestPoint(seedPoint[0],seedPoint[1],seedPoint[2])
 
    connectivityFilter.Update()
 
 
    if enableOutputFiducials:
 
        fiducialList = scene.CreateNodeByClass("vtkMRMLFiducialListNode")
 
        scene.AddNode(fiducialList)
 
        thresholdPoints = slicer.vtkThresholdPoints.New()
 
        thresholdPoints.SetInput(connectivityFilter.GetOutput())
 
        numberOfRegions = connectivityFilter.GetNumberOfExtractedRegions()
 
        for i in range(numberOfRegions):
 
            lower = i - 0.5
 
            upper = i + 0.5
 
            thresholdPoints.ThresholdBetween(lower,upper)
 
            thresholdPoints.Update()
 
            if thresholdPoints.GetOutput().GetNumberOfPoints() > 0:
 
                point = thresholdPoints.GetOutput().GetPoint(0)
 
                fid = fiducialList.AddFiducial()
 
                fiducialList.SetNthFiducialXYZ(fid,point[0],point[1],point[2])
 
                fiducialList.SetNthFiducialLabelText(fid,"Region %d" % i)
 
 
    outputSurface.SetAndObservePolyData(connectivityFilter.GetOutput())
 
 
#    inputSurface.GetDisplayNode().SetVisibilityOff()
 
#    outputSurface.GetDisplayNode().ScalarVisibilityOn()
 
 
    return
 
 
 
 
 
===SurfaceICPRegistration.py===
 
 
 
XML = """<?xml version="1.0" encoding="utf-8"?>
 
<executable>
 
 
  <category>Python Modules</category>
 
  <title>Python Surface ICP Registration</title>
 
  <description>
 
Performes registration of the input surface onto a target surface using on the Iterative Closest Point algorithm.
 
</description>
 
  <version>1.0</version>
 
  <documentation-url></documentation-url>
 
  <license></license>
 
  <contributor>Luca Antiga and Daniel Blezek</contributor>
 
 
  <parameters>
 
    <label>Surface ICP Registration Parameters</label>
 
    <description>Parameters for surface registration</description>
 
 
    <string-enumeration>
 
      <name>landmarkTransformMode</name>
 
      <longflag>landmarkTransformMode</longflag>
 
      <description>Mode of operation of internal landmark transform</description>
 
      <label>Landmark transform mode</label>
 
      <default>RigidBody</default>
 
      <element>RigidBody</element>
 
      <element>Similarity</element>
 
      <element>Affine</element>
 
    </string-enumeration>
 
 
    <string-enumeration>
 
      <name>meanDistanceMode</name>
 
      <longflag>meanDistanceMode</longflag>
 
      <label>Mean distance mode</label>
 
      <description>Set how the mean distance should be computed, RMS (square root of the average of the sum of squares of the closest point distances) or AbsoluteValue (mean of the sum of absolute values of the closest point distances)</description>
 
      <default>RMS</default>
 
      <element>RMS</element>
 
      <element>AbsoluteValue</element>
 
    </string-enumeration>
 
 
    <integer>
 
      <name>maximumNumberOfIterations</name>
 
      <longflag>maximumNumberOfIterations</longflag>
 
      <label>Maximum number of iterations</label>
 
      <description>Maximum number of Interative Closest Point iterations</description>
 
      <default>50</default>
 
      <constraints>
 
        <minimum>1</minimum>
 
        <maximum>1000</maximum>
 
        <step>1</step>
 
      </constraints>
 
    </integer>
 
 
    <integer>
 
      <name>maximumNumberOfLandmarks</name>
 
      <longflag>maximumNumberOfLandmarks</longflag>
 
      <label>Maximum number of landmarks</label>
 
      <description>Maximum number of landmarks on source and target surfaces</description>
 
      <default>200</default>
 
      <constraints>
 
        <minimum>1</minimum>
 
        <maximum>1000</maximum>
 
        <step>1</step>
 
      </constraints>
 
    </integer>
 
 
    <boolean>
 
      <name>startByMatchingCentroids</name>
 
      <longflag>startByMatchingCentroids</longflag>
 
      <label>Start by matching centroids</label>
 
      <description>Toggle starting registration by first translating the source centroid to the target centroid</description>
 
      <default>false</default>
 
    </boolean>
 
 
    <boolean>
 
      <name>checkMeanDistance</name>
 
      <longflag>checkMeanDistance</longflag>
 
      <label>Check mean distance</label>
 
      <description>Force the algorithm to check the mean distance between two iterations</description>
 
      <default>false</default>
 
    </boolean>
 
 
    <double>
 
      <name>maximumMeanDistance</name>
 
      <longflag>maximumMeanDistance</longflag>
 
      <label>Maximum mean distance</label>
 
      <description>Maximum distance between two iterations used for determining convergence</description>
 
      <default>0.01</default>
 
    </double>
 
 
  </parameters>
 
 
  <parameters>
 
    <label>IO</label>
 
    <description>Input/output parameters</description>
 
 
    <geometry>
 
      <name>inputSurface</name>
 
      <label>Input Surface</label>
 
      <channel>input</channel>
 
      <index>0</index>
 
      <description>Input surface to be registered</description>
 
    </geometry>
 
 
    <geometry>
 
      <name>targetSurface</name>
 
      <label>Target Surface</label>
 
      <channel>input</channel>
 
      <index>1</index>
 
      <description>Target surface for registration</description>
 
    </geometry>
 
 
    <geometry>
 
      <name>outputSurface</name>
 
      <label>Output Surface</label>
 
      <channel>output</channel>
 
      <index>2</index>
 
      <description>Output registered surface</description>
 
    </geometry>
 
  </parameters>
 
 
</executable>
 
"""
 
 
 
def Execute (inputSurface, targetSurface, outputSurface, \
 
            landmarkTransformMode="RigidBody", meanDistanceMode="RMS", \
 
            maximumNumberOfIterations=50, maximumNumberOfLandmarks=200, \
 
            startByMatchingCentroids=False, checkMeanDistance=False, maximumMeanDistance=0.01):
 
 
    Slicer = __import__("Slicer")
 
    slicer = Slicer.Slicer()
 
    scene = slicer.MRMLScene
 
    inputSurface = scene.GetNodeByID(inputSurface)
 
    targetSurface = scene.GetNodeByID(targetSurface)
 
    outputSurface = scene.GetNodeByID(outputSurface)
 
 
    icpTransform = slicer.vtkIterativeClosestPointTransform.New()
 
    icpTransform.SetSource(inputSurface.GetPolyData())
 
    icpTransform.SetTarget(targetSurface.GetPolyData())
 
    if landmarkTransformMode == "RigidBody":
 
        icpTransform.GetLandmarkTransform().SetModeToRigidBody()
 
    elif landmarkTransformMode == "Similarity":
 
        icpTransform.GetLandmarkTransform().SetModeToSimilarity()
 
    elif landmarkTransformMode == "Affine":
 
        icpTransform.GetLandmarkTransform().SetModeToAffine()
 
    if meanDistanceMode == "RMS":
 
        icpTransform.SetMeanDistanceModeToRMS()
 
    elif meanDistanceMode == "AbsoluteValue":
 
        icpTransform.SetMeanDistanceModeToAbsoluteValue()
 
    icpTransform.SetMaximumNumberOfIterations(maximumNumberOfIterations)
 
    icpTransform.SetMaximumNumberOfLandmarks(maximumNumberOfLandmarks)
 
    icpTransform.SetStartByMatchingCentroids(int(startByMatchingCentroids))
 
    icpTransform.SetCheckMeanDistance(int(checkMeanDistance))
 
    icpTransform.SetMaximumMeanDistance(maximumMeanDistance)
 
 
    transformFilter = slicer.vtkTransformPolyDataFilter.New()
 
    transformFilter.SetInput(inputSurface.GetPolyData())
 
    transformFilter.SetTransform(icpTransform)       
 
    transformFilter.Update()
 
   
 
    outputSurface.SetAndObservePolyData(transformFilter.GetOutput())
 
 
    return
 
 
 
===SurfaceToolbox.py===
 

Latest revision as of 17:28, 10 July 2017

Home < Slicer3:Execution Model Documentation:Python

Note: We are migrating this content to the slicer.org domain - The newer page is herea