Skip to content

Commit f53c168

Browse files
committed
NF - allow Freesurfer large vector hack
Closes gh-100. Allow Freesurfer hack that puts large vector sizes into the glmin field of the header, but put out a warning, and only allow it for the specific case that Freesurfer appears to use.
1 parent 25512bf commit f53c168

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed

nibabel/nifti1.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
99
''' Header reading / writing functions for nifti1 image format
1010
'''
11+
import warnings
12+
1113
import numpy as np
1214
import numpy.linalg as npl
1315

@@ -620,6 +622,69 @@ def default_structarr(klass, endianness=None):
620622
hdr_data['vox_offset'] = 0
621623
return hdr_data
622624

625+
def get_data_shape(self):
626+
''' Get shape of data
627+
628+
Examples
629+
--------
630+
>>> hdr = Nifti1Header()
631+
>>> hdr.get_data_shape()
632+
(0,)
633+
>>> hdr.set_data_shape((1,2,3))
634+
>>> hdr.get_data_shape()
635+
(1, 2, 3)
636+
637+
Expanding number of dimensions gets default zooms
638+
639+
>>> hdr.get_zooms()
640+
(1.0, 1.0, 1.0)
641+
642+
Notes
643+
-----
644+
Allows for freesurfer hack for large vectors described in
645+
https://github.com/nipy/nibabel/issues/100 and
646+
http://code.google.com/p/fieldtrip/source/browse/trunk/external/freesurfer/save_nifti.m?spec=svn5022&r=5022#77
647+
'''
648+
shape = super(Nifti1Header, self).get_data_shape()
649+
# Apply freesurfer hack for vector
650+
if shape != (-1, 1, 1): # Normal case
651+
return shape
652+
vec_len = int(self._structarr['glmin'])
653+
if vec_len == 0:
654+
raise HeaderDataError('-1 in dim[1] but 0 in glmin; inconsistent '
655+
'freesurfer type header?')
656+
return (vec_len, 1, 1)
657+
658+
def set_data_shape(self, shape):
659+
''' Set shape of data
660+
661+
If ``ndims == len(shape)`` then we set zooms for dimensions higher than
662+
``ndims`` to 1.0
663+
664+
Parameters
665+
----------
666+
shape : sequence
667+
sequence of integers specifying data array shape
668+
669+
Notes
670+
-----
671+
Applies freesurfer hack for large vectors described in
672+
https://github.com/nipy/nibabel/issues/100 and
673+
http://code.google.com/p/fieldtrip/source/browse/trunk/external/freesurfer/save_nifti.m?spec=svn5022&r=5022#77
674+
'''
675+
# Apply freesurfer hack for vector
676+
hdr = self._structarr
677+
if (len(shape) == 3 and shape[1:] == (1, 1) and
678+
shape[0] > np.iinfo(hdr['dim'].dtype.base).max): # Freesurfer case
679+
hdr['glmin'] = shape[0]
680+
if hdr['glmin'] != shape[0]:
681+
raise HeaderDataError('shape[0] %s does not fit in glmax datatype' %
682+
shape[0])
683+
warnings.warn('Using large vector Freesurfer hack; header will '
684+
'not be compatible with SPM or FSL', stacklevel=2)
685+
shape = (-1, 1, 1)
686+
super(Nifti1Header, self).set_data_shape(shape)
687+
623688
def get_qform_quaternion(self):
624689
''' Compute quaternion from b, c, d of quaternion
625690

nibabel/tests/test_nifti1.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,44 @@ def test_nifti_log_checks(self):
169169
assert_equal(message, 'sform_code -1 not valid; '
170170
'setting to 0')
171171

172+
def test_freesurfer_hack(self):
173+
# For large vector images, Freesurfer appears to set dim[1] to -1 and
174+
# then use glmin for the vector length (an i4)
175+
HC = self.header_class
176+
# The standard case
177+
hdr = HC()
178+
hdr.set_data_shape((2, 3, 4))
179+
assert_equal(hdr.get_data_shape(), (2, 3, 4))
180+
assert_equal(hdr['glmin'], 0)
181+
# Just left of the freesurfer case
182+
dim_type = hdr.template_dtype['dim'].base
183+
glmin = hdr.template_dtype['glmin'].base
184+
too_big = int(np.iinfo(dim_type).max) + 1
185+
hdr.set_data_shape((too_big-1, 1, 1))
186+
assert_equal(hdr.get_data_shape(), (too_big-1, 1, 1))
187+
# The freesurfer case
188+
hdr.set_data_shape((too_big, 1, 1))
189+
assert_equal(hdr.get_data_shape(), (too_big, 1, 1))
190+
assert_array_equal(hdr['dim'][:4], [3, -1, 1, 1])
191+
assert_equal(hdr['glmin'], too_big)
192+
# This only works for the case of a 3D with -1, 1, 1
193+
assert_raises(HeaderDataError, hdr.set_data_shape, (too_big,))
194+
assert_raises(HeaderDataError, hdr.set_data_shape, (too_big,1))
195+
assert_raises(HeaderDataError, hdr.set_data_shape, (too_big,1,2))
196+
assert_raises(HeaderDataError, hdr.set_data_shape, (too_big,2,1))
197+
assert_raises(HeaderDataError, hdr.set_data_shape, (1, too_big))
198+
assert_raises(HeaderDataError, hdr.set_data_shape, (1, too_big, 1))
199+
assert_raises(HeaderDataError, hdr.set_data_shape, (1, 1, too_big))
200+
# Outside range of glmin raises error
201+
far_too_big = int(np.iinfo(glmin).max) + 1
202+
hdr.set_data_shape((far_too_big-1, 1, 1))
203+
assert_equal(hdr.get_data_shape(), (far_too_big-1, 1, 1))
204+
assert_raises(HeaderDataError, hdr.set_data_shape, (far_too_big,1,1))
205+
# glmin of zero raises error (implausible vector length)
206+
hdr.set_data_shape((-1,1,1))
207+
hdr['glmin'] = 0
208+
assert_raises(HeaderDataError, hdr.get_data_shape)
209+
172210

173211
class TestNifti1SingleHeader(TestNifti1PairHeader):
174212

0 commit comments

Comments
 (0)