Skip to content

Commit 9f0bb9d

Browse files
authoredOct 15, 2019
Merge pull request #815 from oesteban/enh/obliquity-tool
MRG: Add a utility to calculate obliquity of affines This implementation mimics the implementation of AFNI, and will be useful in #656 .
2 parents de44a10 + 891d85c commit 9f0bb9d

File tree

2 files changed

+41
-2
lines changed

2 files changed

+41
-2
lines changed
 

‎nibabel/affines.py

+28-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
# vi: set ft=python sts=4 ts=4 sw=4 et:
33
""" Utility routines for working with points and affine transforms
44
"""
5-
65
import numpy as np
76

87
from functools import reduce
@@ -296,3 +295,31 @@ def voxel_sizes(affine):
296295
"""
297296
top_left = affine[:-1, :-1]
298297
return np.sqrt(np.sum(top_left ** 2, axis=0))
298+
299+
300+
def obliquity(affine):
301+
r"""
302+
Estimate the *obliquity* an affine's axes represent.
303+
304+
The term *obliquity* is defined here as the rotation of those axes with
305+
respect to the cardinal axes.
306+
This implementation is inspired by `AFNI's implementation
307+
<https://github.com/afni/afni/blob/b6a9f7a21c1f3231ff09efbd861f8975ad48e525/src/thd_coords.c#L660-L698>`_.
308+
For further details about *obliquity*, check `AFNI's documentation
309+
<https://sscc.nimh.nih.gov/sscc/dglen/Obliquity>_.
310+
311+
Parameters
312+
----------
313+
affine : 2D array-like
314+
Affine transformation array. Usually shape (4, 4), but can be any 2D
315+
array.
316+
317+
Returns
318+
-------
319+
angles : 1D array-like
320+
The *obliquity* of each axis with respect to the cardinal axes, in radians.
321+
322+
"""
323+
vs = voxel_sizes(affine)
324+
best_cosines = np.abs(affine[:-1, :-1] / vs).max(axis=1)
325+
return np.arccos(best_cosines)

‎nibabel/tests/test_affines.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from ..eulerangles import euler2mat
99
from ..affines import (AffineError, apply_affine, append_diag, to_matvec,
10-
from_matvec, dot_reduce, voxel_sizes)
10+
from_matvec, dot_reduce, voxel_sizes, obliquity)
1111

1212

1313
from nose.tools import assert_equal, assert_raises
@@ -178,3 +178,15 @@ def test_voxel_sizes():
178178
rot_affine[:3, :3] = rotation
179179
full_aff = rot_affine.dot(aff)
180180
assert_almost_equal(voxel_sizes(full_aff), vox_sizes)
181+
182+
183+
def test_obliquity():
184+
"""Check the calculation of inclination of an affine axes."""
185+
from math import pi
186+
aligned = np.diag([2.0, 2.0, 2.3, 1.0])
187+
aligned[:-1, -1] = [-10, -10, -7]
188+
R = from_matvec(euler2mat(x=0.09, y=0.001, z=0.001), [0.0, 0.0, 0.0])
189+
oblique = R.dot(aligned)
190+
assert_almost_equal(obliquity(aligned), [0.0, 0.0, 0.0])
191+
assert_almost_equal(obliquity(oblique) * 180 / pi,
192+
[0.0810285, 5.1569949, 5.1569376])

0 commit comments

Comments
 (0)
Please sign in to comment.