Skip to content

Commit a5cd5db

Browse files
matthew-bretteffigies
authored andcommitted
RF+TST: refactor various analyze / nifti checks
We want to allow CIFTI2 to be lenient on 0 qfac and pixdims. Refactor qfac and pixdims checks into their own functions in analyze and nifti1 test classes. Write more lenient CIFTI2 checking functions. Test CIFTI2 header checks with subclass of NIfTI2 header testing class.
1 parent c6193e9 commit a5cd5db

File tree

5 files changed

+93
-29
lines changed

5 files changed

+93
-29
lines changed

nibabel/cifti2/parse_cifti2_fast.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
Cifti2Vertices, Cifti2Volume, CIFTI_BrainStructures,
2121
CIFTI_MODEL_TYPES, _underscore, CIFTI2HeaderError)
2222
from .. import xmlutils as xml
23+
from ..spatialimages import HeaderDataError
2324
from ..externals.six import BytesIO
2425
from ..batteryrunners import Report
2526
from ..nifti1 import Nifti1Extension, extension_codes, intent_codes
@@ -90,24 +91,25 @@ def may_contain_header(klass, binaryblock):
9091
hdr = klass(binaryblock=binaryblock[:klass.sizeof_hdr])
9192
return klass._valid_intent_code(hdr.get_intent('code')[0])
9293

93-
@classmethod
94-
def _get_checks(klass):
95-
# We need to return our own versions of - e.g. chk_datatype, to
96-
# pick up the Nifti datatypes from our class
97-
return (klass._chk_sizeof_hdr,
98-
klass._chk_datatype,
99-
klass._chk_bitpix,
100-
klass._chk_pixdims,
101-
klass._chk_magic,
102-
klass._chk_offset,
103-
klass._chk_qform_code,
104-
klass._chk_sform_code)
94+
@staticmethod
95+
def _chk_qfac(hdr, fix=False):
96+
# Allow qfac of 0 without complaint for CIFTI2
97+
rep = Report(HeaderDataError)
98+
if hdr['pixdim'][0] in (-1, 0, 1):
99+
return hdr, rep
100+
rep.problem_level = 20
101+
rep.problem_msg = 'pixdim[0] (qfac) should be 1 (default) or 0 or -1'
102+
if fix:
103+
hdr['pixdim'][0] = 1
104+
rep.fix_msg = 'setting qfac to 1'
105+
return hdr, rep
105106

107+
@staticmethod
106108
def _chk_pixdims(hdr, fix=False):
107-
rep = Report(CIFTI2HeaderError)
109+
rep = Report(HeaderDataError)
108110
pixdims = hdr['pixdim']
109111
spat_dims = pixdims[1:4]
110-
if not np.any(spat_dims <= 0):
112+
if not np.any(spat_dims < 0):
111113
return hdr, rep
112114
rep.problem_level = 35
113115
rep.problem_msg = 'pixdim[1,2,3] should be zero or positive'

nibabel/cifti2/tests/test_cifti2io.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,16 @@
1010

1111
from os.path import join as pjoin, dirname
1212
import io
13+
1314
from distutils.version import LooseVersion
1415

1516
import nibabel as nib
1617
from nibabel import cifti2 as ci
18+
from nibabel.cifti2.parse_cifti2_fast import _Cifti2AsNiftiHeader
19+
1720
from nibabel.tmpdirs import InTemporaryDirectory
1821
from nibabel.tests.nibabel_data import get_nibabel_data, needs_nibabel_data
22+
from nibabel.tests.test_nifti2 import TestNifti2SingleHeader
1923

2024
from numpy.testing import assert_array_almost_equal
2125
from nose.tools import (assert_true, assert_equal, assert_raises)
@@ -39,6 +43,7 @@
3943

4044

4145
def test_read_nifti2():
46+
# Error trying to read a CIFTI2 image from a NIfTI2-only image.
4247
filemap = ci.Cifti2Image.make_file_map()
4348
for k in filemap:
4449
filemap[k].fileobj = io.open(NIFTI2_DATA)
@@ -194,3 +199,48 @@ def test_cifti2types():
194199

