Commit 60322251 authored by William Clarke's avatar William Clarke
Browse files

Merge branch 'bf/fslpy_0_3_9_update' into 'master'

BF: update to fslpy 3.9.0

See merge request fsl/fsl_mrs!48
parents 03920951 3327ae1b
Pipeline #13955 canceled with stage
This document contains the FSL-MRS release history in reverse chronological order.
1.1.12 (Wednesday 20th April)
-----------------------------
- Update to fslpy version (to 3.9.0) to substantially speed up MRSI preprocessing.
- Fixes to NIFTI_MRS class for compatibility with new fslpy version.
- Previous versions of FSL-MRS will not be compatible with fslpy >= 3.9.0
1.1.11 (Monday 4th April 2022)
------------------------------
- Now able to choose the number of workers in fsl_mrs_sim.
......
......@@ -327,7 +327,7 @@ class NIFTI_MRS(Image):
dim - None, dimension index (4, 5, 6) or tag. None iterates over all indices.'''
if remove_dim:
dim = self._dim_tag_to_index(remove_dim)
reduced_data = self.data.take(0, axis=dim)
reduced_data = self[:].take(0, axis=dim)
new_obj = NIFTI_MRS(reduced_data, header=self.header)
new_obj._filename = self.filename
......@@ -350,7 +350,7 @@ class NIFTI_MRS(Image):
return new_obj
else:
return NIFTI_MRS(self.data, header=self.header)
return NIFTI_MRS(self[:], header=self.header)
def iterate_over_dims(self, dim=None, iterate_over_space=False, reduce_dim_index=False, voxel_index=None):
'''Return generator to iterate over all indices or one dimension (and FID).
......@@ -364,7 +364,7 @@ class NIFTI_MRS(Image):
index - data location slice object.
'''
data = self.data
data = self[:]
dim = self._dim_tag_to_index(dim)
# Convert indicies to slices to preserve singleton dimensions
......@@ -434,14 +434,14 @@ class NIFTI_MRS(Image):
:yield: Complex FID data with any higher dimensions. Index to data.
:rtype: tuple
"""
data = self.data
data = self[:]
def calc_slice_idx(idx):
slice_obj = list(idx[:3]) + [slice(None), ] * (self.data.ndim - 3)
slice_obj = list(idx[:3]) + [slice(None), ] * (data.ndim - 3)
return tuple(slice_obj)
for idx in np.ndindex(data.shape[:3]):
yield self.data[idx], calc_slice_idx(idx)
yield self[idx], calc_slice_idx(idx)
def generate_mrs(self, dim=None, basis_file=None, basis=None, ref_data=None):
"""Generator for MRS or MRSI objects from the data, optionally returning a whole dimension as a list.
......@@ -578,9 +578,10 @@ class NIFTI_MRS(Image):
return tvar_dict2, tvar_tuple2.reshape(self.shape[4:]), tvar_array
# As of move to fslpy 3.9.0 this is no longer required.
# If save called do some hairy temporary conjugation
# The save method of fsl.data.image.Image makes a call
# to __getitem__ resulting in a final undesired conjugation.
def save(self, filename=None):
self[:] = self[:].conj()
super().save(filename=filename)
# def save(self, filename=None):
# self[:] = self[:].conj()
# super().save(filename=filename)
......@@ -6,6 +6,7 @@ Copyright Will Clarke, University of Oxford, 2021'''
# Imports
from pathlib import Path
from copy import deepcopy
import pytest
import numpy as np
......@@ -53,10 +54,16 @@ def test_nifti_mrs_filename():
def test_nifti_mrs_save(tmp_path):
obj = NIFTI_MRS(data['metab'])
obj.save(tmp_path / 'out')
original = deepcopy(obj[:])
obj.save(tmp_path / 'out')
assert (tmp_path / 'out.nii.gz').exists()
obj_reloaded = NIFTI_MRS(tmp_path / 'out.nii.gz')
assert np.allclose(obj_reloaded[:], obj[:])
assert np.allclose(obj_reloaded[:], original)
def test_nifti_mrs_generator():
obj = NIFTI_MRS(data['unprocessed'])
......@@ -189,6 +196,17 @@ def test_gen_new_nifti_mrs(tmp_path):
assert (tmp_path / 'out.nii.gz').exists()
def test_gen_new_nifti_mrs_conj(tmp_path):
obj_in = NIFTI_MRS(data['metab'])
nmrs = gen_new_nifti_mrs(obj_in[:],
obj_in.dwelltime,
obj_in.spectrometer_frequency[0],
nucleus='1H')
assert np.allclose(nmrs[:], obj_in[:])
def test_add_remove_field():
nmrs = NIFTI_MRS(data['unprocessed'])
......
......@@ -275,7 +275,7 @@ def test_coilcombine(svs_data_uncomb, mrsi_data_uncomb, tmp_path):
# Run directly
directRun = preproc.coilcombine(svsdata)
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
# Run coil combination on both sets of data using the command line
subprocess.check_call(['fsl_mrs_proc',
......@@ -290,7 +290,7 @@ def test_coilcombine(svs_data_uncomb, mrsi_data_uncomb, tmp_path):
# Run directly
directRun = preproc.coilcombine(mrsidata)
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
def test_average(svs_data, mrsi_data, tmp_path):
......@@ -310,7 +310,7 @@ def test_average(svs_data, mrsi_data, tmp_path):
# Run directly
directRun = preproc.average(svsdata, 'DIM_DYN')
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
# Run coil combination on both sets of data using the command line
subprocess.check_call(['fsl_mrs_proc',
......@@ -326,7 +326,7 @@ def test_average(svs_data, mrsi_data, tmp_path):
# Run directly
directRun = preproc.average(mrsidata, 'DIM_DYN')
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
def test_align(svs_data, mrsi_data, tmp_path):
......@@ -347,7 +347,7 @@ def test_align(svs_data, mrsi_data, tmp_path):
# Run directly
directRun = preproc.align(svsdata, 'DIM_DYN', ppmlim=(-10, 10))
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
subprocess.check_call(['fsl_mrs_proc',
'align',
......@@ -384,7 +384,7 @@ def test_align_all(svs_data_uncomb_reps, tmp_path):
# Run directly
directRun = preproc.align(svsdata, 'all', ppmlim=(-10, 10))
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
def test_ecc(svs_data, mrsi_data, tmp_path):
......@@ -404,7 +404,7 @@ def test_ecc(svs_data, mrsi_data, tmp_path):
# Run directly
directRun = preproc.ecc(svsdata, svsdata)
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
# Run coil ecc on both sets of data using the command line
subprocess.check_call(['fsl_mrs_proc',
......@@ -420,7 +420,7 @@ def test_ecc(svs_data, mrsi_data, tmp_path):
# Run directly
directRun = preproc.ecc(mrsidata, mrsidata)
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
def test_remove(svs_data, mrsi_data, tmp_path):
......@@ -440,7 +440,7 @@ def test_remove(svs_data, mrsi_data, tmp_path):
# Run directly
directRun = preproc.remove_peaks(svsdata, (-10, 10))
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
# Run coil combination on both sets of data using the command line
subprocess.check_call(['fsl_mrs_proc',
......@@ -456,7 +456,7 @@ def test_remove(svs_data, mrsi_data, tmp_path):
# Run directly
directRun = preproc.remove_peaks(mrsidata, (-10, 10))
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
def test_model(svs_data, mrsi_data, tmp_path):
......@@ -477,7 +477,7 @@ def test_model(svs_data, mrsi_data, tmp_path):
# Run directly
directRun = preproc.hlsvd_model_peaks(svsdata, (-10, 10), components=5)
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
# Run model on both sets of data using the command line
subprocess.check_call(['fsl_mrs_proc',
......@@ -494,7 +494,7 @@ def test_model(svs_data, mrsi_data, tmp_path):
# Run directly
directRun = preproc.hlsvd_model_peaks(mrsidata, (-10, 10), components=5)
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
def test_align_diff(svs_data_diff, mrsi_data_diff, tmp_path):
......@@ -518,7 +518,7 @@ def test_align_diff(svs_data_diff, mrsi_data_diff, tmp_path):
'add',
ppmlim=(-10, 10))
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
# TODO: finish MRSI test
......@@ -538,7 +538,7 @@ def test_fshift(svs_data, mrsi_data, tmp_path):
# Run directly
directRun = preproc.fshift(svsdata, 1.0 * 123.2)
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
subprocess.check_call(['fsl_mrs_proc',
'fshift',
......@@ -555,7 +555,7 @@ def test_fshift(svs_data, mrsi_data, tmp_path):
# Run directly
directRun = preproc.shift_to_reference(svsdata, 4.0, (-5.0, 5.0))
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
# TODO: finish MRSI test
......@@ -576,7 +576,7 @@ def test_conj(svs_data, mrsi_data, tmp_path):
# Run directly
directRun = preproc.conjugate(svsdata)
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
# Run coil combination on both sets of data using the command line
subprocess.check_call(['fsl_mrs_proc',
......@@ -591,7 +591,7 @@ def test_conj(svs_data, mrsi_data, tmp_path):
# Run directly
directRun = preproc.conjugate(mrsidata)
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
def test_fixed_phase(svs_data, mrsi_data, tmp_path):
......@@ -613,7 +613,7 @@ def test_fixed_phase(svs_data, mrsi_data, tmp_path):
# Run directly
directRun = preproc.apply_fixed_phase(svsdata, 90, 0.001)
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
# Run coil combination on both sets of data using the command line
subprocess.check_call(['fsl_mrs_proc',
......@@ -630,4 +630,4 @@ def test_fixed_phase(svs_data, mrsi_data, tmp_path):
# Run directly
directRun = preproc.apply_fixed_phase(mrsidata, 90, 0.001)
assert np.allclose(data.data, directRun.data)
assert np.allclose(data[:], directRun[:])
......@@ -16,4 +16,4 @@ def test_conjugate():
conjugated = nmrs_tools.conjugate(nmrs)
assert np.allclose(conjugated.data, np.conjugate(nmrs.data))
assert np.allclose(conjugated[:], np.conjugate(nmrs[:]))
......@@ -365,22 +365,22 @@ def test_split():
# Functionality testing
out_1, out_2 = nmrs_tools.split(nmrs, 'DIM_DYN', 31)
assert out_1.data.shape == (1, 1, 1, 4096, 32, 32)
assert out_2.data.shape == (1, 1, 1, 4096, 32, 32)
assert np.allclose(out_1.data, nmrs.data[:, :, :, :, :, 0:32])
assert np.allclose(out_2.data, nmrs.data[:, :, :, :, :, 32:])
assert out_1[:].shape == (1, 1, 1, 4096, 32, 32)
assert out_2[:].shape == (1, 1, 1, 4096, 32, 32)
assert np.allclose(out_1[:], nmrs[:, :, :, :, :, 0:32])
assert np.allclose(out_2[:], nmrs[:, :, :, :, :, 32:])
assert out_1.hdr_ext == nmrs.hdr_ext
assert out_1.hdr_ext == nmrs.hdr_ext
assert np.allclose(out_1.getAffine('voxel', 'world'), nmrs.getAffine('voxel', 'world'))
assert np.allclose(out_2.getAffine('voxel', 'world'), nmrs.getAffine('voxel', 'world'))
out_1, out_2 = nmrs_tools.split(nmrs, 'DIM_DYN', [0, 32, 63])
assert out_1.data.shape == (1, 1, 1, 4096, 32, 61)
assert out_2.data.shape == (1, 1, 1, 4096, 32, 3)
assert out_1[:].shape == (1, 1, 1, 4096, 32, 61)
assert out_2[:].shape == (1, 1, 1, 4096, 32, 3)
test_list = np.arange(0, 64)
test_list = np.delete(test_list, [0, 32, 63])
assert np.allclose(out_1.data, nmrs.data[:, :, :, :, :, test_list])
assert np.allclose(out_2.data, nmrs.data[:, :, :, :, :, [0, 32, 63]])
assert np.allclose(out_1[:], nmrs[:][:, :, :, :, :, test_list])
assert np.allclose(out_2[:], nmrs[:][:, :, :, :, :, [0, 32, 63]])
# Split some synthetic data with header information
nhdr_1 = gen_new_nifti_mrs(np.ones((1, 1, 1, 10, 4), dtype=complex),
......@@ -461,9 +461,9 @@ def test_merge():
# Functionality testing
out = nmrs_tools.merge((nmrs_1, nmrs_2), 'DIM_DYN')
assert out.data.shape == (1, 1, 1, 4096, 32, 4)
assert np.allclose(out.data[:, :, :, :, :, 0:2], nmrs_1.data)
assert np.allclose(out.data[:, :, :, :, :, 2:], nmrs_2.data)
assert out[:].shape == (1, 1, 1, 4096, 32, 4)
assert np.allclose(out[:][:, :, :, :, :, 0:2], nmrs_1[:])
assert np.allclose(out[:][:, :, :, :, :, 2:], nmrs_2[:])
assert out.hdr_ext == nmrs_1.hdr_ext
assert np.allclose(out.getAffine('voxel', 'world'), nmrs_1.getAffine('voxel', 'world'))
......@@ -471,7 +471,7 @@ def test_merge():
nmrs_1_e = nmrs_tools.reorder(nmrs_1, ['DIM_COIL', 'DIM_DYN', 'DIM_EDIT'])
nmrs_2_e = nmrs_tools.reorder(nmrs_2, ['DIM_COIL', 'DIM_DYN', 'DIM_EDIT'])
out = nmrs_tools.merge((nmrs_1_e, nmrs_2_e), 'DIM_EDIT')
assert out.data.shape == (1, 1, 1, 4096, 32, 2, 2)
assert out[:].shape == (1, 1, 1, 4096, 32, 2, 2)
assert out.hdr_ext['dim_7'] == 'DIM_EDIT'
# Merge some synthetic data with header information
......@@ -486,7 +486,7 @@ def test_merge():
nhdr_2.set_dim_header('DIM_DYN', {'RepetitionTime': [1, 2, 3, 4]})
out = nmrs_tools.merge((nhdr_1, nhdr_2, nhdr_2), 'DIM_DYN')
assert out.data.shape == (1, 1, 1, 10, 12)
assert out[:].shape == (1, 1, 1, 10, 12)
assert out.hdr_ext['dim_5'] == 'DIM_DYN'
assert out.hdr_ext['dim_5_header'] == {'RepetitionTime': [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]}
......@@ -494,7 +494,7 @@ def test_merge():
nhdr_2.set_dim_header('DIM_DYN', {'RepetitionTime': [5, 6, 7, 8]})
out = nmrs_tools.merge((nhdr_1, nhdr_2), 'DIM_DYN')
assert out.data.shape == (1, 1, 1, 10, 8)
assert out[:].shape == (1, 1, 1, 10, 8)
assert out.hdr_ext['dim_5'] == 'DIM_DYN'
assert out.hdr_ext['dim_5_header'] == {'RepetitionTime': {'start': 1, 'increment': 1}}
......@@ -514,7 +514,7 @@ def test_merge():
nhdr_2_e.set_dim_header('DIM_EDIT', {'OtherTime': [0.2, ]})
out = nmrs_tools.merge((nhdr_1_e, nhdr_2_e), 'DIM_EDIT')
assert out.data.shape == (1, 1, 1, 10, 4, 2)
assert out[:].shape == (1, 1, 1, 10, 4, 2)
assert out.hdr_ext['dim_6'] == 'DIM_EDIT'
assert out.hdr_ext['dim_6_header'] == {'OtherTime': [0.1, 0.2, ]}
......@@ -536,21 +536,21 @@ def test_reorder():
# Functionality testing
# Swap order of dimensions
out = nmrs_tools.reorder(nmrs, ['DIM_DYN', 'DIM_COIL'])
assert out.data.shape == (1, 1, 1, 4096, 64, 32)
assert np.allclose(np.swapaxes(nmrs.data, 4, 5), out.data)
assert out[:].shape == (1, 1, 1, 4096, 64, 32)
assert np.allclose(np.swapaxes(nmrs[:], 4, 5), out[:])
assert out.hdr_ext['dim_5'] == 'DIM_DYN'
assert out.hdr_ext['dim_6'] == 'DIM_COIL'
# # Add an additional singleton at end (not reported in shape)
out = nmrs_tools.reorder(nmrs, ['DIM_COIL', 'DIM_DYN', 'DIM_EDIT'])
assert out.data.shape == (1, 1, 1, 4096, 32, 64)
assert out[:].shape == (1, 1, 1, 4096, 32, 64)
assert out.hdr_ext['dim_5'] == 'DIM_COIL'
assert out.hdr_ext['dim_6'] == 'DIM_DYN'
assert out.hdr_ext['dim_7'] == 'DIM_EDIT'
# Add an additional singleton at 5 (not reported in shape)
out = nmrs_tools.reorder(nmrs, ['DIM_EDIT', 'DIM_COIL', 'DIM_DYN'])
assert out.data.shape == (1, 1, 1, 4096, 1, 32, 64)
assert out[:].shape == (1, 1, 1, 4096, 1, 32, 64)
assert out.hdr_ext['dim_5'] == 'DIM_EDIT'
assert out.hdr_ext['dim_6'] == 'DIM_COIL'
assert out.hdr_ext['dim_7'] == 'DIM_DYN'
......@@ -18,4 +18,4 @@ def conjugate(nmrs):
:rtype: NIFTI_MRS
"""
return NIFTI_MRS(np.conjugate(nmrs.data), header=nmrs.header)
return NIFTI_MRS(np.conjugate(nmrs[:]), header=nmrs.header)
......@@ -33,9 +33,9 @@ def reshape(nmrs, reshape, d5=None, d6=None, d7=None):
:type d7: str, optional
"""
shape = nmrs.data.shape[0:4]
shape = nmrs[:].shape[0:4]
shape += reshape
nmrs_reshaped = NIFTI_MRS(np.reshape(nmrs.data, shape), header=nmrs.header)
nmrs_reshaped = NIFTI_MRS(np.reshape(nmrs[:], shape), header=nmrs.header)
# reshpaed_hrd = _reshape_hdr(nmrs_reshaped.dynamic_hdr_vals[2],)
......
......@@ -76,8 +76,8 @@ def split(nmrs, dimension, index_or_indicies):
out_hdr_1 = utils.modify_hdr_ext(split_hdr_ext_1, nmrs.header)
out_hdr_2 = utils.modify_hdr_ext(split_hdr_ext_2, nmrs.header)
nmrs_1 = NIFTI_MRS(np.delete(nmrs.data, index, axis=dim_index), header=out_hdr_1)
nmrs_2 = NIFTI_MRS(np.take(nmrs.data, index, axis=dim_index), header=out_hdr_2)
nmrs_1 = NIFTI_MRS(np.delete(nmrs[:], index, axis=dim_index), header=out_hdr_1)
nmrs_2 = NIFTI_MRS(np.take(nmrs[:], index, axis=dim_index), header=out_hdr_2)
return nmrs_1, nmrs_2
......@@ -198,9 +198,9 @@ def merge(array_of_nmrs, dimension):
if nmrs.ndim == dim_index:
# If a squeezed singleton on the end.
to_concat.append(np.expand_dims(nmrs.data, -1))
to_concat.append(np.expand_dims(nmrs[:], -1))
else:
to_concat.append(nmrs.data)
to_concat.append(nmrs[:])
# Merge header extension
if idx == 0:
......@@ -317,7 +317,7 @@ def reorder(nmrs, dim_tag_list):
original_dims = nmrs.ndim
new_dim = sum(x is not None for x in nmrs.dim_tags) + 4
dims_to_add = tuple(range(original_dims, new_dim + 1))
data_with_singleton = np.expand_dims(nmrs.data, dims_to_add)
data_with_singleton = np.expand_dims(nmrs[:], dims_to_add)
# Create list of source indicies
# Create list of destination indicies
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment