Skip to content

Commit 022832e

Browse files
authored
Merge pull request #952 from JulianKlug/patch-1
NF: volume computation function for nifti masks
2 parents 917afab + 35beaba commit 022832e

File tree

5 files changed

+176
-0
lines changed

5 files changed

+176
-0
lines changed

nibabel/cmdline/stats.py

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!python
2+
# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*-
3+
# vi: set ft=python sts=4 ts=4 sw=4 et:
4+
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
5+
#
6+
# See COPYING file distributed along with the NiBabel package for the
7+
# copyright and license terms.
8+
#
9+
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
10+
"""
11+
Compute image statistics
12+
"""
13+
14+
import argparse
15+
from nibabel.loadsave import load
16+
from nibabel.imagestats import mask_volume, count_nonzero_voxels
17+
18+
19+
def _get_parser():
20+
"""Return command-line argument parser."""
21+
p = argparse.ArgumentParser(description=__doc__)
22+
p.add_argument("infile",
23+
help="Neuroimaging volume to compute statistics on.")
24+
p.add_argument("-V", "--Volume", action="store_true", required=False,
25+
help="Compute mask volume of a given mask image.")
26+
p.add_argument("--units", default="mm3", required=False,
27+
choices=("mm3", "vox"), help="Preferred output units")
28+
return p
29+
30+
31+
def main(args=None):
32+
"""Main program function."""
33+
parser = _get_parser()
34+
opts = parser.parse_args(args)
35+
from_img = load(opts.infile)
36+
37+
if opts.Volume:
38+
if opts.units == 'mm3':
39+
computed_volume = mask_volume(from_img)
40+
elif opts.units == 'vox':
41+
computed_volume = count_nonzero_voxels(from_img)
42+
else:
43+
raise ValueError(f'{opts.units} is not a valid unit. Choose "mm3" or "vox".')
44+
print(computed_volume)
45+
return 0

nibabel/cmdline/tests/test_stats.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!python
2+
# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*-
3+
# vi: set ft=python sts=4 ts=4 sw=4 et:
4+
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
5+
#
6+
# See COPYING file distributed along with the NiBabel package for the
7+
# copyright and license terms.
8+
#
9+
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
10+
11+
from io import StringIO
12+
import sys
13+
import numpy as np
14+
15+
from nibabel.loadsave import save
16+
from nibabel.cmdline.stats import main
17+
from nibabel import Nifti1Image
18+
19+
20+
def test_volume(tmpdir, capsys):
21+
mask_data = np.zeros((20, 20, 20), dtype='u1')
22+
mask_data[5:15, 5:15, 5:15] = 1
23+
img = Nifti1Image(mask_data, np.eye(4))
24+
25+
infile = tmpdir / "input.nii"
26+
save(img, infile)
27+
28+
args = (f"{infile} --Volume")
29+
main(args.split())
30+
vol_mm3 = capsys.readouterr()
31+
args = (f"{infile} --Volume --units vox")
32+
main(args.split())
33+
vol_vox = capsys.readouterr()
34+
35+
assert float(vol_mm3[0]) == 1000.0
36+
assert int(vol_vox[0]) == 1000

nibabel/imagestats.py

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*-
2+
# vi: set ft=python sts=4 ts=4 sw=4 et:
3+
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
4+
#
5+
# See COPYING file distributed along with the NiBabel package for the
6+
# copyright and license terms.
7+
#
8+
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
9+
"""
10+
Functions for computing image statistics
11+
"""
12+
13+
import numpy as np
14+
from nibabel.imageclasses import spatial_axes_first
15+
16+
17+
def count_nonzero_voxels(img):
18+
"""
19+
Count number of non-zero voxels
20+
21+
Parameters
22+
----------
23+
img : ``SpatialImage``
24+
All voxels of the mask should be of value 1, background should have value 0.
25+
26+
Returns
27+
-------
28+
count : int
29+
Number of non-zero voxels
30+
31+
"""
32+
return np.count_nonzero(img.dataobj)
33+
34+
35+
def mask_volume(img):
36+
""" Compute volume of mask image.
37+
38+
Equivalent to "fslstats /path/file.nii -V"
39+
40+
Parameters
41+
----------
42+
img : ``SpatialImage``
43+
All voxels of the mask should be of value 1, background should have value 0.
44+
45+
46+
Returns
47+
-------
48+
volume : float
49+
Volume of mask expressed in mm3.
50+
51+
Examples
52+
--------
53+
>>> import numpy as np
54+
>>> import nibabel as nb
55+
>>> mask_data = np.zeros((20, 20, 20), dtype='u1')
56+
>>> mask_data[5:15, 5:15, 5:15] = 1
57+
>>> nb.imagestats.mask_volume(nb.Nifti1Image(mask_data, np.eye(4)))
58+
1000.0
59+
"""
60+
if not spatial_axes_first(img):
61+
raise ValueError("Cannot calculate voxel volume for image with unknown spatial axes")
62+
voxel_volume_mm3 = np.prod(img.header.get_zooms()[:3])
63+
mask_volume_vx = count_nonzero_voxels(img)
64+
mask_volume_mm3 = mask_volume_vx * voxel_volume_mm3
65+
66+
return mask_volume_mm3

nibabel/tests/test_imagestats.py

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*-
2+
# vi: set ft=python sts=4 ts=4 sw=4 et:
3+
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
4+
#
5+
# See COPYING file distributed along with the NiBabel package for the
6+
# copyright and license terms.
7+
#
8+
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
9+
""" Tests for image statistics """
10+
11+
import numpy as np
12+
13+
from .. import imagestats
14+
from .. import Nifti1Image
15+
16+
17+
def test_mask_volume():
18+
# Test mask volume computation
19+
20+
mask_data = np.zeros((20, 20, 20), dtype='u1')
21+
mask_data[5:15, 5:15, 5:15] = 1
22+
img = Nifti1Image(mask_data, np.eye(4))
23+
24+
vol_mm3 = imagestats.mask_volume(img)
25+
vol_vox = imagestats.count_nonzero_voxels(img)
26+
27+
assert vol_mm3 == 1000.0
28+
assert vol_vox == 1000

setup.cfg

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ console_scripts =
7474
nib-ls=nibabel.cmdline.ls:main
7575
nib-dicomfs=nibabel.cmdline.dicomfs:main
7676
nib-diff=nibabel.cmdline.diff:main
77+
nib-stats=nibabel.cmdline.stats:main
7778
nib-nifti-dx=nibabel.cmdline.nifti_dx:main
7879
nib-tck2trk=nibabel.cmdline.tck2trk:main
7980
nib-trk2tck=nibabel.cmdline.trk2tck:main

0 commit comments

Comments
 (0)