ITK/Scripting: Difference between revisions

From KitwarePublic
< ITK
Jump to navigationJump to search
No edit summary
 
(4 intermediate revisions by 4 users not shown)
Line 1: Line 1:
ITK itself is written in C++, however a tool called CableSwig can be used to generate bindings for other languages.  This allows you to use the same runtime (running in optimised C++ code) but develop in a scripting language (and take advantage of all the attendant benefits).
This page needs to be updated for WrapITK, which is now the wrapping method.


The following languages are supported via the bindings:
{{ITK/Template/Footer}}
 
* '''Tcl''' : http://www.tcl.tk/ (see TclLanguage)
* '''Python''' : http://www.python.org/ (see PythonLanguage)
* '''Java''' : http://java.sun.com/ (see JavaLanguage)
 
In order to build the bindings, you will need to:
 
# Build and install CableSwig ([http://www.itk.org/HTML/CableSwig.html more info])
# Ensure you have installed both the runtime '''and''' the development versions of your chosen language
# Build ITK using the appropriate settings for your platform, and enable the CABLE_* option for your language
# Install ITK as per normal
# Test!
 
== Tcl Bindings ==
 
Tcl (Tool Command Language) is a very simple language for writing scripts.
 
== Python Bindings ==
 
Python is a very elegant and powerful language.
 
Here is some example Python code for ITK (taken from `Insight/Examples/Filtering`):
 
#!/usr/bin/env python
&nbsp;
# BinaryThresholdImageFilter.py
&nbsp;
from InsightToolkit import *
&nbsp;
from sys import argv
&nbsp;
reader = itkImageFileReaderUS2_New()
writer = itkImageFileWriterUS2_New()
&nbsp;
filter  = itkBinaryThresholdImageFilterUS2US2_New()
&nbsp;
filter.SetInput( reader.GetOutput() )
writer.SetInput( filter.GetOutput() )
&nbsp;
reader.SetFileName( argv[1] )
writer.SetFileName( argv[2] )
&nbsp;
filter.SetLowerThreshold( eval( argv[3] )  )
filter.SetUpperThreshold( eval( argv[4] )  )
filter.SetOutsideValue(  eval( argv[5] )  )
filter.SetInsideValue(    eval( argv[6] )  )
&nbsp;
writer.Update()
 
== Java Bindings ==
 
Java is a modern object-oriented language.
 
== How to wrap additional classes ==
 
Given that ITK is based on Generic Programming, most of the C++ classes are implemented as templates. Therefore, their final type is not defined until the actual template is  instantiated with specific template arguments. This generality makes difficult to automatically wrap ITK classes for scripting languages such as Tcl and Python. The solution adopted by ITK is to use the powerful Cable-Swig platform and to select specific instantiation for the most commonly used types.  In practice it is impossible to wrap all ITK classes, simply because the combinations of types will result in an very large number of classes.
 
It is then likely that you may find that your favorite class is not being wrapped...
 
What to do in that case ?
 
You have two independent lines of action:
 
# Write an email to the users-list and request the developers to wrap the class that you need.
# Wrap the class yourself by following the instructions in the remaining part of this section.
 
 
Since we have learned that programmers are great at learning-by-example, the following section will teach you how to wrap a new class by walking step-by-step through the process of wrapping a new class. The subject of our example is the <tt>itkOtsuMultipleThresholdCalculator</tt>, simply because at the time of writing this text there was a request pending for adding this class to the wrapping, so we thought that by writing these instructions we will solve two problems simultaneously :-)
 
=== Finding the directory ===
 
The first step in writing the wrapping for a class is to find the directory where such class has been cataloged. In the particular case of our subject, the <tt>itkOtsuMultipleThresholdCalculator</tt>, it is located in the directory
 
    <tt>Insight/Code/Algorithms</tt>
 
Therefore we know that its wrapping files will have to be placed under
 
    <tt>Insight/Wrapping/CSwig/Algorithms</tt>
 
As you probably noticed already, all the files defining the wrapping process are located in the directory
 
    <tt>Insight/Wrapping/CSwig</tt>
 
=== What files to modify ===
 
Now that we found the directory, we have to keep in mind that '''three''' files will have to be modified in order to add wrapping for our subject class. These files are
 
# CMakeLists.txt
# wrap_ITKAlgorithms.cxx
# the new file that defines the instantiations for our class
 
