Skip to content

Commit b40aff7

Browse files
authored
Merge pull request #141 from poldracklab/fix/fsl6-broken-tests
FIX: Double-check dtypes within tests and increase RMSE tolerance
2 parents de90b6f + 06002d8 commit b40aff7

File tree

2 files changed

+65
-17
lines changed

2 files changed

+65
-17
lines changed

nitransforms/conftest.py

+24-5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import tempfile
88

99
_data = None
10+
_brainmask = None
1011
_testdir = Path(os.getenv("TEST_DATA_HOME", "~/.nitransforms/testdata")).expanduser()
1112

1213

@@ -51,29 +52,47 @@ def get_testdata():
5152
if _data is not None:
5253
return _data
5354

54-
img = nb.load(_testdir / "someones_anatomy.nii.gz")
55-
imgaff = img.affine
55+
return _reorient(_testdir / "someones_anatomy.nii.gz")
56+
57+
58+
@pytest.fixture
59+
def get_testmask():
60+
"""Generate data in the requested orientation."""
61+
global _brainmask
5662

63+
if _brainmask is not None:
64+
return _brainmask
65+
66+
return _reorient(_testdir / "someones_anatomy_brainmask.nii.gz")
67+
68+
69+
def _reorient(path):
70+
"""Reorient the input NIfTI file."""
71+
img = nb.load(path)
72+
imgaff = img.affine
5773
_data = {"RAS": img}
5874
newaff = imgaff.copy()
5975
newaff[0, 0] *= -1.0
6076
newaff[0, 3] = imgaff.dot(np.hstack((np.array(img.shape[:3]) - 1, 1.0)))[0]
61-
_data["LAS"] = nb.Nifti1Image(np.flip(img.get_fdata(), 0), newaff, img.header)
77+
_data["LAS"] = nb.Nifti1Image(np.flip(np.asanyarray(img.dataobj), 0), newaff, img.header)
78+
_data["LAS"].set_data_dtype(img.get_data_dtype())
6279
newaff = imgaff.copy()
6380
newaff[0, 0] *= -1.0
6481
newaff[1, 1] *= -1.0
6582
newaff[:2, 3] = imgaff.dot(np.hstack((np.array(img.shape[:3]) - 1, 1.0)))[:2]
6683
_data["LPS"] = nb.Nifti1Image(
67-
np.flip(np.flip(img.get_fdata(), 0), 1), newaff, img.header
84+
np.flip(np.flip(np.asanyarray(img.dataobj), 0), 1), newaff, img.header
6885
)
86+
_data["LPS"].set_data_dtype(img.get_data_dtype())
6987
A = nb.volumeutils.shape_zoom_affine(
7088
img.shape, img.header.get_zooms(), x_flip=False
7189
)
7290
R = nb.affines.from_matvec(nb.eulerangles.euler2mat(x=0.09, y=0.001, z=0.001))
7391
newaff = R.dot(A)
74-
oblique_img = nb.Nifti1Image(img.get_fdata(), newaff, img.header)
92+
oblique_img = nb.Nifti1Image(np.asanyarray(img.dataobj), newaff, img.header)
7593
oblique_img.header.set_qform(newaff, 1)
7694
oblique_img.header.set_sform(newaff, 1)
7795
_data["oblique"] = oblique_img
96+
_data["oblique"].set_data_dtype(img.get_data_dtype())
7897

7998
return _data

nitransforms/tests/test_linear.py

+41-12
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,23 @@
1414
from .. import linear as nitl
1515
from .utils import assert_affines_by_filename
1616

