In Python, how to convert between SimpleITK and ITK Images?

There is a C++ tutorial on how to convert between SimpleITK and ITK, but I’m struggling to translate it into Python.

In [14]: test_arr = np.ones((30, 30, 30), dtype=np.int16)                                       

In [15]: test_arr[15, 15, 15] = 999                                                             

In [16]: sitk_image = sitk.GetImageFromArray(test_arr)                                          

In [17]: sitk_image.GetPixelID()                                                                
Out[17]: 2

In [18]: itk_image = sitk_image.GetITKBase()                                                    

In [19]: itk_image                                                                              
Out[19]: <Swig Object of type 'itk::DataObject *' at 0x7f6115837f30>

In [20]: itk_image.IsNull()                                                                     
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-20-ed3d70ad1fde> in <module>
----> 1 itk_image.IsNull()

AttributeError: 'SwigPyObject' object has no attribute 'IsNull'
In [22]: itk_arr = itk.array_from_image(itk_image)                                              
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-22-1ffbcbc9e438> in <module>
----> 1 itk_arr = itk.array_from_image(itk_image)

~/anaconda3/envs/dl/lib/python3.7/site-packages/itkExtras.py in GetArrayFromImage(image_or_filter, keep_axes, update)
    255     """Get an array with the content of the image buffer
    256     """
--> 257     return _GetArrayFromImage(image_or_filter, "GetArrayFromImage", keep_axes, update)
    258 
    259 array_from_image = GetArrayFromImage

~/anaconda3/envs/dl/lib/python3.7/site-packages/itkExtras.py in _GetArrayFromImage(image_or_filter, function, keep_axes, update)
    246     keys = [k for k in itk.PyBuffer.keys() if k[0] == output(image_or_filter).__class__]
    247     if len(keys ) == 0:
--> 248         raise RuntimeError("No suitable template parameter can be found.")
    249     ImageType = keys[0]
    250     # Create a numpy array of the type of the input image

RuntimeError: No suitable template parameter can be found.

Could you please help me with this (and reverse, ITK -> SimpleITK) conversion?

The safe way to do this is through numpy as the compiled versions of ITK and SimpleITK are not necessarily compatible. Also, be aware that the supported pixel types may differ, so some image types supported by SimpleITK are not supported by ITK and vice versa.

Below is a code snippet that does what you want (written so it is agnostic of the image dimensions):

import SimpleITK as sitk
import itk
import numpy as np


image_dimension = 2
index = [15]*image_dimension

test_arr = np.ones([32]*image_dimension, dtype=np.int16)
test_arr[index] = 999

# Create a simpleitk image from the numpy array
sitk_image = sitk.GetImageFromArray(test_arr)
print('SimpleITK image value at {0}: {1}'.format(index, sitk_image[index]))

# Create an itk image from the simpleitk image via numpy array
itk_image = itk.GetImageFromArray(sitk.GetArrayFromImage(sitk_image), is_vector = sitk_image.GetNumberOfComponentsPerPixel()>1)
itk_image.SetOrigin(sitk_image.GetOrigin())
itk_image.SetSpacing(sitk_image.GetSpacing())   
itk_image.SetDirection(itk.GetMatrixFromArray(np.reshape(np.array(sitk_image.GetDirection()), [image_dimension]*2)))
print('ITK image value at {0}: {1}'.format(index, itk_image.GetPixel(index)))

# Change the pixel value
itk_image.SetPixel(index, 888)

# Back to a simpleitk image from the itk image
new_sitk_image = sitk.GetImageFromArray(itk.GetArrayFromImage(itk_image), isVector=itk_image.GetNumberOfComponentsPerPixel()>1)
new_sitk_image.SetOrigin(tuple(itk_image.GetOrigin()))
new_sitk_image.SetSpacing(tuple(itk_image.GetSpacing()))
new_sitk_image.SetDirection(itk.GetArrayFromMatrix(itk_image.GetDirection()).flatten()) 
print('New SimpleITK image value at {0}: {1}'.format(index, new_sitk_image[index]))

3 Likes

Thank you for this @zivy. I was hoping that a safe conversion method exists which avoids converting to and from numpy arrays.

Both ITK and SimpleITK have a GetArrayView method which does not do a copy the buffer. These methods just use the python BufferObject to hold a reference to the buffer. This will save a copy.

I believe native ITK Python has a method to import a python buffer by reference too (?), but SimpleITK does not. It can be tricky to have to manually keep track of how buffers are shared. Frequently, it leads to more problems than is actually solves.

Hello @alkamid,

I just realized I had a serious omission in the code above which I just corrected, you also need to copy the meta-data (origin, spacing, direction cosine matrix).

This was not an issue with the snippet because we started with a numpy array so our origin was all zeros, spacing all ones, and direction cosine matrix the identity matrix. These are the defaults when an image is created. If you start with an image that you read it will have different values for these which must be copied over.

3 Likes

It’s been a long time since this was posted, but I’m also facing the same problem. Because SITK is ‘simple’, there is a need to sometimes do things in e.g. numpy, or even ITK. Unfortunately there is no way to quickly (as in without any copies) transform one into the other. If you have this problem, then your only option currently is to either switch all your code to ITK or numpy, or take the performance hit. Note that although SimpleITK is ‘simple’, that doesn’t mean there can’t be a possibility to dive into a more low-level API if needed. Great libraries provide multiple levels of abstration.