PageRenderTime 55ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/nibabel/tests/test_orientations.py

https://github.com/agramfort/nibabel
Python | 310 lines | 233 code | 31 blank | 46 comment | 9 complexity | e30160d282dd90244296e91907897787 MD5 | raw file
  1. # emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*-
  2. # vi: set ft=python sts=4 ts=4 sw=4 et:
  3. ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
  4. #
  5. # See COPYING file distributed along with the NiBabel package for the
  6. # copyright and license terms.
  7. #
  8. ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
  9. ''' Testing for orientations module '''
  10. import numpy as np
  11. from nose.tools import assert_true, assert_equal, assert_raises
  12. from numpy.testing import assert_array_equal, assert_array_almost_equal
  13. from ..orientations import (io_orientation, ornt_transform, inv_ornt_aff,
  14. flip_axis, apply_orientation, OrientationError,
  15. ornt2axcodes, axcodes2ornt, aff2axcodes)
  16. from ..affines import from_matvec, to_matvec
  17. IN_ARRS = [np.eye(4),
  18. [[0,0,1,0],
  19. [0,1,0,0],
  20. [1,0,0,0],
  21. [0,0,0,1]],
  22. [[0,1,0,0],
  23. [0,0,1,0],
  24. [1,0,0,0],
  25. [0,0,0,1]],
  26. [[3,1,0,0],
  27. [1,3,0,0],
  28. [0,0,1,0],
  29. [0,0,0,1]],
  30. [[1,3,0,0],
  31. [3,1,0,0],
  32. [0,0,1,0],
  33. [0,0,0,1]],
  34. ]
  35. OUT_ORNTS = [[[0,1],
  36. [1,1],
  37. [2,1]],
  38. [[2,1],
  39. [1,1],
  40. [0,1]],
  41. [[2,1],
  42. [0,1],
  43. [1,1]],
  44. [[0,1],
  45. [1,1],
  46. [2,1]],
  47. [[1,1],
  48. [0,1],
  49. [2,1]],
  50. ]
  51. IN_ARRS = IN_ARRS + [[[np.cos(np.pi/6+i*np.pi/2),np.sin(np.pi/6+i*np.pi/2),0,0],
  52. [-np.sin(np.pi/6+i*np.pi/2),np.cos(np.pi/6+i*np.pi/2),0,0],
  53. [0,0,1,0],
  54. [0,0,0,1]] for i in range(4)]
  55. OUT_ORNTS = OUT_ORNTS + [[[0,1],
  56. [1,1],
  57. [2,1]],
  58. [[1,-1],
  59. [0,1],
  60. [2,1]],
  61. [[0,-1],
  62. [1,-1],
  63. [2,1]],
  64. [[1,1],
  65. [0,-1],
  66. [2,1]]
  67. ]
  68. IN_ARRS = [np.array(arr) for arr in IN_ARRS]
  69. OUT_ORNTS = [np.array(ornt) for ornt in OUT_ORNTS]
  70. def same_transform(taff, ornt, shape):
  71. # Applying transformations implied by `ornt` to a made-up array
  72. # ``arr`` of shape `shape`, results in ``t_arr``. When the point
  73. # indices from ``arr`` are transformed by (the inverse of) `taff`,
  74. # and we index into ``t_arr`` with these transformed points, then we
  75. # should get the same values as we would from indexing into arr with
  76. # the untransformed points.
  77. shape = np.array(shape)
  78. size = np.prod(shape)
  79. arr = np.arange(size).reshape(shape)
  80. # apply ornt transformations
  81. t_arr = apply_orientation(arr, ornt)
  82. # get all point indices in arr
  83. i,j,k = shape
  84. arr_pts = np.mgrid[:i,:j,:k].reshape((3,-1))
  85. # inverse of taff takes us from point index in arr to point index in
  86. # t_arr
  87. itaff = np.linalg.inv(taff)
  88. # apply itaff so that points indexed in t_arr should correspond
  89. o2t_pts = np.dot(itaff[:3,:3], arr_pts) + itaff[:3,3][:,None]
  90. assert np.allclose(np.round(o2t_pts), o2t_pts)
  91. # fancy index out the t_arr values
  92. vals = t_arr[list(o2t_pts.astype('i'))]
  93. return np.all(vals == arr.ravel())
  94. def test_apply():
  95. # most tests are in ``same_transform`` above, via the
  96. # test_io_orientations
  97. a = np.arange(24).reshape((2,3,4))
  98. # Test 4D with an example orientation
  99. ornt = OUT_ORNTS[-1]
  100. t_arr = apply_orientation(a[:,:,:,None], ornt)
  101. assert_equal(t_arr.ndim, 4)
  102. # Orientation errors
  103. assert_raises(OrientationError,
  104. apply_orientation,
  105. a[:,:,1], ornt)
  106. assert_raises(OrientationError,
  107. apply_orientation,
  108. a,
  109. [[0,1],[np.nan,np.nan],[2,1]])
  110. def test_flip_axis():
  111. a = np.arange(24).reshape((2,3,4))
  112. assert_array_equal(
  113. flip_axis(a),
  114. np.flipud(a))
  115. assert_array_equal(
  116. flip_axis(a, axis=0),
  117. np.flipud(a))
  118. assert_array_equal(
  119. flip_axis(a, axis=1),
  120. np.fliplr(a))
  121. # check accepts array-like
  122. assert_array_equal(
  123. flip_axis(a.tolist(), axis=0),
  124. np.flipud(a))
  125. # third dimension
  126. b = a.transpose()
  127. b = np.flipud(b)
  128. b = b.transpose()
  129. assert_array_equal(flip_axis(a, axis=2), b)
  130. def test_io_orientation():
  131. for shape in ((2,3,4), (20, 15, 7)):
  132. for in_arr, out_ornt in zip(IN_ARRS, OUT_ORNTS):
  133. ornt = io_orientation(in_arr)
  134. assert_array_equal(ornt, out_ornt)
  135. taff = inv_ornt_aff(ornt, shape)
  136. assert_true(same_transform(taff, ornt, shape))
  137. for axno in range(3):
  138. arr = in_arr.copy()
  139. ex_ornt = out_ornt.copy()
  140. # flip the input axis in affine
  141. arr[:,axno] *= -1
  142. # check that result shows flip
  143. ex_ornt[axno, 1] *= -1
  144. ornt = io_orientation(arr)
  145. assert_array_equal(ornt, ex_ornt)
  146. taff = inv_ornt_aff(ornt, shape)
  147. assert_true(same_transform(taff, ornt, shape))
  148. # Test nasty hang for zero columns
  149. rzs = np.c_[np.diag([2, 3, 4, 5]), np.zeros((4,3))]
  150. arr = from_matvec(rzs, [15,16,17,18])
  151. ornt = io_orientation(arr)
  152. assert_array_equal(ornt, [[0, 1],
  153. [1, 1],
  154. [2, 1],
  155. [3, 1],
  156. [np.nan, np.nan],
  157. [np.nan, np.nan],
  158. [np.nan, np.nan]])
  159. # Test behavior of thresholding
  160. def_aff = np.array([[1., 1, 0, 0],
  161. [0, 0, 0, 0],
  162. [0, 0, 1, 0],
  163. [0, 0, 0, 1]])
  164. fail_tol = np.array([[0, 1],
  165. [np.nan, np.nan],
  166. [2, 1]])
  167. pass_tol = np.array([[0, 1],
  168. [1, 1],
  169. [2, 1]])
  170. eps = np.finfo(float).eps
  171. # Test that a Y axis appears as we increase the difference between the first
  172. # two columns
  173. for y_val, has_y in ((0, False),
  174. (eps, False),
  175. (eps * 5, False),
  176. (eps * 10, True),
  177. ):
  178. def_aff[1, 1] = y_val
  179. res = pass_tol if has_y else fail_tol
  180. assert_array_equal(io_orientation(def_aff), res)
  181. # Test tol input argument
  182. def_aff[1, 1] = eps
  183. assert_array_equal(io_orientation(def_aff, tol=0), pass_tol)
  184. def_aff[1, 1] = eps * 10
  185. assert_array_equal(io_orientation(def_aff, tol=1e-5), fail_tol)
  186. def test_ornt_transform():
  187. assert_array_equal(ornt_transform([[0,1], [1,1], [2,-1]],
  188. [[1,1], [0,1], [2,1]]),
  189. [[1,1], [0,1], [2,-1]]
  190. )
  191. assert_array_equal(ornt_transform([[0,1], [1,1], [2,1]],
  192. [[2,1], [0,-1], [1,1]]),
  193. [[1,-1], [2,1], [0,1]]
  194. )
  195. #Must have same shape
  196. assert_raises(ValueError,
  197. ornt_transform,
  198. [[0,1], [1,1]],
  199. [[0,1], [1,1], [2, 1]])
  200. #Must be (N,2) in shape
  201. assert_raises(ValueError,
  202. ornt_transform,
  203. [[0,1,1], [1,1,1]],
  204. [[0,1,1], [1,1,1]])
  205. def test_ornt2axcodes():
  206. # Recoding orientation to axis codes
  207. labels = (('left', 'right'),('back', 'front'), ('down', 'up'))
  208. assert_equal(ornt2axcodes([[0,1],
  209. [1,1],
  210. [2,1]], labels), ('right', 'front', 'up'))
  211. assert_equal(ornt2axcodes([[0,-1],
  212. [1,-1],
  213. [2,-1]], labels), ('left', 'back', 'down'))
  214. assert_equal(ornt2axcodes([[2,-1],
  215. [1,-1],
  216. [0,-1]], labels), ('down', 'back', 'left'))
  217. assert_equal(ornt2axcodes([[1,1],
  218. [2,-1],
  219. [0,1]], labels), ('front', 'down', 'right'))
  220. # default is RAS output directions
  221. assert_equal(ornt2axcodes([[0,1],
  222. [1,1],
  223. [2,1]]), ('R', 'A', 'S'))
  224. # dropped axes produce None
  225. assert_equal(ornt2axcodes([[0,1],
  226. [np.nan,np.nan],
  227. [2,1]]), ('R', None, 'S'))
  228. # Non integer axes raises error
  229. assert_raises(ValueError, ornt2axcodes, [[0.1,1]])
  230. # As do directions not in range
  231. assert_raises(ValueError, ornt2axcodes, [[0,0]])
  232. def test_axcodes2ornt():
  233. # Go from axcodes back to orientations
  234. labels = (('left', 'right'),('back', 'front'), ('down', 'up'))
  235. assert_array_equal(axcodes2ornt(('right', 'front', 'up'), labels),
  236. [[0,1],
  237. [1,1],
  238. [2,1]]
  239. )
  240. assert_array_equal(axcodes2ornt(('left', 'back', 'down'), labels),
  241. [[0,-1],
  242. [1,-1],
  243. [2,-1]]
  244. )
  245. assert_array_equal(axcodes2ornt(('down', 'back', 'left'), labels),
  246. [[2,-1],
  247. [1,-1],
  248. [0,-1]]
  249. )
  250. assert_array_equal(axcodes2ornt(('front', 'down', 'right'), labels),
  251. [[1,1],
  252. [2,-1],
  253. [0, 1]]
  254. )
  255. # default is RAS output directions
  256. assert_array_equal(axcodes2ornt(('R', 'A', 'S')),
  257. [[0,1],
  258. [1,1],
  259. [2,1]]
  260. )
  261. #dropped axes produce None
  262. assert_array_equal(axcodes2ornt(('R', None, 'S')),
  263. [[0,1],
  264. [np.nan,np.nan],
  265. [2,1]]
  266. )
  267. def test_aff2axcodes():
  268. labels = (('left', 'right'),('back', 'front'), ('down', 'up'))
  269. assert_equal(aff2axcodes(np.eye(4)), tuple('RAS'))
  270. aff = [[0,1,0,10],[-1,0,0,20],[0,0,1,30],[0,0,0,1]]
  271. assert_equal(aff2axcodes(aff, (('L','R'),('B','F'),('D','U'))),
  272. ('B', 'R', 'U'))
  273. assert_equal(aff2axcodes(aff, (('L','R'),('B','F'),('D','U'))),
  274. ('B', 'R', 'U'))
  275. def test_inv_ornt_aff():
  276. # Extra tests for inv_ornt_aff routines (also tested in
  277. # io_orientations test)
  278. assert_raises(OrientationError, inv_ornt_aff,
  279. [[0, 1], [1, -1], [np.nan, np.nan]], (3, 4, 5))