Repository: scikit-image/scikit-image

Issue:
Deprecate neighbors in convex_hull
## Description

See https://github.com/scikit-image/scikit-image/pull/4018/files#r305598199


Discussion:

Resolving patch:
diff --git a/skimage/measure/_label.py b/skimage/measure/_label.py
--- a/skimage/measure/_label.py
+++ b/skimage/measure/_label.py
@@ -10,12 +10,12 @@ def label(input, neighbors=None, background=None, return_num=False,
     The value refers to the maximum number of orthogonal hops to consider a
     pixel/voxel a neighbor::
 
-      1-connectivity      2-connectivity     diagonal connection close-up
+      1-connectivity     2-connectivity     diagonal connection close-up
 
-           [ ]           [ ]  [ ]  [ ]         [ ]
-            |               \  |  /             |  <- hop 2
-      [ ]--[x]--[ ]      [ ]--[x]--[ ]    [x]--[ ]
-            |               /  |  \         hop 1
+           [ ]           [ ]  [ ]  [ ]             [ ]
+            |               \  |  /                 |  <- hop 2
+      [ ]--[x]--[ ]      [ ]--[x]--[ ]        [x]--[ ]
+            |               /  |  \             hop 1
            [ ]           [ ]  [ ]  [ ]
 
     Parameters
@@ -26,7 +26,7 @@ def label(input, neighbors=None, background=None, return_num=False,
         Whether to use 4- or 8-"connectivity".
         In 3D, 4-"connectivity" means connected pixels have to share face,
         whereas with 8-"connectivity", they have to share only edge or vertex.
-        **Deprecated, use ``connectivity`` instead.**
+        **Deprecated, use** ``connectivity`` **instead.**
     background : int, optional
         Consider all pixels with this value as background pixels, and label
         them as 0. By default, 0-valued pixels are considered as background
diff --git a/skimage/morphology/convex_hull.py b/skimage/morphology/convex_hull.py
--- a/skimage/morphology/convex_hull.py
+++ b/skimage/morphology/convex_hull.py
@@ -93,40 +93,66 @@ def convex_hull_image(image, offset_coordinates=True, tolerance=1e-10):
     return mask
 
 
-def convex_hull_object(image, neighbors=8):
-    """Compute the convex hull image of individual objects in a binary image.
+def convex_hull_object(image, neighbors=None, *, connectivity=None):
+    r"""Compute the convex hull image of individual objects in a binary image.
 
     The convex hull is the set of pixels included in the smallest convex
     polygon that surround all white pixels in the input image.
 
     Parameters
     ----------
-    image : (M, N) array
+    image : (M, N) ndarray
         Binary input image.
-    neighbors : {4, 8}, int
-        Whether to use 4- or 8-connectivity.
+    neighbors : {4, 8}, int, optional
+        Whether to use 4 or 8 adjacent pixels as neighbors.
+        If ``None``, set to 8. **Deprecated, use** ``connectivity`` **instead.**
+    connectivity : {1, 2}, int, optional
+        Determines the neighbors of each pixel. Adjacent elements
+        within a squared distance of ``connectivity`` from pixel center
+        are considered neighbors. If ``None``, set to 2::
+
+            1-connectivity      2-connectivity
+                  [ ]           [ ]  [ ]  [ ]
+                   |               \  |  /
+             [ ]--[x]--[ ]      [ ]--[x]--[ ]
+                   |               /  |  \
+                  [ ]           [ ]  [ ]  [ ]
 
     Returns
     -------
     hull : ndarray of bool
-        Binary image with pixels in convex hull set to True.
+        Binary image with pixels inside convex hull set to ``True``.
 
     Notes
     -----
-    This function uses skimage.morphology.label to define unique objects,
-    finds the convex hull of each using convex_hull_image, and combines
+    This function uses ``skimage.morphology.label`` to define unique objects,
+    finds the convex hull of each using ``convex_hull_image``, and combines
     these regions with logical OR. Be aware the convex hulls of unconnected
     objects may overlap in the result. If this is suspected, consider using
-    convex_hull_image separately on each object.
-
+    convex_hull_image separately on each object or adjust ``connectivity``.
     """
     if image.ndim > 2:
         raise ValueError("Input must be a 2D image")
 
-    if neighbors != 4 and neighbors != 8:
-        raise ValueError('Neighbors must be either 4 or 8.')
+    if neighbors is None and connectivity is None:
+        connectivity = 2
+    elif neighbors is not None:
+        # Backward-compatibility
+        if neighbors == 4:
+            connectivity = 1
+        elif neighbors == 8:
+            connectivity = 2
+        else:
+            raise ValueError('`neighbors` must be either 4 or 8.')
+        warn("The argument `neighbors` is deprecated and will be removed in "
+             "scikit-image 0.18, use `connectivity` instead. "
+             "For neighbors={neighbors}, use connectivity={connectivity}"
+             "".format(neighbors=neighbors, connectivity=connectivity),
+             stacklevel=2)
+    else:
+        if connectivity not in (1, 2):
+            raise ValueError('`connectivity` must be either 1 or 2.')
 
-    connectivity = neighbors // 4
     labeled_im = label(image, connectivity=connectivity, background=0)
     convex_obj = np.zeros(image.shape, dtype=bool)
     convex_img = np.zeros(image.shape, dtype=bool)


Test patch:
diff --git a/skimage/morphology/tests/test_convex_hull.py b/skimage/morphology/tests/test_convex_hull.py
--- a/skimage/morphology/tests/test_convex_hull.py
+++ b/skimage/morphology/tests/test_convex_hull.py
@@ -108,7 +108,7 @@ def test_object():
          [1, 0, 0, 0, 0, 0, 0, 0, 0],
          [0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=bool)
 
-    expected4 = np.array(
+    expected_conn_1 = np.array(
         [[0, 0, 0, 0, 0, 0, 0, 0, 0],
          [1, 0, 0, 0, 0, 0, 0, 0, 0],
          [1, 1, 0, 0, 0, 0, 0, 0, 0],
@@ -119,9 +119,10 @@ def test_object():
          [1, 0, 0, 0, 0, 0, 0, 0, 0],
          [0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=bool)
 
-    assert_array_equal(convex_hull_object(image, 4), expected4)
+    assert_array_equal(convex_hull_object(image, connectivity=1),
+                       expected_conn_1)
 
-    expected8 = np.array(
+    expected_conn_2 = np.array(
         [[0, 0, 0, 0, 0, 0, 0, 0, 0],
          [1, 0, 0, 0, 0, 0, 0, 0, 0],
          [1, 1, 0, 0, 0, 0, 0, 0, 0],
@@ -132,10 +133,15 @@ def test_object():
          [1, 0, 0, 0, 0, 0, 0, 0, 0],
          [0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=bool)
 
-    assert_array_equal(convex_hull_object(image, 8), expected8)
+    assert_array_equal(convex_hull_object(image, connectivity=2),
+                       expected_conn_2)
 
     with testing.raises(ValueError):
-        convex_hull_object(image, 7)
+        convex_hull_object(image, connectivity=3)
+
+    with expected_warnings(['`neighbors` is deprecated']):
+        out = convex_hull_object(image, neighbors=4)
+    assert_array_equal(out, expected_conn_1)
 
 
 def test_non_c_contiguous():