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