Skip to content

Commit fc44eac

Browse files
committed
RF: Add units parameter to set_zooms
- Switch 'canonical' to 'norm' for brevity
1 parent 6e79e35 commit fc44eac

File tree

8 files changed

+203
-114
lines changed

8 files changed

+203
-114
lines changed

nibabel/analyze.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ def from_header(klass, header=None, check=True):
401401
klass))
402402
obj.set_data_dtype(header.get_data_dtype())
403403
obj.set_data_shape(header.get_data_shape())
404-
obj.set_zooms(header.get_zooms(units='raw'))
404+
obj.set_zooms(header.get_zooms(units='raw'), units='raw')
405405
if check:
406406
obj.check_fix()
407407
return obj
@@ -664,16 +664,16 @@ def get_base_affine(self):
664664

665665
get_best_affine = get_base_affine
666666

667-
def get_zooms(self, units='canonical', raise_unknown=False):
667+
def get_zooms(self, units='norm', raise_unknown=False):
668668
''' Get zooms (spacing between voxels along each axis) from header
669669
670670
Parameters
671671
----------
672-
units : {'canonical', 'raw'}, optional
673-
Return zooms in "canonical" units of mm/sec for spatial/temporal or
672+
units : {'norm', 'raw'}, optional
673+
Return zooms in normalized units of mm/sec for spatial/temporal or
674674
as raw values stored in header.
675675
raise_unkown : bool, optional
676-
If canonical units are requested and the units are ambiguous, raise
676+
If normalized units are requested and the units are ambiguous, raise
677677
a ``ValueError``
678678
679679
Returns
@@ -701,10 +701,19 @@ def get_zooms(self, units='canonical', raise_unknown=False):
701701
pixdims = hdr['pixdim']
702702
return tuple(pixdims[1:ndim + 1])
703703

704-
def set_zooms(self, zooms):
704+
def set_zooms(self, zooms, units='norm'):
705705
''' Set zooms into header fields
706706
707707
See docstring for ``get_zooms`` for examples
708+
709+
Parameters
710+
----------
711+
zooms : sequence of floats
712+
Zoom values to set in header
713+
units : {'norm', 'raw'}, optional
714+
Zooms are specified in normalized units of mm/sec for
715+
spatial/temporal or as raw values to be interpreted according to
716+
format specification.
708717
'''
709718
hdr = self._structarr
710719
dims = hdr['dim']
@@ -718,10 +727,6 @@ def set_zooms(self, zooms):
718727
pixdims = hdr['pixdim']
719728
pixdims[1:ndim + 1] = zooms[:]
720729

721-
def set_norm_zooms(self, zooms):
722-
''' Set zooms in mm/s units '''
723-
return self.set_zooms(zooms)
724-
725730
def as_analyze_map(self):
726731
""" Return header as mapping for conversion to Analyze types
727732

nibabel/freesurfer/mghformat.py

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ def _ndims(self):
238238
'''
239239
return 3 + (self._structarr['dims'][3] > 1)
240240

241-
def get_zooms(self, units='canonical', raise_unknown=False):
241+
def get_zooms(self, units='norm', raise_unknown=False):
242242
''' Get zooms from header
243243
244244
Returns the spacing of voxels in the x, y, and z dimensions.
@@ -247,18 +247,18 @@ def get_zooms(self, units='canonical', raise_unknown=False):
247247
TR is stored in milliseconds (see `The MGH/MGZ Volume Format
248248
<mghformat>`_),
249249
so if ``units == 'raw'``, the fourth zoom will be in ms.
250-
If ``units == 'canonical'`` (default), the fourth zoom will be in
250+
If ``units == 'norm'`` (default), the fourth zoom will be in
251251
seconds.
252252
253253
To access only the spatial zooms, use `hdr['delta']`.
254254
255255
Parameters
256256
----------
257-
units : {'canonical', 'raw'}, optional
258-
Return zooms in "canonical" units of mm/sec for spatial/temporal or
257+
units : {'norm', 'raw'}, optional
258+
Return zooms in normalized units of mm/sec for spatial/temporal or
259259
as raw values stored in header.
260260
raise_unkown : bool, optional
261-
If canonical units are requested and the units are ambiguous, raise
261+
If normalized units are requested and the units are ambiguous, raise
262262
a ``ValueError``
263263
264264
Returns
@@ -268,29 +268,40 @@ def get_zooms(self, units='canonical', raise_unknown=False):
268268
269269
.. _mghformat: https://surfer.nmr.mgh.harvard.edu/fswiki/FsTutorial/MghFormat#line-82
270270
'''
271-
if units == 'canonical':
271+
if units == 'norm':
272272
tfactor = 0.001
273273
elif units == 'raw':
274274
tfactor = 1
275275
else:
276-
raise ValueError("`units` parameter must be 'canonical' or 'raw'")
276+
raise ValueError("`units` parameter must be 'norm' or 'raw'")
277277

