Passed
Push — main ( bf7ac6...cabfda )
by Fernando
01:25
created

tests.data.test_image.TestImage.test_verify_path()   A

Complexity

Conditions 2

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 11
nop 1
dl 0
loc 16
rs 9.85
c 0
b 0
f 0
1
#!/usr/bin/env python
2
"""Tests for Image."""
3
4
import copy
5
import sys
6
import tempfile
7
from pathlib import Path
8
9
import nibabel as nib
10
import numpy as np
11
import pytest
12
import torch
13
14
import torchio as tio
15
16
from ..utils import TorchioTestCase
17
18
19
class TestImage(TorchioTestCase):
20
    """Tests for `Image`."""
21
22
    def test_image_not_found(self):
23
        with pytest.raises(FileNotFoundError):
24
            tio.ScalarImage('nopath')
25
26
    @pytest.mark.skipif(sys.platform == 'win32', reason='Path not valid')
27
    def test_wrong_path_value(self):
28
        with pytest.raises(RuntimeError):
29
            tio.ScalarImage('~&./@#"!?X7=+')
30
31
    def test_wrong_path_type(self):
32
        with pytest.raises(TypeError):
33
            tio.ScalarImage(5)
34
35
    def test_wrong_affine(self):
36
        with pytest.raises(TypeError):
37
            tio.ScalarImage(5, affine=1)
38
39
    def test_tensor_flip(self):
40
        sample_input = torch.ones((4, 30, 30, 30))
41
        tio.RandomFlip()(sample_input)
42
43
    def test_tensor_affine(self):
44
        sample_input = torch.ones((4, 10, 10, 10))
45
        tio.RandomAffine()(sample_input)
46
47
    def test_wrong_scalar_image_type(self):
48
        data = torch.ones((1, 10, 10, 10))
49
        with pytest.raises(ValueError):
50
            tio.ScalarImage(tensor=data, type=tio.LABEL)
51
52
    def test_wrong_label_map_type(self):
53
        data = torch.ones((1, 10, 10, 10))
54
        with pytest.raises(ValueError):
55
            tio.LabelMap(tensor=data, type=tio.INTENSITY)
56
57
    def test_no_input(self):
58
        with pytest.raises(ValueError):
59
            tio.ScalarImage()
60
61
    def test_bad_key(self):
62
        with pytest.raises(ValueError):
63
            tio.ScalarImage(path='', data=5)
64
65
    def test_repr(self):
66
        subject = tio.Subject(
67
            t1=tio.ScalarImage(self.get_image_path('repr_test')),
68
        )
69
        assert 'memory' not in repr(subject['t1'])
70
        subject.load()
71
        assert 'memory' in repr(subject['t1'])
72
73
    def test_data_tensor(self):
74
        subject = copy.deepcopy(self.sample_subject)
75
        subject.load()
76
        assert subject.t1.data is subject.t1.tensor
77
78
    def test_bad_affine(self):
79
        with pytest.raises(ValueError):
80
            tio.ScalarImage(tensor=torch.rand(1, 2, 3, 4), affine=np.eye(3))
81
82
    def test_nans_tensor(self):
83
        tensor = np.random.rand(1, 2, 3, 4)
84
        tensor[0, 0, 0, 0] = np.nan
85
        with pytest.warns(RuntimeWarning):
86
            image = tio.ScalarImage(tensor=tensor, check_nans=True)
87
        image.set_check_nans(False)
88
89
    def test_get_center(self):
90
        tensor = torch.rand(1, 3, 3, 3)
91
        image = tio.ScalarImage(tensor=tensor)
92
        ras = image.get_center()
93
        lps = image.get_center(lps=True)
94
        assert ras == (1, 1, 1)
95
        assert lps == (-1, -1, 1)
96
97
    def test_with_list_of_missing_files(self):
98
        with pytest.raises(FileNotFoundError):
99
            tio.ScalarImage(path=['nopath', 'error'])
100
101
    def test_with_sequences_of_paths(self):
102
        shape = (5, 5, 5)
103
        path1 = self.get_image_path('path1', shape=shape)
104
        path2 = self.get_image_path('path2', shape=shape)
105
        paths_tuple = path1, path2
106
        paths_list = list(paths_tuple)
107
        for sequence in (paths_tuple, paths_list):
108
            image = tio.ScalarImage(path=sequence)
109
            assert image.shape == (2, 5, 5, 5)
110
            assert image[tio.STEM] == ['path1', 'path2']
111
112
    def test_with_a_list_of_images_with_different_shapes(self):
113
        path1 = self.get_image_path('path1', shape=(5, 5, 5))
