From d7c706975e8fc8c6a2fac9d83c2f006c236b4c6c Mon Sep 17 00:00:00 2001 From: Julian Klug Date: Wed, 9 Sep 2020 23:52:38 +0200 Subject: [PATCH 1/9] Added a volume computation function Compute volume of mask image. Equivalent to "fslstats /path/file.nii -V" --- nibabel/funcs.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/nibabel/funcs.py b/nibabel/funcs.py index df5eb0e96f..37eda9d66e 100644 --- a/nibabel/funcs.py +++ b/nibabel/funcs.py @@ -215,3 +215,34 @@ def _aff_is_diag(aff): """ Utility function returning True if affine is nearly diagonal """ rzs_aff = aff[:3, :3] return np.allclose(rzs_aff, np.diag(np.diag(rzs_aff))) + + +def mask_volume(img): + """ Compute volume of mask image. + Equivalent to "fslstats /path/file.nii -V" + + Parameters + ---------- + img : ``SpatialImage`` + All voxels of the mask should be of value 1, background should have value 0. + + Returns + ------- + mask_volume_mm3: float + Volume of mask expressed in mm3. + + Examples + -------- + >>> import nibabel as nf + >>> img = nf.load(path) # path is contains a path to an example nifti mask + >>> mask_volume(img) + 50.3021 + """ + header = img.header + _, vx, vy, vz, _, _, _, _ = header['pixdim'] + voxel_volume_mm3 = vx * vy * vz + mask = img.get_fdata() + mask_volume_vx = np.sum(mask) + mask_volume_mm3 = mask_volume_vx * voxel_volume_mm3 + + return mask_volume_mm3 From 73bff0ff6742ae4e4e04f33dcbe64f6d710f98ae Mon Sep 17 00:00:00 2001 From: Julian Klug Date: Sun, 18 Oct 2020 10:20:45 +0300 Subject: [PATCH 2/9] Added commandline and tests for imagestats volume computation --- bin/nib-stats.py | 18 +++++++++ nibabel/cmdline/stats.py | 39 ++++++++++++++++++++ nibabel/cmdline/tests/test_stats.py | 33 +++++++++++++++++ nibabel/funcs.py | 30 --------------- nibabel/imagestats.py | 57 +++++++++++++++++++++++++++++ nibabel/tests/test_imagestats.py | 29 +++++++++++++++ 6 files changed, 176 insertions(+), 30 deletions(-) create mode 100644 bin/nib-stats.py create mode 100644 nibabel/cmdline/stats.py create mode 100644 nibabel/cmdline/tests/test_stats.py create mode 100644 nibabel/imagestats.py create mode 100644 nibabel/tests/test_imagestats.py diff --git a/bin/nib-stats.py b/bin/nib-stats.py new file mode 100644 index 0000000000..4544b415aa --- /dev/null +++ b/bin/nib-stats.py @@ -0,0 +1,18 @@ +#!python +# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## +# +# See COPYING file distributed along with the NiBabel package for the +# copyright and license terms. +# +### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## +""" +Compute image statistics +""" + +from nibabel.cmdline.stats import main + + +if __name__ == '__main__': + main() diff --git a/nibabel/cmdline/stats.py b/nibabel/cmdline/stats.py new file mode 100644 index 0000000000..81aded8041 --- /dev/null +++ b/nibabel/cmdline/stats.py @@ -0,0 +1,39 @@ +#!python +# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## +# +# See COPYING file distributed along with the NiBabel package for the +# copyright and license terms. +# +### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## +""" +Compute image statistics +""" + +import argparse +from nibabel.loadsave import load +from nibabel.imagestats import mask_volume + + +def _get_parser(): + """Return command-line argument parser.""" + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("infile", + help="Neuroimaging volume to compute statistics on.") + p.add_argument("-V", "--Volume", action="store_true", required=False, + help="Compute mask volume of a given mask image.") + p.add_argument("--units", default="mm3", required=False, + help="Preferred output units of {mm3, vox}. Defaults to mm3") + return p + +def main(args=None): + """Main program function.""" + parser = _get_parser() + opts = parser.parse_args(args) + from_img = load(opts.infile) + + if opts.Volume: + computed_volume = mask_volume(from_img, opts.units) + print(computed_volume) + return computed_volume diff --git a/nibabel/cmdline/tests/test_stats.py b/nibabel/cmdline/tests/test_stats.py new file mode 100644 index 0000000000..4fa8a14094 --- /dev/null +++ b/nibabel/cmdline/tests/test_stats.py @@ -0,0 +1,33 @@ +#!python +# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## +# +# See COPYING file distributed along with the NiBabel package for the +# copyright and license terms. +# +### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## + +import unittest + +import pytest + +from nibabel.testing import test_data +from nibabel.cmdline.stats import main +from nibabel.optpkg import optional_package + +_, have_scipy, _ = optional_package('scipy.ndimage') +needs_scipy = unittest.skipUnless(have_scipy, 'These tests need scipy') + + +def test_volume(): + infile = test_data(fname="anatomical.nii") + args = (f"{infile} --Volume") + vol_mm3 = main(args.split()) + args = (f"{infile} --Volume --units vox") + vol_vox = main(args.split()) + + assert float(vol_mm3) == 2273328656.0 + assert float(vol_vox) == 284166082.0 + + diff --git a/nibabel/funcs.py b/nibabel/funcs.py index 37eda9d66e..e12acee57d 100644 --- a/nibabel/funcs.py +++ b/nibabel/funcs.py @@ -216,33 +216,3 @@ def _aff_is_diag(aff): rzs_aff = aff[:3, :3] return np.allclose(rzs_aff, np.diag(np.diag(rzs_aff))) - -def mask_volume(img): - """ Compute volume of mask image. - Equivalent to "fslstats /path/file.nii -V" - - Parameters - ---------- - img : ``SpatialImage`` - All voxels of the mask should be of value 1, background should have value 0. - - Returns - ------- - mask_volume_mm3: float - Volume of mask expressed in mm3. - - Examples - -------- - >>> import nibabel as nf - >>> img = nf.load(path) # path is contains a path to an example nifti mask - >>> mask_volume(img) - 50.3021 - """ - header = img.header - _, vx, vy, vz, _, _, _, _ = header['pixdim'] - voxel_volume_mm3 = vx * vy * vz - mask = img.get_fdata() - mask_volume_vx = np.sum(mask) - mask_volume_mm3 = mask_volume_vx * voxel_volume_mm3 - - return mask_volume_mm3 diff --git a/nibabel/imagestats.py b/nibabel/imagestats.py new file mode 100644 index 0000000000..c6b855ae8e --- /dev/null +++ b/nibabel/imagestats.py @@ -0,0 +1,57 @@ +# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## +# +# See COPYING file distributed along with the NiBabel package for the +# copyright and license terms. +# +### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## +""" +Functions for computing image statistics +""" + +import numpy as np + + +def mask_volume(img, units='mm3'): + """ Compute volume of mask image. + Equivalent to "fslstats /path/file.nii -V" + + Parameters + ---------- + img : ``SpatialImage`` + All voxels of the mask should be of value 1, background should have value 0. + + units : string {"mm3", "vox"}, optional + Unit of the returned mask volume. Defaults to "mm3". + + Returns + ------- + mask_volume_vx: float + Volume of mask expressed in voxels. + + or + + mask_volume_mm3: float + Volume of mask expressed in mm3. + + Examples + -------- + >>> import nibabel as nf + >>> img = nf.load(path) # path is contains a path to an example nifti mask + >>> mask_volume(img) + 50.3021 + """ + header = img.header + _, vx, vy, vz, _, _, _, _ = header['pixdim'] + voxel_volume_mm3 = vx * vy * vz + mask = img.get_fdata() + mask_volume_vx = np.sum(mask) + mask_volume_mm3 = mask_volume_vx * voxel_volume_mm3 + + if units == 'vox': + return mask_volume_vx + elif units == 'mm3': + return mask_volume_mm3 + else: + raise ValueError(f'{units} is not a valid unit. Choose "mm3" or "vox".') diff --git a/nibabel/tests/test_imagestats.py b/nibabel/tests/test_imagestats.py new file mode 100644 index 0000000000..918bf38fc3 --- /dev/null +++ b/nibabel/tests/test_imagestats.py @@ -0,0 +1,29 @@ +# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## +# +# See COPYING file distributed along with the NiBabel package for the +# copyright and license terms. +# +### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## +""" Tests for image statistics """ + +import numpy as np + +from .. import imagestats +from nibabel.testing import test_data +from nibabel.loadsave import load + +import pytest + + +def test_mask_volume(): + # Test mask volume computation + infile = test_data(fname="anatomical.nii") + img = load(infile) + vol_mm3 = imagestats.mask_volume(img) + vol_vox = imagestats.mask_volume(img, units='vox') + + assert float(vol_mm3) == 2273328656.0 + assert float(vol_vox) == 284166082.0 + From 57f7723ca46d964e710ad5cefbf2bcd40c096563 Mon Sep 17 00:00:00 2001 From: Julian Klug Date: Sun, 18 Oct 2020 10:41:02 +0300 Subject: [PATCH 3/9] Fix style errors - blank lines - variable declaration in example --- nibabel/imagestats.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nibabel/imagestats.py b/nibabel/imagestats.py index c6b855ae8e..1877c1fff4 100644 --- a/nibabel/imagestats.py +++ b/nibabel/imagestats.py @@ -38,6 +38,7 @@ def mask_volume(img, units='mm3'): Examples -------- >>> import nibabel as nf + >>> path = 'path/to/nifti/mask.nii' >>> img = nf.load(path) # path is contains a path to an example nifti mask >>> mask_volume(img) 50.3021 From e15bb8d1c43ca6d20996bcbd022195b7a77029c7 Mon Sep 17 00:00:00 2001 From: Julian Klug Date: Sun, 18 Oct 2020 19:43:34 +0200 Subject: [PATCH 4/9] Apply suggestions from code review: Removing unused code Co-authored-by: Chris Markiewicz --- nibabel/cmdline/stats.py | 4 ++-- nibabel/cmdline/tests/test_stats.py | 9 --------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/nibabel/cmdline/stats.py b/nibabel/cmdline/stats.py index 81aded8041..e7e4348003 100644 --- a/nibabel/cmdline/stats.py +++ b/nibabel/cmdline/stats.py @@ -24,7 +24,7 @@ def _get_parser(): p.add_argument("-V", "--Volume", action="store_true", required=False, help="Compute mask volume of a given mask image.") p.add_argument("--units", default="mm3", required=False, - help="Preferred output units of {mm3, vox}. Defaults to mm3") + choices=("mm3", "vox"), help="Preferred output units") return p def main(args=None): @@ -36,4 +36,4 @@ def main(args=None): if opts.Volume: computed_volume = mask_volume(from_img, opts.units) print(computed_volume) - return computed_volume + return 0 diff --git a/nibabel/cmdline/tests/test_stats.py b/nibabel/cmdline/tests/test_stats.py index 4fa8a14094..dd14044486 100644 --- a/nibabel/cmdline/tests/test_stats.py +++ b/nibabel/cmdline/tests/test_stats.py @@ -8,16 +8,8 @@ # ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## -import unittest - -import pytest - from nibabel.testing import test_data from nibabel.cmdline.stats import main -from nibabel.optpkg import optional_package - -_, have_scipy, _ = optional_package('scipy.ndimage') -needs_scipy = unittest.skipUnless(have_scipy, 'These tests need scipy') def test_volume(): @@ -30,4 +22,3 @@ def test_volume(): assert float(vol_mm3) == 2273328656.0 assert float(vol_vox) == 284166082.0 - From 7412ba9789d431297ddee02db4403aa14bd57d2d Mon Sep 17 00:00:00 2001 From: Julian Klug Date: Sun, 18 Oct 2020 19:55:12 +0200 Subject: [PATCH 5/9] Apply suggestions from code review: Use header.get_zooms instead of header.pixdim Co-authored-by: Chris Markiewicz --- nibabel/imagestats.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nibabel/imagestats.py b/nibabel/imagestats.py index 1877c1fff4..9b4356ca8a 100644 --- a/nibabel/imagestats.py +++ b/nibabel/imagestats.py @@ -11,6 +11,7 @@ """ import numpy as np +from nibabel.imageclasses import spatial_axes_first def mask_volume(img, units='mm3'): @@ -43,11 +44,10 @@ def mask_volume(img, units='mm3'): >>> mask_volume(img) 50.3021 """ - header = img.header - _, vx, vy, vz, _, _, _, _ = header['pixdim'] - voxel_volume_mm3 = vx * vy * vz - mask = img.get_fdata() - mask_volume_vx = np.sum(mask) + if not spatial_axes_first(img): + raise ValueError("Cannot calculate voxel volume for image with unknown spatial axes") + voxel_volume_mm3 = np.prod(img.header.get_zooms()[:3]) + mask_volume_vx = np.count_nonzero(img.dataobj) mask_volume_mm3 = mask_volume_vx * voxel_volume_mm3 if units == 'vox': From d357eae8d70d53f0f6c2d41265fafc38a3cbac17 Mon Sep 17 00:00:00 2001 From: Julian Klug Date: Mon, 19 Oct 2020 01:11:02 +0300 Subject: [PATCH 6/9] Split Mask_volume into a function for voxels and a function for volume in mm3 - use a mask-like volume for testing --- nibabel/cmdline/stats.py | 9 ++++-- nibabel/cmdline/tests/test_stats.py | 39 ++++++++++++++++++++------ nibabel/imagestats.py | 43 +++++++++++++++-------------- nibabel/tests/test_imagestats.py | 16 ++++++----- setup.cfg | 1 + 5 files changed, 71 insertions(+), 37 deletions(-) diff --git a/nibabel/cmdline/stats.py b/nibabel/cmdline/stats.py index e7e4348003..0ea4617936 100644 --- a/nibabel/cmdline/stats.py +++ b/nibabel/cmdline/stats.py @@ -13,7 +13,7 @@ import argparse from nibabel.loadsave import load -from nibabel.imagestats import mask_volume +from nibabel.imagestats import mask_volume, count_nonzero_voxels def _get_parser(): @@ -34,6 +34,11 @@ def main(args=None): from_img = load(opts.infile) if opts.Volume: - computed_volume = mask_volume(from_img, opts.units) + if opts.units == 'mm3': + computed_volume = mask_volume(from_img) + elif opts.units == 'vox': + computed_volume = count_nonzero_voxels(from_img) + else: + raise ValueError(f'{opts.units} is not a valid unit. Choose "mm3" or "vox".') print(computed_volume) return 0 diff --git a/nibabel/cmdline/tests/test_stats.py b/nibabel/cmdline/tests/test_stats.py index dd14044486..2a3e43a4d5 100644 --- a/nibabel/cmdline/tests/test_stats.py +++ b/nibabel/cmdline/tests/test_stats.py @@ -8,17 +8,40 @@ # ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## -from nibabel.testing import test_data +from io import StringIO +import sys +import numpy as np + +from nibabel.loadsave import save from nibabel.cmdline.stats import main +from nibabel import Nifti1Image + + +class Capturing(list): + def __enter__(self): + self._stdout = sys.stdout + sys.stdout = self._stringio = StringIO() + return self + def __exit__(self, *args): + self.extend(self._stringio.getvalue().splitlines()) + del self._stringio # free up some memory + sys.stdout = self._stdout -def test_volume(): - infile = test_data(fname="anatomical.nii") +def test_volume(tmpdir): + mask_data = np.zeros((20, 20, 20), dtype='u1') + mask_data[5:15, 5:15, 5:15] = 1 + img = Nifti1Image(mask_data, np.eye(4)) + + infile = tmpdir / "input.nii" + save(img, infile) + args = (f"{infile} --Volume") - vol_mm3 = main(args.split()) + with Capturing() as vol_mm3: + main(args.split()) args = (f"{infile} --Volume --units vox") - vol_vox = main(args.split()) - - assert float(vol_mm3) == 2273328656.0 - assert float(vol_vox) == 284166082.0 + with Capturing() as vol_vox: + main(args.split()) + assert float(vol_mm3[0]) == 1000.0 + assert int(vol_vox[0]) == 1000 \ No newline at end of file diff --git a/nibabel/imagestats.py b/nibabel/imagestats.py index 9b4356ca8a..20422fee54 100644 --- a/nibabel/imagestats.py +++ b/nibabel/imagestats.py @@ -13,8 +13,23 @@ import numpy as np from nibabel.imageclasses import spatial_axes_first +def count_nonzero_voxels(img): + """ + Count number of non-zero voxels + Parameters + ---------- + img : ``SpatialImage`` + All voxels of the mask should be of value 1, background should have value 0. + + Returns + ------- + non zero voxel volume: int + Number of non-zero voxels + + """ + return np.count_nonzero(img.dataobj) -def mask_volume(img, units='mm3'): +def mask_volume(img): """ Compute volume of mask image. Equivalent to "fslstats /path/file.nii -V" @@ -23,36 +38,24 @@ def mask_volume(img, units='mm3'): img : ``SpatialImage`` All voxels of the mask should be of value 1, background should have value 0. - units : string {"mm3", "vox"}, optional - Unit of the returned mask volume. Defaults to "mm3". Returns ------- - mask_volume_vx: float - Volume of mask expressed in voxels. - - or - mask_volume_mm3: float Volume of mask expressed in mm3. Examples -------- - >>> import nibabel as nf - >>> path = 'path/to/nifti/mask.nii' - >>> img = nf.load(path) # path is contains a path to an example nifti mask - >>> mask_volume(img) - 50.3021 + >>> import nibabel as nb + >>> mask_data = np.zeros((20, 20, 20), dtype='u1') + >>> mask_data[5:15, 5:15, 5:15] = 1 + >>> nb.imagestats.mask_volume(nb.Nifti1Image(mask_data, np.eye(4)) + 1000.0 """ if not spatial_axes_first(img): raise ValueError("Cannot calculate voxel volume for image with unknown spatial axes") voxel_volume_mm3 = np.prod(img.header.get_zooms()[:3]) - mask_volume_vx = np.count_nonzero(img.dataobj) + mask_volume_vx = count_nonzero_voxels(img) mask_volume_mm3 = mask_volume_vx * voxel_volume_mm3 - if units == 'vox': - return mask_volume_vx - elif units == 'mm3': - return mask_volume_mm3 - else: - raise ValueError(f'{units} is not a valid unit. Choose "mm3" or "vox".') + return mask_volume_mm3 diff --git a/nibabel/tests/test_imagestats.py b/nibabel/tests/test_imagestats.py index 918bf38fc3..d8ef2018c1 100644 --- a/nibabel/tests/test_imagestats.py +++ b/nibabel/tests/test_imagestats.py @@ -11,19 +11,21 @@ import numpy as np from .. import imagestats -from nibabel.testing import test_data -from nibabel.loadsave import load +from .. import Nifti1Image import pytest def test_mask_volume(): # Test mask volume computation - infile = test_data(fname="anatomical.nii") - img = load(infile) + + mask_data = np.zeros((20, 20, 20), dtype='u1') + mask_data[5:15, 5:15, 5:15] = 1 + img = Nifti1Image(mask_data, np.eye(4)) + vol_mm3 = imagestats.mask_volume(img) - vol_vox = imagestats.mask_volume(img, units='vox') + vol_vox = imagestats.count_nonzero_voxels(img) - assert float(vol_mm3) == 2273328656.0 - assert float(vol_vox) == 284166082.0 + assert vol_mm3 == 1000.0 + assert vol_vox == 1000 diff --git a/setup.cfg b/setup.cfg index c2bf604f94..141a37caec 100644 --- a/setup.cfg +++ b/setup.cfg @@ -73,6 +73,7 @@ console_scripts = nib-ls=nibabel.cmdline.ls:main nib-dicomfs=nibabel.cmdline.dicomfs:main nib-diff=nibabel.cmdline.diff:main + nib-stats=nibabel.cmdline.stats:main nib-nifti-dx=nibabel.cmdline.nifti_dx:main nib-tck2trk=nibabel.cmdline.tck2trk:main nib-trk2tck=nibabel.cmdline.trk2tck:main From 93532dedbb547597bbc29444bd67e5068b5a9752 Mon Sep 17 00:00:00 2001 From: Julian Klug Date: Mon, 19 Oct 2020 09:12:41 +0300 Subject: [PATCH 7/9] Fix failing docstring --- nibabel/imagestats.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nibabel/imagestats.py b/nibabel/imagestats.py index 20422fee54..74506a07ff 100644 --- a/nibabel/imagestats.py +++ b/nibabel/imagestats.py @@ -46,10 +46,11 @@ def mask_volume(img): Examples -------- + >>> import numpy as np >>> import nibabel as nb >>> mask_data = np.zeros((20, 20, 20), dtype='u1') >>> mask_data[5:15, 5:15, 5:15] = 1 - >>> nb.imagestats.mask_volume(nb.Nifti1Image(mask_data, np.eye(4)) + >>> nb.imagestats.mask_volume(nb.Nifti1Image(mask_data, np.eye(4))) 1000.0 """ if not spatial_axes_first(img): From 2331e9d33677cb7fb14eec84d925b7dd18cce740 Mon Sep 17 00:00:00 2001 From: Julian Klug Date: Mon, 19 Oct 2020 22:10:13 +0200 Subject: [PATCH 8/9] Apply suggestions from code review Co-authored-by: Chris Markiewicz --- nibabel/imagestats.py | 6 ++++-- nibabel/tests/test_imagestats.py | 3 --- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/nibabel/imagestats.py b/nibabel/imagestats.py index 74506a07ff..3af2d8a089 100644 --- a/nibabel/imagestats.py +++ b/nibabel/imagestats.py @@ -16,6 +16,7 @@ def count_nonzero_voxels(img): """ Count number of non-zero voxels + Parameters ---------- img : ``SpatialImage`` @@ -23,7 +24,7 @@ def count_nonzero_voxels(img): Returns ------- - non zero voxel volume: int + count : int Number of non-zero voxels """ @@ -31,6 +32,7 @@ def count_nonzero_voxels(img): def mask_volume(img): """ Compute volume of mask image. + Equivalent to "fslstats /path/file.nii -V" Parameters @@ -41,7 +43,7 @@ def mask_volume(img): Returns ------- - mask_volume_mm3: float + volume : float Volume of mask expressed in mm3. Examples diff --git a/nibabel/tests/test_imagestats.py b/nibabel/tests/test_imagestats.py index d8ef2018c1..e104013ddd 100644 --- a/nibabel/tests/test_imagestats.py +++ b/nibabel/tests/test_imagestats.py @@ -13,8 +13,6 @@ from .. import imagestats from .. import Nifti1Image -import pytest - def test_mask_volume(): # Test mask volume computation @@ -28,4 +26,3 @@ def test_mask_volume(): assert vol_mm3 == 1000.0 assert vol_vox == 1000 - From 35beabab98e6ab2a924fd91055c70277bf91cf7b Mon Sep 17 00:00:00 2001 From: Julian Klug Date: Tue, 20 Oct 2020 00:34:49 +0300 Subject: [PATCH 9/9] Style issues, use capsys --- bin/nib-stats.py | 18 ------------------ nibabel/cmdline/stats.py | 1 + nibabel/cmdline/tests/test_stats.py | 21 +++++---------------- nibabel/funcs.py | 1 - nibabel/imagestats.py | 2 ++ 5 files changed, 8 insertions(+), 35 deletions(-) delete mode 100644 bin/nib-stats.py diff --git a/bin/nib-stats.py b/bin/nib-stats.py deleted file mode 100644 index 4544b415aa..0000000000 --- a/bin/nib-stats.py +++ /dev/null @@ -1,18 +0,0 @@ -#!python -# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*- -# vi: set ft=python sts=4 ts=4 sw=4 et: -### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## -# -# See COPYING file distributed along with the NiBabel package for the -# copyright and license terms. -# -### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## -""" -Compute image statistics -""" - -from nibabel.cmdline.stats import main - - -if __name__ == '__main__': - main() diff --git a/nibabel/cmdline/stats.py b/nibabel/cmdline/stats.py index 0ea4617936..91b9f7c104 100644 --- a/nibabel/cmdline/stats.py +++ b/nibabel/cmdline/stats.py @@ -27,6 +27,7 @@ def _get_parser(): choices=("mm3", "vox"), help="Preferred output units") return p + def main(args=None): """Main program function.""" parser = _get_parser() diff --git a/nibabel/cmdline/tests/test_stats.py b/nibabel/cmdline/tests/test_stats.py index 2a3e43a4d5..1ceac90231 100644 --- a/nibabel/cmdline/tests/test_stats.py +++ b/nibabel/cmdline/tests/test_stats.py @@ -17,18 +17,7 @@ from nibabel import Nifti1Image -class Capturing(list): - def __enter__(self): - self._stdout = sys.stdout - sys.stdout = self._stringio = StringIO() - return self - def __exit__(self, *args): - self.extend(self._stringio.getvalue().splitlines()) - del self._stringio # free up some memory - sys.stdout = self._stdout - - -def test_volume(tmpdir): +def test_volume(tmpdir, capsys): mask_data = np.zeros((20, 20, 20), dtype='u1') mask_data[5:15, 5:15, 5:15] = 1 img = Nifti1Image(mask_data, np.eye(4)) @@ -37,11 +26,11 @@ def test_volume(tmpdir): save(img, infile) args = (f"{infile} --Volume") - with Capturing() as vol_mm3: - main(args.split()) + main(args.split()) + vol_mm3 = capsys.readouterr() args = (f"{infile} --Volume --units vox") - with Capturing() as vol_vox: - main(args.split()) + main(args.split()) + vol_vox = capsys.readouterr() assert float(vol_mm3[0]) == 1000.0 assert int(vol_vox[0]) == 1000 \ No newline at end of file diff --git a/nibabel/funcs.py b/nibabel/funcs.py index e12acee57d..df5eb0e96f 100644 --- a/nibabel/funcs.py +++ b/nibabel/funcs.py @@ -215,4 +215,3 @@ def _aff_is_diag(aff): """ Utility function returning True if affine is nearly diagonal """ rzs_aff = aff[:3, :3] return np.allclose(rzs_aff, np.diag(np.diag(rzs_aff))) - diff --git a/nibabel/imagestats.py b/nibabel/imagestats.py index 3af2d8a089..4520d7f612 100644 --- a/nibabel/imagestats.py +++ b/nibabel/imagestats.py @@ -13,6 +13,7 @@ import numpy as np from nibabel.imageclasses import spatial_axes_first + def count_nonzero_voxels(img): """ Count number of non-zero voxels @@ -30,6 +31,7 @@ def count_nonzero_voxels(img): """ return np.count_nonzero(img.dataobj) + def mask_volume(img): """ Compute volume of mask image.