278278
# Do not return time zoom (TR) if 3D image
279279
tzoom = (self['tr'] * tfactor,) if self._ndims() > 3 else ()
280280
return tuple(self._structarr['delta']) + tzoom
281281

282-
def set_zooms(self, zooms):
282+
def set_zooms(self, zooms, units='norm'):
283283
''' Set zooms into header fields
284284
285285
Sets the spacing of voxels in the x, y, and z dimensions.
286-
For four-dimensional files, a temporal zoom (repetition time, or TR, in
287-
ms) may be provided as a fourth sequence element.
286+
For four-dimensional files, a temporal zoom (repetition time, or TR)
287+
may be provided as a fourth sequence element.
288+
289+
TR is stored in milliseconds (see `The MGH/MGZ Volume Format
290+
<mghformat>`_),
291+
so if ``units == 'raw'``, the fourth zoom will be interpreted as ms
292+
and stored unmodified.
293+
If ``units == 'norm'`` (default), the fourth zoom will be interpreted
294+
as seconds, and converted to ms before storing in the header.
288295
289296
Parameters
290297
----------
291298
zooms : sequence
292299
sequence of floats specifying spatial and (optionally) temporal
293300
zooms
301+
units : {'norm', 'raw'}, optional
302+
Zooms are specified in normalized units of mm/sec for
303+
spatial/temporal dimensions or as raw values to be stored in
304+
header.
294305
'''
295306
hdr = self._structarr
296307
zooms = np.asarray(zooms)
@@ -306,13 +317,13 @@ def set_zooms(self, zooms):
306317
if zooms[3] < 0:
307318
raise HeaderDataError('TR must be non-negative; got {!r}'
308319
''.format(zooms[3]))
309-
hdr['tr'] = zooms[3]
310-
311-
def set_norm_zooms(self, zooms):
312-
if len(zooms) == 4:
313-
zooms = zooms[:3] + (zooms[3] * 1000,)
314-
315-
self.set_zooms(zooms)
320+
if units == 'norm':
321+
tfactor = 1000
322+
elif units == 'raw':
323+
tfactor = 1
324+
else:
325+
raise ValueError("`units` parameter must be 'norm' or 'raw'")
326+
hdr['tr'] = zooms[3] * tfactor
316327

