Type annotations and itk.Image[...]

I am writing some Python code built on itk and other packages. Many of the methods take itk images as arguments.

What is the best way add type annotations for itk images? In most cases I don’t care about the pixel type of the image, but do care about the dimension.

  • Is there a corresponding base type in Python for 2D images, or 3D images?
  • Or should I use typing.Union and list all the image types I except to receive, like:
def do_something(img: Union[itk.Image[itk.UC,2], itk.Image[itk.F,2]], a:int, b:float):

@hjmjohnson or @matt.mccormick could best answer this.

I found ImageLike and ImageBase, i.e.
from itk.support.types import ImageLike, ImageBase

This seems to be more or less what I want, although the dimension is lost. I guess I could do a Union of images with all valid pixel types.

2 Likes

Hi!
I’m currently working on generating typehints for ITK’s Python wrapped classes and methods which should help new users understand the package quicker.

While there may not be exactly what you want there is a itkImageBase2 class that is inherited by all 2 dimensional itk Images in Python regardless of pixel type.

You could try the following:

from itk.itkImagePython import itkImageBase2
def do_something(img: itkImageBase2, a:int, b:float):
    ...

Hope this helps!

4 Likes

thanks. that helps

Hi @kjweimer

I started using pylance/pyright and get errors when I try to use ImageLike

from itk.support.types import ImageLike

def identity(x: ImageLike) -> ImageLike:
    return x

Pyright says:

Expected class type but received “Unknown | Type[ndarray] | Literal[‘itk.ImageBase’]”
“Literal[‘itk.ImageBase’]” is not a class

mypy does not complain. I don’t understand enough of typing.Literal to figure out why it fails for pylance. Is there a type annotation with the same meaning as ImageLike that I could use with pylance (e.g. in vscode)?

Specs:
Windows 10, Python 3.6, itk 5.2

Hello @dyoll !
Unfortunately, it seems that the ITK package does not work well with strict type checkers. This is primarily due to the dynamic module generation that is done which means classes like itk.Image are not availiable before the code is ran.
The itk.ImageLike type annotation includes literal strings that can make sense to a user but not to the type checker causing the issue.
You could construct annotations that are similar to those given from ITK but that are compatible with pylance:

from typing import Any, Union
from itk.itkImagePython import itkImageBase2
from numpy import dtype
from numpy import ndarray as ArrayLike

itkImage = Union[ArrayLike[dtype[Any], dtype[Any]], itkImageBase2]

def identity(x: itkImage) -> itkImage:
    return x

However this solution would require you to add another itkImageBase<Dimension> variable for each dimension that you would like to support.
You would also need to ignore the reportMissingTypeStubs warning from Pylance, as stubs do not exist for the itkImagePython file.
Let me know if this works for you, if not I can try to look into something else.

Hi @kjweimer

Thanks for the suggestion. I tried it but unfortunately it doesn’t work well in combination with the ITK python interface, e.g.

def as_array(img: Union[itkImageBase2, itkImageBase3]):
  return itk.array_from_numpy(img)

yields following error:

Argument of type “itkImageBase2 | itkImageBase3” cannot be assigned to parameter “image_or_filter” of type “Literal[‘itk.ImageBase’, ‘itk.ImageSource’]” in function “GetArrayFromImage”
Type “itkImageBase2 | itkImageBase3” cannot be assigned to type “Literal[‘itk.ImageBase’, ‘itk.ImageSource’]”
Type “itkImageBase2” cannot be assigned to type “Literal[‘itk.ImageBase’, ‘itk.ImageSource’]”
“itkImageBase2” cannot be assigned to type “Literal[‘itk.ImageBase’]”
“itkImageBase2” cannot be assigned to type “Literal[‘itk.ImageSource’]”
Type “itkImageBase3” cannot be assigned to type “Literal[‘itk.ImageBase’, ‘itk.ImageSource’]”
“itkImageBase3” cannot be assigned to type “Literal[‘itk.ImageBase’]”
“itkImageBase3” cannot be assigned to type “Literal[‘itk.ImageSource’]”

And following code:

def resample(img: Union[itkImageBase2, itkImageBase3], target_spacing: Optional[Sequence] = None) -> Union[itkImageBase2, itkImageBase3]:
    dim = img.GetImageDimension()
    interpolator = itk.LinearInterpolateImageFunction.New(img)
    transform = itk.IdentityTransform[itk.D, dim].New()
    ...
    resampled = itk.resample_image_filter(
    ...

gives following errors:

“LinearInterpolateImageFunction” is not a known member of module
“IdentityTransform” is not a known member of module
“resample_image_filter” is not a known member of module

It seems using pyright is not compatible with itk. Are there any plans to go in that direction? Would it help if itk would ship type stubs (I did not find any pyi files)?
Or would you recommend that I stick to mypy, which seems to either skip checking the code, or understand the dynamic types better?

@dyoll Sorry about that.
I think you are correct in that the current state of ITK is not compatible with Pyright. Unfortunately, you may have to disable it for now.
My current work includes creating code to generate the type stub (pyi) files for ITK. I am not sure when this will be released but it is getting close to complete. However I am still unsure if ITK will become compatible with Pyright after this change, I will need to add some testing to see if it will be possible.
I would recommend that you use mypy for the meantime until the type stub code change is released.
Best,
Kian

Hi @kjweimer

Thanks. That sounds promising. In case it helps to set up CI, here is some github action yaml code to run pyright:

      - uses: actions/setup-node@v2
        with:
          node-version: '16'
      - name: static analysis
        working-directory: my_project
        run: |
          npm install pyright
          pyright 

In my repo I added a file called pyrightconfig.json with following content:

{
    "include": [
        "src/",
        "tests/",
    ],
    "exclude": [
        "docs/",
    ],
    "useLibraryCodeForTypes": true,
    "reportPrivateImportUsage": false,
    "reportMissingImports": "warning"
}

Thanks, Bryn

1 Like

Hi,

from itk.support.types import ImageLike

Expected class type but received “Unknown | Type[ndarray] | Literal[‘itk.ImageBase’]”
“Literal[‘itk.ImageBase’]” is not a class

n | Type[ndarray] | Literal[‘itk.ImageBase’]”, “Literal[‘itk.ImageBase’]” is not a class

Specs:
Windows 10, Python 3.6, itk 5.2

The itk.support.types uses forward references, and CPython introduced fixes for forward references and other typing fixes in more recent versions. The Literal types mentioned in the error are supported in Python 3.8. It may help to use a more recent version of CPython. itk-5.3rc2, will have Python 3.10 support, which has the best typing support.