|
9 | 9 | import nibabel as nb
|
10 | 10 | from nitransforms.resampling import apply
|
11 | 11 | from nitransforms.base import TransformError
|
12 |
| -from nitransforms.io.base import TransformFileError |
13 | 12 | from nitransforms.nonlinear import (
|
14 | 13 | BSplineFieldTransform,
|
15 | 14 | DenseFieldTransform,
|
16 | 15 | )
|
17 |
| -from nitransforms import io |
18 |
| -from ..io.itk import ITKDisplacementsField |
19 |
| - |
20 |
| - |
21 |
| -@pytest.mark.parametrize("size", [(20, 20, 20), (20, 20, 20, 3)]) |
22 |
| -def test_itk_disp_load(size): |
23 |
| - """Checks field sizes.""" |
24 |
| - with pytest.raises(TransformFileError): |
25 |
| - ITKDisplacementsField.from_image( |
26 |
| - nb.Nifti1Image(np.zeros(size), np.eye(4), None) |
27 |
| - ) |
28 |
| - |
29 |
| - |
30 |
| -@pytest.mark.parametrize("size", [(20, 20, 20), (20, 20, 20, 2, 3), (20, 20, 20, 1, 4)]) |
31 |
| -def test_displacements_bad_sizes(size): |
32 |
| - """Checks field sizes.""" |
33 |
| - with pytest.raises(TransformError): |
34 |
| - DenseFieldTransform(nb.Nifti1Image(np.zeros(size), np.eye(4), None)) |
35 |
| - |
36 |
| - |
37 |
| -def test_itk_disp_load_intent(): |
38 |
| - """Checks whether the NIfTI intent is fixed.""" |
39 |
| - with pytest.warns(UserWarning): |
40 |
| - field = ITKDisplacementsField.from_image( |
41 |
| - nb.Nifti1Image(np.zeros((20, 20, 20, 1, 3)), np.eye(4), None) |
42 |
| - ) |
43 |
| - |
44 |
| - assert field.header.get_intent()[0] == "vector" |
45 | 16 |
|
46 | 17 |
|
47 | 18 | def test_displacements_init():
|
@@ -96,76 +67,39 @@ def test_bsplines_references(testdata_path):
|
96 | 67 | )
|
97 | 68 |
|
98 | 69 |
|
| 70 | +@pytest.mark.xfail( |
| 71 | + reason="Disable while #266 is developed.", |
| 72 | + strict=False, |
| 73 | +) |
99 | 74 | def test_bspline(tmp_path, testdata_path):
|
| 75 | + """ |
| 76 | + Cross-check B-Splines and deformation field. |
| 77 | +
|
| 78 | + This test is disabled and will be split into two separate tests. |
| 79 | + The current implementation will be moved into test_resampling.py, |
| 80 | + since that's what it actually tests. |
| 81 | +
|
| 82 | + In GH-266, this test will be re-implemented by testing the equivalence |
| 83 | + of the B-Spline and deformation field transforms by calling the |
| 84 | + transform's `map()` method on points. |
| 85 | +
|
| 86 | + """ |
| 87 | + assert True |
| 88 | + |
| 89 | + |
| 90 | +def test_map_bspline_vs_displacement(tmp_path, testdata_path): |
100 | 91 | """Cross-check B-Splines and deformation field."""
|
101 | 92 | os.chdir(str(tmp_path))
|
102 | 93 |
|
103 | 94 | img_name = testdata_path / "someones_anatomy.nii.gz"
|
104 | 95 | disp_name = testdata_path / "someones_displacement_field.nii.gz"
|
105 | 96 | bs_name = testdata_path / "someones_bspline_coefficients.nii.gz"
|
106 | 97 |
|
107 |
| - bsplxfm = BSplineFieldTransform(bs_name, reference=img_name) |
| 98 | + bsplxfm = BSplineFieldTransform(bs_name, reference=img_name).to_field() |
108 | 99 | dispxfm = DenseFieldTransform(disp_name)
|
109 | 100 |
|
110 |
| - out_disp = apply(dispxfm, img_name) |
111 |
| - out_bspl = apply(bsplxfm, img_name) |
112 |
| - |
113 |
| - out_disp.to_filename("resampled_field.nii.gz") |
114 |
| - out_bspl.to_filename("resampled_bsplines.nii.gz") |
115 |
| - |
116 |
| - assert ( |
117 |
| - np.sqrt( |
118 |
| - (out_disp.get_fdata(dtype="float32") - out_bspl.get_fdata(dtype="float32")) |
119 |
| - ** 2 |
120 |
| - ).mean() |
121 |
| - < 0.2 |
122 |
| - ) |
123 |
| - |
124 |
| - |
125 |
| -@pytest.mark.parametrize("is_deltas", [True, False]) |
126 |
| -def test_densefield_x5_roundtrip(tmp_path, is_deltas): |
127 |
| - """Ensure dense field transforms roundtrip via X5.""" |
128 |
| - ref = nb.Nifti1Image(np.zeros((2, 2, 2), dtype="uint8"), np.eye(4)) |
129 |
| - disp = nb.Nifti1Image(np.random.rand(2, 2, 2, 3).astype("float32"), np.eye(4)) |
130 |
| - |
131 |
| - xfm = DenseFieldTransform(disp, is_deltas=is_deltas, reference=ref) |
132 |
| - |
133 |
| - node = xfm.to_x5(metadata={"GeneratedBy": "pytest"}) |
134 |
| - assert node.type == "nonlinear" |
135 |
| - assert node.subtype == "densefield" |
136 |
| - assert node.representation == "displacements" if is_deltas else "deformations" |
137 |
| - assert node.domain.size == ref.shape |
138 |
| - assert node.metadata["GeneratedBy"] == "pytest" |
139 |
| - |
140 |
| - fname = tmp_path / "test.x5" |
141 |
| - io.x5.to_filename(fname, [node]) |
142 |
| - |
143 |
| - xfm2 = DenseFieldTransform.from_filename(fname, fmt="X5") |
144 |
| - |
145 |
| - assert xfm2.reference.shape == ref.shape |
146 |
| - assert np.allclose(xfm2.reference.affine, ref.affine) |
147 |
| - assert xfm == xfm2 |
148 |
| - |
149 |
| - |
150 |
| -def test_bspline_to_x5(tmp_path): |
151 |
| - """Check BSpline transforms export to X5.""" |
152 |
| - coeff = nb.Nifti1Image(np.zeros((2, 2, 2, 3), dtype="float32"), np.eye(4)) |
153 |
| - ref = nb.Nifti1Image(np.zeros((2, 2, 2), dtype="uint8"), np.eye(4)) |
154 |
| - |
155 |
| - xfm = BSplineFieldTransform(coeff, reference=ref) |
156 |
| - node = xfm.to_x5(metadata={"tool": "pytest"}) |
157 |
| - assert node.type == "nonlinear" |
158 |
| - assert node.subtype == "bspline" |
159 |
| - assert node.representation == "coefficients" |
160 |
| - assert node.metadata["tool"] == "pytest" |
161 |
| - |
162 |
| - fname = tmp_path / "bspline.x5" |
163 |
| - io.x5.to_filename(fname, [node]) |
164 |
| - |
165 |
| - xfm2 = BSplineFieldTransform.from_filename(fname, fmt="X5") |
166 |
| - assert np.allclose(xfm._coeffs, xfm2._coeffs) |
167 |
| - assert xfm2.reference.shape == ref.shape |
168 |
| - assert np.allclose(xfm2.reference.affine, ref.affine) |
| 101 | + # Interpolating the field should be reasonably similar |
| 102 | + np.testing.assert_allclose(dispxfm._field, bsplxfm._field, atol=1e-1, rtol=1e-4) |
169 | 103 |
|
170 | 104 |
|
171 | 105 | @pytest.mark.parametrize("is_deltas", [True, False])
|
|
0 commit comments