[Insight-developers] [ITK + Python] Wrapping classes proposal
Benoit Regrain
benoit.regrain at creatis.insa-lyon.fr
Tue Jun 14 06:34:57 EDT 2005
----- Original Message -----
From: "Gaetan Lehmann" <gaetan.lehmann at jouy.inra.fr>
To: "Benoit Regrain" <benoit.regrain at creatis.insa-lyon.fr>;
<insight-developers at itk.org>
> On Mon, 13 Jun 2005 14:06:40 +0200, Benoit Regrain
> <benoit.regrain at creatis.insa-lyon.fr> wrote:
>
>> ----- Original Message ----- From: "Gaetan Lehmann"
>> <gaetan.lehmann at jouy.inra.fr>
>>
>>> 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 :-)
>> It's an interessant concept. With that, we have the list of all possible
>> wrapped types for one ITK process. But in python, the 2 values 'US2'
>> or ('US',2) or img.GetTemplateType() are different but specifie the same
>> resulting class.
>
> The real thing which defines the type is the string 'US2'. Tuple notation
> is there only to make things easier and is just a concatenation of all
> objects in tuples:
>
> def __seq2str__(self, seq) :
> if not isinstance(seq, str) and hasattr(seq, '__getitem__') :
> return "".join([self.__seq2str__(e) for e in seq])
> else :
> return str(seq)
>
> Without it, I have to use :
>
> imageType = pixelType + str(dim)
>
> I find it really better with tuples :-)
> If your img.GetTemplateType() return the same string ('US2') as unique id,
> there is no problem there.
> Do you use str(itkobject) to get types ?
No, I'm using the mangled name of the itk wrapped classes (like you).
I have made a parser for that. But I know that it's very dependant to the
mangling rules :-(
>> When the call of a function (in your case, the __getitem__ function),
>> we can mask this problem and make internal solutions like I do in
>> my 'itkImage' or 'itkImage_New' function.
>>
>>
>> This proposal have a second problem :
>> The user must know the instanciation type.
>> Consider the following example :
>>
>> 1 def Write( img, name):
>> X imgType = ???
>>
>> 2 writer = itk.ImageFileWriter[imgType].New()
>> 2b writer = itk.ImageFileWriter_New( img )
>>
>> 3 writer.SetInput( img )
>> 4 writer.SetFileName( name )
>> 5 writer.Update()
>>
>> To create the writer, we must know the image type. But the image don't
>> have
>> its type (with the actual wrapping). At line X, the use must be able to
>> get
>> the image type from the image (line 2).
>> With my solution (line 2b), the image type is directly found from the img
>> instance (actually with an heuristic :-( )
>
> Interesting !
> I haven't though at this kind of usage.
> Another usage of this feature would be a show(img) function which display
> the image, without taking care of type. Great for prototyping :-)
> (sorry for boring everyone with prototyping, but i'm spending most of my
> time with it)
I have implemented it in my code. I obtain the same use than you, but I can
use
a class or instance in parameter :-)
>> In third remark, I will consider the itk.FucntionBase class.
>> ITK_WRAP_OBJECT2( FunctionBase, image::F2, double, itkFunctionBaseIF2D )
>> ITK_WRAP_OBJECT2( FunctionBase, point::F2, double, itkFunctionBasePF2D )
>>
>> Will you have :
>> FunctionBase[ (IF2,D) ].New() => to create itkFunctionBaseIF2D
>> FunctionBase[ (PF2,D) ].New() => to create itkFunctionBasePF2D
>> or
>> FunctionBase[ (F2,D) ].New() => to create itkFunctionBaseIF2D
>> FunctionBase[ (F2,D) ].New() => to create itkFunctionBasePF2D
>> With a problem to do the difference between the 2 types F2,D
>>
>> And in the example, we do the difference between image and point by the
>> I or
>> P prefix. So, why this prefer is not used to present all image or point
>> type
>> in other processes (problem of consistence in the mangled names) ?
>
> I agree with you.
> But if it's possible to add an attribute to the class which cleany
> identify the type (as you say below), we don't have to worry about name :
> we should just use the new attributes.
> If names are not changed, we keep full compatibility with older itk
> versions...
Yes, you have right in theory. And I understand the will to keep this
compatibility. But I'm not sure that's easily possible.
Consider the itk::Image < itk::Vector<float,2>, 2> with an attribute to
get the mangled add.
So, for the itk::Vector<float,2>, the attribute will have 'VF2'
So, when we will search the type of the image, we will search 'VF22'
But the wrapped image classes for the want type are 'VF2' or 'V2F2'
and never 'VF22'
So, I see only one solution. Create the _ItkClassXxx while wrapping
and generate the link between a wanted 'VF22' and a real 'V2F2' or 'VF2'
But I don't know if it's possible with CableSwig or Swig.
In other hand, is it really a problem to not keep the backward compatibility
in all cases ?
Actually, the known persons working with ITK (in the same society than me)
are afraid by the use of itk in python. But after a little sounding, it
appears that
a change of the mangled names with a simplest use in the wrapped languages
will be more attractive.
It's why I prefer a change of the mangled name (if we don't have other
choice)
>>> 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.
>> This can be a good addict. But some processes in ITK don't have the
>> SetInput
>> method (reader processes for example). How does the function
>> react in this case ? Does your solution work in all cases ?
>
> I use the SetInput(int, image) method, and if it fail (for
> itkImageToVTKImageFilter for example), I try with SetImage(image). If it
> still fail, it return a AttributeError, exactly as using SetInput if it
> does not exists.
> I don't see case where it can't work...
For me, it will stay in a second (interessant) point. At this time, I focus
on a
best use of the itk wrapping (essentially with python).
>>> 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 );
>>
>> The images are : IF2
>> For the structure element, we can prefix by SE, so we have the result :
>> SEF2
>> Finally, we obtain : BinaryDilateUmageFilterIF2IF2SEF2
>>
>> But we may consider that the structure element can be different that
>> structuringElement::F2
>> for each 2D images process, so the resulting mangled name will be :
>> BinaryDilateUmageFilterIF2IF2
>
> I thinkk your sentence is not ended :-)
Are you sure ? :-( lol
>> I will see to have a mix between our solutions. But I think the first
>> point
>> to solve is to have a coherence between all mangled names.
>
> If you can implement class creation at wrapping time, it would be really
> great : it's important for me to be able to use completion.
> Also, I found usage of [type] (__getitem__) much nice than giving the type
> in New function, and it allow to use New for other things.
> On the other side, your typing mechanism is stronger than mine (which is
> nothing more than refinding the string used in InsightToolkit module) and
> allow interesting things, such as the example you have given :-)
I have finished to mix our 2 codes. The result is very good and offers a
simple use
But with a first parse of the itk classes :-(
I have used my parsing and adapted it to your classes. I have added some
usefull
methods in classes to have facilities.
In your solution, you have kept the _New, _Pointer, _SuperClass, etc.
extentions
to create corresponding methods New, Pointer, SuperClass. What is the
interest
to keep Pointer, Superclas, etc. ? Have the New method seems to me the only
usefull method.
>>> |-ImageFileReader
>>> | |-US2
>>> | | |-New
>>> | | |-Ptr
>>> | | +-Pointer
>>> | |-US3
>>> | |-F2
>>> | +-F3
>>> |-MedianImageFilter
>>> | |-US2US2
>>> | |-US3US3
>>> | +-...
>>> |-...
> Can we get your code somewhere ?
Yes, I have made a CVS repository on our server :
CVSROOT : :pserver:anonymous at cvs.creatis.insa-lyon.fr:2402/cvs/public
No password or anonymous ... I never remember it
Module name : itkWrapping
All is in the python directory :
itkParser.py : the parser of the itk classes
itk.py : creation of classes
itkTestX.py : 3 tests presenting the use and limitations
Now, I will care of the implementation of this while the wrapping of ITK
classes
Cheers
Benoit Regrain
More information about the Insight-developers
mailing list