ITK 5.1.2 Python XArray to image bug

Hi message board,

I’m trying to create a script in Python where I create a cine clip of a 3D volume. I want to make an itk image out of a 4D x array with data labels ‘x’, ‘y’, ‘z’, ‘t’.

I made a simple script where I repeat an image volume of a sphere at each time point. When I try to create the itk image I get an error message. My python script and error messages are below. Are there any suggestions for what the problem could be?

Code:

import numpy as np
import xarray as xr
import itk

###3D sphere
npim = np.zeros( (101,101,101) )
xx, yy, zz = np.mgrid[0:101, 0:101, 0:101]
rr = np.sqrt( (xx-50)**2 + (yy-50)**2 + (zz-50)**2 )
npim[rr < 15] = 1

###Replicate across t dimension
fourd = npim
fourd.shape = ( 101, 101,101, 1)
fourd = np.tile(fourd, [1,1,1,5])
fourdxr = xr.DataArray(fourd, dims=['x','y','z','t'])

###Create an itk image
print('Creating a 4D image')
itkim = itk.image_from_xarray(fourdxr)

Error Message:
Traceback (most recent call last):
File “testScript.py”, line 24, in
itkim = itk.image_from_xarray(fourdxr)
File “/home/nick/Software/InsightToolkit-5.1.2/build/Wrapping/Generators/Python/itkExtras.py”, line 453, in image_from_xarray
origin.reverse()
TypeError: Expecting an itkVectorD4, an int, a float, a sequence of int or a sequence of float.
Additional information:
Wrong number or type of arguments for overloaded function ‘itkImageBase4_SetSpacing’.
Possible C/C++ prototypes are:
itkImageBase4::SetSpacing(itkVectorD4 const &)
itkImageBase4::SetSpacing(double const *)
itkImageBase4::SetSpacing(float const *)

ITK script where my code sample errors out:

def image_from_xarray(data_array):
    """Convert an xarray.DataArray to an itk.Image.

    Metadata encoded with xarray_from_image is applied to the itk.Image.

    This interface is and behavior is experimental and is subject to possible
    future changes."""
    import numpy as np
    import itk

    spatial_dims = list({'z', 'y', 'x'}.intersection(set(data_array.dims)))
    spatial_dims.sort(reverse=True)
    spatial_dimension = len(spatial_dims)
    ordered_dims = ('z', 'y', 'x')[-spatial_dimension:]
    if ordered_dims != tuple(spatial_dims):
        raise ValueError('Spatial dimensions do not have the required order: ' + str(ordered_dims))

    is_vector = 'c' in data_array.dims
    itk_image = itk.image_view_from_array(data_array.values, is_vector=is_vector)

    origin = [0.0]*spatial_dimension
    spacing = [1.0]*spatial_dimension
    for index, dim in enumerate(spatial_dims):
        coords = data_array.coords[dim]
        if coords.shape[0] > 1:
            origin[index] = float(coords[0])
            spacing[index] = float(coords[1]) - float(coords[0])
    spacing.reverse()
    itk_image.SetSpacing(spacing)
    origin.reverse()
    itk_image.SetOrigin(origin)
    if 'direction' in data_array.attrs:
        direction = data_array.attrs['direction']
        itk_image.SetDirection(np.flip(direction))

    return itk_image

Before version 5.2RC1, ITK’s python wrapping was compiled with dimensions 2 and 3, but not 4. That is probably the issue, which should be resolved by itk5.2rc1. Give the new version a try and report back.

I did turn on 4d wrapping in the cmake options when compiling ITK. Are issues with 4d images expected in 5.1 even when the CMake option to wrap 2;3;4 dimensional images is turned on?

I will try downloading the 5.2 version.

If you compiled yourself and turned on 4th dimension, that should have been enough. As a recent addition, image_from_xarray is not thoroughly tested.

In spacing[index] = float(coords[1]) - float(coords[0]) the float might need to be double. Or spacing somehow else adjusted to be accepted by itk_image.SetSpacing().

Hello @Nick_Rubert! :wave:

As @dzenanz, support was not yet added for a t dimension. This is added in this PR:

1 Like