[Insight-developers] [ITK + Python] Wrapping classes proposal

Gaetan Lehmann gaetan.lehmann at jouy.inra.fr
Fri Jun 17 09:49:13 EDT 2005


On Thu, 16 Jun 2005 17:48:24 +0200, Miller, James V (Research)  
<millerjv at crd.ge.com> wrote:

>> 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()
>
> While it is nice to use python's ability to refer to parameters as  
> name=value pairs,

Happy to read that :-)

> I would hesitate to have the python API diverge greatly from the  
> C++ API. In particular,
> when you do
>
> sub = itk.SubtractImageFilter[imageType, imageType,  
> imageType].New(reader, reader2)
>
> I assume that you have changed New() to run through its parameters list,  
> call and
> set methods based on name-value pairs and then use the remaining  
> parameters to be
> the input1 and input2.  Then you make calls in the New() to set the  
> inputs as
>
> 	self.SetInput1( reader.GetOutput() )
>       self.SetInput2( reader2.GetOutput() )
>

exactly

> But what if I wanted to grab the 2nd output of a filter to pass in as  
> the first
> input to the subtract filter?  I guess this is not supported by your  
> New() method and I would have to fall back to multiline coding.

You're right, it's not supported, but multiline is not needed :  
filterType.New(Input1=f1.GetOutput1(), Input2=f1.GetOutput(2)) will work.

> Is this "inconsistency"
> confusing to users?
>

I don't think so. It's a small shortcut, not a complicated feature, and it  
really make less code to write :-)

The ability to give a filter in New method is a convenient way to decrease  
amount of code to create a pipeline. When writing code in interpreter, it  
really annoying to always have to write the 'f1.SetInput(f2.GetOutput())'  
line.

I'm sure that, if I show itk python code to the friend who learned me to  
use matlab, he won't be interested to use itk, even if itk is fast,  
powerful, opensource... in matlab, we write things like :

    marker = imread('MedianImageFilter.png');
    mask = imread('mask.png');
    median = medfilt2(mask);
    recons = imreconstruct(marker, median);
    imwrite(recons, 'out.png');

which is quite more understandable and smaller than itk equivalent code :

    marker = itk.ImageFileReader.US2.New()
    marker.SetFileName( 'MedianImageFilter.png' )
    mask = itk.ImageFileReader.US2.New()
    mask.SetFileName('mask.png' )
    median = itk.MedianImageFilter.US2US2.New()
    median.SetInput(mask.GetOutput())
    recons = itk.GrayscaleReconstructionByDilatationImageFilter.US2US2.New()
    recons.SetMaskImage(median.GetOutput())
    recons.SetMarkerImage(marker.GetOutput())
    writer = itk.ImageFileWriter.US2.New()
    writer.SetFileName( 'out.png' )
    writer.Update()

We should really do all the possible to decrease code size.

Gaetan.



PS : Just to have an idea of what the code can be with New method feature :

same code with key=value notation (not used to set input images)

    marker = itk.ImageFileReader.US2.New( FileName='MedianImageFilter.png' )
    mask = itk.ImageFileReader.US2.New( FileName='mask.png' )
    median = itk.MedianImageFilter.US2US2.New()
    median.SetInput(mask.GetOutput())
    recons = itk.GrayscaleReconstructionByDilatationImageFilter.US2US2.New()
    recons.SetMaskImage(median.GetOutput())
    recons.SetMarkerImage(marker.GetOutput())
    writer = itk.ImageFileWriter.US2.New( FileName='out.png' )
    writer.Update()

same code with key=value notation (also used to set input image)

    marker = itk.ImageFileReader.US2.New( FileName='MedianImageFilter.png' )
    mask = itk.ImageFileReader.US2.New( FileName='mask.png' )
    median = itk.MedianImageFilter.US2US2.New( Input=mask.GetOutput() )
    recons =  
itk.GrayscaleReconstructionByDilatationImageFilter.US2US2.New( MaskImage=median.GetOutput(),  
MarkerImage=marker.GetOutput() )
    writer = itk.ImageFileWriter.US2.New( FileName='out.png' )
    writer.Update()

same code with the key=value notation and the New method shortcut to set  
input image

    marker = itk.ImageFileReader.US2.New( FileName='MedianImageFilter.png' )
    mask = itk.ImageFileReader.US2.New( FileName='mask.png' )
    median = itk.MedianImageFilter.US2US2.New( mask )
    recons =  
itk.GrayscaleReconstructionByDilatationImageFilter.US2US2.New( marker,  
median )
    writer = itk.ImageFileWriter.US2.New( recons, FileName='out.png' )
    writer.Update()


> Jim
>
>
>
>
>
> -----Original Message-----
> From: insight-developers-bounces+millerjv=crd.ge.com at itk.org  
> [mailto:insight-developers-bounces+millerjv=crd.ge.com at itk.org]On Behalf  
> Of Gaetan Lehmann
> Sent: Friday, June 10, 2005 1:26 PM
> To: insight-developers at itk.org
> Subject: Re: [Insight-developers] [ITK + Python] Wrapping classes  
> proposal
>
>
>
> 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
> _______________________________________________
> Insight-developers mailing list
> Insight-developers at itk.org
> http://www.itk.org/mailman/listinfo/insight-developers



-- 
Gaetan Lehmann <gaetan.lehmann at jouy.inra.fr>
Tel: +33 1 34 65 29 66
Biologie du Développement et de la Reproduction
INRA de Jouy-en-Josas (France)
Web: http://voxel.jouy.inra.fr


More information about the Insight-developers mailing list