195200
for klass, count in counter.items():
196201
assert_true(count > 0, "No exercise of " + klass.__name__)
202+
203+
204+
class TestCifti2SingleHeader(TestNifti2SingleHeader):
205+
header_class = _Cifti2AsNiftiHeader
206+
_pixdim_message = 'pixdim[1,2,3] should be zero or positive'
207+
208+
def test_pixdim_checks(self):
209+
hdr_t = self.header_class()
210+
for i in (1, 2, 3):
211+
hdr = hdr_t.copy()
212+
hdr['pixdim'][i] = -1
213+
assert_equal(self._dxer(hdr), self._pixdim_message)
214+
215+
def test_nifti_qfac_checks(self):
216+
# Test qfac is 1 or -1 or 0
217+
hdr = self.header_class()
218+
# 1, 0, -1 all OK
219+
hdr['pixdim'][0] = 1
220+
self.log_chk(hdr, 0)
221+
hdr['pixdim'][0] = 0
222+
self.log_chk(hdr, 0)
223+
hdr['pixdim'][0] = -1
224+
self.log_chk(hdr, 0)
225+
# Anything else is not
226+
hdr['pixdim'][0] = 2
227+
fhdr, message, raiser = self.log_chk(hdr, 20)
228+
assert_equal(fhdr['pixdim'][0], 1)
229+
assert_equal(message,
230+
'pixdim[0] (qfac) should be 1 '
231+
'(default) or 0 or -1; setting qfac to 1')
232+
233+
def test_pixdim_log_checks(self):
234+
# pixdim can be zero or positive
235+
HC = self.header_class
236+
hdr = HC()
237+
hdr['pixdim'][1] = -2 # severity 35
238+
fhdr, message, raiser = self.log_chk(hdr, 35)
239+
assert_equal(fhdr['pixdim'][1], 2)
240+
assert_equal(message, self._pixdim_message +
241+
'; setting to abs of pixdim values')
242+
assert_raises(*raiser)
243+
hdr = HC()
244+
hdr['pixdim'][1:4] = 0 # No error or warning
245+
fhdr, message, raiser = self.log_chk(hdr, 0)
246+
assert_equal(raiser, ())

nibabel/tests/test_analyze.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ def test_checks(self):
130130
hdr = hdr_t.copy()
131131
hdr['bitpix'] = 0
132132
assert_equal(self._dxer(hdr), 'bitpix does not match datatype')
133+
134+
def test_pixdim_checks(self):
135+
hdr_t = self.header_class()
133136
for i in (1, 2, 3):
134137
hdr = hdr_t.copy()
135138
hdr['pixdim'][i] = -1
@@ -176,7 +179,10 @@ def test_log_checks(self):
176179
assert_equal(message, 'bitpix does not match datatype; '
177180
'setting bitpix to match datatype')
178181
assert_raises(*raiser)
182+
183+
def test_pixdim_log_checks(self):
179184
# pixdim positive
185+
HC = self.header_class
180186
hdr = HC()
181187
hdr['pixdim'][1] = -2 # severity 35
182188
fhdr, message, raiser = self.log_chk(hdr, 35)
@@ -232,18 +238,21 @@ def test_logger_error(self):
232238
# Make a new logger
233239
str_io = StringIO()
234240
logger = logging.getLogger('test.logger')
235-
logger.setLevel(30) # defaultish level
236241
logger.addHandler(logging.StreamHandler(str_io))
237-
# Prepare an error
238-
hdr['pixdim'][1] = 0 # severity 30
242+
# Prepare a defect: bitpix not matching data type
243+
hdr['datatype'] = 16 # float32
244+
hdr['bitpix'] = 16 # severity 10
245+
logger.setLevel(10)
239246
log_cache = imageglobals.logger, imageglobals.error_level
240247
try:
241248
# Check log message appears in new logger
242249
imageglobals.logger = logger
243250
hdr.copy().check_fix()
244-
assert_equal(str_io.getvalue(), PIXDIM0_MSG + '\n')
251+
assert_equal(str_io.getvalue(),
252+
'bitpix does not match datatype; '
253+
'setting bitpix to match datatype\n')
245254
# Check that error_level in fact causes error to be raised
246-
imageglobals.error_level = 30
255+
imageglobals.error_level = 10
247256
assert_raises(HeaderDataError, hdr.copy().check_fix)
248257
finally:
249258
imageglobals.logger, imageglobals.error_level = log_cache

nibabel/tests/test_nifti1.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,17 +186,25 @@ def test_slope_inter(self):
186186
assert_array_equal([hdr['scl_slope'], hdr['scl_inter']],
187187
raw_values)
188188

189-
def test_nifti_qsform_checks(self):
190-
# qfac, qform, sform checks
191-
# qfac
192-
HC = self.header_class
193-
hdr = HC()
189+
def test_nifti_qfac_checks(self):
190+
# Test qfac is 1 or -1
191+
hdr = self.header_class()
192+
# 1, -1 OK
193+
hdr['pixdim'][0] = 1
194+
self.log_chk(hdr, 0)
195+
hdr['pixdim'][0] = -1
196+
self.log_chk(hdr, 0)
197+
# 0 is not
194198
hdr['pixdim'][0] = 0
195199
fhdr, message, raiser = self.log_chk(hdr, 20)
196200
assert_equal(fhdr['pixdim'][0], 1)
197201
assert_equal(message,
198202
'pixdim[0] (qfac) should be 1 '
199203
'(default) or -1; setting qfac to 1')
204+
205+
def test_nifti_qsform_checks(self):
206+
# qform, sform checks
207+
HC = self.header_class
200208
# qform, sform
201209
hdr = HC()
202210
hdr['qform_code'] = -1

nibabel/tests/test_nifti2.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,6 @@ class TestNifti2Image(TestNifti1Image):
7878
image_class = Nifti2Image
7979

8080

81-
class TestNifti2Image(TestNifti1Image):
82-
# Run analyze-flavor spatialimage tests
83-
image_class = Nifti2Image
84-
85-
8681
class TestNifti2Pair(TestNifti1Pair):
8782
# Run analyze-flavor spatialimage tests
8883
image_class = Nifti2Pair

0 commit comments

Comments
 (0)