thunder-project / thunder-registration

algorithms for registering sequences of images
MIT License
14 stars 4 forks source link

SimpleITK integration #5

Closed d-v-b closed 8 years ago

d-v-b commented 8 years ago

This PR adds support for using registration algorithms from SimpleITK within thunder-registration.

SimpleITK provides a variety of image transformations (translation, rotation, affine, similarity, non-linear) with a multi-resolution framework for efficient optimization.

In the implementation suggested by this PR, the user creates a SimpleITK.ImageRegistrationMethod object which has properties including: an image similarity metric, an optimizer, an optimization procedure (e.g., whether to use multi-resolution, whether to blur, etc), and a transform to optimize.

Once the user defines the registration method appropriately, it is passed to the constructor for the registration.SimpleITK_Registration class, after which the API is consistent with the extant image registration methods in thunder-registration

This adds a great deal of functionality to thunder-registration and would make it a much more attractive library for members of the medical imaging community; however, the cost is additional complexity, including including SimpleITK and its compiled binaries. SimpleITK itself represents an entire medical image processing toolkit and as such has lots of features we may not need in thunder-registration, like image segmentation and filters. But I think there are no existing python libs that offer comparable registration functionality as SimpleITK (although dipy is promising), and it would be a tremendous effort to re-implement that same functionality anew.

Here's example (lifted from test/test_sitk_registration.py) demonstrating estimation of an image transformation with sub-pixel accuracy:

from numpy import linspace, exp, allclose, meshgrid
from scipy.ndimage.interpolation import shift
from SimpleITK import ImageRegistrationMethod, TranslationTransform
from registration import SimpleITK_Registration

x, y = meshgrid(linspace(-4,4,100), linspace(-4,4,100))
reference = exp(-x**2 + -y**2)
r = ImageRegistrationMethod()
r.SetMetricAsCorrelation()
r.SetOptimizerAsRegularStepGradientDescent(learningRate=0.1,
                                           minStep=1e-5,
                                           numberOfIterations=10000,
                                           gradientMagnitudeTolerance=1e-8)
r.SetInitialTransform(TranslationTransform(len(reference.shape)), inPlace=False)
algorithm = SimpleITK_Registration(r)
deltas = [[1.5, -10], [-1.5, 10]]
shifted = [shift(reference, delta) for delta in deltas]
model = algorithm.fit(shifted, reference=reference)
    # flip the dimensions of model.toarray() before comparing to deltas because SimpleITK uses xy ordering.
model_deltas = map(lambda v: v[::-1], model.toarray())
assert allclose(model_deltas, deltas)
d-v-b commented 8 years ago

travis is failing here:

Collecting simpleitk (from -r requirements.txt (line 3))
  Could not find a version that satisfies the requirement simpleitk (from -r requirements.txt (line 3)) (from versions: )
No matching distribution found for simpleitk (from -r requirements.txt (line 3))
The command "pip install -r requirements.txt" failed and exited with 1 during .

Which is confusing, because simpleitk is on pypi and I can make the same test env on my local machine and run pip install -r requirements.txt fine. Maybe the version of Linux in the docker image used by travis is the issue?

freeman-lab commented 8 years ago

Really great work @d-v-b !

One high level thought before getting into the details: does this make sense as a new algorithm in thunder-regression, or maybe we should think about just making a small standalone package that offers a nicer scikit-learn style API on top of SimpleITK? To be clear, I think all the great work you've done could be leveraged toward either approach!

One reason to make it separate is that it might be useful for people who want to use SimpleITK but don't care about thunder and/or parallelization. And then you still get easy integration with thunder data objects more or less for free, just by calling images.map.

d-v-b commented 8 years ago

Thanks @freeman-lab, those are interesting suggestions.

Regarding your high-level thought -- There's lots of functionality in SimpleITK I don't want to touch at all, like image segmentation, image filters, etc. See how much functionality there is available here: https://itk.org/SimpleITKDoxygen09/html/files.html. It would be a lot of work to simplify the api for all this stuff when ultimately I only want the image registration functionality. Honestly I wish the design of simpleitk was more modular and we could only depend on the registration stuff.

I think whether this PR makes sense depends heavily on how we envision thunder-registration working -- should we be offering simplified interfaces to existing packages, or should we be writing our own registration algorithms. I'm pulled in both directions. If I had infinite time, I would want to write everything from scratch. But I've realized that you need a lot of software infrastructure for robust, fast image registration, and you need to support a pretty wide parameter space -- there are different image similarity metrics, different optimizers, different transforms, and that's just for linear transformations. Until someone does all this work, I don't see how thunder-registration can support registration methods that are standard in medical imaging.

d-v-b commented 8 years ago

After more testing, I realized that SimpleITK is full of objects that can't be serialized via pickle, which greatly limits its usability in a distributed computing context. In searching for a solution to this issue, I found the align module in dipy (https://github.com/nipy/dipy), which has a better API than SimpleITK and also features objects that can be picked. So I'm closing this PR!