Skip to content

Commit 7a79872

Browse files
committed
BF: FreeSurfer nifti surfaces can have >3 dimensions
1 parent e471f8c commit 7a79872

File tree

2 files changed

+44
-17
lines changed

2 files changed

+44
-17
lines changed

nibabel/nifti1.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -704,16 +704,16 @@ def get_data_shape(self):
704704
https://code.google.com/p/fieldtrip/source/browse/trunk/external/freesurfer/save_nifti.m?r=8776#50
705705
'''
706706
shape = super(Nifti1Header, self).get_data_shape()
707-
# Apply freesurfer hack for vector
708-
if shape == (-1, 1, 1):
707+
# Apply freesurfer hack for large vectors
708+
if shape[:3] == (-1, 1, 1):
709709
vec_len = int(self._structarr['glmin'])
710710
if vec_len == 0:
711711
raise HeaderDataError('-1 in dim[1] but 0 in glmin; '
712712
'inconsistent freesurfer type header?')
713-
return (vec_len, 1, 1)
713+
return (vec_len, 1, 1) + shape[3:]
714714
# Apply freesurfer hack for ico7 surface
715-
elif shape == (27307, 1, 6):
716-
return (163842, 1, 1)
715+
elif shape[:3] == (27307, 1, 6):
716+
return (163842, 1, 1) + shape[3:]
717717
else: # Normal case
718718
return shape
719719

@@ -723,6 +723,11 @@ def set_data_shape(self, shape):
723723
If ``ndims == len(shape)`` then we set zooms for dimensions higher than
724724
``ndims`` to 1.0
725725
726+
Nifti1 images can have up to seven dimensions. For Nifti surface files,
727+
the first dimension is assumed to correspond to vertices/nodes on a
728+
surface, and dimensions two and three are constrained to have depth of
729+
1. Dimensions 4-7 are constrained only by type bounds.
730+
726731
Parameters
727732
----------
728733
shape : sequence
@@ -744,10 +749,10 @@ def set_data_shape(self, shape):
744749
shape = tuple(shape)
745750

746751
# Apply freesurfer hack for ico7 surface
747-
if shape == (163842, 1, 1):
748-
shape = (27307, 1, 6)
749-
# Apply freesurfer hack for vector
750-
elif (len(shape) == 3 and shape[1:] == (1, 1) and
752+
if shape[:3] == (163842, 1, 1):
753+
shape = (27307, 1, 6) + shape[3:]
754+
# Apply freesurfer hack for large vectors
755+
elif (len(shape) >= 3 and shape[1:3] == (1, 1) and
751756
shape[0] > np.iinfo(hdr['dim'].dtype.base).max):
752757
try:
753758
hdr['glmin'] = shape[0]
@@ -760,7 +765,7 @@ def set_data_shape(self, shape):
760765
'datatype' % shape[0])
761766
warnings.warn('Using large vector Freesurfer hack; header will '
762767
'not be compatible with SPM or FSL', stacklevel=2)
763-
shape = (-1, 1, 1)
768+
shape = (-1, 1, 1) + shape[3:]
764769
super(Nifti1Header, self).set_data_shape(shape)
765770

766771
def get_qform_quaternion(self):

nibabel/tests/test_nifti1.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -261,19 +261,28 @@ def test_freesurfer_large_vector_hack(self):
261261
hdr.set_data_shape((too_big-1, 1, 1))
262262
assert_equal(hdr.get_data_shape(), (too_big-1, 1, 1))
263263
# The freesurfer case
264+
full_shape = (too_big, 1, 1, 1, 1, 1, 1)
265+
for dim in range(3, 8):
266+
expected_dim = np.array([dim, -1, 1, 1, 1, 1, 1, 1])
267+
with suppress_warnings():
268+
hdr.set_data_shape(full_shape[:dim])
269+
assert_equal(hdr.get_data_shape(), full_shape[:dim])
270+
assert_array_equal(hdr['dim'], expected_dim)
271+
assert_equal(hdr['glmin'], too_big)
272+
# Allow the fourth dimension to vary
264273
with suppress_warnings():
265-
hdr.set_data_shape((too_big, 1, 1))
266-
assert_equal(hdr.get_data_shape(), (too_big, 1, 1))
267-
assert_array_equal(hdr['dim'][:4], [3, -1, 1, 1])
268-
assert_equal(hdr['glmin'], too_big)
269-
# This only works for the case of a 3D with -1, 1, 1
274+
hdr.set_data_shape((too_big, 1, 1, 4))
275+
assert_equal(hdr.get_data_shape(), (too_big, 1, 1, 4))
276+
assert_array_equal(hdr['dim'][:5], np.array([4, -1, 1, 1, 4]))
277+
# This only works when the first 3 dimensions are -1, 1, 1
270278
assert_raises(HeaderDataError, hdr.set_data_shape, (too_big,))
271279
assert_raises(HeaderDataError, hdr.set_data_shape, (too_big,1))
272280
assert_raises(HeaderDataError, hdr.set_data_shape, (too_big,1,2))
273281
assert_raises(HeaderDataError, hdr.set_data_shape, (too_big,2,1))
274282
assert_raises(HeaderDataError, hdr.set_data_shape, (1, too_big))
275283
assert_raises(HeaderDataError, hdr.set_data_shape, (1, too_big, 1))
276284
assert_raises(HeaderDataError, hdr.set_data_shape, (1, 1, too_big))
285+
assert_raises(HeaderDataError, hdr.set_data_shape, (1, 1, 1, too_big))
277286
# Outside range of glmin raises error
278287
far_too_big = int(np.iinfo(glmin).max) + 1
279288
with suppress_warnings():
@@ -295,9 +304,22 @@ def test_freesurfer_large_vector_hack(self):
295304
def test_freesurfer_ico7_hack(self):
296305
HC = self.header_class
297306
hdr = HC()
307+
full_shape = (163842, 1, 1, 1, 1, 1, 1)
298308
# Test that using ico7 shape automatically uses factored dimensions
299-
hdr.set_data_shape((163842, 1, 1))
300-
assert_array_equal(hdr._structarr['dim'][1:4], np.array([27307, 1, 6]))
309+
for dim in range(3, 8):
310+
expected_dim = np.array([dim, 27307, 1, 6, 1, 1, 1, 1])
311+
hdr.set_data_shape(full_shape[:dim])
312+
assert_equal(hdr.get_data_shape(), full_shape[:dim])
313+
assert_array_equal(hdr._structarr['dim'], expected_dim)
314+
# Only works on dimensions >= 3
315+
assert_raises(HeaderDataError, hdr.set_data_shape, full_shape[:1])
316+
assert_raises(HeaderDataError, hdr.set_data_shape, full_shape[:2])
317+
# Bad shapes
318+
assert_raises(HeaderDataError, hdr.set_data_shape, (163842, 2, 1))
319+
assert_raises(HeaderDataError, hdr.set_data_shape, (163842, 1, 2))
320+
assert_raises(HeaderDataError, hdr.set_data_shape, (1, 163842, 1))
321+
assert_raises(HeaderDataError, hdr.set_data_shape, (1, 1, 163842))
322+
assert_raises(HeaderDataError, hdr.set_data_shape, (1, 1, 1, 163842))
301323
# Test consistency of data in .mgh and mri_convert produced .nii
302324
nitest_path = os.path.join(get_nibabel_data(), 'nitest-freesurfer')
303325
mgh = mghload(os.path.join(nitest_path, 'fsaverage', 'surf',

0 commit comments

Comments
 (0)