317328
def get_data_shape(self):
318329
''' Get shape of data

nibabel/freesurfer/tests/test_mghformat.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,12 @@ def test_read_mgh():
7070
assert h['dof'] == 0
7171
assert h['goodRASFlag'] == 1
7272
assert_array_equal(h['dims'], [3, 4, 5, 2])
73-
assert_almost_equal(h['tr'], 2.0)
73+
assert_almost_equal(h['tr'], 2)
7474
assert_almost_equal(h['flip_angle'], 0.0)
7575
assert_almost_equal(h['te'], 0.0)
7676
assert_almost_equal(h['ti'], 0.0)
77-
assert_array_almost_equal(h.get_zooms(), [1, 1, 1, 2])
77+
assert_array_almost_equal(h.get_zooms(units='raw'), [1, 1, 1, 2])
78+
assert_array_almost_equal(h.get_zooms(units='norm'), [1, 1, 1, 0.002])
7879
assert_array_almost_equal(h.get_vox2ras(), v2r)
7980
assert_array_almost_equal(h.get_vox2ras_tkr(), v2rtkr)
8081

@@ -147,7 +148,7 @@ def test_write_noaffine_mgh():
147148
def test_set_zooms():
148149
mgz = load(MGZ_FNAME)
149150
h = mgz.header
150-
assert_array_almost_equal(h.get_zooms(), [1, 1, 1, 2])
151+
assert_array_almost_equal(h.get_zooms(), [1, 1, 1, 0.002])
151152
h.set_zooms([1, 1, 1, 3])
152153
assert_array_almost_equal(h.get_zooms(), [1, 1, 1, 3])
153154
for zooms in ((-1, 1, 1, 1),
@@ -397,28 +398,28 @@ def test_zooms_edge_cases(self):
397398

398399
assert_array_almost_equal(img.header.get_zooms(units='raw'),
399400
(1, 1, 1, 0))
400-
assert_array_almost_equal(img.header.get_zooms(units='canonical'),
401+
assert_array_almost_equal(img.header.get_zooms(units='norm'),
401402
(1, 1, 1, 0))
402403

403-
img.header.set_zooms((1, 1, 1, 2000))
404+
img.header.set_zooms((1, 1, 1, 2000), units='raw')
404405
assert_array_almost_equal(img.header.get_zooms(units='raw'),
405406
(1, 1, 1, 2000))
406-
assert_array_almost_equal(img.header.get_zooms(units='canonical'),
407+
assert_array_almost_equal(img.header.get_zooms(units='norm'),
407408
(1, 1, 1, 2))
408409
assert_array_almost_equal(img.header.get_zooms(), (1, 1, 1, 2))
409410

410-
img.header.set_norm_zooms((2, 2, 2, 3))
411+
img.header.set_zooms((2, 2, 2, 3), units='norm')
411412
assert_array_almost_equal(img.header.get_zooms(units='raw'),
412413
(2, 2, 2, 3000))
413-
assert_array_almost_equal(img.header.get_zooms(units='canonical'),
414+
assert_array_almost_equal(img.header.get_zooms(units='norm'),
414415
(2, 2, 2, 3))
415416
assert_array_almost_equal(img.header.get_zooms(), (2, 2, 2, 3))
416417

417418
# It's legal to set zooms for spatial dimensions only
418-
img.header.set_norm_zooms((3, 3, 3))
419+
img.header.set_zooms((3, 3, 3), units='norm')
419420
assert_array_almost_equal(img.header.get_zooms(units='raw'),
420421
(3, 3, 3, 3000))
421-
assert_array_almost_equal(img.header.get_zooms(units='canonical'),
422+
assert_array_almost_equal(img.header.get_zooms(units='norm'),
422423
(3, 3, 3, 3))
423424
assert_array_almost_equal(img.header.get_zooms(), (3, 3, 3, 3))
424425

@@ -427,24 +428,24 @@ def test_zooms_edge_cases(self):
427428

428429
assert_array_almost_equal(img.header.get_zooms(units='raw'),
429430
(1, 1, 1))
430-
assert_array_almost_equal(img.header.get_zooms(units='canonical'),
431+
assert_array_almost_equal(img.header.get_zooms(units='norm'),
431432
(1, 1, 1))
432433

433-
img.header.set_zooms((2, 2, 2))
434+
img.header.set_zooms((2, 2, 2), units='raw')
434435
assert_array_almost_equal(img.header.get_zooms(units='raw'),
435436
(2, 2, 2))
436-
assert_array_almost_equal(img.header.get_zooms(units='canonical'),
437+
assert_array_almost_equal(img.header.get_zooms(units='norm'),
437438
(2, 2, 2))
438439

439-
img.header.set_norm_zooms((3, 3, 3))
440+
img.header.set_zooms((3, 3, 3), units='norm')
440441
assert_array_almost_equal(img.header.get_zooms(units='raw'),
441442
(3, 3, 3))
442-
assert_array_almost_equal(img.header.get_zooms(units='canonical'),
443+
assert_array_almost_equal(img.header.get_zooms(units='norm'),
443444
(3, 3, 3))
444445

445446
# Cannot set TR as zoom for 3D image
446-
assert_raises(HeaderDataError, img.header.set_zooms, (4, 4, 4, 5))
447-
assert_raises(HeaderDataError, img.header.set_norm_zooms, (4, 4, 4, 5))
447+
assert_raises(HeaderDataError, img.header.set_zooms, (4, 4, 4, 5), 'raw')
448+
assert_raises(HeaderDataError, img.header.set_zooms, (4, 4, 4, 5), 'norm')
448449

449450

450451
class TestMGHHeader(_TestLabeledWrapStruct):

0 commit comments

Comments
 (0)