Skip to content

Commit b61d708

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

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
@@ -402,7 +402,7 @@ def from_header(klass, header=None, check=True):
402402
klass))
403403
obj.set_data_dtype(header.get_data_dtype())
404404
obj.set_data_shape(header.get_data_shape())
405-
obj.set_zooms(header.get_zooms(units='raw'))
405+
obj.set_zooms(header.get_zooms(units='raw'), units='raw')
406406
if check:
407407
obj.check_fix()
408408
return obj
@@ -665,16 +665,16 @@ def get_base_affine(self):
665665

666666
get_best_affine = get_base_affine
667667

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

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

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

nibabel/freesurfer/mghformat.py

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

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

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

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

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

nibabel/freesurfer/tests/test_mghformat.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,12 @@ def test_read_mgh():
7272
assert_equal(h['dof'], 0)
7373
assert_equal(h['goodRASFlag'], 1)
7474
assert_array_equal(h['dims'], [3, 4, 5, 2])
75-
assert_almost_equal(h['tr'], 2.0)
75+
assert_almost_equal(h['tr'], 2)
7676
assert_almost_equal(h['flip_angle'], 0.0)
7777
assert_almost_equal(h['te'], 0.0)
7878
assert_almost_equal(h['ti'], 0.0)
79-
assert_array_almost_equal(h.get_zooms(), [1, 1, 1, 2])
79+
assert_array_almost_equal(h.get_zooms(units='raw'), [1, 1, 1, 2])
80+
assert_array_almost_equal(h.get_zooms(units='norm'), [1, 1, 1, 0.002])
8081
assert_array_almost_equal(h.get_vox2ras(), v2r)
8182
assert_array_almost_equal(h.get_vox2ras_tkr(), v2rtkr)
8283

@@ -149,7 +150,7 @@ def test_write_noaffine_mgh():
149150
def test_set_zooms():
150151
mgz = load(MGZ_FNAME)
151152
h = mgz.header
152-
assert_array_almost_equal(h.get_zooms(), [1, 1, 1, 2])
153+
assert_array_almost_equal(h.get_zooms(), [1, 1, 1, 0.002])
153154
h.set_zooms([1, 1, 1, 3])
154155
assert_array_almost_equal(h.get_zooms(), [1, 1, 1, 3])
155156
for zooms in ((-1, 1, 1, 1),
@@ -398,28 +399,28 @@ def test_zooms_edge_cases(self):
398399

399400
assert_array_almost_equal(img.header.get_zooms(units='raw'),
400401
(1, 1, 1, 0))
401-
assert_array_almost_equal(img.header.get_zooms(units='canonical'),
402+
assert_array_almost_equal(img.header.get_zooms(units='norm'),
402403
(1, 1, 1, 0))
403404

404-
img.header.set_zooms((1, 1, 1, 2000))
405+
img.header.set_zooms((1, 1, 1, 2000), units='raw')
405406
assert_array_almost_equal(img.header.get_zooms(units='raw'),
406407
(1, 1, 1, 2000))
407-
assert_array_almost_equal(img.header.get_zooms(units='canonical'),
408+
assert_array_almost_equal(img.header.get_zooms(units='norm'),
408409
(1, 1, 1, 2))
409410
assert_array_almost_equal(img.header.get_zooms(), (1, 1, 1, 2))
410411

411-
img.header.set_norm_zooms((2, 2, 2, 3))
412+
img.header.set_zooms((2, 2, 2, 3), units='norm')
412413
assert_array_almost_equal(img.header.get_zooms(units='raw'),
413414
(2, 2, 2, 3000))
414-
assert_array_almost_equal(img.header.get_zooms(units='canonical'),
415+
assert_array_almost_equal(img.header.get_zooms(units='norm'),
415416
(2, 2, 2, 3))
416417
assert_array_almost_equal(img.header.get_zooms(), (2, 2, 2, 3))
417418

418419
# It's legal to set zooms for spatial dimensions only
419-
img.header.set_norm_zooms((3, 3, 3))
420+
img.header.set_zooms((3, 3, 3), units='norm')
420421
assert_array_almost_equal(img.header.get_zooms(units='raw'),
421422
(3, 3, 3, 3000))
422-
assert_array_almost_equal(img.header.get_zooms(units='canonical'),
423+
assert_array_almost_equal(img.header.get_zooms(units='norm'),
423424
(3, 3, 3, 3))
424425
assert_array_almost_equal(img.header.get_zooms(), (3, 3, 3, 3))
425426

@@ -428,24 +429,24 @@ def test_zooms_edge_cases(self):
428429

429430
assert_array_almost_equal(img.header.get_zooms(units='raw'),
430431
(1, 1, 1))
431-
assert_array_almost_equal(img.header.get_zooms(units='canonical'),
432+
assert_array_almost_equal(img.header.get_zooms(units='norm'),
432433
(1, 1, 1))
433434

434-
img.header.set_zooms((2, 2, 2))
435+
img.header.set_zooms((2, 2, 2), units='raw')
435436
assert_array_almost_equal(img.header.get_zooms(units='raw'),
436437
(2, 2, 2))
437-
assert_array_almost_equal(img.header.get_zooms(units='canonical'),
438+
assert_array_almost_equal(img.header.get_zooms(units='norm'),
438439
(2, 2, 2))
439440

440-
img.header.set_norm_zooms((3, 3, 3))
441+
img.header.set_zooms((3, 3, 3), units='norm')
441442
assert_array_almost_equal(img.header.get_zooms(units='raw'),
442443
(3, 3, 3))
443-
assert_array_almost_equal(img.header.get_zooms(units='canonical'),
444+
assert_array_almost_equal(img.header.get_zooms(units='norm'),
444445
(3, 3, 3))
445446

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

450451

451452
class TestMGHHeader(_TestLabeledWrapStruct):

0 commit comments

Comments
 (0)