Skip to content

Commit d357eae

Browse files
committed
Split Mask_volume into a function for voxels and a function for volume in mm3
- use a mask-like volume for testing
1 parent 7412ba9 commit d357eae

File tree

5 files changed

+71
-37
lines changed

5 files changed

+71
-37
lines changed

nibabel/cmdline/stats.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
import argparse
1515
from nibabel.loadsave import load
16-
from nibabel.imagestats import mask_volume
16+
from nibabel.imagestats import mask_volume, count_nonzero_voxels
1717

1818

1919
def _get_parser():
@@ -34,6 +34,11 @@ def main(args=None):
3434
from_img = load(opts.infile)
3535

3636
if opts.Volume:
37-
computed_volume = mask_volume(from_img, opts.units)
37+
if opts.units == 'mm3':
38+
computed_volume = mask_volume(from_img)
39+
elif opts.units == 'vox':
40+
computed_volume = count_nonzero_voxels(from_img)
41+
else:
42+
raise ValueError(f'{opts.units} is not a valid unit. Choose "mm3" or "vox".')
3843
print(computed_volume)
3944
return 0

nibabel/cmdline/tests/test_stats.py

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,40 @@
88
#
99
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
1010

11-
from nibabel.testing import test_data
11+
from io import StringIO
12+
import sys
13+
import numpy as np
14+
15+
from nibabel.loadsave import save
1216
from nibabel.cmdline.stats import main
17+
from nibabel import Nifti1Image
18+
19+
20+
class Capturing(list):
21+
def __enter__(self):
22+
self._stdout = sys.stdout
23+
sys.stdout = self._stringio = StringIO()
24+
return self
25+
def __exit__(self, *args):
26+
self.extend(self._stringio.getvalue().splitlines())
27+
del self._stringio # free up some memory
28+
sys.stdout = self._stdout
1329

1430

15-
def test_volume():
16-
infile = test_data(fname="anatomical.nii")
31+
def test_volume(tmpdir):
32+
mask_data = np.zeros((20, 20, 20), dtype='u1')
33+
mask_data[5:15, 5:15, 5:15] = 1
34+
img = Nifti1Image(mask_data, np.eye(4))
35+
36+
infile = tmpdir / "input.nii"
37+
save(img, infile)
38+
1739
args = (f"{infile} --Volume")
18-
vol_mm3 = main(args.split())
40+
with Capturing() as vol_mm3:
41+
main(args.split())
1942
args = (f"{infile} --Volume --units vox")
20-
vol_vox = main(args.split())
21-
22-
assert float(vol_mm3) == 2273328656.0
23-
assert float(vol_vox) == 284166082.0
43+
with Capturing() as vol_vox:
44+
main(args.split())
2445

46+
assert float(vol_mm3[0]) == 1000.0
47+
assert int(vol_vox[0]) == 1000

nibabel/imagestats.py

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,23 @@
1313
import numpy as np
1414
from nibabel.imageclasses import spatial_axes_first
1515

16+
def count_nonzero_voxels(img):
17+
"""
18+
Count number of non-zero voxels
19+
Parameters
20+
----------
21+
img : ``SpatialImage``
22+
All voxels of the mask should be of value 1, background should have value 0.
23+
24+
Returns
25+
-------
26+
non zero voxel volume: int
27+
Number of non-zero voxels
28+
29+
"""
30+
return np.count_nonzero(img.dataobj)
1631

17-
def mask_volume(img, units='mm3'):
32+
def mask_volume(img):
1833
""" Compute volume of mask image.
1934
Equivalent to "fslstats /path/file.nii -V"
2035
@@ -23,36 +38,24 @@ def mask_volume(img, units='mm3'):
2338
img : ``SpatialImage``
2439
All voxels of the mask should be of value 1, background should have value 0.
2540
26-
units : string {"mm3", "vox"}, optional
27-
Unit of the returned mask volume. Defaults to "mm3".
2841
2942
Returns
3043
-------
31-
mask_volume_vx: float
32-
Volume of mask expressed in voxels.
33-
34-
or
35-
3644
mask_volume_mm3: float
3745
Volume of mask expressed in mm3.
3846
3947
Examples
4048
--------
41-
>>> import nibabel as nf
42-
>>> path = 'path/to/nifti/mask.nii'
43-
>>> img = nf.load(path) # path is contains a path to an example nifti mask
44-
>>> mask_volume(img)
45-
50.3021
49+
>>> import nibabel as nb
50+
>>> mask_data = np.zeros((20, 20, 20), dtype='u1')
51+
>>> mask_data[5:15, 5:15, 5:15] = 1
52+
>>> nb.imagestats.mask_volume(nb.Nifti1Image(mask_data, np.eye(4))
53+
1000.0
4654
"""
4755
if not spatial_axes_first(img):
4856
raise ValueError("Cannot calculate voxel volume for image with unknown spatial axes")
4957
voxel_volume_mm3 = np.prod(img.header.get_zooms()[:3])
50-
mask_volume_vx = np.count_nonzero(img.dataobj)
58+
mask_volume_vx = count_nonzero_voxels(img)
5159
mask_volume_mm3 = mask_volume_vx * voxel_volume_mm3
5260

53-
if units == 'vox':
54-
return mask_volume_vx
55-
elif units == 'mm3':
56-
return mask_volume_mm3
57-
else:
58-
raise ValueError(f'{units} is not a valid unit. Choose "mm3" or "vox".')
61+
return mask_volume_mm3

nibabel/tests/test_imagestats.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,21 @@
1111
import numpy as np
1212

1313
from .. import imagestats
14-
from nibabel.testing import test_data
15-
from nibabel.loadsave import load
14+
from .. import Nifti1Image
1615

1716
import pytest
1817

1918

2019
def test_mask_volume():
2120
# Test mask volume computation
22-
infile = test_data(fname="anatomical.nii")
23-
img = load(infile)
21+
22+
mask_data = np.zeros((20, 20, 20), dtype='u1')
23+
mask_data[5:15, 5:15, 5:15] = 1
24+
img = Nifti1Image(mask_data, np.eye(4))
25+
2426
vol_mm3 = imagestats.mask_volume(img)
25-
vol_vox = imagestats.mask_volume(img, units='vox')
27+
vol_vox = imagestats.count_nonzero_voxels(img)
2628

27-
assert float(vol_mm3) == 2273328656.0
28-
assert float(vol_vox) == 284166082.0
29+
assert vol_mm3 == 1000.0
30+
assert vol_vox == 1000
2931

setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ console_scripts =
7373
nib-ls=nibabel.cmdline.ls:main
7474
nib-dicomfs=nibabel.cmdline.dicomfs:main
7575
nib-diff=nibabel.cmdline.diff:main
76+
nib-stats=nibabel.cmdline.stats:main
7677
nib-nifti-dx=nibabel.cmdline.nifti_dx:main
7778
nib-tck2trk=nibabel.cmdline.tck2trk:main
7879
nib-trk2tck=nibabel.cmdline.trk2tck:main

0 commit comments

Comments
 (0)