ITK Python: Image Pixel Types
Don’t worry about the pixel type
In previous posts about the Insight Toolkit (ITK), we talked about installing ITK Python packages from PyPI, sharing data between ITK and NumPy, and even create your own ITK module with its Python wrapping automatically generated.
This post will use examples from the NumPy post and explain in more detail how ITK Python filter instantiation works!
First, make sure that ITK Python package is installed:
python -m pip install --upgrade pip python -m pip install itk
An example used in a previous post showed how to process an image with ITK Python. The code used was:
#!/usr/bin/env python import itk image = itk.imread("input_filename.png") median = itk.MedianImageFilter.New(image, Radius = 2) itk.imwrite(median, "output_filename.png")
In the example above, the function imread() will automatically detect the pixel type and dimensions of the image. If the input image was an RGB image with unsigned char components, the pixel type in the image object will also be RGB with unsigned char components. For the ITK developers used to C++, this is a major difference. When using ITK in C++, filter template parameters have to be specified.
When calling itk.MedianImageFilter.New(), the image type is not specified either. Using a similar mechanism than the function imread() allows the correct filter to automatically be instantiated for the input image type.
This is called implicit instantiation. A developer can write an entire processing pipeline without needing to specify the image pixel type and dimension.
Advanced usage
In certain cases, you want to choose the pixel type of the image that is read. The functions imread() and imwrite() give you this flexibility:
PixelType = itk.ctype("unsigned char") # shorthand: itk.UC itk.imread(‘my_file.png’, PixelType)
The RGB image would in this case be converted to a grayscale image with pixels of type unsigned char. The image dimension is automatically detected.
Similarly, some filters require the user to specify its parameter types. This is the case for itk.CastImageFilter. In the following example, we read an image that could be of any dimension. We then convert the image from unsigned char to float, and save the result.
#!/usr/bin/env python import itk PixelType = itk.ctype(“unsigned char”) image = itk.imread("input_filename.png", PixelType) dimension = image.GetImageDimension() InputImageType = type(image) OutputPixelType = itk.ctype(“float”) OutputImageType = itk.Image[OutputPixelType, dimension] cast = itk.CastImageFilter[InputImageType, OutputImageType].New(image) itk.imwrite(cast, "output_filename.png")
Limitations
Certain filters only accept certain types of images. In Python, a developer can write an entire pipeline without ever specifying the pixel type of the image. However, when running the pipeline, the script could fail because an image with the wrong pixel type is set as the input of a filter. For example itk.MedianImageFilter cannot process RGB images. If one tried to run the first example on an RGB image, the script will fail and print the following error message.
RuntimeError: No suitable template parameter can be found.
To list the types that a filter supports, you can call the method GetTypes() :
itk.MedianImageFilter.GetTypes() <itkTemplate itk::MedianImageFilter> Options: [<class 'itkImagePython.itkImageF2'>, <class 'itkImagePython.itkImageF2'>] [<class 'itkImagePython.itkImageF3'>, <class 'itkImagePython.itkImageF3'>] [<class 'itkImagePython.itkImageSS2'>, <class 'itkImagePython.itkImageSS2'>] [<class 'itkImagePython.itkImageSS3'>, <class 'itkImagePython.itkImageSS3'>] [<class 'itkImagePython.itkImageUC2'>, <class 'itkImagePython.itkImageUC2'>] [<class 'itkImagePython.itkImageUC3'>, <class 'itkImagePython.itkImageUC3'>]
Finally, programmatically, you can get the template parameter of an image with itk.template(image). This returns a tuple containing the different template parameters:
(<itkTemplate itk::Image>, (<itkCType signed short>, 3))
One can access the different values of the tuple, and use them, for example, to define a new type.
Conclusion
ITK’s Python wrapping can implicitly determine an appropriate image processing filter with compile-time optimization for your input image. You can forget about the types of the images when it does not matter, or specify it when it does. Do not hesitate to refer to the ITK Python Quick Start Guide and ITK Discourse forum to find more information.
What is the equivalent to
For getting the pixel type?
Whoops, missed it there at the end of the post. Boils down to: