Repository: yeatmanlab/pyAFQ
Pull request #415 (2020-09-09 19:53:39)

Raise error earlier for empty ROI
In response to #397 . Does not fix issue but makes a more clear error.

- [x] Add test

pep8speaks: Hello @36000! Thanks for opening this PR. We checked the lines you've touched for [PEP 8](https://www.python.org/dev/peps/pep-0008) issues, and found:

* In the file [`AFQ/api.py`](https://github.com/yeatmanlab/pyAFQ/blob/5d8014f6f18b537264b6eb61146404aeb5a51b2f/AFQ/api.py):

> [Line 1093:29](https://github.com/yeatmanlab/pyAFQ/blob/5d8014f6f18b537264b6eb61146404aeb5a51b2f/AFQ/api.py#L1093): [E131](https://duckduckgo.com/?q=pep8%20E131) continuation line unaligned for hanging indent

* In the file [`AFQ/mask.py`](https://github.com/yeatmanlab/pyAFQ/blob/5d8014f6f18b537264b6eb61146404aeb5a51b2f/AFQ/mask.py):

> [Line 249:29](https://github.com/yeatmanlab/pyAFQ/blob/5d8014f6f18b537264b6eb61146404aeb5a51b2f/AFQ/mask.py#L249): [E131](https://duckduckgo.com/?q=pep8%20E131) continuation line unaligned for hanging indent






36000: @arokem does this look like a smart way to handle this?

Patch:
diff --git a/AFQ/api.py b/AFQ/api.py
index e5e8415f..f26116ee 100644
--- a/AFQ/api.py
+++ b/AFQ/api.py
@@ -1089,7 +1089,8 @@ class AFQ(object):
                     warped_roi = auv.patch_up_roi(
                         (mapping.transform_inverse(
                             roi.get_fdata(),
-                            interpolation='linear')) > 0).astype(int)
+                            interpolation='linear')) > 0,
+                        bundle_name=bundle).astype(int)
 
                     # Cast to float32, so that it can be read in by MI-Brain:
                     self.log_and_save_nii(
diff --git a/AFQ/mask.py b/AFQ/mask.py
index 9ea709d1..01dda77f 100644
--- a/AFQ/mask.py
+++ b/AFQ/mask.py
@@ -245,7 +245,8 @@ class RoiMask(StrInstantiatesMixin):
                     warped_roi = auv.patch_up_roi(
                         api.mapping.transform_inverse(
                             roi.get_fdata().astype(np.float32),
-                            interpolation='linear'))
+                            interpolation='linear'),
+                        bundle_name=bundle_name)
 
                     if mask_data is None:
                         mask_data = np.zeros(warped_roi.shape)
diff --git a/AFQ/segmentation.py b/AFQ/segmentation.py
index e2f47c1f..66e34d25 100644
--- a/AFQ/segmentation.py
+++ b/AFQ/segmentation.py
@@ -348,7 +348,8 @@ class Segmentation:
             if not isinstance(roi, np.ndarray):
                 roi = roi.get_fdata()
             warped_roi = auv.patch_up_roi(self.mapping.transform_inverse(
-                roi.astype(np.float32), interpolation='linear'))
+                roi.astype(np.float32), interpolation='linear'),
+                bundle_name=bundle)
 
             if rule:
                 # include ROI:
diff --git a/AFQ/utils/volume.py b/AFQ/utils/volume.py
index fdcccba5..8fa254c4 100644
--- a/AFQ/utils/volume.py
+++ b/AFQ/utils/volume.py
@@ -1,10 +1,13 @@
+import logging
+import numpy as np
+
 import scipy.ndimage as ndim
 from skimage.filters import gaussian
 from skimage.morphology import convex_hull_image
 from scipy.spatial.qhull import QhullError
 
 
-def patch_up_roi(roi):
+def patch_up_roi(roi, bundle_name="ROI"):
     """
     After being non-linearly transformed, ROIs tend to have holes in them.
     We perform a couple of computational geometry operations on the ROI to
@@ -21,12 +24,20 @@ def patch_up_roi(roi):
     truncate : float
         The truncation for the Gaussian
 
+    bundle_name : str, optional
+        Name of bundle, which may be useful for error messages.
+        Default: None
+
     Returns
     -------
     ROI after dilation and hole-filling
     """
 
     hole_filled = ndim.binary_fill_holes(roi > 0)
+    if not np.any(hole_filled):
+        raise ValueError((
+            f"{bundle_name} found to be empty after "
+            "applying the mapping."))
     try:
         return convex_hull_image(hole_filled)
     except QhullError:
diff --git a/examples/plot_tract_profile.py b/examples/plot_tract_profile.py
index a33264b6..186ea54e 100644
--- a/examples/plot_tract_profile.py
+++ b/examples/plot_tract_profile.py
@@ -112,7 +112,8 @@ if not op.exists('dti_streamlines.trk'):
                 warped_roi = patch_up_roi(
                     mapping.transform_inverse(
                         roi.get_fdata().astype(np.float32),
-                        interpolation='linear'))
+                        interpolation='linear'),
+                        bundle_name=bundle)
 
                 nib.save(nib.Nifti1Image(warped_roi.astype(float), img.affine),
                          f"{bundle}_{idx+1}.nii.gz")


Test patch:
diff --git a/AFQ/utils/tests/test_volume.py b/AFQ/utils/tests/test_volume.py
new file mode 100644
index 00000000..46a6cea7
--- /dev/null
+++ b/AFQ/utils/tests/test_volume.py
@@ -0,0 +1,13 @@
+import numpy as np
+import pytest
+
+import AFQ.utils.volume as AFV
+
+
+def test_patch_up_roi():
+    roi_bad = np.zeros((10, 10, 10))
+    roi_good = np.ones((10, 10, 10))
+
+    AFV.patch_up_roi(roi_good)
+    with pytest.raises(ValueError):
+        AFV.patch_up_roi(roi_bad)