114
        path2 = self.get_image_path('path2', shape=(7, 5, 5))
115
        image = tio.ScalarImage(path=[path1, path2])
116
        with pytest.raises(RuntimeError):
117
            image.load()
118
119
    def test_with_a_list_of_images_with_different_affines(self):
120
        path1 = self.get_image_path('path1', spacing=(1, 1, 1))
121
        path2 = self.get_image_path('path2', spacing=(1, 2, 1))
122
        image = tio.ScalarImage(path=[path1, path2])
123
        with pytest.warns(RuntimeWarning):
124
            image.load()
125
126
    def test_with_a_list_of_2d_paths(self):
127
        shape = (5, 6)
128
        path1 = self.get_image_path('path1', shape=shape, suffix='.nii')
129
        path2 = self.get_image_path('path2', shape=shape, suffix='.img')
130
        path3 = self.get_image_path('path3', shape=shape, suffix='.hdr')
131
        image = tio.ScalarImage(path=[path1, path2, path3])
132
        assert image.shape == (3, 5, 6, 1)
133
        assert image[tio.STEM] == ['path1', 'path2', 'path3']
134
135
    def test_axis_name_2d(self):
136
        path = self.get_image_path('im2d', shape=(5, 6))
137
        image = tio.ScalarImage(path)
138
        height_idx = image.axis_name_to_index('t')
139
        width_idx = image.axis_name_to_index('l')
140
        assert image.height == image.shape[height_idx]
141
        assert image.width == image.shape[width_idx]
142
143
    @pytest.mark.slow
144
    @pytest.mark.skipif(sys.platform == 'win32', reason='Unstable on Windows')
145
    def test_plot(self):
146
        image = self.sample_subject.t1
147
        image.plot(show=False, output_path=self.dir / 'image.png')
148
149
    def test_data_type_uint16_array(self):
150
        tensor = np.random.rand(1, 3, 3, 3).astype(np.uint16)
151
        image = tio.ScalarImage(tensor=tensor)
152
        assert image.data.dtype == torch.int32
153
154
    def test_data_type_uint32_array(self):
155
        tensor = np.random.rand(1, 3, 3, 3).astype(np.uint32)
156
        image = tio.ScalarImage(tensor=tensor)
157
        assert image.data.dtype == torch.int64
158
159
    def test_save_image_with_data_type_boolean(self):
160
        tensor = np.random.rand(1, 3, 3, 3).astype(bool)
161
        image = tio.ScalarImage(tensor=tensor)
162
        image.save(self.dir / 'image.nii')
163
164
    def test_load_uint(self):
165
        affine = np.eye(4)
166
        for dtype in np.uint16, np.uint32:
167
            data = np.ones((3, 3, 3), dtype=dtype)
168
            img = nib.Nifti1Image(data, affine)
169
            with tempfile.NamedTemporaryFile(suffix='.nii', delete=False) as f:
170
                nib.save(img, f.name)
171
                tio.ScalarImage(f.name).load()
172
173
    def test_pil_3d(self):
174
        with pytest.raises(RuntimeError):
175
            tio.ScalarImage(tensor=torch.rand(1, 2, 3, 4)).as_pil()
176
177
    def test_pil_1(self):
178
        tio.ScalarImage(tensor=torch.rand(1, 2, 3, 1)).as_pil()
179
180
    def test_pil_2(self):
181
        with pytest.raises(RuntimeError):
182
            tio.ScalarImage(tensor=torch.rand(2, 2, 3, 1)).as_pil()
183
184
    def test_pil_3(self):
185
        tio.ScalarImage(tensor=torch.rand(3, 2, 3, 1)).as_pil()
186
187
    def test_set_data(self):
188
        im = self.sample_subject.t1
189
        with pytest.deprecated_call():
190
            im.data = im.data
191
192
    def test_no_type(self):
193
        with pytest.warns(FutureWarning):
194
            tio.Image(tensor=torch.rand(1, 2, 3, 4))
195
196
    def test_custom_reader(self):
197
        path = self.dir / 'im.npy'
198
199
        def numpy_reader(path):
200
            return np.load(path), np.eye(4)
201
202
        def assert_shape(shape_in, shape_out):
203
            np.save(path, np.random.rand(*shape_in))
204
            image = tio.ScalarImage(path, reader=numpy_reader)
205
            assert image.shape == shape_out
206
207
        assert_shape((5, 5), (1, 5, 5, 1))
208
        assert_shape((5, 5, 3), (3, 5, 5, 1))
209
        assert_shape((3, 5, 5), (3, 5, 5, 1))
210
        assert_shape((5, 5, 5), (1, 5, 5, 5))
