[Insight-developers] [ITK + Python] Wrapping classes proposal
Gaetan Lehmann
gaetan.lehmann at jouy.inra.fr
Mon Jun 13 10:15:28 EDT 2005
On Mon, 13 Jun 2005 14:06:40 +0200, Benoit Regrain
<benoit.regrain at creatis.insa-lyon.fr> wrote:
> Hi,
>
> ----- Original Message ----- From: "Gaetan Lehmann"
> <gaetan.lehmann at jouy.inra.fr>
> To: <insight-developers at itk.org>
> Sent: Friday, June 10, 2005 7:26 PM
> 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 :-)
> 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 ?
5> str(m)
5> '<C itk::SmartPointer<(itk::MedianImageFilter<(itk::Image<(unsigned
short,2)>,itk::Image<(unsigned short,2)>)>)> instance at
_880f7c08_p_itk__SmartPointerTitk__MedianImageFilterTitk__ImageTunsigned_short_2_t_itk__ImageTunsigned_short_2_t_t_t>'
6> str(m.GetOutput())
6> '<C itk::Image<(unsigned short,2)> instance at
_7819c808_p_itk__ImageTunsigned_short_2_t>'
It doesn't seem really difficult to parse, but much difficult to generate
'US2' string :-)
> 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)
>
>
> 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...
>
>
> Last, your parsing have some problems with classes ending by IO.
Hey, your right ! I thought that I could have problem with names ended by
an uppercase letter (IO, 2D, 3D, ...), but I thought that actually, there
were no name like that wrapped. I was wrong.
There is only a small number of name like that :
[glehmann at pcconf5 Insight]$ find Code -name '*.h' | egrep '[A-Z]\.h'
Code/IO/itkGEAdwImageIO.h
Code/IO/itkNrrdImageIO.h
Code/IO/itkSiemensVisionImageIO.h
Code/IO/itkJPEGImageIO.h
Code/IO/itkStimulateImageIO.h
Code/IO/itkVTKImageIO.h
Code/IO/itkPNGImageIO.h
Code/IO/itkGiplImageIO.h
Code/IO/itkGE4ImageIO.h
Code/IO/itkRawImageIO.h
Code/IO/itkAnalyzeImageIO.h
Code/IO/itkBrains2MaskImageIO.h
Code/IO/itkMetaImageIO.h
Code/IO/itkGE5ImageIO.h
Code/IO/itkGDCMImageIO.h
Code/IO/itkIPLCommonImageIO.h
Code/IO/itkDicomImageIO.h
Code/IO/itkTIFFImageIO.h
Code/IO/itkBMPImageIO.h
Code/Common/itkDiffusionTensor3D.h
Code/Common/itkArray2D.h
Code/Common/itkChainCodePath2D.h
Code/Algorithms/itkVoronoiDiagram2D.h
Code/Numerics/FEM/itkFEMLoadBCMFC.h
Code/Numerics/FEM/itkFEMLinearSystemWrapperDenseVNL.h
Code/Numerics/FEM/itkFEMLoadBC.h
Code/Numerics/FEM/itkFEM.h
Code/Numerics/FEM/itkFEMP.h
Code/Numerics/FEM/itkFEMLinearSystemWrapperVNL.h
Code/BasicFilters/itkLazyEdgeDetectionImageFilter3D.h
It shouldn't be so hard to manage those exceptions.
But the real problem comes of lacks of separator between class name and
type name...
> But I don't think have a more robust parsing of the all classes.
> I think that a post-importation parsing to find classes isn't a good
> solution.
> It will be better to add class's methods when generating wrapping by
> CableSwig
> (it's possible with swig). With these methods we will get the class name,
> the added mangle suffix (in a string or a list of components).
Sounds great :-)
>
>
>
>> 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...
>
>
>
>> 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 :-)
>
>
>
> 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 :-)
Can we get your code somewhere ?
Gaetan.
--
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