@@ -38,43 +38,77 @@ def test_epi_mask(tmpdir, testdata_dir):
3838@pytest .mark .parametrize ("padding" , [0 , 1 , 4 ])
3939@pytest .mark .parametrize ("factor" , [1 , 4 , 0.8 ])
4040@pytest .mark .parametrize ("centered" , [True , False ])
41- @pytest .mark .parametrize ("rotate" , [
42- np .eye (4 ),
43- # Rotate 90 degrees around x-axis
44- np .array ([[0 , 1 , 0 , 0 ], [0 , 0 , 1 , 0 ], [1 , 0 , 0 , 0 ], [0 , 0 , 0 , 1 ]]),
45- ])
46- def test_deoblique_and_zooms (tmpdir , padding , factor , centered , rotate ):
41+ @pytest .mark .parametrize (
42+ "rotate" ,
43+ [
44+ np .eye (4 ),
45+ # Rotate 90 degrees around x-axis
46+ np .array ([[0 , 1 , 0 , 0 ], [0 , 0 , 1 , 0 ], [1 , 0 , 0 , 0 ], [0 , 0 , 0 , 1 ]]),
47+ # Rotate 30 degrees around x-axis
48+ nb .affines .from_matvec (
49+ nb .eulerangles .euler2mat (0 , 0 , 30 * np .pi / 180 ),
50+ (0 , 0 , 0 ),
51+ ),
52+ # Rotate 48 degrees around y-axis and translation
53+ nb .affines .from_matvec (
54+ nb .eulerangles .euler2mat (0 , 48 * np .pi / 180 , 0 ),
55+ (2.0 , 1.2 , 0.7 ),
56+ ),
57+ ],
58+ )
59+ def test_deoblique_and_zooms (tmpdir , padding , factor , centered , rotate , debug = False ):
4760 """Check deoblique and denser."""
4861 tmpdir .chdir ()
4962
5063 # Generate an example reference image
51- ref_data = np .zeros ((20 , 32 , 40 ), dtype = np .float32 )
64+ ref_shape = (80 , 128 , 160 ) if factor < 1 else (20 , 32 , 40 )
65+ ref_data = np .zeros (ref_shape , dtype = np .float32 )
5266 ref_data [1 :- 2 , 10 :- 11 , 1 :- 2 ] = 1
5367 ref_affine = np .diag ([2.0 , 1.25 , 1.0 , 1.0 ])
5468 ref_zooms = nb .affines .voxel_sizes (ref_affine )
5569 if centered :
5670 ref_affine [:3 , 3 ] -= nb .affines .apply_affine (
57- ref_affine , 0.5 * (np .array (ref_data .shape ) - 1 ),
71+ ref_affine ,
72+ 0.5 * (np .array (ref_data .shape ) - 1 ),
5873 )
5974 ref_img = nb .Nifti1Image (ref_data , ref_affine )
75+ ref_img .header .set_qform (ref_affine , 1 )
76+ ref_img .header .set_sform (ref_affine , 0 )
6077
6178 # Generate an example oblique image
62- ob_img = nb .Nifti1Image (ref_data , rotate @ ref_affine )
79+ mov_affine = rotate @ ref_affine
80+ mov_img = nb .Nifti1Image (ref_data , mov_affine )
81+ mov_img .header .set_qform (mov_affine , 1 )
82+ mov_img .header .set_sform (mov_affine , 0 )
6383
6484 # Call function with default parameters
65- out_img = deoblique_and_zooms (ref_img , ob_img , padding = padding , factor = factor )
85+ out_img = deoblique_and_zooms (ref_img , mov_img , padding = padding , factor = factor )
6686
6787 # Check output shape and zooms
6888 assert np .allclose (out_img .header .get_zooms ()[:3 ], ref_zooms / factor )
6989
90+ # Check idempotency with a lot of tolerance
7091 ref_resampled = Affine (reference = out_img ).apply (ref_img , order = 0 )
71- resampled = Affine (reference = out_img ).apply (ob_img , order = 0 )
72- ref_img .to_filename ("reference.nii.gz" )
73- ob_img .to_filename ("moving.nii.gz" )
74- ref_resampled .to_filename ("ref_resampled.nii.gz" )
75- resampled .to_filename ("resampled.nii.gz" )
76- # import pdb; pdb.set_trace()
77- ref_volume = ref_data .sum () * ref_zooms .prod ()
78- res_volume = resampled .get_fdata ().sum () * np .prod (resampled .header .get_zooms ())
79- # 20% of change in volume is too high, must be an error somewhere
80- assert abs (ref_volume - res_volume ) < ref_volume * 0.2
92+ ref_back = Affine (reference = ref_img ).apply (ref_resampled , order = 0 )
93+ resampled = Affine (reference = out_img ).apply (mov_img , order = 2 )
94+ if debug :
95+ ref_img .to_filename ("reference.nii.gz" )
96+ ref_back .to_filename ("reference_back.nii.gz" )
97+ ref_resampled .to_filename ("reference_resampled.nii.gz" )
98+ mov_img .to_filename ("moving.nii.gz" )
99+ resampled .to_filename ("resampled.nii.gz" )
100+
101+ # Allow up to 3% pixels wrong after up(down)sampling and walk back.
102+ assert (
103+ np .abs (np .clip (ref_back .get_fdata (), 0 , 1 ) - ref_data ).sum ()
104+ < ref_data .size * 0.03
105+ )
106+
107+ vox_vol_out = np .prod (out_img .header .get_zooms ())
108+ vox_vol_mov = np .prod (mov_img .header .get_zooms ())
109+ vol_factor = vox_vol_out / vox_vol_mov
110+
111+ ref_volume = ref_data .sum ()
112+ res_volume = np .clip (resampled .get_fdata (), 0 , 1 ).sum () * vol_factor
113+ # Tolerate up to 2% variation of the volume of the moving image
114+ assert abs (1 - res_volume / ref_volume ) < 0.02
0 commit comments