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 spatialimages
10
11"""
12from ..py3k import BytesIO
13
14import numpy as np
15
16from ..spatialimages import (Header, SpatialImage, HeaderDataError,
17                             ImageDataError)
18
19from unittest import TestCase
20
21from nose.tools import (assert_true, assert_false, assert_equal,
22                        assert_not_equal, assert_raises)
23
24from numpy.testing import assert_array_equal, assert_array_almost_equal
25
26
27def test_header_init():
28    # test the basic header
29    hdr = Header()
30    assert_equal(hdr.get_data_dtype(), np.dtype(np.float32))
31    assert_equal(hdr.get_data_shape(), (0,))
32    assert_equal(hdr.get_zooms(), (1.0,))
33    hdr = Header(np.float64)
34    assert_equal(hdr.get_data_dtype(), np.dtype(np.float64))
35    assert_equal(hdr.get_data_shape(), (0,))
36    assert_equal(hdr.get_zooms(), (1.0,))
37    hdr = Header(np.float64, shape=(1,2,3))
38    assert_equal(hdr.get_data_dtype(), np.dtype(np.float64))
39    assert_equal(hdr.get_data_shape(), (1,2,3))
40    assert_equal(hdr.get_zooms(), (1.0, 1.0, 1.0))
41    hdr = Header(np.float64, shape=(1,2,3), zooms=None)
42    assert_equal(hdr.get_data_dtype(), np.dtype(np.float64))
43    assert_equal(hdr.get_data_shape(), (1,2,3))
44    assert_equal(hdr.get_zooms(), (1.0, 1.0, 1.0))
45    hdr = Header(np.float64, shape=(1,2,3), zooms=(3.0, 2.0, 1.0))
46    assert_equal(hdr.get_data_dtype(), np.dtype(np.float64))
47    assert_equal(hdr.get_data_shape(), (1,2,3))
48    assert_equal(hdr.get_zooms(), (3.0, 2.0, 1.0))
49
50
51def test_from_header():
52    # check from header class method.  Note equality checks below,
53    # equality methods used here too.
54    empty = Header.from_header()
55    assert_equal(Header(), empty)
56    empty = Header.from_header(None)
57    assert_equal(Header(), empty)
58    hdr = Header(np.float64, shape=(1,2,3), zooms=(3.0, 2.0, 1.0))
59    copy = Header.from_header(hdr)
60    assert_equal(hdr, copy)
61    assert_false(hdr is copy)
62    class C(object):
63        def get_data_dtype(self): return np.dtype('u2')
64        def get_data_shape(self): return (5,4,3)
65        def get_zooms(self): return (10.0, 9.0, 8.0)
66    converted = Header.from_header(C())
67    assert_true(isinstance(converted, Header))
68    assert_equal(converted.get_data_dtype(), np.dtype('u2'))
69    assert_equal(converted.get_data_shape(), (5,4,3))
70    assert_equal(converted.get_zooms(), (10.0,9.0,8.0))
71
72
73def test_eq():
74    hdr = Header()
75    other = Header()
76    assert_equal(hdr, other)
77    other = Header('u2')
78    assert_not_equal(hdr, other)
79    other = Header(shape=(1,2,3))
80    assert_not_equal(hdr, other)
81    hdr = Header(shape=(1,2))
82    other = Header(shape=(1,2))
83    assert_equal(hdr, other)
84    other = Header(shape=(1,2), zooms=(2.0,3.0))
85    assert_not_equal(hdr, other)
86
87
88def test_copy():
89    # test that copy makes independent copy
90    hdr = Header(np.float64, shape=(1,2,3), zooms=(3.0, 2.0, 1.0))
91    hdr_copy = hdr.copy()
92    hdr.set_data_shape((4,5,6))
93    assert_equal(hdr.get_data_shape(), (4,5,6))
94    assert_equal(hdr_copy.get_data_shape(), (1,2,3))
95    hdr.set_zooms((4,5,6))
96    assert_equal(hdr.get_zooms(), (4,5,6))
97    assert_equal(hdr_copy.get_zooms(), (3,2,1))
98    hdr.set_data_dtype(np.uint8)
99    assert_equal(hdr.get_data_dtype(), np.dtype(np.uint8))
100    assert_equal(hdr_copy.get_data_dtype(), np.dtype(np.float64))
101
102
103def test_shape_zooms():
104    hdr = Header()
105    hdr.set_data_shape((1, 2, 3))
106    assert_equal(hdr.get_data_shape(), (1,2,3))
107    assert_equal(hdr.get_zooms(), (1.0,1.0,1.0))
108    hdr.set_zooms((4, 3, 2))
109    assert_equal(hdr.get_zooms(), (4.0,3.0,2.0))
110    hdr.set_data_shape((1, 2))
111    assert_equal(hdr.get_data_shape(), (1,2))
112    assert_equal(hdr.get_zooms(), (4.0,3.0))
113    hdr.set_data_shape((1, 2, 3))
114    assert_equal(hdr.get_data_shape(), (1,2,3))
115    assert_equal(hdr.get_zooms(), (4.0,3.0,1.0))
116    # null shape is (0,)
117    hdr.set_data_shape(())
118    assert_equal(hdr.get_data_shape(), (0,))
119    assert_equal(hdr.get_zooms(), (1.0,))
120    # zooms of wrong lengths raise error
121    assert_raises(HeaderDataError, hdr.set_zooms, (4.0, 3.0))
122    assert_raises(HeaderDataError,
123                        hdr.set_zooms,
124                        (4.0, 3.0, 2.0, 1.0))
125    # as do negative zooms
126    assert_raises(HeaderDataError,
127                        hdr.set_zooms,
128                        (4.0, 3.0, -2.0))
129
130
131def test_data_dtype():
132    hdr = Header()
133    assert_equal(hdr.get_data_dtype(), np.dtype(np.float32))
134    hdr.set_data_dtype(np.float64)
135    assert_equal(hdr.get_data_dtype(), np.dtype(np.float64))
136    hdr.set_data_dtype('u2')
137    assert_equal(hdr.get_data_dtype(), np.dtype(np.uint16))
138
139
140def test_affine():
141    hdr = Header(np.float64, shape=(1,2,3), zooms=(3.0, 2.0, 1.0))
142    assert_array_almost_equal(hdr.get_default_affine(),
143                                    [[-3.0,0,0,0],
144                                     [0,2,0,-1],
145                                     [0,0,1,-1],
146                                     [0,0,0,1]])
147    hdr.default_x_flip = False
148    assert_array_almost_equal(hdr.get_default_affine(),
149                                    [[3.0,0,0,0],
150                                     [0,2,0,-1],
151                                     [0,0,1,-1],
152                                     [0,0,0,1]])
153    assert_array_equal(hdr.get_base_affine(),
154                             hdr.get_default_affine())
155
156
157def test_read_data():
158    hdr = Header(np.int32, shape=(1,2,3), zooms=(3.0, 2.0, 1.0))
159    fobj = BytesIO()
160    data = np.arange(6).reshape((1,2,3))
161    hdr.data_to_fileobj(data, fobj)
162    assert_equal(fobj.getvalue(),
163                       data.astype(np.int32).tostring(order='F'))
164    fobj.seek(0)
165    data2 = hdr.data_from_fileobj(fobj)
166    assert_array_equal(data, data2)
167
168
169class DataLike(object):
170    # Minimal class implementing 'data' API
171    shape = (3,)
172    def __array__(self):
173        return np.arange(3)
174
175
176class TestSpatialImage(TestCase):
177    # class for testing images
178    image_class = SpatialImage
179
180    def test_isolation(self):
181        # Test image isolated from external changes to header and affine
182        img_klass = self.image_class
183        arr = np.arange(3, dtype=np.int16)
184        aff = np.eye(4)
185        img = img_klass(arr, aff)
186        assert_array_equal(img.get_affine(), aff)
187        aff[0,0] = 99
188        assert_false(np.all(img.get_affine() == aff))
189        # header, created by image creation
190        ihdr = img.get_header()
191        # Pass it back in
192        img = img_klass(arr, aff, ihdr)
193        # Check modifying header outside does not modify image
194        ihdr.set_zooms((4,))
195        assert_not_equal(img.get_header(), ihdr)
196
197    def test_float_affine(self):
198        # Check affines get converted to float
199        img_klass = self.image_class
200        arr = np.arange(3, dtype=np.int16)
201        img = img_klass(arr, np.eye(4, dtype=np.float32))
202        assert_equal(img.get_affine().dtype, np.dtype(np.float64))
203        img = img_klass(arr, np.eye(4, dtype=np.int16))
204        assert_equal(img.get_affine().dtype, np.dtype(np.float64))
205
206    def test_images(self):
207        # Assumes all possible images support int16
208        # See https://github.com/nipy/nibabel/issues/58
209        arr = np.arange(3, dtype=np.int16)
210        img = self.image_class(arr, None)
211        assert_array_equal(img.get_data(), arr)
212        assert_equal(img.get_affine(), None)
213        hdr = self.image_class.header_class()
214        hdr.set_data_shape(arr.shape)
215        hdr.set_data_dtype(arr.dtype)
216        assert_equal(img.get_header(), hdr)
217
218    def test_data_api(self):
219        # Test minimal api data object can initialize
220        img = self.image_class(DataLike(), None)
221        assert_array_equal(img.get_data(), np.arange(3))
222        assert_equal(img.shape, (3,))
223
224    def test_data_default(self):
225        # check that the default dtype comes from the data if the header
226        # is None, and that unsupported dtypes raise an error
227        img_klass = self.image_class
228        hdr_klass = self.image_class.header_class
229        data = np.arange(24, dtype=np.int32).reshape((2,3,4))
230        affine = np.eye(4)
231        img = img_klass(data, affine)
232        assert_equal(data.dtype, img.get_data_dtype())
233        header = hdr_klass()
234        img = img_klass(data, affine, header)
235        assert_equal(img.get_data_dtype(), np.dtype(np.float32))
236
237    def test_data_shape(self):
238        # Check shape correctly read
239        img_klass = self.image_class
240        # Assumes all possible images support int16
241        # See https://github.com/nipy/nibabel/issues/58
242        arr = np.arange(4, dtype=np.int16)
243        img = img_klass(arr, np.eye(4))
244        assert_equal(img.shape, (4,))
245        img = img_klass(np.zeros((2,3,4)), np.eye(4))
246        assert_equal(img.shape, (2,3,4))
247
248    def test_str(self):
249        # Check something comes back from string representation
250        img_klass = self.image_class
251        # Assumes all possible images support int16
252        # See https://github.com/nipy/nibabel/issues/58
253        arr = np.arange(5, dtype=np.int16)
254        img = img_klass(arr, np.eye(4))
255        assert_true(len(str(img)) > 0)
256        assert_equal(img.shape, (5,))
257        img = img_klass(np.zeros((2,3,4), dtype=np.int16), np.eye(4))
258        assert_true(len(str(img)) > 0)
259
260    def test_get_shape(self):
261        # Check there is a get_shape method
262        # (it is deprecated)
263        img_klass = self.image_class
264        # Assumes all possible images support int16
265        # See https://github.com/nipy/nibabel/issues/58
266        img = img_klass(np.arange(1, dtype=np.int16), np.eye(4))
267        assert_equal(img.get_shape(), (1,))
268        img = img_klass(np.zeros((2,3,4), np.int16), np.eye(4))
269        assert_equal(img.get_shape(), (2,3,4))
270