Skip to content

Commit 596994c

Browse files
committed
enh: add tests for affines stored in AFNI format (non-oblique images)
1 parent fe74efb commit 596994c

File tree

6 files changed

+25
-12
lines changed

6 files changed

+25
-12
lines changed

nibabel/tests/data/affine-LAS.afni

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
affine-RAS.afni

nibabel/tests/data/affine-LPS.afni

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
affine-RAS.afni

nibabel/tests/data/affine-RAS.afni

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# 3dvolreg matrices (DICOM-to-DICOM, row-by-row):
2+
0.999999 -0.000999999 -0.001 -4 0.00140494 0.621609 0.783327 -2 -0.000161717 -0.783327 0.62161 -1

nibabel/tests/test_transform.py

+4
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,19 @@ def test_affines_save(image_orientation):
5757
itk = nbl.load(os.path.join(data_path, 'affine-%s-itk.tfm' % image_orientation),
5858
fmt='itk')
5959
fsl = np.loadtxt(os.path.join(data_path, 'affine-%s.fsl' % image_orientation))
60+
afni = np.loadtxt(os.path.join(data_path, 'affine-%s.afni' % image_orientation))
6061

6162
with InTemporaryDirectory():
6263
xfm.to_filename('M.tfm', fmt='itk')
6364
xfm.to_filename('M.fsl', fmt='fsl')
65+
xfm.to_filename('M.afni', fmt='afni')
6466

6567
nb_itk = nbl.load('M.tfm', fmt='itk')
6668
nb_fsl = np.loadtxt('M.fsl')
69+
nb_afni = np.loadtxt('M.afni')
6770

6871
assert_equal(itk, nb_itk)
6972
assert_almost_equal(fsl, nb_fsl)
73+
assert_almost_equal(afni, nb_afni)
7074

7175
# Create version not aligned to canonical

nibabel/transform/linear.py

+10-11
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ def _to_hdf5(self, x5_root):
204204

205205
def to_filename(self, filename, fmt='X5', moving=None):
206206
"""Store the transform in BIDS-Transforms HDF5 file format (.x5)."""
207-
if fmt.lower() in ['itk', 'ants', 'elastix', 'nifty']:
207+
if fmt.lower() in ['itk', 'ants', 'elastix']:
208208
with open(filename, 'w') as f:
209209
f.write('#Insight Transform File V1.0\n')
210210

@@ -229,26 +229,25 @@ def to_filename(self, filename, fmt='X5', moving=None):
229229
T = self.matrix.copy()
230230
pre = LPS
231231
post = LPS
232-
if any(obliquity(self.reference.affine) * 180 / pi > OBLIQUITY_THRESHOLD_DEG):
232+
if obliquity(self.reference.affine).min() * 180 / pi > OBLIQUITY_THRESHOLD_DEG:
233233
print('Reference affine axes are oblique.')
234234
M = self.reference.affine
235235
A = shape_zoom_affine(self.reference.shape,
236-
voxel_sizes(M), x_flip=True)
237-
pre = M.dot(np.linalg.inv(A))
236+
voxel_sizes(M), x_flip=False, y_flip=False)
237+
pre = M.dot(np.linalg.inv(A)).dot(LPS)
238238

239239
if not moving:
240240
moving = self.reference
241241

242-
if moving and any(obliquity(moving.affine) * 180 / pi > OBLIQUITY_THRESHOLD_DEG):
242+
if moving and obliquity(moving.affine).min() * 180 / pi > OBLIQUITY_THRESHOLD_DEG:
243243
print('Moving affine axes are oblique.')
244-
M = moving.affine
245-
A = shape_zoom_affine(moving.shape,
246-
voxel_sizes(M), x_flip=True)
247-
post = M.dot(np.linalg.inv(A))
244+
M2 = moving.affine
245+
A2 = shape_zoom_affine(moving.shape,
246+
voxel_sizes(M2), x_flip=True, y_flip=True)
247+
post = A2.dot(np.linalg.inv(M2))
248248

249249
# swapaxes is necessary, as axis 0 encodes series of transforms
250-
T = np.swapaxes(post.dot(self.matrix.copy().dot(pre)), 0, 1)
251-
parameters = np.swapaxes(post.dot(T.dot(pre)), 0, 1)
250+
parameters = np.swapaxes(post.dot(self.matrix.copy().dot(pre)), 0, 1)
252251
parameters = parameters[:, :3, :].reshape((T.shape[0], -1))
253252
np.savetxt(filename, parameters, delimiter='\t', header="""\
254253
3dvolreg matrices (DICOM-to-DICOM, row-by-row):""", fmt='%g')

nibabel/volumeutils.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -1456,7 +1456,7 @@ def finite_range(arr, check_nan=False):
14561456
return np.nanmin(mins), np.nanmax(maxes)
14571457

14581458

1459-
def shape_zoom_affine(shape, zooms, x_flip=True):
1459+
def shape_zoom_affine(shape, zooms, x_flip=True, y_flip=False):
14601460
''' Get affine implied by given shape and zooms
14611461
14621462
We get the translations from the center of the image (implied by
@@ -1471,6 +1471,9 @@ def shape_zoom_affine(shape, zooms, x_flip=True):
14711471
x_flip : {True, False}
14721472
whether to flip the X row of the affine. Corresponds to
14731473
radiological storage on disk.
1474+
y_flip : {False, True}
1475+
whether to flip the Y row of the affine. Corresponds to
1476+
DICOM storage on disk when x_flip is also True.
14741477
14751478
Returns
14761479
-------
@@ -1510,6 +1513,9 @@ def shape_zoom_affine(shape, zooms, x_flip=True):
15101513
zooms = full_zooms
15111514
if x_flip:
15121515
zooms[0] *= -1
1516+
1517+
if y_flip:
1518+
zooms[1] *= -1
15131519
# Get translations from center of image
15141520
origin = (shape - 1) / 2.0
15151521
aff = np.eye(4)

0 commit comments

Comments
 (0)