|
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