Skip to content
Snippets Groups Projects
Commit 0ee48df0 authored by Paul McCarthy's avatar Paul McCarthy :mountain_bicyclist:
Browse files

Merge branch 'enh/wrappers_support_fsl_image' into 'master'

Support fsl data image in wrappers

See merge request fsl/fslpy!53
parents 523b1f6e 14b4211d
No related branches found
No related tags found
No related merge requests found
...@@ -2,6 +2,18 @@ This document contains the ``fslpy`` release history in reverse chronological ...@@ -2,6 +2,18 @@ This document contains the ``fslpy`` release history in reverse chronological
order. order.
1.8.1 (Friday May 11th 2018)
----------------------------
Changed
^^^^^^^
* The :func:`.fileOrImage` decorator function now accepts :class:`.Image`
objects as well as ``nibabel`` image objects.
1.8.0 (Thursday May 3rd 2018) 1.8.0 (Thursday May 3rd 2018)
----------------------------- -----------------------------
......
...@@ -48,12 +48,10 @@ Some extra dependencies are listed in `requirements.txt ...@@ -48,12 +48,10 @@ Some extra dependencies are listed in `requirements.txt
on the mesh. on the mesh.
If you are using Linux, need to install wxPython first, as binaries are not
available on PyPI. Change the URL for your specific platform::
To install these additional dependencies, you first need to install wxPython, pip install -f https://extras.wxpython.org/wxPython4/extras/linux/gtk2/ubuntu-16.04/ wxpython
which is still in pre-relaes.
- **macOS**: ``pip install --pre wxPython``
- **Linux** (change the URL for your specific platform): ``pip install --only-binary wxpython -f https://extras.wxpython.org/wxPython4/extras/linux/gtk2/ubuntu-16.04/ wxpython``
The ``rtree`` library also assumes that ``libspatialindex`` is installed on The ``rtree`` library also assumes that ``libspatialindex`` is installed on
......
...@@ -42,6 +42,7 @@ the following labels (this convention has been inherited from `nibabel ...@@ -42,6 +42,7 @@ the following labels (this convention has been inherited from `nibabel
* *DOC*: for all kinds of documentation related commits * *DOC*: for all kinds of documentation related commits
* *TEST*: for adding or changing tests * *TEST*: for adding or changing tests
* *MAINT*: for administrative/maintenance changes * *MAINT*: for administrative/maintenance changes
* *CI*: for continuous-integration changes
Version number Version number
......
...@@ -635,13 +635,25 @@ class _FileOrThing(object): ...@@ -635,13 +635,25 @@ class _FileOrThing(object):
def fileOrImage(*imgargs): def fileOrImage(*imgargs):
"""Decorator which can be used to ensure that any NIfTI images are saved """Decorator which can be used to ensure that any NIfTI images are saved
to file, and output images can be loaded and returned as ``nibabel`` to file, and output images can be loaded and returned as ``nibabel``
image objects. image objects or :class:`.Image` objects.
""" """
# keep track of the input argument
# types on each call, so we know
# whether to return a fsl.Image or
# a nibabel image
intypes = []
def prepIn(workdir, name, val): def prepIn(workdir, name, val):
infile = None infile = None
if isinstance(val, (fslimage.Image, nib.nifti1.Nifti1Image)):
intypes.append(type(val))
if isinstance(val, fslimage.Image):
val = val.nibImage
if isinstance(val, nib.nifti1.Nifti1Image): if isinstance(val, nib.nifti1.Nifti1Image):
infile = val.get_filename() infile = val.get_filename()
...@@ -661,13 +673,28 @@ def fileOrImage(*imgargs): ...@@ -661,13 +673,28 @@ def fileOrImage(*imgargs):
# create an independent in-memory # create an independent in-memory
# copy of the image file # copy of the image file
img = nib.load(path) img = nib.load(path)
return nib.nifti1.Nifti1Image(img.get_data(), None, img.header)
# if any arguments were fsl images,
# that takes precedence.
if fslimage.Image in intypes:
return fslimage.Image(img.get_data(), header=img.header)
# but if all inputs were file names,
# nibabel takes precedence
elif nib.nifti1.Nifti1Image in intypes or len(intypes) == 0:
return nib.nifti1.Nifti1Image(img.get_data(), None, img.header)
# this function should not be called
# under any other circumstances
else:
raise RuntimeError('Cannot handle type: {}'.format(intypes))
def decorator(func): def decorator(func):
fot = _FileOrThing(func, prepIn, prepOut, load, *imgargs) fot = _FileOrThing(func, prepIn, prepOut, load, *imgargs)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
return fot(*args, **kwargs) result = fot(*args, **kwargs)
intypes[:] = []
return result
return _update_wrapper(wrapper, func) return _update_wrapper(wrapper, func)
......
...@@ -15,6 +15,7 @@ from fsl.data.atlases import Atlas ...@@ -15,6 +15,7 @@ from fsl.data.atlases import Atlas
from pytest import raises from pytest import raises
from .test_image import make_image from .test_image import make_image
import os import os
import pytest
datadir = op.join(op.dirname(__file__), 'testdata') datadir = op.join(op.dirname(__file__), 'testdata')
...@@ -115,6 +116,7 @@ def test_image_out(): ...@@ -115,6 +116,7 @@ def test_image_out():
assert args.image_out == 'test.surf.gii' + extension assert args.image_out == 'test.surf.gii' + extension
@pytest.mark.fsltest
def test_atlas(): def test_atlas():
atlas_parser = argparse.ArgumentParser('reads an atlas') atlas_parser = argparse.ArgumentParser('reads an atlas')
atlas_parser.add_argument('atlas', type=parse_data.Atlas) atlas_parser.add_argument('atlas', type=parse_data.Atlas)
...@@ -123,4 +125,4 @@ def test_atlas(): ...@@ -123,4 +125,4 @@ def test_atlas():
assert isinstance(args.atlas, Atlas) assert isinstance(args.atlas, Atlas)
with raises(SystemExit): with raises(SystemExit):
atlas_parser.parse_args(['fake']) atlas_parser.parse_args(['fake'])
\ No newline at end of file
...@@ -21,6 +21,7 @@ import nibabel as nib ...@@ -21,6 +21,7 @@ import nibabel as nib
import fsl.utils.tempdir as tempdir import fsl.utils.tempdir as tempdir
import fsl.utils.run as run import fsl.utils.run as run
import fsl.utils.fslsub as fslsub import fsl.utils.fslsub as fslsub
import fsl.data.image as fslimage
import fsl.wrappers.wrapperutils as wutils import fsl.wrappers.wrapperutils as wutils
...@@ -284,6 +285,28 @@ def test_fileOrImage(): ...@@ -284,6 +285,28 @@ def test_fileOrImage():
assert np.all(nib.load('output.nii').get_data() == expected) assert np.all(nib.load('output.nii').get_data() == expected)
os.remove('output.nii') os.remove('output.nii')
# fslimage, file, load
result = func(fslimage.Image(img1), img2='img2.nii',
output=wutils.LOAD)['output']
assert isinstance(result, fslimage.Image)
assert np.all(result[:].squeeze() == expected)
# fslimage, fslimage, load
result = func(fslimage.Image(img1), img2=fslimage.Image(img2),
output=wutils.LOAD)['output']
assert isinstance(result, fslimage.Image)
assert np.all(result[:].squeeze() == expected)
# fslimage, nib.image, load
result = func(fslimage.Image(img1), img2=img2,
output=wutils.LOAD)['output']
assert isinstance(result, fslimage.Image)
assert np.all(result[:].squeeze() == expected)
# nib.image, nib.image, load
result = func(img1, img2=img2, output=wutils.LOAD)['output']
assert isinstance(result, nib.nifti1.Nifti1Image)
assert np.all(result.get_data()[:] == expected)
def test_chained_fileOrImageAndArray(): def test_chained_fileOrImageAndArray():
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment