1313from ..io .itk import ITKDisplacementsField
1414
1515
16- TESTS_BORDER_TOLERANCE = 0.05
16+ RMSE_TOL = 0.05
1717APPLY_NONLINEAR_CMD = {
1818 "itk" : """\
1919 antsApplyTransforms -d 3 -r {reference} -i {moving} \
20- -o resampled.nii.gz -n NearestNeighbor -t {transform} --float \
20+ -o {output} -n NearestNeighbor -t {transform} {extra} \
2121 """ .format ,
2222 "afni" : """\
2323 3dNwarpApply -nwarp {transform} -source {moving} \
24- -master {reference} -interp NN -prefix resampled.nii.gz
24+ -master {reference} -interp NN -prefix {output} {extra} \
2525 """ .format ,
2626 'fsl' : """\
27- applywarp -i {moving} -r {reference} -o resampled.nii.gz \
27+ applywarp -i {moving} -r {reference} -o {output} {extra} \
2828 -w {transform} --interp=nn""" .format ,
2929}
3030
@@ -56,13 +56,23 @@ def test_itk_disp_load_intent():
5656@pytest .mark .parametrize ("image_orientation" , ["RAS" , "LAS" , "LPS" , "oblique" ])
5757@pytest .mark .parametrize ("sw_tool" , ["itk" , "afni" ])
5858@pytest .mark .parametrize ("axis" , [0 , 1 , 2 , (0 , 1 ), (1 , 2 ), (0 , 1 , 2 )])
59- def test_displacements_field1 (tmp_path , get_testdata , image_orientation , sw_tool , axis ):
59+ def test_displacements_field1 (
60+ tmp_path ,
61+ get_testdata ,
62+ get_testmask ,
63+ image_orientation ,
64+ sw_tool ,
65+ axis ,
66+ ):
6067 """Check a translation-only field on one or more axes, different image orientations."""
6168 if (image_orientation , sw_tool ) == ("oblique" , "afni" ) and axis in ((1 , 2 ), (0 , 1 , 2 )):
6269 pytest .skip ("AFNI Deoblique unsupported." )
6370 os .chdir (str (tmp_path ))
6471 nii = get_testdata [image_orientation ]
72+ msk = get_testmask [image_orientation ]
6573 nii .to_filename ("reference.nii.gz" )
74+ msk .to_filename ("mask.nii.gz" )
75+
6676 fieldmap = np .zeros (
6777 (* nii .shape [:3 ], 1 , 3 ) if sw_tool != "fsl" else (* nii .shape [:3 ], 3 ),
6878 dtype = "float32" ,
@@ -83,24 +93,50 @@ def test_displacements_field1(tmp_path, get_testdata, image_orientation, sw_tool
8393 # Then apply the transform and cross-check with software
8494 cmd = APPLY_NONLINEAR_CMD [sw_tool ](
8595 transform = os .path .abspath (xfm_fname ),
86- reference = tmp_path / "reference.nii.gz" ,
87- moving = tmp_path / "reference.nii.gz" ,
96+ reference = tmp_path / "mask.nii.gz" ,
97+ moving = tmp_path / "mask.nii.gz" ,
98+ output = tmp_path / "resampled_brainmask.nii.gz" ,
99+ extra = "--output-data-type uchar" if sw_tool == "itk" else "" ,
88100 )
89101
90102 # skip test if command is not available on host
91103 exe = cmd .split (" " , 1 )[0 ]
92104 if not shutil .which (exe ):
93105 pytest .skip ("Command {} not found on host" .format (exe ))
94106
107+ # resample mask
108+ exit_code = check_call ([cmd ], shell = True )
109+ assert exit_code == 0
110+ sw_moved_mask = nb .load ("resampled_brainmask.nii.gz" )
111+ nt_moved_mask = xfm .apply (msk , order = 0 )
112+ nt_moved_mask .set_data_dtype (msk .get_data_dtype ())
113+ diff = np .asanyarray (sw_moved_mask .dataobj ) - np .asanyarray (nt_moved_mask .dataobj )
114+
115+ assert np .sqrt ((diff ** 2 ).mean ()) < RMSE_TOL
116+ brainmask = np .asanyarray (nt_moved_mask .dataobj , dtype = bool )
117+
118+ # Then apply the transform and cross-check with software
119+ cmd = APPLY_NONLINEAR_CMD [sw_tool ](
120+ transform = os .path .abspath (xfm_fname ),
121+ reference = tmp_path / "reference.nii.gz" ,
122+ moving = tmp_path / "reference.nii.gz" ,
123+ output = tmp_path / "resampled.nii.gz" ,
124+ extra = "--output-data-type uchar" if sw_tool == "itk" else ""
125+ )
126+
95127 exit_code = check_call ([cmd ], shell = True )
96128 assert exit_code == 0
97129 sw_moved = nb .load ("resampled.nii.gz" )
98130
99131 nt_moved = xfm .apply (nii , order = 0 )
100132 nt_moved .to_filename ("nt_resampled.nii.gz" )
101- diff = sw_moved .get_fdata () - nt_moved .get_fdata ()
133+ sw_moved .set_data_dtype (nt_moved .get_data_dtype ())
134+ diff = (
135+ np .asanyarray (sw_moved .dataobj , dtype = sw_moved .get_data_dtype ())
136+ - np .asanyarray (nt_moved .dataobj , dtype = nt_moved .get_data_dtype ())
137+ )
102138 # A certain tolerance is necessary because of resampling at borders
103- assert ( np .abs ( diff ) > 1e-3 ). sum () / diff . size < TESTS_BORDER_TOLERANCE
139+ assert np .sqrt (( diff [ brainmask ] ** 2 ). mean ()) < RMSE_TOL
104140
105141
106142@pytest .mark .parametrize ("sw_tool" , ["itk" , "afni" ])
@@ -116,7 +152,11 @@ def test_displacements_field2(tmp_path, testdata_path, sw_tool):
116152
117153 # Then apply the transform and cross-check with software
118154 cmd = APPLY_NONLINEAR_CMD [sw_tool ](
119- transform = xfm_fname , reference = img_fname , moving = img_fname
155+ transform = xfm_fname ,
156+ reference = img_fname ,
157+ moving = img_fname ,
158+ output = "resampled.nii.gz" ,
159+ extra = "" ,
120160 )
121161
122162 # skip test if command is not available on host
@@ -130,6 +170,10 @@ def test_displacements_field2(tmp_path, testdata_path, sw_tool):
130170
131171 nt_moved = xfm .apply (img_fname , order = 0 )
132172 nt_moved .to_filename ("nt_resampled.nii.gz" )
133- diff = sw_moved .get_fdata () - nt_moved .get_fdata ()
173+ sw_moved .set_data_dtype (nt_moved .get_data_dtype ())
174+ diff = (
175+ np .asanyarray (sw_moved .dataobj , dtype = sw_moved .get_data_dtype ())
176+ - np .asanyarray (nt_moved .dataobj , dtype = nt_moved .get_data_dtype ())
177+ )
134178 # A certain tolerance is necessary because of resampling at borders
135- assert ( np .abs ( diff ) > 1e-3 ). sum () / diff . size < TESTS_BORDER_TOLERANCE
179+ assert np .sqrt (( diff ** 2 ). mean ()) < RMSE_TOL
0 commit comments