17-
TESTS_BORDER_TOLERANCE = 0.05
17+
RMSE_TOL = 0.1
1818
APPLY_LINEAR_CMD = {
1919
"fsl": """\
2020
flirt -setbackground 0 -interp nearestneighbour -in {moving} -ref {reference} \
21-
-applyxfm -init {transform} -out resampled.nii.gz\
21+
-applyxfm -init {transform} -out {resampled}\
2222
""".format,
2323
"itk": """\
2424
antsApplyTransforms -d 3 -r {reference} -i {moving} \
25-
-o resampled.nii.gz -n NearestNeighbor -t {transform} --float\
25+
-o {resampled} -n NearestNeighbor -t {transform} --float\
2626
""".format,
2727
"afni": """\
2828
3dAllineate -base {reference} -input {moving} \
29-
-prefix resampled.nii.gz -1Dmatrix_apply {transform} -final NN\
29+
-prefix {resampled} -1Dmatrix_apply {transform} -final NN\
3030
""".format,
3131
"fs": """\
3232
mri_vol2vol --mov {moving} --targ {reference} --lta {transform} \
33-
--o resampled.nii.gz --nearest""".format,
33+
--o {resampled} --nearest""".format,
3434
}
3535

3636

@@ -121,13 +121,15 @@ def test_linear_save(tmpdir, data_path, get_testdata, image_orientation, sw_tool
121121
assert_affines_by_filename(xfm_fname1, xfm_fname2)
122122

123123

124-
@pytest.mark.parametrize("image_orientation", ["RAS", "LAS", "LPS",]) # 'oblique',
124+
@pytest.mark.parametrize("image_orientation", ["RAS", "LAS", "LPS", ]) # 'oblique',
125125
@pytest.mark.parametrize("sw_tool", ["itk", "fsl", "afni", "fs"])
126-
def test_apply_linear_transform(tmpdir, get_testdata, image_orientation, sw_tool):
126+
def test_apply_linear_transform(tmpdir, get_testdata, get_testmask, image_orientation, sw_tool):
127127
"""Check implementation of exporting affines to formats."""
128128
tmpdir.chdir()
129129

130130
img = get_testdata[image_orientation]
131+
msk = get_testmask[image_orientation]
132+
131133
# Generate test transform
132134
T = from_matvec(euler2mat(x=0.9, y=0.001, z=0.001), [4.0, 2.0, -1.0])
133135
xfm = nitl.Affine(T)
@@ -140,33 +142,60 @@ def test_apply_linear_transform(tmpdir, get_testdata, image_orientation, sw_tool
140142
ext = ".lta"
141143

142144
img.to_filename("img.nii.gz")
145+
msk.to_filename("mask.nii.gz")
146+
147+
# Write out transform file (software-dependent)
143148
xfm_fname = "M.%s%s" % (sw_tool, ext)
144149
xfm.to_filename(xfm_fname, fmt=sw_tool)
145150

146151
cmd = APPLY_LINEAR_CMD[sw_tool](
147152
transform=os.path.abspath(xfm_fname),
148-
reference=os.path.abspath("img.nii.gz"),
149-
moving=os.path.abspath("img.nii.gz"),
153+
reference=os.path.abspath("mask.nii.gz"),
154+
moving=os.path.abspath("mask.nii.gz"),
155+
resampled=os.path.abspath("resampled_brainmask.nii.gz"),
150156
)
151157

152158
# skip test if command is not available on host
153159
exe = cmd.split(" ", 1)[0]
154160
if not shutil.which(exe):
155161
pytest.skip("Command {} not found on host".format(exe))
156162

163+
# resample mask
164+
exit_code = check_call([cmd], shell=True)
165+
assert exit_code == 0
166+
sw_moved_mask = nb.load("resampled_brainmask.nii.gz")
167+
nt_moved_mask = xfm.apply(msk, order=0)
168+
nt_moved_mask.set_data_dtype(msk.get_data_dtype())
169+
diff = np.asanyarray(sw_moved_mask.dataobj) - np.asanyarray(nt_moved_mask.dataobj)
170+
171+
assert np.sqrt((diff ** 2).mean()) < RMSE_TOL
172+
173+
cmd = APPLY_LINEAR_CMD[sw_tool](
174+
transform=os.path.abspath(xfm_fname),
175+
reference=os.path.abspath("img.nii.gz"),
176+
moving=os.path.abspath("img.nii.gz"),
177+
resampled=os.path.abspath("resampled.nii.gz"),
178+
)
179+
180+
brainmask = np.asanyarray(nt_moved_mask.dataobj, dtype=bool)
181+
157182
exit_code = check_call([cmd], shell=True)
158183
assert exit_code == 0
159184
sw_moved = nb.load("resampled.nii.gz")
185+
sw_moved.set_data_dtype(img.get_data_dtype())
160186

161187
nt_moved = xfm.apply(img, order=0)
162-
diff = sw_moved.get_fdata() - nt_moved.get_fdata()
188+
diff = (sw_moved.get_fdata() - nt_moved.get_fdata())
189+
diff[~brainmask] = 0.0
190+
diff[np.abs(diff) < 1e-3] = 0
191+
163192
# A certain tolerance is necessary because of resampling at borders
164-
assert (np.abs(diff) > 1e-3).sum() / diff.size < TESTS_BORDER_TOLERANCE
193+
assert np.sqrt((diff ** 2).mean()) < RMSE_TOL
165194

166195
nt_moved = xfm.apply("img.nii.gz", order=0)
167196
diff = sw_moved.get_fdata() - nt_moved.get_fdata()
168197
# A certain tolerance is necessary because of resampling at borders
169-
assert (np.abs(diff) > 1e-3).sum() / diff.size < TESTS_BORDER_TOLERANCE
198+
assert np.sqrt((diff[brainmask] ** 2).mean()) < RMSE_TOL
170199

171200

172201
def test_Affine_to_x5(tmpdir, testdata_path):

0 commit comments

Comments
 (0)