Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH,WIP: New transforms module #656

Closed
wants to merge 36 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
3cfff0d
ENH: Add an utility to calculate obliquity of affines
oesteban Sep 20, 2019
c92d560
enh(tests): add not-oblique test, move tests to ``test_affines.py``
oesteban Sep 20, 2019
da8da56
enh: return radians unless degrees=True
oesteban Sep 22, 2019
253a256
enh: address @matthew-brett's comments
oesteban Sep 23, 2019
04584b6
doc: add link to AFNI's documentation about *obliquity* [skip ci]
oesteban Sep 24, 2019
70d9620
Initial commit
oesteban Aug 1, 2018
36a1764
add TransformBase
oesteban Aug 1, 2018
2a700c9
add new module
oesteban Aug 1, 2018
fda2bfe
add documentation, core functionality
oesteban Aug 1, 2018
3ef5adc
add a deformation field transform
oesteban Aug 1, 2018
189a662
optimize deformation field
oesteban Aug 2, 2018
2619ae4
pre-cache transformed indexes
oesteban Aug 2, 2018
4e7c19b
used cached field
oesteban Aug 2, 2018
23d7002
add caching of deltas in voxel coordinates
oesteban Aug 3, 2018
2acd9f3
add bspline cython extension
oesteban Aug 3, 2018
385ceff
a smarter ImageSpace
oesteban Aug 4, 2018
fa030a4
add comment
oesteban Aug 10, 2018
fd418fe
remove cython module
oesteban Aug 10, 2018
13d722f
cleanup
oesteban Aug 11, 2018
253aed5
starting with bspline transform
oesteban Aug 11, 2018
47c2a57
finishing b-spline interpolation
oesteban Aug 13, 2018
4478c06
Add base implementation of transforms and change base implementation …
oesteban Mar 12, 2019
99fa15d
export to hdf5
oesteban Mar 13, 2019
5eed01d
corrections to adhere the current x5 format draft
oesteban Mar 14, 2019
632e068
fix coordinates translation in affines, import itk affines
oesteban Mar 15, 2019
f2c062e
wip: reads & writes ITK's MatrixOffsetTransformBase transforms, with …
oesteban Mar 16, 2019
8eb670d
fix: print warnings to stderr, sty: minimal fixes
oesteban Mar 16, 2019
799fb6e
enh(affines): write out AFNI 12-parameters files
oesteban Mar 19, 2019
ce36f06
enh(transforms): finish up a x5-to-fsl writer of affines
oesteban Mar 21, 2019
8e1bf44
enh(transforms): improve generation of x5 structures of reference spaces
oesteban Mar 21, 2019
e2df7cc
fix(transforms): string formating forgotten when writing ITK transforms
oesteban Mar 22, 2019
c5d10ed
wip(transforms): writing up some linear transform readers
oesteban Mar 22, 2019
a18ce1d
enh(transform): HDF5 - set values as attributes; generalize affines t…
oesteban Mar 22, 2019
9902f50
enh: apply some early comments from @effigies.
oesteban Sep 19, 2019
fe74efb
fix: add a first battery of tests
oesteban Sep 26, 2019
596994c
enh: add tests for affines stored in AFNI format (non-oblique images)
oesteban Sep 27, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
add a deformation field transform
oesteban committed Sep 27, 2019
commit 3ef5adc04fb4a8843343b8bd5d7157044410690a
1 change: 1 addition & 0 deletions nibabel/transform/__init__.py
Original file line number Diff line number Diff line change
@@ -17,3 +17,4 @@
"""
from __future__ import absolute_import
from .base import ImageSpace, Affine
from .nonlinear import DeformationFieldTransform
63 changes: 57 additions & 6 deletions nibabel/transform/base.py
Original file line number Diff line number Diff line change
@@ -51,9 +51,60 @@ def reference(self):
def reference(self, image):
self._reference = ImageSpace(image)

def resample(self, moving, order=3, dtype=None):
'''A virtual method to resample the moving image in the reference space'''
raise NotImplementedError
def resample(self, moving, order=3, mode='constant', cval=0.0, prefilter=True,
output_dtype=None):
'''Resample the moving image in reference space

Parameters
----------

moving : `spatialimage`
The image object containing the data to be resampled in reference
space
order : int, optional
The order of the spline interpolation, default is 3.
The order has to be in the range 0-5.
mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
Determines how the input image is extended when the resamplings overflows
a border. Default is 'constant'.
cval : float, optional
Constant value for ``mode='constant'``. Default is 0.0.
prefilter: bool, optional
Determines if the moving image's data array is prefiltered with
a spline filter before interpolation. The default is ``True``,
which will create a temporary *float64* array of filtered values
if *order > 1*. If setting this to ``False``, the output will be
slightly blurred if *order > 1*, unless the input is prefiltered,
i.e. it is the result of calling the spline filter on the original
input.

Returns
-------

moved_image : `spatialimage`
The moving imaged after resampling to reference space.

'''

if output_dtype is None:
output_dtype = moving.header.get_data_dtype()

moving_data = moving.get_data()
moved = ndi.geometric_transform(
moving_data,
mapping=self.map_voxel,
output_shape=self.reference.shape,
output=output_dtype,
order=order,
mode=mode,
cval=cval,
prefilter=prefilter,
extra_keywords={'moving': moving},
)

moved_image = moving.__class__(moved, self.reference.affine, moving.header)
moved_image.header.set_data_dtype(output_dtype)
return moved_image

def map_point(self, coords):
'''Find the coordinates in moving space corresponding to the
@@ -164,10 +215,10 @@ def resample(self, moving, order=3, mode='constant', cval=0.0, prefilter=True,
output_shape=reference.shape, order=order, mode=mode,
cval=cval, prefilter=prefilter)

img = moving.__class__(moved, moving.affine, moving.header)
img.header.set_data_dtype(output_dtype)
moved_image = moving.__class__(moved, reference.affine, moving.header)
moved_image.header.set_data_dtype(output_dtype)

return img
return moved_image

def map_point(self, coords, forward=True):
coords = np.array(coords)
55 changes: 55 additions & 0 deletions nibabel/transform/nonlinear.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vi: set ft=python sts=4 ts=4 sw=4 et:
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
#
# See COPYING file distributed along with the NiBabel package for the
# copyright and license terms.
#
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
''' Common interface for transforms '''
from __future__ import division, print_function, absolute_import
import numpy as np

from .base import TransformBase
from ..funcs import four_to_three


class DeformationFieldTransform(TransformBase):
'''Represents linear transforms on image data'''
__slots__ = ['_field']

def __init__(self, field):
self.reference = four_to_three(field)[0]
# Set the field components in front
self._field = field.get_data()

def map_voxel(self, index, moving=None):
'''

Examples
--------

>>> import numpy as np
>>> import nibabel as nb
>>> ref = nb.load('t1_weighted.nii.gz')
>>> field = np.zeros(tuple(list(ref.shape) + [3]))
>>> field[..., 0] = 4.0
>>> fieldimg = nb.Nifti1Image(field, ref.affine, ref.header)
>>> xfm = nb.transform.DeformationFieldTransform(fieldimg)
>>> new = xfm.resample(ref)
>>> new.to_filename('deffield.nii.gz')

'''
vox2ras = self.reference.affine
ras2vox = np.linalg.inv(vox2ras if moving is None
else moving.affine)

index = np.array(index)
if index.shape[0] == vox2ras.shape[0] - 1:
index = np.append(index, [1.0])

idx = tuple([int(i) for i in index[:-1]])
point = vox2ras.dot(index)[:-1]
delta = np.squeeze(self._field[idx + (slice(None), )])
newindex = ras2vox.dot(np.append(point + delta, [1]))[:-1]
return tuple(newindex)