Difference between revisions of "Slicer3:Python"

From Slicer Wiki
Jump to: navigation, search
Line 183: Line 183:
 
* [http://matplotlib.sourceforge.net/ MatPlotLib]
 
* [http://matplotlib.sourceforge.net/ MatPlotLib]
  
== Writing Slicer Modules in Python ===
+
=== Writing Slicer Modules in Python ===
  
 
Painless Development in Slicer3 with Python, talk given by Demian Wassermann at the LMI, sept. 2008
 
Painless Development in Slicer3 with Python, talk given by Demian Wassermann at the LMI, sept. 2008

Revision as of 14:23, 20 September 2008

Home < Slicer3:Python

Status of Python in Slicer

In post-3.2 Slicer3, Python support is currently disabled by default. However, it is easy to enable and should compile on all tested platform (Mac, Windows, Linux). Though matplotlib is still not properly interacting with Slicer3, NumPy and Python support should work well for all platforms. Python command line modules are well supported, as is building full GUI modules in Python.

Python for scientific computing

Python has a fairly comprehensive package for scientific computing called SciPy. Of main interest to Slicer users/developers is NumPy. NumPy provides most of the features of the Matlab image processing toolbox and numeric computations, but in an Open Source package. A compelling reason to use NumPy is the ease of interaction and integration with Slicer3.

Slicer3 and Python

Enabling Python in the Slicer Build

  1. Edit slicer_variables.tcl
    1. Change "set ::USE_PYTHON "off"" to "on"
  2. Optional: If you prefer to use your system Python installation, change set ::USE_SYSTEM_PYTHON "false" to "true"
    1. This will give an error under Linux and Windows, but it can be easily fixed by adding the system path for Python, just search for "if { $::USE_SYSTEM_PYTHON } {"
  3. Optional: installing SciPy
    1. Download the scipy egg for your platform
    2. Unzip the egg in any directory that is part of Slicer's Python path, eg Slicer3-build/lib/Slicer3/Plugins
    3. SciPy should now be available in Slicer's Python interpreter

Build Slicer3 using "getbuildtest.tcl", i.e.:

Macintosh:Slicer3 blezek$ Scripts/getbuildtest.tcl

A new menu command Python Interpreter should appear on the Window menu.

PythonMenu.png

This command should bring up the Python Console window.

PythonConsole.png

Basic Slicer/Python tutorial

Note: this is initial documentation only, and is subject to change as the API evolves.

The Slicer Python interpreter has access to all Python modules referenced by the environment variable PYTHONPATH as well as the Slicer internal module. The Slicer module also supports the interface between Slicer Volume Nodes and NumPy. It would be instructive for the reader to review the NumPy documentation. For serious users of NumPy, there is the Guide to NumPy by Travis Oliphant for purchase.

The main interface to Slicer from Python is through the Slicer Python module. From there, the Python user can access any of Slicer's global objects and MRML tree. For example:

 >>> import Slicer
 >>> Slicer.slicer
 <Slicer.Slicer object at 0x9f325f0>
 >>> Slicer.slicer.MRMLScene.GetNumberOfNodesByClass ( 'vtkMRMLVolumeNode' )
 0
 >>> 

In this case, no 'vtkMRMLVolumeNode's were loaded into Slicer.

VTK objects may be easily constructed, as well as any other object available from the C++ Slicer API.

>>> a = Slicer.slicer.vtkPolyData()
>>> help ( a )
Help on vtkPolyData in module Slicer object:

class vtkPolyData(vtkPointSet)
 |  Method resolution order:
 |      vtkPolyData
 |      vtkPointSet
 |      vtkDataSet
 <output truncated>

VTK objects created in this manner conform as closely as possible to the VTK Python bindings, but must be created through the slicer object inside the Slicer module. Each object may be fully subclassed following the meta object pattern.

A few details on Python in Slicer

Although it is possible to use Slicer within Python, enabling Python in a Slicer build does not trigger Python wrapping of C++ classes. Instead, Python has access to the Slicer API through Tcl. Whenever a constructor for an object is invoked, e.g.

from Slicer import slicer 
surface = slicer.vtkPolyData()

to create a VTK object, or

modelNode = slicer.vtkMRMLModelNode()

to create a MRML node, the Python interpreter tells the Tcl interpreter to instantiate a vtkPolyData or vtkMRMLModelNode (this happens through the Tcl wrappers which are built as part of the basic Slicer) and makes it available to the Python interface. More in detail, Python asks Tcl to illustrate what is a vtkPolyData, what are the parent classes and the methods, creates the whole class hierarchy on the fly, and creates an instance of that object. When one calls an object's method, like

numberOfPoints = surface.GetNumberOfPoints()

the Python interpreter relies the call to the Tcl interpreter and returns the proper return value. Any object available to Tcl is the same object available to Python, and this allows for a great flexibility, as everything lives in a common space.

Writing modules (especially interactive modules) in Python can lead to quite a speed up in the development cycle, since the code is more concise, it requires less file shuffling and, more than anything, it doesn't need to be recompiled (in some cases you don't even need to exit Slicer).

More than this, enabling Python in Slicer opens up the possibility of accessing a very wide range of Python libraries, such as Numpy and Scipy, from within Slicer (interactively or in modules), making the Slicer use cases potentially explode.


NumPy tutorial

Python and NumPy give direct access to the volume data in Slicer by wrapping the image data in a NumPy array object through the .ToArray() method on the volume node. This tutorial assumes you have installed SciPy.

>>> # Access the image data directly
>>> from Slicer import slicer
>>> nodes = slicer.ListVolumeNodes()
>>> t2 = nodes['T2.hdr']
>>> data = t2.GetImageData().ToArray()
>>> print data
[[[ 0  0  0 ...,  0  0  0]
  [ 0  0  1 ...,  0  1  0]
  [ 0  0  0 ...,  0  0  0]
  ..., 
  [ 0  8  0 ...,  0  1  2]
  [ 0  0  0 ...,  4  1  2]
  [ 0  0  2 ...,  6  0  2]]

<<< Some output truncated >>> 

 [[ 0  0  0 ...,  0  0  0]
  [ 0  0  0 ...,  0  0  0]
  [ 0  0  0 ...,  0  0  0]
  ..., 
  [ 0  3  3 ...,  0  0  8]
  [ 0  6  0 ...,  5  3 13]
  [ 0  5  1 ...,  9  3  8]]]
>>> # Load the image filtering package from scipy
>>> import scipy.ndimage
>>> # Filter into a new array
>>> temp = scipy.ndimage.gaussian_filter ( data, 2.0 )
>>> # Copy back into Slicer
>>> data[:] = temp[:]
>>> t2.Modified()

Before:

PythonBefore.png

After:

PythonAfter.png

The NumPy command line module

In order to speed up experimentation, a Numpy command line module is available.

It takes in input one or two volume nodes and an output volume node, plus a path to a Python script residing in a text file.

Upon execution, input volume nodes are converted to ndarray variables named iarray and iarray2. The user simply has to put Numpy/Scipy code in the text file, taking care that the output array is stored in a variable named oarray. For example, the file could look like

import scipy.ndimage
oarray = scipy.ndimage.gaussian_filter ( iarray, 2.0 )

The module will take care of taking the content of oarray and storing it in the output volume node (note: differently from the example above, and more in line with Slicer practices, the result of processing is stored in a different output volume, which is selected from the node selector).

To rerun different NumPy code on the input image, simply edit the script file, save it and rerun the module.

Matplotlib plotting functionality

Matplotlib may be used from within Slicer, but currently (on the Mac) the Tk backend locks up and crashes Slicer. However, Matplotlib may still be used through one of the non-GUI backends. More details can be found on the MatPlotLib pages.

import matplotlib
matplotlib.use ( 'Agg' )
from pylab import *
t1 = arange(0.0, 5.0, 0.1)
t2 = arange(0.0, 5.0, 0.02)
t3 = arange(0.0, 2.0, 0.01) 

subplot(211)
plot(t1, cos(2*pi*t1)*exp(-t1), 'bo', t2, cos(2*pi*t2)*exp(-t2), 'k')
grid(True)
title('A tale of 2 subplots')
ylabel('Damped')

subplot(212)
plot(t3, cos(2*pi*t3), 'r--')
grid(True)
xlabel('time (s)')
ylabel('Undamped')
savefig ( 'MatplotlibExample.png' )

MatplotlibExample.png

Where to go from here

Far from exhaustive, this documentation is intended to whet the appitite of researchers who love the power of Matlab, but feel trapped inside, unable to break out and deploy applications. This Python interface to Slicer is intended to help! Here are some selected resources to begin to understand the power of what the combined NumPy - Slicer package offers.

Python Language Information

Numpy and Scipy Information

Writing Slicer Modules in Python

Painless Development in Slicer3 with Python, talk given by Demian Wassermann at the LMI, sept. 2008