Skip to content

Commit

Permalink
move utils.load_data to io.load_data
Browse files Browse the repository at this point in the history
  • Loading branch information
frodeaa committed Oct 4, 2018
1 parent 316653a commit a5cbacb
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 91 deletions.
6 changes: 3 additions & 3 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,14 @@
.. _calibration_ref:


:mod:`tedana.io`: Utility functions
:mod:`tedana.io`: IO functions
--------------------------------------------------

.. automodule:: tedana.utils
.. automodule:: tedana.io
:no-members:
:no-inherited-members:

.. autosummary:: tedana.utils
.. autosummary:: tedana.io
:toctree: generated/
:template: module.rst

Expand Down
52 changes: 52 additions & 0 deletions tedana/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from numpy.linalg import lstsq

from tedana import model, utils
from tedana.utils import load_image

LGR = logging.getLogger(__name__)

Expand Down Expand Up @@ -593,3 +594,54 @@ def filewrite(data, filename, ref_img, gzip=False, copy_header=True):
out.to_filename(name)

return name


def load_data(data, n_echos=None):
"""
Coerces input `data` files to required 3D array output
Parameters
----------
data : (X x Y x M x T) array_like or :obj:`list` of img_like
Input multi-echo data array, where `X` and `Y` are spatial dimensions,
`M` is the Z-spatial dimensions with all the input echos concatenated,
and `T` is time. A list of image-like objects (e.g., .nii) are
accepted, as well
n_echos : :obj:`int`, optional
Number of echos in provided data array. Only necessary if `data` is
array_like. Default: None
Returns
-------
fdata : (S x E x T) :obj:`numpy.ndarray`
Output data where `S` is samples, `E` is echos, and `T` is time
ref_img : :obj:`str` or :obj:`numpy.ndarray`
Filepath to reference image for saving output files or NIFTI-like array
"""
if n_echos is None:
raise ValueError('Number of echos must be specified. '
'Confirm that TE times are provided with the `-e` argument.')

if isinstance(data, list):
if len(data) == 1: # a z-concatenated file was provided
data = data[0]
elif len(data) == 2: # inviable -- need more than 2 echos
raise ValueError('Cannot run `tedana` with only two echos: '
'{}'.format(data))
else: # individual echo files were provided (surface or volumetric)
fdata = np.stack([load_image(f) for f in data], axis=1)
ref_img = check_niimg(data[0])
ref_img.header.extensions = []
return np.atleast_3d(fdata), ref_img

img = check_niimg(data)
(nx, ny), nz = img.shape[:2], img.shape[2] // n_echos
fdata = load_image(img.get_data().reshape(nx, ny, nz, n_echos, -1, order='F'))

# create reference image
ref_img = img.__class__(np.zeros((nx, ny, nz)), affine=img.affine,
header=img.header, extra=img.extra)
ref_img.header.extensions = []
ref_img.header.set_sform(ref_img.header.get_sform(), code=1)

return fdata, ref_img
4 changes: 2 additions & 2 deletions tedana/tests/test_decay.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import numpy as np
import pytest

from tedana import utils, decay as me
from tedana import io, utils, decay as me
from tedana.tests.utils import get_test_data_path


