Skip to content

Commit 6ba8b66

Browse files
committed
RF: get_norm_zooms -> get_zooms(units="canonical")
1 parent eb1d8ff commit 6ba8b66

File tree

4 files changed

+106
-31
lines changed

4 files changed

+106
-31
lines changed

nibabel/analyze.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -661,13 +661,22 @@ def get_base_affine(self):
661661

662662
get_best_affine = get_base_affine
663663

664-
def get_zooms(self):
665-
""" Get zooms from header
664+
def get_zooms(self, units='canonical', raise_unknown=False):
665+
""" Get zooms (spacing between voxels along each axis) from header
666+
667+
Parameters
668+
----------
669+
units : {'canonical', 'raw'}, optional
670+
Return zooms in "canonical" units of mm/sec for spatial/temporal or
671+
as raw values stored in header.
672+
raise_unkown : bool, optional
673+
If canonical units are requested and the units are ambiguous, raise
674+
a ``ValueError``
666675
667676
Returns
668677
-------
669-
z : tuple
670-
tuple of header zoom values
678+
zooms : tuple
679+
tuple of header zoom values
671680
672681
Examples
673682
--------
@@ -706,10 +715,6 @@ def set_zooms(self, zooms):
706715
pixdims = hdr['pixdim']
707716
pixdims[1:ndim + 1] = zooms[:]
708717

709-
def get_norm_zooms(self, raise_unknown=False):
710-
''' Get zooms in mm/s units '''
711-
return self.get_zooms()
712-
713718
def set_norm_zooms(self, zooms):
714719
''' Set zooms in mm/s units '''
715720
return self.set_zooms(zooms)

