[Insight-developers] [ITK + Python] Wrapping classes proposal
Gaetan Lehmann
gaetan.lehmann at jouy.inra.fr
Fri Jun 10 13:26:10 EDT 2005
Hi Benoit,
I have also written a module some time ago to try to make itk python more
pleasant to use (see
http://www.itk.org/Bug/bug.php?op=show&bugid=1766&pos=0), and use some
similar things.
I'm really happy to see that others than me are interested in this subject. I
hope we will see nice solutions in future release :-)
I don't take the problem the same way : I was quite frustrated while
prototyping with itk python. There was so much names in InsightToolkit module
that it was really difficult to use completion in interperter (ipython). I
decided to reorganize the module, so now it appear as a tree :
itk
|-ImageFileReader
| |-US2
| | |-New
| | |-Ptr
| | +-Pointer
| |-US3
| |-F2
| +-F3
|-MedianImageFilter
| |-US2US2
| |-US3US3
| +-...
|-...
It highly decrease number of names in module and highly improve usability in
ipython interperter :-)
Note that I have also drop the redundant itk prefix, and BTW, decrease line
length of 3 pixels ;-)
Where my idea is similar to yours, is that I made type accessible at run time.
Instead of writing
itk.ImageFileReader.US2.New()
we can write
itk.ImageFileReader['US2'].New()
or
pixelType = 'US'
dim = 2
imageType = (pixelType, dim)
itk.ImageFileReader[imageType].New()
Which is quite similar to c++ coding style :-)
But there is good and less good things in c++ style, and I find quite boring
to have to set parameters on several lines, or to have to call
a.SetInput(b.GetOutput()) for each filter, especially in interperter.
So I have given the ability to set attribute value in New method :
reader = itk.ImageFileReader[imageType].New(FileName='image.png')
and to make construction of the process pipeline easier, if a var is used as
parameter in New method, it is used as source of image :
reader2 = itk.ImageFileReader[imageType].New(FileName='image2.png')
sub = itk.SubtractImageFilter[imageType, imageType, imageType].New(reader,
reader2)
writer = itk.ImageFileWriter[imageType].New(sub, FileName='out.png')
writer.Update()
All of this make code short, readable and flexible.
I agree that type names are not really consistent, but I'm not sure your
solution is totally doable. How would you call wrapped
BinaryDilateImageFilter :
ITK_WRAP_OBJECT3(BinaryDilateImageFilter, image::F2 , image::F2 ,
structuringElement::F2, itkBinaryDilateImageFilterF2F2 );
for example ?
Gaetan
On Friday 10 June 2005 17:35, Benoit Regrain wrote:
> Motivation:
> -----------
>
> I would like to use ITK from Python (hey, I'm a heavy VTKPython user and
> I would like to have the same pleasure with ITKPyton :).
> Whenever if possible I would like to keep the code written in ITKPython
> as close as possible to the corresponding ITK code written in C++
> (this was the case for VTK/Python versus VTK/C++, and it was really neat).
>
> I understand the wrapping stage requires instantiating the template classes
> and that it is impossible to wrap all possible instantiations of ITK
> templates (for obvious combinatorial explosion reasons and also because
> there can be user-defined template arguments).
>
> Still it would be nice to be able to instantiate an ITK template
> at the Python level (instead of doing the instantiation at the C++ level
> and then wrapping the code). If this feature is not provided, ITKPython
> scripts will not be able to define new types on the fly (think of an
> instantiation produced by user through GUIs or simple prototyping).
>
> Ideally, I would like to be able to write ITKPython code looking like
> the following snippet:
>
> 1 reader=itk.itkImageFileReader_New(ITK_US,2)
> 2 reader.SetFileName("Data/homersbrain.bmp")
> 3 img=reader.GetOutput()
> 4 process=itk.itkThresholdImageFilter_New(img)
> 5 process.SetInput(img)
> 6 process.SetOutsideValue(50)
> 7 process.ThresholdBelow(200)
> 8 cast=itk.itkCastImageFilter_New(img,itk.itkImage(ITK_UC,2))
> 9 cast.SetInput(process.GetOutput())
> 10 img=cast.GetOutput()
> 11 writer=itk.itkImageFileWriter_New(img)
> 12 writer.SetInput(img)
> 13 writer.SetFileName("test2.bmp")
> 14 writer.Write()
>
> In the above snippet, the important lines are line 8, i.e.
> cast=itk.itkCastImageFilter_New(img,itk.itkImage(ITK_UC,2))
> where we can make reference to an instantiated ITKPython class named
> itk.itkImage(ITK_UC,2)
> which needs to be implicitely instantiated somewhere !
> But we also need the functionality of line 11 i.e.
> writer=itk.itkImageFileWriter_New(img)
> where we construct a new ITK object of required type (and the constructor
> should return an exception if the required type isn't wrapped).
>
>
> The specified parameters for the pseudo-template could be one of the 3
> following possibilities :
> - explicit type like ITK_UC or 2
> example (line 8) : itk.itkImage_New(ITK_UC,2)
> - an itk python class like the result of call to itk.itkImage(ITK_UC,2)
> that returns : itk.itkImageUC2
> example (line 8) :
> itk.itkCastImageFilter_New(img,itk.itkImage(ITK_UC,2)) - an itk python
> instance like img
> example (line 11) : itk.itkImageFileWriter_New(img)
>
>
> I was able to realise a small Python module that offers me the possibility
> to instanciate my ITKPython classes on the fly and to get the above snippet
> running.
> The key for this litte module to work in the following:
> it uses Python internal mechanisms to recover all the required
> template classes and then creates the required constructor methods.
>
> For example for the above snippet, my python module
> 1/ finds the python classes itkImageUC2, itkImageUS2, etc.
> 2/ searches the common radix class name that is itkImage
> 3/ creates 2 methods :
> + itkImage(*args) that returns the itkImageUC2, itkImageUS2, etc.
> class in function of args
> + itkImage_New(*args) that returns the itkImageUC2, itkImageUS2, etc.
> class instanciation in function of args
>
> This works fairly well but alas I couldn't get it running for all cases !
>
>
> The snag is that ITK wrapping doesn't seem to be consistent when
> choosing the mangled names for Python of the underlying ITK classes
> (the mangled name is choosed manually).
> Or at least the rules are fairly obscur and don't seem to be systematic.
> For example, in the above snippet the specified template arguments falls
> within one of the following categories :
> - The itkImage as a PixelType specified as itkVector(type,dim) or
> itkConvariantVector(type,dim) :
> example : itkImage_New( itkVector(ITK_F, 2), 2)
> - For the creation of itkFunctionBase class instance with a class (or
> instance) in python
> example : itkFunctionBase_New( itkImage(ITK_F, 2), ITK_D)
>
> But this strategy relies on consistent mangled class names at the Python
> level. More precisely :
> - if we require a 2D image with vector<float,2> scalar type as template
> parameter, we would like to write :
> itk.itkImage( itk.itkVector( ITK_FLOAT, 2), 2)
> and the method will search the itk.itkImageVF22 class.
>
> ALAS ITK doesn't produce this mangled name ! Indeed, the valid ITKPython
> class names are either itk.itkImageV2F2 or itk.itkImageVF2 !
> - if we require a processing on 2D image with float scalar type as
> template parameter, we would like to write :
> itk.itkThresholdFilter( itk.itkImage( ITK_FLOAT, 2) )
> and the method will search the itk.itkThresholdFilterIF2 class.
>
> ALAS ITK doesn't produce this mangled name ! Indeed, the valid ITKPython
> class names are either itk.itkThresholdFilterF2 !
> - if we require a function on 2D image with float scalar type as template
> parameter, we would like to write :
> itk.itkFucntionBase( itk.itkImage( ITK_FLOAT, 2), ITK_D )
> and the method will search the itk.itkFunctionBaseIF2D class.
>
> ITK produces this mangled name !
> The ITK wrappers constructs the above mangled named (well actually, they
> are manually defined in the wrap_Xxx files)! But those mangled names
> don't seem to be globaly coherent. And we probably rely on the
> choices of the author of each wrap_Xxx file to attain global
> coherence (well maybe I am wrong on this one).
>
> This raises the two following questions:
>
>
> First question: why is the ITK name mangling apparently inconsitent ?
> ---------------------------------------------------------------------------
>
> I went looking at the wrapping of ITK classes. I don't fully
> understand the logic used to create the wrapping. The mangled class's
> name doesn't seem to be coherent/consistent for all the wrapped classes.
> In the Wrapping/itkCSwigImages.h file, we can find the following code :
> 1 typedef itk::Image< itkvector::F2, 2 > VF2;
> 2 typedef itk::Image< itkvector::F3, 3 > VF3;
> 3 typedef itk::Image< itkvector::F2, 2 > VD2;
> 4 typedef itk::Image< itkvector::F3, 3 > VD3;
> 5 typedef itk::Image< itkvector::F2, 2 > V2F2;
> 6 typedef itk::Image< itkvector::F2, 3 > V2F3;
> 7 typedef itk::Image< covariantvector::F2, 2 > CVF2;
> 8 typedef itk::Image< covariantvector::F3, 3 > CVF3;
> 9 typedef itk::Image< covariantvector::D2, 2 > CVD2;
> 10 typedef itk::Image< covariantvector::D3, 3 > CVD3;
>
> + The lines 1, 3 & 5 represent the same type of datas but the typedef
> names are different !?
> + The lines 3 & 4 indicate the definition for floating images but the
> typedef name indicates a double !?
> + An image having float as scalar type and dimension 2 has the F2
> mangled type. But, an image having Vector<float,2> as scalar type and
> dimension 2 has either VF2 or V2F2 as mangled type !?
> Wouldn't it be better to use VF22 to avoid confusion ? VF22 would
> more clearly indicate : Image< V< F,2 >, 2>
> + A process like ImageThresholdFilter that only has one input might be
> mangled as:
> ImageThreshold< Image<F,2> > ==> ImageThresholdIF2
> The I letter indicates that we have an image (like in few wrapped
> classes or when having ImageVF2... the V letter indicating Vector)
>
> Could any one clarify the ITK mangling strategy for me ?
>
>
> Second question: CableSwig versus Swig ?
> ----------------------------------------
>
> I would like to know what is today the advantage of CableSwig ?
> (as opposed to Swig)
>
> I understand the historic choice, when Swig didn't handle template classes.
> But Swig evolved and is now able to handle template classes (and
> nested classes, though their appear to still be some kind of limitations on
> heavily nester classes, as I understand it).
> What is now the CableSwig additional value compared to Swig ? Does
> CableSwig add any special wrapping features for ITK ? [e.g. by adding or
> removing some specific methods or any other ITK specific reason] ?
>
>
> Other informations for ITK :
> ----------------------------------------
>
> If the ITK-Kitware folks are interested to work on this subject, I would
> be deligthed to try to provide some help and feedback... since I need
> this feature bad [ I'm a VTKPython drug addict: please help to switch
> ...to ITKPython ;-] .
>
>
> Yours,
> Benoit Regrain
More information about the Insight-developers
mailing list