|
8 | 8 |
|
9 | 9 | import numpy as np
|
10 | 10 | import nibabel as nb
|
| 11 | +from ..io.base import TransformFileError |
11 | 12 | from ..nonlinear import DisplacementsFieldTransform
|
| 13 | +from ..io.itk import ITKDisplacementsField |
12 | 14 |
|
13 | 15 | TESTS_BORDER_TOLERANCE = 0.05
|
14 | 16 | APPLY_NONLINEAR_CMD = {
|
|
19 | 21 | }
|
20 | 22 |
|
21 | 23 |
|
| 24 | +@pytest.mark.parametrize('size', [(20, 20, 20), (20, 20, 20, 3)]) |
| 25 | +def test_itk_disp_load(size): |
| 26 | + """Checks field sizes.""" |
| 27 | + with pytest.raises(TransformFileError): |
| 28 | + ITKDisplacementsField.from_image( |
| 29 | + nb.Nifti1Image(np.zeros(size), None, None)) |
| 30 | + |
| 31 | + |
| 32 | +@pytest.mark.parametrize('size', [(20, 20, 20), (20, 20, 20, 1, 3)]) |
| 33 | +def test_displacements_bad_sizes(size): |
| 34 | + """Checks field sizes.""" |
| 35 | + with pytest.raises(ValueError): |
| 36 | + DisplacementsFieldTransform( |
| 37 | + nb.Nifti1Image(np.zeros(size), None, None)) |
| 38 | + |
| 39 | + |
| 40 | +def test_itk_disp_load_intent(): |
| 41 | + """Checks whether the NIfTI intent is fixed.""" |
| 42 | + with pytest.warns(UserWarning): |
| 43 | + field = ITKDisplacementsField.from_image( |
| 44 | + nb.Nifti1Image(np.zeros((20, 20, 20, 1, 3)), None, None)) |
| 45 | + |
| 46 | + assert field.header.get_intent()[0] == 'vector' |
| 47 | + |
| 48 | + |
| 49 | +@pytest.mark.parametrize('image_orientation', ['RAS', 'LAS', 'LPS', 'oblique']) |
22 | 50 | @pytest.mark.parametrize('sw_tool', ['itk'])
|
23 |
| -def test_displacements_field(tmp_path, data_path, sw_tool): |
| 51 | +@pytest.mark.parametrize('axis', [0, 1, 2, (0, 1), (1, 2), (0, 1, 2)]) |
| 52 | +def test_displacements_field1(tmp_path, get_testdata, image_orientation, sw_tool, axis): |
| 53 | + """Check a translation-only field on one or more axes, different image orientations.""" |
24 | 54 | os.chdir(str(tmp_path))
|
25 |
| - img_fname = str(data_path / 'tpl-OASIS30ANTs_T1w.nii.gz') |
26 |
| - xfm_fname = str( |
27 |
| - data_path / 'ds-005_sub-01_from-OASIS_to-T1_warp.nii.gz') |
28 |
| - ants_warp = nb.load(xfm_fname) |
29 |
| - hdr = ants_warp.header.copy() |
| 55 | + nii = get_testdata[image_orientation] |
| 56 | + nii.to_filename('reference.nii.gz') |
| 57 | + fieldmap = np.zeros((*nii.shape[:3], 1, 3), dtype='float32') |
| 58 | + fieldmap[..., axis] = -10.0 |
| 59 | + |
| 60 | + _hdr = nii.header.copy() |
| 61 | + _hdr.set_intent('vector') |
| 62 | + _hdr.set_data_dtype('float32') |
30 | 63 |
|
31 |
| - # fieldmap = np.squeeze(np.asanyarray(ants_warp.dataobj)) |
32 | 64 | xfm_fname = 'warp.nii.gz'
|
33 |
| - nii = nb.load(img_fname) |
34 |
| - fieldmap = np.zeros((*nii.shape[:3], 1, 3)) |
35 |
| - fieldmap[..., 2] = -10.0 |
36 |
| - # fieldmap = np.flip(np.flip(fieldmap, 1), 0) |
37 |
| - ants_warp = nb.Nifti1Image(fieldmap, nii.affine, hdr) |
38 |
| - ants_warp.to_filename(xfm_fname) |
39 |
| - fieldmap = np.squeeze(np.asanyarray(ants_warp.dataobj)) |
40 |
| - field = nb.Nifti1Image( |
41 |
| - fieldmap, |
42 |
| - ants_warp.affine, ants_warp.header |
43 |
| - ) |
44 |
| - |
45 |
| - xfm = DisplacementsFieldTransform(field) |
| 65 | + field = nb.Nifti1Image(fieldmap, nii.affine, _hdr) |
| 66 | + field.to_filename(xfm_fname) |
| 67 | + |
| 68 | + xfm = DisplacementsFieldTransform( |
| 69 | + ITKDisplacementsField.from_image(field)) |
46 | 70 |
|
47 | 71 | # Then apply the transform and cross-check with software
|
48 | 72 | cmd = APPLY_NONLINEAR_CMD[sw_tool](
|
49 | 73 | transform=os.path.abspath(xfm_fname),
|
| 74 | + reference=tmp_path / 'reference.nii.gz', |
| 75 | + moving=tmp_path / 'reference.nii.gz') |
| 76 | + |
| 77 | + # skip test if command is not available on host |
| 78 | + exe = cmd.split(" ", 1)[0] |
| 79 | + if not shutil.which(exe): |
| 80 | + pytest.skip("Command {} not found on host".format(exe)) |
| 81 | + |
| 82 | + exit_code = check_call([cmd], shell=True) |
| 83 | + assert exit_code == 0 |
| 84 | + sw_moved = nb.load('resampled.nii.gz') |
| 85 | + |
| 86 | + nt_moved = xfm.apply(nii, order=0) |
| 87 | + nt_moved.to_filename('nt_resampled.nii.gz') |
| 88 | + diff = sw_moved.get_fdata() - nt_moved.get_fdata() |
| 89 | + # A certain tolerance is necessary because of resampling at borders |
| 90 | + assert (np.abs(diff) > 1e-3).sum() / diff.size < TESTS_BORDER_TOLERANCE |
| 91 | + |
| 92 | + |
| 93 | +@pytest.mark.parametrize('sw_tool', ['itk']) |
| 94 | +def test_displacements_field2(tmp_path, data_path, sw_tool): |
| 95 | + """Check a translation-only field on one or more axes, different image orientations.""" |
| 96 | + os.chdir(str(tmp_path)) |
| 97 | + img_fname = data_path / 'tpl-OASIS30ANTs_T1w.nii.gz' |
| 98 | + xfm_fname = data_path / 'ds-005_sub-01_from-OASIS_to-T1_warp.nii.gz' |
| 99 | + |
| 100 | + xfm = DisplacementsFieldTransform( |
| 101 | + ITKDisplacementsField.from_filename(xfm_fname)) |
| 102 | + |
| 103 | + # Then apply the transform and cross-check with software |
| 104 | + cmd = APPLY_NONLINEAR_CMD[sw_tool]( |
| 105 | + transform=xfm_fname, |
50 | 106 | reference=img_fname,
|
51 | 107 | moving=img_fname)
|
52 | 108 |
|
|
0 commit comments