-
Notifications
You must be signed in to change notification settings - Fork 258
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
NF: volume computation function for nifti masks #952
Conversation
Compute volume of mask image. Equivalent to "fslstats /path/file.nii -V"
A few questions to help me guide this PR:
|
Codecov Report
@@ Coverage Diff @@
## master #952 +/- ##
==========================================
+ Coverage 91.84% 91.89% +0.04%
==========================================
Files 97 101 +4
Lines 12428 12548 +120
Branches 2191 2208 +17
==========================================
+ Hits 11415 11531 +116
- Misses 678 680 +2
- Partials 335 337 +2
Continue to review full report at Codecov.
|
I think you've forgot a
or
|
If we want to implement something equivalent to The command line should be in |
@0rC0 actually there is a return on line 248, it is just hidden by the codecov messages. return mask_volume_mm3 |
@effigies : Sounds good to me as well. But do you think every element of fslstats should be implemented with a single release or should we implement them gradually? |
I think gradually is fine. Let's call it Also, don't feel compelled to mimic the One thing to consider: |
Hello @JulianKlug, Thank you for updating! Cheers! There are no style issues detected in this Pull Request. 🍻 To test for issues locally, Comment last updated at 2020-10-19 21:36:20 UTC |
- blank lines - variable declaration in example
There seems to be an issue where the example part of the docstring is interpreted as code and not as a string which then lets the tests fail. >>> path = 'path/to/nifti/mask.nii'
>>> img = nf.load(path) # path is contains a path to an example nifti mask
>>> mask_volume(img) Not quite sure how to resolve this... |
would be ready for #960 if the aforementioned issue is resolved |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pushing on this. I've left a batch of comments. Feel free to argue and ask questions. :-)
nibabel/tests/test_imagestats.py
Outdated
assert float(vol_mm3) == 2273328656.0 | ||
assert float(vol_vox) == 284166082.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$ fslstats nibabel/tests/data/anatomical.nii -V
33825 270600.000000
I'm guessing (but haven't checked) the discrepancy is from values > 1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought so too, but hadn't time to check.
The volume computation is only reliable for 0-1 masks, but as I didn't find any in the sample data, I didn’t use one for the test.
bin/nib-stats.py
Outdated
|
||
|
||
if __name__ == '__main__': | ||
main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These files are actually out-of-date. Just add an entry to the entry points:
Lines 71 to 81 in 917afab
[options.entry_points] | |
console_scripts = | |
nib-conform=nibabel.cmdline.conform:main | |
nib-ls=nibabel.cmdline.ls:main | |
nib-dicomfs=nibabel.cmdline.dicomfs:main | |
nib-diff=nibabel.cmdline.diff:main | |
nib-nifti-dx=nibabel.cmdline.nifti_dx:main | |
nib-tck2trk=nibabel.cmdline.tck2trk:main | |
nib-trk2tck=nibabel.cmdline.trk2tck:main | |
nib-roi=nibabel.cmdline.roi:main | |
parrec2nii=nibabel.cmdline.parrec2nii:main |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great hadn't seen that!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove this file.
nibabel/imagestats.py
Outdated
import numpy as np | ||
|
||
|
||
def mask_volume(img, units='mm3'): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about this a bit, if units=="vox"
, then the output should be integer; there's no good reason to have a float for counting non-zero voxels. That makes the types a bit messy. What if we did (simplified):
def count_nonzero_voxels(img):
return np.count_nonzero(img.dataobj)
def mask_volume(img):
nz_vox = count_nonzero_voxels(img)
return np.prod(img.header.get_zooms()[:3]) * nz_vox
The conditional logic inside this function basically evaporates, and is moved into cmdline.stats.main
to select which function to call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me. I have simply left the code a bit more verbose for better readability.
Co-authored-by: Chris Markiewicz <[email protected]>
…eader.pixdim Co-authored-by: Chris Markiewicz <[email protected]>
…e in mm3 - use a mask-like volume for testing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly style suggestions. And please use flake8
to check for any additional issues.
bin/nib-stats.py
Outdated
|
||
|
||
if __name__ == '__main__': | ||
main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove this file.
nibabel/cmdline/tests/test_stats.py
Outdated
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the capsys
fixture..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks, didn't know about that one
nibabel/funcs.py
Outdated
@@ -215,3 +215,4 @@ 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))) | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to touch this file.
Co-authored-by: Chris Markiewicz <[email protected]>
Sorry for the style issues. Should be fixed now. |
Thanks a lot! This is a good start to a handy utility, and I look forward to seeing it grow. |
Adds a function to compute the volume of a mask image in mm3.
Simple vanilla python equivalent to "fslstats /path/file.nii -V".
Reason: Useful for pure python projects where one can not rely on fsl.
To Do: