PageRenderTime 51ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/nipy/io/tests/test_image_io.py

https://github.com/VirgileFritsch/nipy
Python | 262 lines | 197 code | 32 blank | 33 comment | 20 complexity | 3f3466bdaa2398c8c5b1566f65d91cad MD5 | raw file
  1. # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
  2. # vi: set ft=python sts=4 ts=4 sw=4 et:
  3. from __future__ import with_statement
  4. import numpy as np
  5. from nibabel.spatialimages import ImageFileError, HeaderDataError
  6. from nibabel import Nifti1Header
  7. from ..api import load_image, save_image, as_image
  8. from nipy.core.api import AffineTransform as AfT, Image, vox2mni
  9. from nipy.testing import (assert_true, assert_equal, assert_raises,
  10. assert_array_equal, assert_array_almost_equal,
  11. assert_almost_equal, funcfile, anatfile)
  12. from nibabel.tmpdirs import InTemporaryDirectory
  13. from nipy.testing.decorators import if_templates
  14. from nipy.utils import templates, DataError
  15. from nibabel.tests.test_round_trip import big_bad_ulp
  16. gimg = None
  17. def setup_module():
  18. global gimg
  19. try:
  20. gimg = load_template_img()
  21. except DataError:
  22. pass
  23. def load_template_img():
  24. return load_image(
  25. templates.get_filename(
  26. 'ICBM152', '2mm', 'T1.nii.gz'))
  27. def test_badfile():
  28. filename = "bad_file.foo"
  29. assert_raises(ImageFileError, load_image, filename)
  30. @if_templates
  31. def test_maxminmean_values():
  32. # loaded array values from SPM
  33. y = gimg.get_data()
  34. yield assert_equal, y.shape, tuple(gimg.shape)
  35. yield assert_array_almost_equal, y.max(), 1.000000059
  36. yield assert_array_almost_equal, y.mean(), 0.273968048
  37. yield assert_equal, y.min(), 0.0
  38. @if_templates
  39. def test_nondiag():
  40. gimg.affine[0,1] = 3.0
  41. with InTemporaryDirectory():
  42. save_image(gimg, 'img.nii')
  43. img2 = load_image('img.nii')
  44. assert_almost_equal(img2.affine, gimg.affine)
  45. def randimg_in2out(rng, in_dtype, out_dtype, name):
  46. in_dtype = np.dtype(in_dtype)
  47. out_dtype = np.dtype(out_dtype)
  48. shape = (2,3,4)
  49. if in_dtype.kind in 'iu':
  50. info = np.iinfo(in_dtype)
  51. dmin, dmax = info.min, info.max
  52. # Numpy bug for np < 1.6.0 allows overflow for range that does not fit
  53. # into C long int (int32 on 32-bit, int64 on 64-bit)
  54. try:
  55. data = rng.randint(dmin, dmax, size=shape)
  56. except ValueError:
  57. from random import randint
  58. vals = [randint(dmin, dmax) for v in range(np.prod(shape))]
  59. data = np.array(vals).astype(in_dtype).reshape(shape)
  60. elif in_dtype.kind == 'f':
  61. info = np.finfo(in_dtype)
  62. dmin, dmax = info.min, info.max
  63. # set some value for scaling our data
  64. scale = np.iinfo(np.uint16).max * 2.0
  65. data = rng.normal(size=shape, scale=scale)
  66. data[0,0,0] = dmin
  67. data[1,0,0] = dmax
  68. data = data.astype(in_dtype)
  69. img = Image(data, vox2mni(np.eye(4)))
  70. # The dtype_from dtype won't be visible until the image is loaded
  71. newimg = save_image(img, name, dtype_from=out_dtype)
  72. return newimg.get_data(), data
  73. def test_scaling_io_dtype():
  74. # Does data dtype get set?
  75. # Is scaling correctly applied?
  76. rng = np.random.RandomState(19660520) # VBD
  77. ulp1_f32 = np.finfo(np.float32).eps
  78. types = (np.uint8, np.uint16, np.int16, np.int32, np.float32)
  79. with InTemporaryDirectory():
  80. for in_type in types:
  81. for out_type in types:
  82. data, _ = randimg_in2out(rng, in_type, out_type, 'img.nii')
  83. img = load_image('img.nii')
  84. # Check the output type is as expected
  85. hdr = img.metadata['header']
  86. assert_equal(hdr.get_data_dtype().type, out_type)
  87. # Check the data is within reasonable bounds. The exact bounds
  88. # are a little annoying to calculate - see
  89. # nibabel/tests/test_round_trip for inspiration
  90. data_back = img.get_data().copy() # copy to detach from file
  91. del img
  92. top = np.abs(data - data_back)
  93. nzs = (top !=0) & (data !=0)
  94. abs_err = top[nzs]
  95. if abs_err.size != 0: # all exact, that's OK.
  96. continue
  97. rel_err = abs_err / data[nzs]
  98. if np.dtype(out_type).kind in 'iu':
  99. slope, inter = hdr.get_slope_inter()
  100. abs_err_thresh = slope / 2.0
  101. rel_err_thresh = ulp1_f32
  102. elif np.dtype(out_type).kind == 'f':
  103. abs_err_thresh = big_bad_ulp(data.astype(out_type))[nzs]
  104. rel_err_thresh = ulp1_f32
  105. assert_true(np.all(
  106. (abs_err <= abs_err_thresh) |
  107. (rel_err <= rel_err_thresh)))
  108. def assert_dt_no_end_equal(a, b):
  109. """ Assert two numpy dtype specifiers are equal apart from byte order
  110. Avoids failed comparison between int32 / int64 and intp
  111. """
  112. a = np.dtype(a).newbyteorder('=')
  113. b = np.dtype(b).newbyteorder('=')
  114. assert_equal(a.str, b.str)
  115. def test_output_dtypes():
  116. shape = (4, 2, 3)
  117. rng = np.random.RandomState(19441217) # IN-S BD
  118. data = rng.normal(4, 20, size=shape)
  119. aff = np.diag([2.2, 3.3, 4.1, 1])
  120. cmap = vox2mni(aff)
  121. img = Image(data, cmap)
  122. fname_root = 'my_file'
  123. with InTemporaryDirectory():
  124. for ext in 'img', 'nii':
  125. out_fname = fname_root + '.' + ext
  126. # Default is for data to come from data dtype
  127. save_image(img, out_fname)
  128. img_back = load_image(out_fname)
  129. hdr = img_back.metadata['header']
  130. assert_dt_no_end_equal(hdr.get_data_dtype(), np.float)
  131. del img_back # lets window re-use the file
  132. # All these types are OK for both output formats
  133. for out_dt in 'i2', 'i4', np.int16, '<f4', '>f8':
  134. # Specified output dtype
  135. save_image(img, out_fname, out_dt)
  136. img_back = load_image(out_fname)
  137. hdr = img_back.metadata['header']
  138. assert_dt_no_end_equal(hdr.get_data_dtype(), out_dt)
  139. del img_back # windows file re-use
  140. # Output comes from data by default
  141. data_typed = data.astype(out_dt)
  142. img_again = Image(data_typed, cmap)
  143. save_image(img_again, out_fname)
  144. img_back = load_image(out_fname)
  145. hdr = img_back.metadata['header']
  146. assert_dt_no_end_equal(hdr.get_data_dtype(), out_dt)
  147. del img_back
  148. # Even if header specifies otherwise
  149. in_hdr = Nifti1Header()
  150. in_hdr.set_data_dtype(np.dtype('c8'))
  151. img_more = Image(data_typed, cmap, metadata={'header': in_hdr})
  152. save_image(img_more, out_fname)
  153. img_back = load_image(out_fname)
  154. hdr = img_back.metadata['header']
  155. assert_dt_no_end_equal(hdr.get_data_dtype(), out_dt)
  156. del img_back
  157. # But can come from header if specified
  158. save_image(img_more, out_fname, dtype_from='header')
  159. img_back = load_image(out_fname)
  160. hdr = img_back.metadata['header']
  161. assert_dt_no_end_equal(hdr.get_data_dtype(), 'c8')
  162. del img_back
  163. # u2 only OK for nifti
  164. save_image(img, 'my_file.nii', 'u2')
  165. img_back = load_image('my_file.nii')
  166. hdr = img_back.metadata['header']
  167. assert_dt_no_end_equal(hdr.get_data_dtype(), 'u2')
  168. # Check analyze can't save u2 datatype
  169. assert_raises(HeaderDataError, save_image, img, 'my_file.img', 'u2')
  170. del img_back
  171. def test_header_roundtrip():
  172. img = load_image(anatfile)
  173. hdr = img.metadata['header']
  174. # Update some header values and make sure they're saved
  175. hdr['slice_duration'] = 0.200
  176. hdr['intent_p1'] = 2.0
  177. hdr['descrip'] = 'descrip for TestImage:test_header_roundtrip'
  178. hdr['slice_end'] = 12
  179. with InTemporaryDirectory():
  180. save_image(img, 'img.nii.gz')
  181. newimg = load_image('img.nii.gz')
  182. newhdr = newimg.metadata['header']
  183. assert_array_almost_equal(newhdr['slice_duration'],
  184. hdr['slice_duration'])
  185. assert_equal(newhdr['intent_p1'], hdr['intent_p1'])
  186. assert_equal(newhdr['descrip'], hdr['descrip'])
  187. assert_equal(newhdr['slice_end'], hdr['slice_end'])
  188. def test_file_roundtrip():
  189. img = load_image(anatfile)
  190. data = img.get_data()
  191. with InTemporaryDirectory():
  192. save_image(img, 'img.nii.gz')
  193. img2 = load_image('img.nii.gz')
  194. data2 = img2.get_data()
  195. # verify data
  196. assert_almost_equal(data2, data)
  197. assert_almost_equal(data2.mean(), data.mean())
  198. assert_almost_equal(data2.min(), data.min())
  199. assert_almost_equal(data2.max(), data.max())
  200. # verify shape and ndims
  201. assert_equal(img2.shape, img.shape)
  202. assert_equal(img2.ndim, img.ndim)
  203. # verify affine
  204. assert_almost_equal(img2.affine, img.affine)
  205. def test_roundtrip_from_array():
  206. data = np.random.rand(10,20,30)
  207. img = Image(data, AfT('kji', 'xyz', np.eye(4)))
  208. with InTemporaryDirectory():
  209. save_image(img, 'img.nii.gz')
  210. img2 = load_image('img.nii.gz')
  211. data2 = img2.get_data()
  212. # verify data
  213. assert_almost_equal(data2, data)
  214. assert_almost_equal(data2.mean(), data.mean())
  215. assert_almost_equal(data2.min(), data.min())
  216. assert_almost_equal(data2.max(), data.max())
  217. # verify shape and ndims
  218. assert_equal(img2.shape, img.shape)
  219. assert_equal(img2.ndim, img.ndim)
  220. # verify affine
  221. assert_almost_equal(img2.affine, img.affine)
  222. def test_as_image():
  223. # test image creation / pass through function
  224. img = as_image(funcfile) # string filename
  225. img1 = as_image(unicode(funcfile))
  226. img2 = as_image(img)
  227. assert_equal(img.affine, img1.affine)
  228. assert_array_equal(img.get_data(), img1.get_data())
  229. assert_true(img is img2)