nibabel/freesurfer/mghformat.py

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -237,25 +237,45 @@ def _ndims(self):
237237
"""
238238
return 3 + (self._structarr['dims'][3] > 1)
239239

240-
def get_zooms(self):
240+
def get_zooms(self, units='canonical', raise_unknown=False):
241241
""" Get zooms from header
242242
243243
Returns the spacing of voxels in the x, y, and z dimensions.
244244
For four-dimensional files, a fourth zoom is included, equal to the
245-
repetition time (TR) in ms (see `The MGH/MGZ Volume Format
246-
<mghformat>`_).
245+
repetition time (TR).
246+
TR is stored in milliseconds (see `The MGH/MGZ Volume Format
247+
<mghformat>`_),
248+
so if ``units == 'raw'``, the fourth zoom will be in ms.
249+
If ``units == 'canonical'`` (default), the fourth zoom will be in
250+
seconds.
247251
248252
To access only the spatial zooms, use `hdr['delta']`.
249253
254+
Parameters
255+
----------
256+
units : {'canonical', 'raw'}, optional
257+
Return zooms in "canonical" units of mm/sec for spatial/temporal or
258+
as raw values stored in header.
259+
raise_unkown : bool, optional
260+
If canonical units are requested and the units are ambiguous, raise
261+
a ``ValueError``
262+
250263
Returns
251264
-------
252-
z : tuple
253-
tuple of header zoom values
265+
zooms : tuple
266+
tuple of header zoom values
254267
255268
.. _mghformat: https://surfer.nmr.mgh.harvard.edu/fswiki/FsTutorial/MghFormat#line-82
256269
"""
270+
if units == 'canonical':
271+
tfactor = 0.001
272+
elif units == 'raw':
273+
tfactor = 1
274+
else:
275+
raise ValueError("`units` parameter must be 'canonical' or 'raw'")
276+
257277
# Do not return time zoom (TR) if 3D image
258-
tzoom = (self['tr'],) if self._ndims() > 3 else ()
278+
tzoom = (self['tr'] * tfactor,) if self._ndims() > 3 else ()
259279
return tuple(self._structarr['delta']) + tzoom
260280

261281
def set_zooms(self, zooms):
@@ -285,15 +305,6 @@ def set_zooms(self, zooms):
285305
raise HeaderDataError(f'TR must be non-negative; got {zooms[3]}')
286306
hdr['tr'] = zooms[3]
287307

288-
def get_norm_zooms(self, raise_unknown=False):
289-
''' Get zooms in mm/s units '''
290-
zooms = self.get_zooms()
291-
292-
if len(zooms) == 4:
293-
zooms = zooms[:3] + (zooms[3] / 1000,)
294-
295-
return zooms
296-
297308
def set_norm_zooms(self, zooms):
298309
if len(zooms) == 4:
299310
zooms = zooms[:3] + (zooms[3] * 1000,)

nibabel/nifti1.py

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1675,9 +1675,56 @@ def set_xyzt_units(self, xyz=None, t=None):
16751675
t_code = unit_codes[t]
16761676
self.structarr['xyzt_units'] = xyz_code + t_code
16771677

1678-
def get_norm_zooms(self, raise_unknown=False):
1679-
''' Get zooms in mm/s units '''
1680-
raw_zooms = self.get_zooms()
1678+
def get_zooms(self, units=None, raise_unknown=False):
1679+
''' Get zooms (spacing between voxels along each axis) from header
1680+
1681+
NIfTI1 headers may specify that zooms are encoded in units other than
1682+
mm and sec (see ``get_xyzt_units``).
1683+
Default behavior has been to return the raw zooms, and leave it to the
1684+
programmer to handle non-standard units.
1685+
However, most files indicate mm/sec units or have unspecified units,
1686+
and it is common practice to neglect specified units and assume all
1687+
files will be in mm/sec.
1688+
1689+
The default behavior for ``get_zooms`` will remain to return the raw
1690+
zooms until version 4.0, when it will change to return zooms in
1691+
canonical mm/sec units.
1692+
Because the default behavior will change, a warning will be given to
1693+
prompt programmers to specify whether they intend to retrieve raw
1694+
values, or values coerced into canonical units.
1695+
1696+
Parameters
1697+
----------
1698+
units : {'canonical', 'raw'}
1699+
Return zooms in "canonical" units of mm/sec for spatial/temporal or
1700+
as raw values stored in header.
1701+
raise_unkown : bool, optional
1702+
If canonical units are requested and the units are ambiguous, raise
1703+
a ``ValueError``
1704+
1705+
Returns
1706+
-------
1707+
zooms : tuple
1708+
tuple of header zoom values
1709+
1710+
'''
1711+
if units is None:
1712+
units = 'raw'
1713+
warnings.warn('Units not specified in `{}.get_zooms`. Returning '
1714+
'raw zooms, but default will change to canonical.\n'
1715+
'Please explicitly specify units parameter.'
1716+
''.format(self.__class__.__name__),
1717+
FutureWarning, stacklevel=2)
1718+
1719+
raw_zooms = super(Nifti1Header, self).get_zooms(units='raw',
1720+
raise_unknown=False)
1721+
1722+
if units == 'raw':
1723+
return raw_zooms
1724+
1725+
elif units != 'canonical':
1726+
raise ValueError("`units` parameter must be 'canonical' or 'raw'")
1727+
16811728
xyz_zooms = raw_zooms[:3]
16821729
t_zoom = raw_zooms[3] if len(raw_zooms) > 3 else None
16831730

nibabel/spatialimages.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,23 @@ def set_data_shape(self, shape):
224224
nzs = min(len(self._zooms), ndim)
225225
self._zooms = self._zooms[:nzs] + (1.0,) * (ndim - nzs)
226226

227-
def get_zooms(self):
227+
def get_zooms(self, units='canonical', raise_unknown=False):
228+
''' Get zooms (spacing between voxels along each axis) from header
229+
230+
Parameters
231+
----------
232+
units : {'canonical', 'raw'}, optional
233+
Return zooms in "canonical" units of mm/sec for spatial/temporal or
234+
as raw values stored in header.
235+
raise_unkown : bool, optional
236+
If canonical units are requested and the units are ambiguous, raise
237+
a ``ValueError``
238+
239+
Returns
240+
-------
241+
zooms : tuple
242+
Spacing between voxels along each axis
243+
'''
228244
return self._zooms
229245

230246
def set_zooms(self, zooms):
@@ -238,10 +254,6 @@ def set_zooms(self, zooms):
238254
raise HeaderDataError('zooms must be positive')
239255
self._zooms = zooms
240256

241-
def get_norm_zooms(self, raise_unknown=False):
242-
''' Get zooms in mm/s units '''
243-
return self.get_zooms()
244-
245257
def set_norm_zooms(self, zooms):
246258
''' Get zooms in mm/s units '''
247259
return self.set_zooms(zooms)

0 commit comments

Comments
 (0)