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 10import os 11import warnings 12 13import numpy as np 14 15from ..openers import Opener 16from ..ecat import (EcatHeader, EcatSubHeader, EcatImage, read_mlist, 17 get_frame_order, get_series_framenumbers) 18 19from unittest import TestCase 20import pytest 21 22from numpy.testing import assert_array_equal, assert_array_almost_equal 23 24from ..testing import data_path, suppress_warnings 25from ..tmpdirs import InTemporaryDirectory 26 27from .test_wrapstruct import _TestWrapStructBase 28from .test_fileslice import slicer_samples 29 30ecat_file = os.path.join(data_path, 'tinypet.v') 31 32 33class TestEcatHeader(_TestWrapStructBase): 34 header_class = EcatHeader 35 example_file = ecat_file 36 37 def test_header_size(self): 38 assert self.header_class.template_dtype.itemsize == 512 39 40 def test_empty(self): 41 hdr = self.header_class() 42 assert len(hdr.binaryblock) == 512 43 assert hdr['magic_number'] == b'MATRIX72' 44 assert hdr['sw_version'] == 74 45 assert hdr['num_frames'] == 0 46 assert hdr['file_type'] == 0 47 assert hdr['ecat_calibration_factor'] == 1.0 48 49 def _set_something_into_hdr(self, hdr): 50 # Called from test_bytes test method. Specific to the header data type 51 hdr['scan_start_time'] = 42 52 53 def test_dtype(self): 54 # dtype not specified in header, only in subheaders 55 hdr = self.header_class() 56 with pytest.raises(NotImplementedError): 57 hdr.get_data_dtype() 58 59 def test_header_codes(self): 60 fid = open(ecat_file, 'rb') 61 hdr = self.header_class() 62 newhdr = hdr.from_fileobj(fid) 63 fid.close() 64 assert newhdr.get_filetype() == 'ECAT7_VOLUME16' 65 assert (newhdr.get_patient_orient() == 66 'ECAT7_Unknown_Orientation') 67 68 def test_update(self): 69 hdr = self.header_class() 70 assert hdr['num_frames'] == 0 71 hdr['num_frames'] = 2 72 assert hdr['num_frames'] == 2 73 74 def test_from_eg_file(self): 75 # Example header is big-endian 76 with Opener(self.example_file) as fileobj: 77 hdr = self.header_class.from_fileobj(fileobj, check=False) 78 assert hdr.endianness == '>' 79 80 81class TestEcatMlist(TestCase): 82 header_class = EcatHeader 83 example_file = ecat_file 84 85 def test_mlist(self): 86 fid = open(self.example_file, 'rb') 87 hdr = self.header_class.from_fileobj(fid) 88 mlist = read_mlist(fid, hdr.endianness) 89 fid.seek(0) 90 fid.seek(512) 91 dat = fid.read(128 * 32) 92 dt = np.dtype([('matlist', np.int32)]) 93 dt = dt.newbyteorder('>') 94 mats = np.recarray(shape=(32, 4), dtype=dt, buf=dat) 95 fid.close() 96 # tests 97 assert mats['matlist'][0, 0] + mats['matlist'][0, 3] == 31 98 assert get_frame_order(mlist)[0][0] == 0 99 assert get_frame_order(mlist)[0][1] == 16842758.0 100 # test badly ordered mlist 101 badordermlist = np.array([[1.68427540e+07, 3.00000000e+00, 102 1.20350000e+04, 1.00000000e+00], 103 [1.68427530e+07, 1.20360000e+04, 104 2.40680000e+04, 1.00000000e+00], 105 [1.68427550e+07, 2.40690000e+04, 106 3.61010000e+04, 1.00000000e+00], 107 [1.68427560e+07, 3.61020000e+04, 108 4.81340000e+04, 1.00000000e+00], 109 [1.68427570e+07, 4.81350000e+04, 110 6.01670000e+04, 1.00000000e+00], 111 [1.68427580e+07, 6.01680000e+04, 112 7.22000000e+04, 1.00000000e+00]]) 113 with suppress_warnings(): # STORED order 114 assert get_frame_order(badordermlist)[0][0] == 1 115 116 def test_mlist_errors(self): 117 fid = open(self.example_file, 'rb') 118 hdr = self.header_class.from_fileobj(fid) 119 hdr['num_frames'] = 6 120 mlist = read_mlist(fid, hdr.endianness) 121 mlist = np.array([[1.68427540e+07, 3.00000000e+00, 122 1.20350000e+04, 1.00000000e+00], 123 [1.68427530e+07, 1.20360000e+04, 124 2.40680000e+04, 1.00000000e+00], 125 [1.68427550e+07, 2.40690000e+04, 126 3.61010000e+04, 1.00000000e+00], 127 [1.68427560e+07, 3.61020000e+04, 128 4.81340000e+04, 1.00000000e+00], 129 [1.68427570e+07, 4.81350000e+04, 130 6.01670000e+04, 1.00000000e+00], 131 [1.68427580e+07, 6.01680000e+04, 132 7.22000000e+04, 1.00000000e+00]]) 133 with suppress_warnings(): # STORED order 134 series_framenumbers = get_series_framenumbers(mlist) 135 # first frame stored was actually 2nd frame acquired 136 assert series_framenumbers[0] == 2 137 order = [series_framenumbers[x] for x in sorted(series_framenumbers)] 138 # true series order is [2,1,3,4,5,6], note counting starts at 1 139 assert order == [2, 1, 3, 4, 5, 6] 140 mlist[0, 0] = 0 141 with suppress_warnings(): 142 frames_order = get_frame_order(mlist) 143 neworder = [frames_order[x][0] for x in sorted(frames_order)] 144 assert neworder == [1, 2, 3, 4, 5] 145 with suppress_warnings(): 146 with pytest.raises(IOError): 147 get_series_framenumbers(mlist) 148 149 150class TestEcatSubHeader(TestCase): 151 header_class = EcatHeader 152 subhdr_class = EcatSubHeader 153 example_file = ecat_file 154 fid = open(example_file, 'rb') 155 hdr = header_class.from_fileobj(fid) 156 mlist = read_mlist(fid, hdr.endianness) 157 subhdr = subhdr_class(hdr, mlist, fid) 158 159 def test_subheader_size(self): 160 assert self.subhdr_class._subhdrdtype.itemsize == 510 161 162 def test_subheader(self): 163 assert self.subhdr.get_shape() == (10, 10, 3) 164 assert self.subhdr.get_nframes() == 1 165 assert (self.subhdr.get_nframes() == 166 len(self.subhdr.subheaders)) 167 assert self.subhdr._check_affines() is True 168 assert_array_almost_equal(np.diag(self.subhdr.get_frame_affine()), 169 np.array([2.20241979, 2.20241979, 3.125, 1.])) 170 assert self.subhdr.get_zooms()[0] == 2.20241978764534 171 assert self.subhdr.get_zooms()[2] == 3.125 172 assert self.subhdr._get_data_dtype(0) == np.int16 173 #assert_equal(self.subhdr._get_frame_offset(), 1024) 174 assert self.subhdr._get_frame_offset() == 1536 175 dat = self.subhdr.raw_data_from_fileobj() 176 assert dat.shape == self.subhdr.get_shape() 177 assert self.subhdr.subheaders[0]['scale_factor'].item() == 1.0 178 ecat_calib_factor = self.hdr['ecat_calibration_factor'] 179 assert ecat_calib_factor == 25007614.0 180 181 182class TestEcatImage(TestCase): 183 image_class = EcatImage 184 example_file = ecat_file 185 img = image_class.load(example_file) 186 187 def test_file(self): 188 assert (self.img.file_map['header'].filename == 189 self.example_file) 190 assert (self.img.file_map['image'].filename == 191 self.example_file) 192 193 def test_save(self): 194 tmp_file = 'tinypet_tmp.v' 195 with InTemporaryDirectory(): 196 self.img.to_filename(tmp_file) 197 other = self.image_class.load(tmp_file) 198 assert_array_equal(self.img.get_fdata(), other.get_fdata()) 199 # Delete object holding reference to temporary file to make Windows 200 # happier. 201 del other 202 203 def test_data(self): 204 dat = self.img.get_fdata() 205 assert dat.shape == self.img.shape 206 frame = self.img.get_frame(0) 207 assert_array_equal(frame, dat[:, :, :, 0]) 208 209 def test_array_proxy(self): 210 # Get the cached data copy 211 dat = self.img.get_fdata() 212 # Make a new one to test arrayproxy 213 img = self.image_class.load(self.example_file) 214 data_prox = img.dataobj 215 data2 = np.array(data_prox) 216 assert_array_equal(data2, dat) 217 # Check it rereads 218 data3 = np.array(data_prox) 219 assert_array_equal(data3, dat) 220 221 def test_array_proxy_slicing(self): 222 # Test slicing of array proxy 223 arr = self.img.get_fdata() 224 prox = self.img.dataobj 225 assert prox.is_proxy 226 for sliceobj in slicer_samples(self.img.shape): 227 assert_array_equal(arr[sliceobj], prox[sliceobj]) 228 229 def test_isolation(self): 230 # Test image isolated from external changes to affine 231 img_klass = self.image_class 232 arr, aff, hdr, sub_hdr, mlist = (self.img.get_fdata(), 233 self.img.affine, 234 self.img.header, 235 self.img.get_subheaders(), 236 self.img.get_mlist()) 237 img = img_klass(arr, aff, hdr, sub_hdr, mlist) 238 assert_array_equal(img.affine, aff) 239 aff[0, 0] = 99 240 assert not np.all(img.affine == aff) 241 242 def test_get_affine_deprecated(self): 243 with pytest.deprecated_call(match="from version: 2.1"): 244 aff = self.img.get_affine() 245 assert np.array_equal(aff, self.img.affine) 246 247 def test_float_affine(self): 248 # Check affines get converted to float 249 img_klass = self.image_class 250 arr, aff, hdr, sub_hdr, mlist = (self.img.get_fdata(), 251 self.img.affine, 252 self.img.header, 253 self.img.get_subheaders(), 254 self.img.get_mlist()) 255 img = img_klass(arr, aff.astype(np.float32), hdr, sub_hdr, mlist) 256 assert img.affine.dtype == np.dtype(np.float64) 257 img = img_klass(arr, aff.astype(np.int16), hdr, sub_hdr, mlist) 258 assert img.affine.dtype == np.dtype(np.float64) 259 260 def test_data_regression(self): 261 # Test whether data read has changed since 1.3.0 262 # These values came from reading the example image using nibabel 1.3.0 263 vals = dict(max=248750736458.0, 264 min=1125342630.0, 265 mean=117907565661.46666) 266 data = self.img.get_fdata() 267 assert data.max() == vals['max'] 268 assert data.min() == vals['min'] 269 assert_array_almost_equal(data.mean(), vals['mean']) 270 271 def test_mlist_regression(self): 272 # Test mlist is as same as for nibabel 1.3.0 273 assert_array_equal(self.img.get_mlist(), 274 [[16842758, 3, 3011, 1]]) 275 276 277def test_from_filespec_deprecation(): 278 # Check from_filespec raises Deprecation 279 with pytest.deprecated_call() as w: 280 # No warning for standard load 281 img_loaded = EcatImage.load(ecat_file) 282 assert len(w) == 0 283 # Warning for from_filespec 284 img_speced = EcatImage.from_filespec(ecat_file) 285 assert len(w) == 1 286 assert_array_equal(img_loaded.get_fdata(), img_speced.get_fdata()) 287