Getting Started with Python-InsightToolkit
This short tutorial will get you started using the Insight Toolkit (ITK) with Python in Ununtu. We will explore basic file reading, image processing, and volume rendering. It is an introduction, the ITK Software Guide and Python Tutorial contain more in depth information.
Basics
This tutorial requires installation of the packages as described in my previous tutorial. Also, a few other packages are needed.
sudo apt-get install python-scipy insighttoolkit-examples
Insighttoolkit-examples contains images we are going to use in this demo. Uncompress the image first.
sudo gunzip /usr/share/doc/insighttoolkit-examples/examples/Data/BrainProtonDensity3Slices.raw.gz
I prefer to use IPython instead of python for the command line. IPython gives nice features like auto-completion, text coloring, and debugging. It's great.
sudo apt-get install ipython
ipython
Alright, we are ready to start using python. All the following code samples are typed into the IPython command line. Begin by importing the ITK module.
import itk
ITK is a heavily templated system. As a result, everything requires an image type. For example, a three dimensional image of type unsigned char is defined by
image_type = itk.Image[itk.UC, 3]
A large combination of image types are available, in fact ITK puts no restriction of data types and image dimensions. Unfortunately, this template system doesn't translate into python. I had to compromise and include a sub-set of dimensions and data-types. If you need more functionality, let me know and I can add it to subsequent versions of python-insighttoolkit. The following data-types for 2, 3, and 4 dimensional images are included
- unsigned char
- unsigned short
- RGB unsigned short
- float
- complex float
- vector float
- covariant vector float
Reading and Writing Files
How do we read files? Just enter
file_name = '/usr/share/doc/insighttoolkit-examples/examples/Data/BrainProtonDensity3Slices.mha'
reader = itk.ImageFileReader[image_type].New()
reader.SetFileName( file_name )
reader.Update()
That's it. See how the reader required an image_type. This is slightly restrictive because you need to know the dimensionality and data type of the image before you read it. But not so bad.
These four short lines of code will read TIFF, JPEG, PNG, BMP, DICOM, GIPL, Bio-Rad, LSM, Nifti, Analyze, SDT/SPR (Stimulate), Nearly Raw Raster Data (Nrrd), and VTK images. I don't know what half of those file formats are, but someone will find them useful.
Simple Image Processing Example
Alright, an image is loaded into memory and ready to go. We'll start with a simple example, a median filter.
median_filter = itk.MedianImageFilter[image_type, image_type].New()
Notice that image_type is used not once, but twice. The first and second image type refer to the input and output type, respectively. One image type definition was necessary for the reader above, because there is only an output image type. Filters require an input and output type.
Filters often expose parameters to adjust their function. For example, setting the radius to 1 specifies the median filter will operate on 3x3x3 neighborhoods.
median_filter.SetRadius( 1 )
Set the filter input to the output of the reader and update.
median_filter.SetInput( reader.GetOutput() )
median_filter.Update()
Remember earlier when we called reader.Update(). Well, this isn't necessary. When median_filter.Update() is called, ITK will look at its input, and if necessary update it. If you put together a long string of filters, an update on the final filter will cascade all the way back to the beginning and update everything. Of course ITK is even smarter than this and will only rerun filters if its parameters or input has changed.
There are many more operations you can perform on your images, but we need to move on.
ITK and VTK
ITK is great for reading, writing, and processing images. However, ITK is not equipped for visualization. This is where the Visualization Toolkit (VTK) comes in. Fortunately, ITK and VTK were created by some of the same people, so the concepts are very similar. In addition, connecting ITK and VTK is trivial with the python-insighttoolkit-extras package.
itk_vtk_converter = itk.ImageToVTKImageFilter[image_type].New()
itk_vtk_converter.SetInput( median_filter.GetOutput() )
itk_vtk_converter.Update()
Now itk_vtk_converter.GetOutput() returns VTK data. Very handy, especially if you want to render the volume.
Volume Rendering with VTK
This section is a quick introduction to volume rendering with VTK. It is low on details because there are better introductions to VTK and python already available.
First we need to import VTK.
import vtk
A volume mapper determines how image data is rendered to the screen. In this case we use a high quality ray casting mapper. Depending on your video card, you can use the higher performance vtk.vtkVolumeTextureMapper3D() or vtk.vtkVolumeTextureMapper2D() mappers.
volume_mapper = vtk.vtkVolumeRayCastMapper()
volume_mapper.SetInput( itk_vtk_converter.GetOutput() )
The Ray Cast Mapper requires a composite function.
composite_function = vtk.vtkVolumeRayCastCompositeFunction()
volume_mapper.SetVolumeRayCastFunction( composite_function )
Our input image is 8 bit data with values from 0 to 255. A mapping is required between these values and the displayed color. For instance, the following code will map the image intensity to the blue channel. This is a linear function; 0 will map to black (0.0, 0.0, 0.0) and 255 will map to solid blue (0.0, 0.0, 1.0). Everything in between will be a linear interpolation between the two end points, i.e. 127 will map to dark blue (0.0, 0.0, 0.5).
color_transfer_func = vtk.vtkColorTransferFunction()
color_transfer_func.AddRGBPoint( 0, 0.0, 0.0, 0.0 )
color_transfer_func.AddRGBPoint( 255, 0.0, 0.0, 1.0 )
Each voxel in the input image must also map to opacity. The following maps image intensities of 0 to completely transparent (0.0), and 255 to completely opaque (1.0). Again, values between 0 and 1 are interpolated.
opacity_transfer_func = vtk.vtkPiecewiseFunction()
opacity_transfer_func.AddPoint( 0, 0.0 )
opacity_transfer_func.AddPoint( 255, 1.0 )
Encapsulate the above properties in a vtkVolumeProperty.
volume_properties = vtk.vtkVolumeProperty()
volume_properties.SetColor( color_transfer_func )
volume_properties.SetScalarOpacity( opacity_transfer_func )
If you are familiar with VTK, a VTK volume is similar to a VTK actor. It encapsulates the data, properties, and rendering method for one 'object' in the scene.
volume = vtk.vtkVolume()
volume.SetMapper( volume_mapper )
volume.SetProperty( volume_properties )
Now lets create a few objects used for rendering.
renderer = vtk.vtkRenderer()
render_window = vtk.vtkRenderWindow()
window_interactor = vtk.vtkRenderWindowInteractor()
The renderer handles the volume rendering, the render window is where the rendered volume will appear, and the window interactor handles user input to adjust the viewpoint (zoom, rotate, move, etc). Hook 'em up, and add our volume.
render_window.AddRenderer( renderer )
window_interactor.SetRenderWindow( render_window )
renderer.AddVolume( volume )
Just render the scene and start the window interactor so you can rotate the volume with your mouse.
render_window.Render()
window_interactor.Start()
To stop the interactor hit 'q'.
Numpy and ITK
Working with images with ITK is great, but when working in python, you want all the functionality of python. Fortunately, converting ITK images to python arrays is simple with the python-insighttoolkit-extras package.
itk_py_converter = itk.PyBuffer[image_type]
image_array = itk_py_converter.GetArrayFromImage( reader.GetOutput() )
How about converting a 10x10x10 python array to an ITK image.
import scipy
another_image_array = scipy.zeros( (10,10,10) )
itk_image = itk_py_converter.GetImageFromArray( another_image_array )
Does it get any easier?
terrific! his sure does justice to the excellent WrapITK project!
Thanks Paul. It is really a time saver, the Ubuntu repository.
I'm working on a thesis in Spain about MRI images processing. Just starting, I'd been thinking about working in C++ because I know it. But I think I should learn python, to code faster my experiments.
Will keep in touch.
Regards.
http://alexsavio.blogspot.com
Thanks, Paul. These are great! My think my next big programming project will be in python now...