211
        assert_shape((1, 5, 5, 5), (1, 5, 5, 5))
212
        assert_shape((4, 5, 5, 5), (4, 5, 5, 5))
213
214
    def test_fast_gif(self):
215
        with pytest.warns(RuntimeWarning):
216
            with tempfile.NamedTemporaryFile(suffix='.gif', delete=False) as f:
217
                self.sample_subject.t1.to_gif(0, 0.0001, f.name)
218
219
    def test_gif_rgb(self):
220
        with tempfile.NamedTemporaryFile(suffix='.gif', delete=False) as f:
221
            tio.ScalarImage(tensor=torch.rand(3, 4, 5, 6)).to_gif(0, 1, f.name)
222
223
    @pytest.mark.slow
224
    def test_hist(self):
225
        self.sample_subject.t1.hist(density=False, show=False)
226
        self.sample_subject.t1.hist(density=True, show=False)
227
228
    def test_count(self):
229
        image = self.sample_subject.label
230
        max_n = image.data.numel()
231
        nonzero = image.count_nonzero()
232
        assert 0 <= nonzero <= max_n
233
        counts = image.count_labels()
234
        assert tuple(counts) == (0, 1)
235
        assert 0 <= counts[0] <= max_n
236
        assert 0 <= counts[1] <= max_n
237
238
    def test_affine_multipath(self):
239
        # https://github.com/TorchIO-project/torchio/issues/762
240
        path1 = self.get_image_path('multi1')
241
        path2 = self.get_image_path('multi2')
242
        paths = path1, path2
243
        image = tio.ScalarImage(paths)
244
        self.assert_tensor_equal(image.affine, np.eye(4))
245
246
    def test_bad_numpy_type_reader(self):
247
        # https://github.com/TorchIO-project/torchio/issues/764
248
        def numpy_reader(path):
249
            return np.load(path), np.eye(4)
250
251
        tensor = np.random.rand(1, 2, 3, 4).astype(np.uint16)
252
        test_path = self.dir / 'test_image.npy'
253
        np.save(test_path, tensor)
254
        image = tio.ScalarImage(test_path, reader=numpy_reader)
255
        image.load()
256
257
    def test_load_unload(self):
258
        path = self.get_image_path('unload')
259
        image = tio.ScalarImage(path)
260
        with self.assertRaises(RuntimeError):
261
            image.unload()
262
        image.load()
263
        assert image._loaded
264
        image.unload()
265
        assert not image._loaded
266
        assert image[tio.DATA] is None
267
        assert image[tio.AFFINE] is None
268
        assert not image._loaded
269
270
    def test_unload_no_path(self):
271
        tensor = torch.rand(1, 2, 3, 4)
272
        image = tio.ScalarImage(tensor=tensor)
273
        with self.assertRaises(RuntimeError):
274
            image.unload()
275
276
    def test_copy_no_data(self):
277
        # https://github.com/TorchIO-project/torchio/issues/974
278
        path = self.get_image_path('im_copy')
279
        my_image = tio.LabelMap(path)
280
        assert not my_image._loaded
281
        new_image = copy.copy(my_image)
282
        assert not my_image._loaded
283
        assert not new_image._loaded
284
285
        my_image.load()
286
        new_image = copy.copy(my_image)
287
        assert my_image._loaded
288
        assert new_image._loaded
289
290
    def test_slicing(self):
291
        path = self.get_image_path('im_slicing')
292
        image = tio.ScalarImage(path)
293
294
        assert image.shape == (1, 10, 20, 30)
295
296
        cropped = image[0]
297
        assert cropped.shape == (1, 1, 20, 30)
298
299
        cropped = image[:, 2:-3]
300
        assert cropped.shape == (1, 10, 15, 30)
301
302
        cropped = image[-5:, 5:]
303
        assert cropped.shape == (1, 5, 15, 30)
304
305
        with pytest.raises(NotImplementedError):
306
            image[..., 5]
307
308
        with pytest.raises(ValueError):
309
            image[0:8:-1]
310
311
        with pytest.raises(ValueError):
312
            image[3::-1]
313
314
    def test_verify_path(self):
315
        path = Path(self.get_image_path('im_verify'))
316
317
        image = tio.ScalarImage(path, verify_path=False)
318
        assert image.path == path
319
320
        image = tio.ScalarImage(path, verify_path=True)
321
        assert image.path == path
322
323
        fake_path = Path('fake_path.nii')
324
325
        image = tio.ScalarImage(fake_path, verify_path=False)
326
        assert image.path == fake_path
327
328
        with pytest.raises(FileNotFoundError):
329
            tio.ScalarImage(fake_path, verify_path=True)
330