Closed thewtex closed 3 years ago
ndim
, dtype
, shape
added in #1780
Nice! Thanks for the update Matt 😄
Woot. Have we tried this out in a workflow yet? If not, it might be fun to try something and throw it into a "Better ITK and Dask Integration" blogpost.
On Wed, Apr 22, 2020 at 8:40 PM jakirkham notifications@github.com wrote:
Nice! Thanks for the update Matt 😄
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/InsightSoftwareConsortium/ITK/issues/1091#issuecomment-618158352, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACKZTGLLS3TBULKKDHCHJ3RN62CXANCNFSM4IDR56VQ .
@mrocklin great idea -- I'll see about following up on our initial post with some denoising...
This issue has been automatically marked as stale because it has not had recent activity. Thank you for your contributions.
This issue has been automatically marked as stale because it has not had recent activity. Thank you for your contributions.
Is this something I can help along?
Hi @GenevieveBuckley,
Help on this would be amazing! :pray:
We need to add pickle support to itk.Image
.
The process may be a great example basis for a Dask Blog post on how to add required pickle support for data structures from native Python packages.
We need to get this test to pass:
import itk
import pickle
import numpy as np
# Create an itk.Image
array = np.random.randint(0, 256, (8, 12)).astype(np.uint8)
image = itk.image_from_array(array)
image.SetSpacing([1.0, 2.0])
image.SetOrigin([11.0, 4.0])
theta = np.radians(30)
cosine = np.cos(theta)
sine = np.sin(theta)
rotation = np.array(((cosine, -sine), (sine, cosine)))
image.SetDirection(rotation)
# Verify serialization works with a np.ndarray
serialized = pickle.dumps(array)
deserialized = pickle.loads(serialized)
assert np.array_equal(array, deserialized)
# How to check for image consistency
image_copy = itk.image_duplicator(image)
compared = itk.comparison_image_filter(image, image_copy)
assert np.sum(compared) == 0
# We need to add support for this
serialized = pickle.dumps(image)
deserialized = pickle.loads(serialized)
compared = itk.comparison_image_filter(image, deserialized)
assert np.sum(compared) == 0
As first step, I would create a conda environment, pip install itk
and hack on site-packages/itk/itkImagePython.py
to work out the details that make the test pass -- this is a generated file. The class to add the required dunder methods in that file for this test is class itkImageUC2
. The pickle process in Python is a bit complex. I found that the CPython docs helpful but stepping through the code with pdb
was also required for my understanding.
We need to def __getstate__
, def __setstate__
, and likely __reduce_ex__
.
Here is what we did for the itk
module: https://github.com/InsightSoftwareConsortium/ITK/blob/27f26685cc10be8bee74989a1c03639cd89705ce/Wrapping/Generators/Python/itk/support/lazy.py#L145-L174
To get the state, here is an example that represents the itk.Image
state as a Python Dictionary of fundamental types + a np.ndarray (which has pickle support): https://github.com/InsightSoftwareConsortium/itkwidgets/blob/c4f3b158719bbb2720ef2fadcc3df8990a3feb95/itkwidgets/trait_types.py#L163-L172.
For re-constructing the image, it will require:
image = itk.image_view_from_array(state.pixel_array, ttype=state.imageType)
image.SetOrigin(state.origin)
image.SetSpacing(state.spacing)
image.SetDirection(state.direction)
There's also some discussion about improving pickle
in ITK in issue ( https://github.com/InsightSoftwareConsortium/ITK/issues/1948 )
So I think I'm almost there.
Reusing the itkimage_to_json
& itkimage_from_json
functions, from the link Matt gave here seems like the best approach.
... then it looks like all we need is to add __getstate__
and setstate` methods like this:
# For pickle support
def __getstate__(self):
state = itkimage_to_json(self)
return state
# For pickle support
def __setstate__(self, state):
deserialized = itkimage_from_json(state)
self.__dict__['this'] = deserialized
self.SetOrigin(state['origin'])
self.SetSpacing(state['spacing'])
# FIXME! Something is not quite right about the way I'm setting the direction here
# ref https://discourse.itk.org/t/set-image-direction-from-numpy-array/844
direction_data = np.reshape(state['direction']['data'], (state['direction']['rows'], state['direction']['columns']))
direction_data_vnl = itk.GetVnlMatrixFromArray(direction_data)
direction = self.GetDirection()
direction.GetVnlMatrix().copy_in(direction_data_vnl.data_block())
self.SetDirection(direction)
When I run the example test script the itk.comparison_image_filter
function fails, I think because the array direction isn't being set correctly.
The line assert np.sum(compared) == 0
does pass, which I'm hoping means the actual image array values are all correct. I'm not very familiar with itk objects.
__getstate__
and __setstate__
methods onto the itkImageUC2
class, because that's the specific array type generated in the example test script)@GenevieveBuckley thank you so much for your work on this! :pray: Sorry for the late reply. It seems we are quite close!
What am I doing wrong with setting the direction?
We have improved interfaces to the direction
. Following #2828, we can get the direction for serialization as NumPy array with
direction = np.asarray(self.GetDirection())
ndarray
ships with pickling support, so let's work with that.
Recent itk
supports setting the direction
with a ndarray
directly, so we can use
# direction is an NxN ndarray
self.SetDirection(direction)
in __setstate__
.
Where should these changes live?
__getstate__
and __setstate__
can go here: https://github.com/InsightSoftwareConsortium/ITK/blob/fd91b95034fdc680459fbaf930c3d81ee2aa1d1b/Wrapping/Generators/Python/PyBase/pyBase.i#L391-L525
Feel free to use the CI to test builds / changes :-)
Excellent, I've opened a PR over at https://github.com/InsightSoftwareConsortium/ITK/pull/2829, and outlined what the next steps are here.
To avoid explicit type coersion in Dask Array
map_blocks
, consider addingdtype
shape
ndim
__array_function__
to
itk.Image
.@jakirkham @mrocklin