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

#1

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?

(Ziv Yaniv) #2

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]))

2 Likes
#3

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

(Bradley Lowekamp) #4

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.

(Ziv Yaniv) #5

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.

2 Likes