Additional python types for ITK remote/external modules

ITK python wrapping infrastructure allows external/remote modules to extend the available types for a specific class.
This has been heavily used when wrapping RTK for python, so that rtk python packages work with the pip-installable itk package. Here is an example of this approach, forcing wrapping of double with dimension 1 for itk::Point.

A first question would be: Is it the right way to go? Does itk provides other ways to specify additional templates?

We are now facing issues trying to extend itkPyBuffer. This class internally configures swig files to provide additional function definition.
It is possible to extend the wrapped types for itk::PyBuffer and its internal swig files the same way we did it for other itk classes.
The issues arises when the automatically generated file itkPyBuffer.i wants to include the configured file PyBuffer.i. This is triggered by “additional library files” being passed to swig through the WRAPPER_SWIG_LIBRARY_FILES variable.
This works perfectly when there is only one submodule (i.e. one class to wrap) in the current module.
This is the case for itkBridgeNumpy but not for RTK. Thus WRAPPER_SWIG_LIBRARY_FILES is propagated to every submodule being wrapped, which forces all generated swig file to include PyBuffer.i.
Multiple inclusion of the configured PyBuffer.i results in a mix of “%include” and “%import” calls in each class generated swig file, which ends up with no replacement of “%pythoncode” chunk where we expect it.
I can provide more information on this behavior if needed, but the only thing that is required is to be able to set WRAPPER_SWIG_LIBRARY_FILES at a submodule level in opposition to setting it for the whole module. Do you know any way to do this?
All the magic seem to happen in macro itk_end_wrap_module() and I could not find a way to decouple that for each submodule.

Hope everything I described makes sense, please do not hesitate if I missed something.
Any help would be appreciated.

@simon.rit

1 Like

Hi @LucasGandel,

Welcome to the ITK Discourse! :sunny: :slight_smile:

pip install itk-rtk – fantastic!! :rocket:

Yes, this works.

itk.PyBuffer is a special case in that it is is more complex. If you need additional types, we could just wrap them in ITK’s build via a pull request to the ITK repository.

Hi @matt.mccormick,

Thank you very much for taking the time to answer! And thank you for the little “ITK” gift I received, it means a lot to me :slight_smile:

itk.PyBuffer is a special case in that it is is more complex. If you need additional types, we could just wrap them in ITK’s build via a pull request to the ITK repository.

Extending itk.PyBuffer types is a very specific use case for RTK, where we need vector components up to dimension 5. I think adding this in itkPyBuffer would require to also add those types for itk.Image and so on… I don’t think this is something we want to support in ITK, especially when building python packages using ITKPythonBuilds?

1 Like

@LucasGandel thank you for your contributions! You are an important member of the community.

For the 5-component image, does the wrapping of VectorImage support your use case?

Hi Matt,
We are using vector images for spectral x-ray CT. @cyril.mory, who developed this part of RTK (among others), developped the first algorithms using VectorImage but it turned out to be hard to use in practice and the latest developments used itk::Image<itk::Vector. I don’t fully remember why but I think that working with the pixels internally to do vector matrix multiplications was not easy with VectorImage. It eventually required a cast to an itk::Vector which significantly slowed down computations.
In short, no, VectorImage currently doesn’t support our use case. Extending itkPyBuffer for a few additional types would be perfect although making it within RTK would make more sense. We have to check what that entails, I can already see that it also means wrapping itk::ImageDuplicator
Simon

1 Like

@simon.rit makes sense.

One possibility may be to refactor the NumPy wrapping code in:

ITK/Modules/Bridge/NumPy/wrapping/

into macros that are defined at

ITK/Wrapping/TypedefMacros.cmake

e.g. itk_wrap_pybuffer. This could then be called in RTK’s Python configuration.

1 Like

@matt.mccormick Thank you very much for the pointers. We might give this a try.

So far we could work around this by moving the code in PyBuffer.i.in from the %include statement to the %import statement (but results in ugly duplication of code).

I’ll try to do a proof of concept using macros and keep you and @simon.rit updated.

Posting the solution we found in case people are interested.

It is possible to skip WRAPPER_SWIG_LIBRARY_FILES to include additional swig files only at a submodule level (i.e. the file is not included for all wrapped classes in the current module).
This is done by appending the list of additional swig includes to ITK_WRAP_PYTHON_SWIG_EXT at the end of the submodule .wrap file. This variable sets the content of the submodule _ext.i file that is only included by the submodule .i file.
Check this change on the RTK module for an example.

4 Likes