Expand All @@ -16,7 +16,7 @@ def testdata1():
tes = np.array([14.5, 38.5, 62.5])
in_files = [op.join(get_test_data_path(), 'echo{0}.nii.gz'.format(i+1))
for i in range(3)]
data, _ = utils.load_data(in_files, n_echos=len(tes))
data, _ = io.load_data(in_files, n_echos=len(tes))
mask, mask_sum = utils.make_adaptive_mask(data, minimum=False, getsum=True)
data_dict = {'data': data,
'tes': tes,
Expand Down
35 changes: 31 additions & 4 deletions tedana/tests/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,46 @@
"""

import nibabel as nib
import numpy as np
import pytest

import tedana.io
from tedana import utils
from tedana import io as me
from tedana.tests.test_utils import fnames, tes


def test_new_nii_like():
data, ref = utils.load_data(fnames, n_echos=len(tes))
nimg = tedana.io.new_nii_like(ref, data)
data, ref = me.load_data(fnames, n_echos=len(tes))
nimg = me.new_nii_like(ref, data)

assert isinstance(nimg, nib.Nifti1Image)
assert nimg.shape == (39, 50, 33, 3, 5)


def test_filewrite():
pass


def test_load_data():
fimg = [nib.load(f) for f in fnames]
exp_shape = (64350, 3, 5)

# list of filepath to images
d, ref = me.load_data(fnames, n_echos=len(tes))
assert d.shape == exp_shape
assert isinstance(ref, nib.Nifti1Image)
assert np.allclose(ref.get_data(), nib.load(fnames[0]).get_data())

# list of img_like
d, ref = me.load_data(fimg, n_echos=len(tes))
assert d.shape == exp_shape
assert isinstance(ref, nib.Nifti1Image)
assert ref == fimg[0]

# imagine z-cat img
d, ref = me.load_data(fnames[0], n_echos=3)
assert d.shape == (21450, 3, 5)
assert isinstance(ref, nib.Nifti1Image)
assert ref.shape == (39, 50, 11)

with pytest.raises(ValueError):
me.load_data(fnames[0])
32 changes: 3 additions & 29 deletions tedana/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import numpy as np
import pytest

from tedana import utils
from tedana import (utils, io)

rs = np.random.RandomState(1234)
datadir = pjoin(dirname(__file__), 'data')
Expand Down Expand Up @@ -118,35 +118,9 @@ def test_load_image():
assert utils.load_image(fimg.get_data()).shape == exp_shape


def test_load_data():
fimg = [nib.load(f) for f in fnames]
exp_shape = (64350, 3, 5)

# list of filepath to images
d, ref = utils.load_data(fnames, n_echos=len(tes))
assert d.shape == exp_shape
assert isinstance(ref, nib.Nifti1Image)
assert np.allclose(ref.get_data(), nib.load(fnames[0]).get_data())

# list of img_like
d, ref = utils.load_data(fimg, n_echos=len(tes))
assert d.shape == exp_shape
assert isinstance(ref, nib.Nifti1Image)
assert ref == fimg[0]

# imagine z-cat img
d, ref = utils.load_data(fnames[0], n_echos=3)
assert d.shape == (21450, 3, 5)
assert isinstance(ref, nib.Nifti1Image)
assert ref.shape == (39, 50, 11)

with pytest.raises(ValueError):
utils.load_data(fnames[0])


def test_make_adaptive_mask():
# load data make masks
data = utils.load_data(fnames, n_echos=len(tes))[0]
data = io.load_data(fnames, n_echos=len(tes))[0]
minmask = utils.make_adaptive_mask(data)
mask, masksum = utils.make_adaptive_mask(data, minimum=False, getsum=True)

Expand Down Expand Up @@ -174,7 +148,7 @@ def test_make_adaptive_mask():

def test_make_min_mask():
# load data make mask
data = utils.load_data(fnames, n_echos=len(tes))[0]
data = io.load_data(fnames, n_echos=len(tes))[0]
minmask = utils.make_min_mask(data)

assert minmask.shape == (64350,)
Expand Down
51 changes: 0 additions & 51 deletions tedana/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,57 +98,6 @@ def load_image(data):
return fdata


def load_data(data, n_echos=None):
"""
Coerces input `data` files to required 3D array output
Parameters
----------
data : (X x Y x M x T) array_like or :obj:`list` of img_like
Input multi-echo data array, where `X` and `Y` are spatial dimensions,
`M` is the Z-spatial dimensions with all the input echos concatenated,
and `T` is time. A list of image-like objects (e.g., .nii) are
accepted, as well
n_echos : :obj:`int`, optional
Number of echos in provided data array. Only necessary if `data` is
array_like. Default: None
Returns
-------
fdata : (S x E x T) :obj:`numpy.ndarray`
Output data where `S` is samples, `E` is echos, and `T` is time
ref_img : :obj:`str` or :obj:`numpy.ndarray`
Filepath to reference image for saving output files or NIFTI-like array
"""
if n_echos is None:
raise ValueError('Number of echos must be specified. '
'Confirm that TE times are provided with the `-e` argument.')

if isinstance(data, list):
if len(data) == 1: # a z-concatenated file was provided
data = data[0]
elif len(data) == 2: # inviable -- need more than 2 echos
raise ValueError('Cannot run `tedana` with only two echos: '
'{}'.format(data))
else: # individual echo files were provided (surface or volumetric)
fdata = np.stack([load_image(f) for f in data], axis=1)
ref_img = check_niimg(data[0])
ref_img.header.extensions = []
return np.atleast_3d(fdata), ref_img

img = check_niimg(data)
(nx, ny), nz = img.shape[:2], img.shape[2] // n_echos
fdata = load_image(img.get_data().reshape(nx, ny, nz, n_echos, -1, order='F'))

# create reference image
ref_img = img.__class__(np.zeros((nx, ny, nz)), affine=img.affine,
header=img.header, extra=img.extra)
ref_img.header.extensions = []
ref_img.header.set_sform(ref_img.header.get_sform(), code=1)

return fdata, ref_img


def make_adaptive_mask(data, mask=None, minimum=True, getsum=False):
"""
Makes map of `data` specifying longest echo a voxel can be sampled with
Expand Down
2 changes: 1 addition & 1 deletion tedana/workflows/t2smap.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ def t2smap_workflow(data, tes, mask=None, fitmode='all', combmode='t2s',
data = [data]

LGR.info('Loading input data: {}'.format([f for f in data]))
catd, ref_img = utils.load_data(data, n_echos=n_echos)
catd, ref_img = io.load_data(data, n_echos=n_echos)
n_samp, n_echos, n_vols = catd.shape
LGR.debug('Resulting data shape: {}'.format(catd.shape))

Expand Down
2 changes: 1 addition & 1 deletion tedana/workflows/tedana.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ def tedana_workflow(data, tes, mask=None, mixm=None, ctab=None, manacc=None,
data = [data]

LGR.info('Loading input data: {}'.format([f for f in data]))
catd, ref_img = utils.load_data(data, n_echos=n_echos)
catd, ref_img = io.load_data(data, n_echos=n_echos)
n_samp, n_echos, n_vols = catd.shape
LGR.debug('Resulting data shape: {}'.format(catd.shape))

Expand Down

0 comments on commit a5cbacb

Please sign in to comment.