Let's start with the CMakeLists.txt file since it will be the simplest change to make. When you open any of the CMakeLists.txt files in the Wrapping/CSwig subdirectories you will find that the initial section contains a list of source files to wrap. Something like
 
 
  SET(WRAP_SOURCES
      wrap_itkCurvatureFlowImageFilter
      wrap_itkDemonsRegistrationFilter
      wrap_itkHistogramMatchingImageFilter
      wrap_itkImageRegistrationMethod
      ...
      )
 
The only thing that you have to change in this file is to add your class to this list. Note that you must add the prefix "wrap_itk" to the name of your class. In our case,
for wrapping the <tt>itkOtsuMultipleThresholdCalculator</tt> we will add it as
 
 
  SET(WRAP_SOURCES
      wrap_itkCurvatureFlowImageFilter
      wrap_itkDemonsRegistrationFilter
      '''wrap_itkOtsuMultipleThresholdCalculator'''
      wrap_itkHistogramMatchingImageFilter
      wrap_itkImageRegistrationMethod
      ...
      )
 
 
Let's now move into the <tt>wrap_ITKAlgorithms.cxx</tt> file. If you open this file, you will find code like the following.
 
 
  #ifdef CABLE_CONFIGURATION
  #include "itkCSwigMacros.h"
  namespace _cable_
  {
    const char* const package = ITK_WRAP_PACKAGE_NAME(ITK_WRAP_PACKAGE);
    const char* const groups[] =
      {
      ITK_WRAP_GROUP(itkCurvatureFlowImageFilter),
      ITK_WRAP_GROUP(itkDemonsRegistrationFilter),
      ITK_WRAP_GROUP(itkHistogramMatchingImageFilter),
      ITK_WRAP_GROUP(itkImageRegistrationMethod),
      ITK_WRAP_GROUP(itkImageToImageMetric),
      ITK_WRAP_GROUP(itkMeanSquaresImageToImageMetric),
      ...
 
 
As you probably already guessed, the only thing that you have to do in this file is to add a line for your class to be wrapped, in our case we add the line
 
      ITK_WRAP_GROUP(itkOutsuMultipleThresholdCalculator),
 
and the code will look like
 
 
  #ifdef CABLE_CONFIGURATION
  #include "itkCSwigMacros.h"
  namespace _cable_
  {
    const char* const package = ITK_WRAP_PACKAGE_NAME(ITK_WRAP_PACKAGE);
    const char* const groups[] =
      {
      ITK_WRAP_GROUP(itkCurvatureFlowImageFilter),
      ITK_WRAP_GROUP(itkDemonsRegistrationFilter),
      ITK_WRAP_GROUP(itkHistogramMatchingImageFilter),
      '''ITK_WRAP_GROUP(itkOutsuMultipleThresholdCalculator),'''
      ITK_WRAP_GROUP(itkImageRegistrationMethod),
      ITK_WRAP_GROUP(itkImageToImageMetric),
      ITK_WRAP_GROUP(itkMeanSquaresImageToImageMetric),
      ...
 
 
At this point we are done with the easy part  :-)
 
Let's now take a look at the new file that we must write in order to specify the instantiations that we want to create for our class, the beloved <tt>itkOutsuMultipleThresholdCalculator</tt>.  First of all, the name of the file must match what we already wrote in the CMakeLists.txt file, that is the file must be named '''wrap_itkOutsuMultipleThresholdCalculator.cxx'''. The ''skeleton'' of this file should contain the following code
 
    #include "itkOtsuThresholdImageCalculator.h"
    #include "itkImage.h"
    #ifdef CABLE_CONFIGURATION
    #include "itkCSwigMacros.h"
    #include "itkCSwigImages.h"
    namespace _cable_
    {
      const char* const group = ITK_WRAP_GROUP(itkOtsuThresholdImageCalculator);
      namespace wrappers
      {
      ... Instantitation go here ...
      }
    }
    #endif
 
Note the presence in the first line of the header file of our class to be wrapped. The second line includes the header of the <tt>itkImage</tt> because we anticipate that the Otsu calculator will be templated over the image type. This also motivates the includsion of the fifth line
 
    #include "itkCSwigImages.h"
 
that contains a large set of pre-instantiated image types.
 
Line #8 defines the name of the GROUP. The group is a set of multiple instantiations of the same class. Groups help to indicate to the wrappers what code must be put in the same compilation unit (.obj or .o file). By convention, the name of the group should always match the name of the class to be wrapped.
 
Now we are down to the fun part !!
 
Defining the instantiations for this class
 
 
=== Specifying the instantiations ===
 
Once we have the skeleton of the file as it is described in the previous section, we can proceed to specify the instantiation that we want for our class. For this purpose, ITK has defined a large set of macros that facilitate the writing of the instantiation. The Macro to use depend on the number of template parameters of the class to be wrapped. In the case of the <tt>itkOutsuMultipleThresholdCalculator</tt>, this class has a only one template parameter, that is the ImageType. Therefore we need the Macro that wrap a class with a single template parameter, this is the <tt>ITK_WRAP_OBJECT1()</tt> macro. Where the number "1" states that this macro is intended for a class with a single template parameter. The macro itself expects the following three parameters
 
# Name of the class to be wrapped (without the itk prefix)
# Type to be used for its template parameter
# Name that you want to be used at the level of the scripting language
 
A typical instantiation for our calculator will look like
 
    ITK_WRAP_OBJECT1(OtsuThresholdImageCalculator, image::F2,itkOtsuThresholdImageCalculatorF2);
 
The first argument <tt>OtsuThresholdImageCalculator</tt> is the name of our class. The second argument "image::F2" is a name predefined for an Image fo pixel type <tt>float</tt> and dimension <tt>2</tt>. This symbol has been defined in the file "itkCSwigImages.h", you may want to look at that file for other image types already defined. The last argument of the macro is the class name that you will use at the level of your scripting language once the class has been wrapped.  In principle you could use any name you want, but calling your class 'ElvisPrestley' may be confusing for your scripts  :-).  Therefore we defined a "convention" in ITK in order to have a consisten naming scheme. This convention specifies that the wrapped class is composed by the C++ classname and we add letters and numbers representing the fundamental types and dimensions for which this class has been wrapped. In the particular case of the instantiation above, we are saying that the Otsu calculator has been wrapped for images of pixel type <tt>float</tt> (that is the reason for the "F"), and dimension == 2. This, of course, matches the image type that we used for the second argument of the macro: image::F2.
 
A typicall full set of instantiations will look like:
 
    ITK_WRAP_OBJECT1( OtsuThresholdImageCalculator, image::F2,  itkOtsuThresholdImageCalculatorF2);
    ITK_WRAP_OBJECT1( OtsuThresholdImageCalculator, image::F3,  itkOtsuThresholdImageCalculatorF3);
    ITK_WRAP_OBJECT1( OtsuThresholdImageCalculator, image::US2,  itkOtsuThresholdImageCalculatorUS2);
    ITK_WRAP_OBJECT1(OtsuThresholdImageCalculator,  image::US3,  itkOtsuThresholdImageCalculatorUS3);
 
Where we are specifying in order
 
# Images of pixel type <tt>float</tt> and Dimension 2  (F2)
# Images of pixel type <tt>float</tt> and Dimension 3  (F3)
# Images of pixel type <tt>unsigned short</tt> and Dimension 2  (US2)
# Images of pixel type <tt>unsigned short</tt> and Dimension 3  (US3)
 
 
Once we put this line inside the skeleton of code that we describe above, we will end up with a complete file that looks like
 
 
  #include "itkOtsuThresholdImageCalculator.h"
  #include "itkImage.h"
  #ifdef CABLE_CONFIGURATION
  #include "itkCSwigMacros.h"
  #include "itkCSwigImages.h"
  namespace _cable_
  {
    const char* const group = ITK_WRAP_GROUP(itkOtsuThresholdImageCalculator);
    namespace wrappers
    {
      ITK_WRAP_OBJECT1(OtsuThresholdImageCalculator, image::F2,itkOtsuThresholdImageCalculatorF2);
      ITK_WRAP_OBJECT1(OtsuThresholdImageCalculator, image::F3,itkOtsuThresholdImageCalculatorF3);
      ITK_WRAP_OBJECT1(OtsuThresholdImageCalculator, image::US2,itkOtsuThresholdImageCalculatorUS2);
      ITK_WRAP_OBJECT1(OtsuThresholdImageCalculator, image::US3,itkOtsuThresholdImageCalculatorUS3);
    }
  }
  #endif
 
 
and... that's it !!
 
You are done writing your code. Now you just need to rerun CMake in order to update the configuration of your build according to the new information that you put in the modified files. Once you are done running CMake you can trigger the build of your binary directory.  Note that adding one class to the wrapping may trigger an update of the entire wrapping system. This is due to unpredictable dependencies that can only be cleaned up by making a large scale rebuild.  Therefore be prepared to sit comfortably and read that chapter of the book that you have been post-ponning for a long time, or if time is appropriate, simply go for lunch.
 
 
=== What can go wrong ? ===
 
This is the optimistic section...
 
Many things can go wrong the first time you wrap a class. The reason is that thing must match between the multiple files that you have to modify, and it is therefore quite easy to make a mistake when you are not familiar with the modifications.
 
One first important thing to keep in mind is that when you wrap a class, the parent '''must''' be already wrapped. This is of coure, transitive, meaning that the grand parent, and the grand grand parent must also be wrapped. It is quite likely that you will find situations in which you have to wrap two or three classes of a hierarchy in order to get to the class that you want.  Two signs will tell you when one of the parent class has not been wrapped
 
# The build will print out a warnig.
# When you attempt to use the class at the level of your interpreted language you will find that some methods are not available.
 
 
You probably will see the first of these two signs, in the form of a message similar to the following
 
  Building wrap_itkBinaryThresholdImageFilterTcl.cxx from  /.../ITKGcc3.3Tcl/Wrapping/CSwig/BasicFiltersA/wrap_itkBinaryThresholdImageFilterTcl.cxx...
  Building wrap_itkBinaryDilateImageFilterTcl.cxx from  /.../ITKGcc3.3Tcl/Wrapping/CSwig/BasicFiltersA/wrap_itkBinaryDilateImageFilterTcl.cxx...
  Building wrap_itkBinaryErodeImageFilterTcl.cxx from  /.../ITKGcc3.3Tcl/Wrapping/CSwig/BasicFiltersA/wrap_itkBinaryErodeImageFilterTcl.cxx...
  Building wrap_itkCannyEdgeDetectionImageFilterTcl.cxx from  /.../ITKGcc3.3Tcl/Wrapping/CSwig/BasicFiltersA/wrap_itkCannyEdgeDetectionImageFilterTcl.cxx...
  Building wrap_itkCastImageFilter_2DTcl.cxx from  /.../ITKGcc3.3Tcl/Wrapping/CSwig/BasicFiltersA/wrap_itkCastImageFilter_2DTcl.cxx...
  Building wrap_itkCastImageFilter_3DTcl.cxx from  /.../ITKGcc3.3Tcl/Wrapping/CSwig/BasicFiltersA/wrap_itkCastImageFilter_3DTcl.cxx...
  Warning itk::UnaryFunctorImageFilter<itk::Image<float, 2>,itk::Image<unsigned int, 2>,itk::Functor::Cast<float, unsigned int> >
  has a class hierarchy that has wrapping gaps.
  The class hierarchy wrapping is as follows:
  Not wrapped: itk::InPlaceImageFilter<(itk::Image<(float,2)>,itk::Image<(unsigned int,2)>)>
      Wrapped: itk::ImageToImageFilter<(itk::Image<(float,2)>,itk::Image<(unsigned int,2)>)>
      Wrapped: itk::ImageSource<(itk::Image<(unsigned int,2)>)>
      Wrapped: itk::ProcessObject
 
 
Note that the message is quite clear, it tells you
 
  Warning "ClassName"
  has a class hierarchy that has wrapping gaps.
 
then it tells you the entire hierarchy of parents of this class, and will point out which ones are wrapped and which ones are not.
 
  Not wrapped: itk::InPlaceImageFilter<(itk::Image<(float,2)>,itk::Image<(unsigned int,2)>)>
      Wrapped: itk::ImageToImageFilter<(itk::Image<(float,2)>,itk::Image<(unsigned int,2)>)>
      Wrapped: itk::ImageSource<(itk::Image<(unsigned int,2)>)>
      Wrapped: itk::ProcessObject
 
Therefore you know that the class
 
      itk::InPlaceImageFilter<(itk::Image<(float,2)>,itk::Image<(unsigned int,2)>)>
 
has not been wrapped.
 
That typically means that the InPlaceImageFilter may have been wrapped for other image types, but not for the specific combination of
 
        InputImageType  = Image< float, 2 >
        OutputImageType = Image< unsigned int, 2 >
 
The solution in this case is to locate the files where the <tt>InPlaceImageFilter</tt> has been wrapped and proceed to add this specific combination of types.
 
 
=== May the force be with You... ===
 
That's just the final advice: 
 
Wrapping is a complex process and therefore you must be patient and persistant if you want to get throught it. The positive aspect is that once you have understood the procees for wrapping one class, the others must become very easy to manage. Of course you are always welcome to post messages to the users list and ask for assistance.

Latest revision as of 17:00, 11 February 2012

This page needs to be updated for WrapITK, which is now the wrapping method.



ITK: [Welcome | Site Map]