Repository: scikit-image/scikit-image

Issue:
measure.label speed
This is triggered by [this Stackoverflow question](https://stackoverflow.com/questions/62804953/performance-differences-between-bwlabeln-on-matlab-and-skimage-measure-label-on/62842582#62842582). When I have large binary arrays to label and performance issues, I usually resort to calling the ndimage version. Could we imagine having a `fast_binary` flag which would call the ndimage function? A factor of 3-4 (from a few tests I just ran) is not bad...


Discussion:
Why don't we just automatically call ndimage on binary arrays? In that case the functions are equivalent, right?
In which case this makes for a nice sprint issue! 😊 
To clarify, what I'm proposing is that at the top of the [`skimage.measure._label.label`](https://github.com/scikit-image/scikit-image/blob/7b62ec14e995af4d392d0cff81db304b6d8f9aa5/skimage/measure/_label.py#L92) function we do something like:

```python
if input.dtype == bool:
    # use ndimage.label
else:
    # current call to clabel
```
Your suggestion sounds good to me @jni. I think @stefanv implemented the original algorithm so maybe he can comment.
Confirmed by Stéfan (off Github), so definitely a good issue for the sprint!
Seems to be not achieved during the sprint. I take it.

Resolving patch:
diff --git a/skimage/measure/_label.py b/skimage/measure/_label.py
--- a/skimage/measure/_label.py
+++ b/skimage/measure/_label.py
@@ -1,6 +1,34 @@
+from scipy import ndimage
 from ._ccomp import label_cython as clabel
 
 
+def _label_bool(image, background=None, return_num=False, connectivity=None):
+    """Faster implementation of clabel for boolean input.
+
+    See context: https://github.com/scikit-image/scikit-image/issues/4833
+    """
+    from ..morphology._util import _resolve_neighborhood
+    if background == 1:
+        image = ~image
+
+    if connectivity is None:
+        connectivity = image.ndim
+
+    if not 1 <= connectivity <= image.ndim:
+        raise ValueError(
+            f'Connectivity for {image.ndim}D image should '
+            f'be in [1, ..., {image.ndim}]. Got {connectivity}.'
+        )
+
+    selem = _resolve_neighborhood(None, connectivity, image.ndim)
+    result = ndimage.label(image, structure=selem)
+
+    if return_num:
+        return result
+    else:
+        return result[0]
+
+
 def label(input, background=None, return_num=False, connectivity=None):
     r"""Label connected regions of an integer array.
 
@@ -84,4 +112,8 @@ def label(input, background=None, return_num=False, connectivity=None):
      [1 1 2]
      [0 0 0]]
     """
-    return clabel(input, background, return_num, connectivity)
+    if input.dtype == bool:
+        return _label_bool(input, background=background,
+                           return_num=return_num, connectivity=connectivity)
+    else:
+        return clabel(input, background, return_num, connectivity)


Test patch:
diff --git a/skimage/measure/tests/test_label.py b/skimage/measure/tests/test_label.py
new file mode 100644
--- /dev/null
+++ b/skimage/measure/tests/test_label.py
@@ -0,0 +1,49 @@
+import pytest
+from skimage import data
+from skimage.measure._label import _label_bool
+from skimage.measure._ccomp import label_cython as clabel
+
+from skimage._shared import testing
+
+# In this testsuite, we ensure that the results provided by
+# label_cython are identical to the one from _label_bool,
+# which is based on ndimage.
+
+
+def test_no_option():
+    img = data.binary_blobs(length=256, blob_size_fraction=0.15, n_dim=3)
+    l_ndi = _label_bool(img)
+    l_cy = clabel(img)
+    testing.assert_equal(l_ndi, l_cy)
+
+
+def test_background():
+    img = data.binary_blobs(length=256, blob_size_fraction=0.15, n_dim=3)
+    l_ndi = _label_bool(img, background=0)
+    l_cy = clabel(img, background=0)
+    testing.assert_equal(l_ndi, l_cy)
+
+    l_ndi = _label_bool(img, background=1)
+    l_cy = clabel(img, background=1)
+    testing.assert_equal(l_ndi, l_cy)
+
+
+def test_return_num():
+    img = data.binary_blobs(length=256, blob_size_fraction=0.15, n_dim=3)
+    l_ndi = _label_bool(img, return_num=True)
+    l_cy = clabel(img, return_num=True)
+    testing.assert_equal(l_ndi, l_cy)
+
+
+def test_connectivity():
+    img = data.binary_blobs(length=256, blob_size_fraction=0.15, n_dim=3)
+    for c in (1, 2, 3):
+        l_ndi = _label_bool(img, connectivity=c)
+        l_cy = clabel(img, connectivity=c)
+        testing.assert_equal(l_ndi, l_cy)
+
+    for c in (0, 4):
+        with pytest.raises(ValueError):
+            l_ndi = _label_bool(img, connectivity=c)
+        with pytest.raises(ValueError):
+            l_cy = clabel(img, connectivity=c)