prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Segfault in peak_local_max with large numbed of segments
  ## Description
  
  scikit-image dives to (absolutely uncatchable and untrackable) segfault in peak_local_max.
  
  ## Way to reproduce
  ```python
  import numpy as np
  from scipy.ndimage import distance_transform_edt
  from skimage.feature import peak_local_max
  
  
  def segment(binary_image):
      distance = distance_transform_edt(binary_image)
      peak_local_max(
          distance, min_distance=100, footprint=np.ones((3, 3)), labels=binary_image,
      )
  
  for p in [0.05, 0.95, 0.001, 0.999]:
      print(p)
      segment(np.random.random([2048, 2048]) < p)
  
  
  ```
  
  
  ## Version information
  ```python
  # Paste the output of the following python commands
  from __future__ import print_function
  import sys; print(sys.version)
  import platform; print(platform.platform())
  import skimage; print(f'scikit-image version: {skimage.__version__}')
  import numpy; print(f'numpy version: {numpy.__version__}')
  ```
  
  ```python
  3.8.10 (default, Sep 28 2021, 16:10:42) 
  [GCC 9.3.0]
  Linux-5.10.47-linuxkit-x86_64-with-glibc2.29
  scikit-image version: 0.18.3
  numpy version: 1.21.4
  ```
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `skimage/_shared/coord.py`
  Content:
  ```
  1 import numpy as np
  2 from scipy.spatial import cKDTree, distance
  3 
  4 
  5 def _ensure_spacing(coord, spacing, p_norm, max_out):
  6     """Returns a subset of coord where a minimum spacing is guaranteed.
  7 
  8     Parameters
  9     ----------
  10     coord : ndarray
  11         The coordinates of the considered points.
  12     spacing : float
  13         the maximum allowed spacing between the points.
  14     p_norm : float
  15         Which Minkowski p-norm to use. Should be in the range [1, inf].
  16         A finite large p may cause a ValueError if overflow can occur.
  17         ``inf`` corresponds to the Chebyshev distance and 2 to the
  18         Euclidean distance.
  19     max_out: int
  20         If not None, at most the first ``max_out`` candidates are
  21         returned.
  22 
  23     Returns
  24     -------
  25     output : ndarray
  26         A subset of coord where a minimum spacing is guaranteed.
  27 
  28     """
  29 
  30     # Use KDtree to find the peaks that are too close to each other
  31     tree = cKDTree(coord)
  32 
  33     indices = tree.query_ball_point(coord, r=spacing, p=p_norm)
  34     rejected_peaks_indices = set()
  35     naccepted = 0
  36     for idx, candidates in enumerate(indices):
  37         if idx not in rejected_peaks_indices:
  38             # keep current point and the points at exactly spacing from it
  39             candidates.remove(idx)
  40             dist = distance.cdist([coord[idx]],
  41                                   coord[candidates],
  42                                   distance.minkowski,
  43                                   p=p_norm).reshape(-1)
  44             candidates = [c for c, d in zip(candidates, dist)
  45                           if d < spacing]
  46 
  47             # candidates.remove(keep)
  48             rejected_peaks_indices.update(candidates)
  49             naccepted += 1
  50             if max_out is not None and naccepted >= max_out:
  51                 break
  52 
  53     # Remove the peaks that are too close to each other
  54     output = np.delete(coord, tuple(rejected_peaks_indices), axis=0)
  55     if max_out is not None:
  56         output = output[:max_out]
  57 
  58     return output
  59 
  60 
  61 def ensure_spacing(coords, spacing=1, p_norm=np.inf, min_split_size=50,
  62                    max_out=None):
  63     """Returns a subset of coord where a minimum spacing is guaranteed.
  64 
  65     Parameters
  66     ----------
  67     coords : array_like
  68         The coordinates of the considered points.
  69     spacing : float
  70         the maximum allowed spacing between the points.
  71     p_norm : float
  72         Which Minkowski p-norm to use. Should be in the range [1, inf].
  73         A finite large p may cause a ValueError if overflow can occur.
  74         ``inf`` corresponds to the Chebyshev distance and 2 to the
  75         Euclidean distance.
  76     min_split_size : int
  77         Minimum split size used to process ``coord`` by batch to save
  78         memory. If None, the memory saving strategy is not applied.
  79     max_out : int
  80         If not None, only the first ``max_out`` candidates are returned.
  81 
  82     Returns
  83     -------
  84     output : array_like
  85         A subset of coord where a minimum spacing is guaranteed.
  86 
  87     """
  88 
  89     output = coords
  90     if len(coords):
  91 
  92         coords = np.atleast_2d(coords)
  93         if min_split_size is None:
  94             batch_list = [coords]
  95         else:
  96             coord_count = len(coords)
  97             split_count = int(np.log2(coord_count / min_split_size)) + 1
  98             split_idx = np.cumsum(
  99                 [coord_count // (2 ** i) for i in range(1, split_count)])
  100             batch_list = np.array_split(coords, split_idx)
  101 
  102         output = np.zeros((0, coords.shape[1]), dtype=coords.dtype)
  103         for batch in batch_list:
  104             output = _ensure_spacing(np.vstack([output, batch]),
  105                                      spacing, p_norm, max_out)
  106             if max_out is not None and len(output) >= max_out:
  107                 break
  108 
  109     return output
  110
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/skimage/_shared/coord.py b/skimage/_shared/coord.py
  --- a/skimage/_shared/coord.py
  +++ b/skimage/_shared/coord.py
  @@ -59,7 +59,7 @@
   
   
   def ensure_spacing(coords, spacing=1, p_norm=np.inf, min_split_size=50,
  -                   max_out=None):
  +                   max_out=None, *, max_split_size=2000):
       """Returns a subset of coord where a minimum spacing is guaranteed.
   
       Parameters
  @@ -74,10 +74,19 @@
           ``inf`` corresponds to the Chebyshev distance and 2 to the
           Euclidean distance.
       min_split_size : int
  -        Minimum split size used to process ``coord`` by batch to save
  +        Minimum split size used to process ``coords`` by batch to save
           memory. If None, the memory saving strategy is not applied.
       max_out : int
           If not None, only the first ``max_out`` candidates are returned.
  +    max_split_size : int
  +        Maximum split size used to process ``coords`` by batch to save
  +        memory. This number was decided by profiling with a large number
  +        of points. Too small a number results in too much looping in
  +        Python instead of C, slowing down the process, while too large
  +        a number results in large memory allocations, slowdowns, and,
  +        potentially, in the process being killed -- see gh-6010. See
  +        benchmark results `here
  +        <https://github.com/scikit-image/scikit-image/pull/6035#discussion_r751518691>`_.
   
       Returns
       -------
  @@ -94,9 +103,12 @@
               batch_list = [coords]
           else:
               coord_count = len(coords)
  -            split_count = int(np.log2(coord_count / min_split_size)) + 1
  -            split_idx = np.cumsum(
  -                [coord_count // (2 ** i) for i in range(1, split_count)])
  +            split_idx = [min_split_size]
  +            split_size = min_split_size
  +            while coord_count - split_idx[-1] > max_split_size:
  +                split_size *= 2
  +                split_idx.append(split_idx[-1] + min(split_size,
  +                                                     max_split_size))
               batch_list = np.array_split(coords, split_idx)
   
           output = np.zeros((0, coords.shape[1]), dtype=coords.dtype)
  
verification_info: {"golden_diff": "diff --git a/skimage/_shared/coord.py b/skimage/_shared/coord.py\n--- a/skimage/_shared/coord.py\n+++ b/skimage/_shared/coord.py\n@@ -59,7 +59,7 @@\n \n \n def ensure_spacing(coords, spacing=1, p_norm=np.inf, min_split_size=50,\n-                   max_out=None):\n+                   max_out=None, *, max_split_size=2000):\n     \"\"\"Returns a subset of coord where a minimum spacing is guaranteed.\n \n     Parameters\n@@ -74,10 +74,19 @@\n         ``inf`` corresponds to the Chebyshev distance and 2 to the\n         Euclidean distance.\n     min_split_size : int\n-        Minimum split size used to process ``coord`` by batch to save\n+        Minimum split size used to process ``coords`` by batch to save\n         memory. If None, the memory saving strategy is not applied.\n     max_out : int\n         If not None, only the first ``max_out`` candidates are returned.\n+    max_split_size : int\n+        Maximum split size used to process ``coords`` by batch to save\n+        memory. This number was decided by profiling with a large number\n+        of points. Too small a number results in too much looping in\n+        Python instead of C, slowing down the process, while too large\n+        a number results in large memory allocations, slowdowns, and,\n+        potentially, in the process being killed -- see gh-6010. See\n+        benchmark results `here\n+        <https://github.com/scikit-image/scikit-image/pull/6035#discussion_r751518691>`_.\n \n     Returns\n     -------\n@@ -94,9 +103,12 @@\n             batch_list = [coords]\n         else:\n             coord_count = len(coords)\n-            split_count = int(np.log2(coord_count / min_split_size)) + 1\n-            split_idx = np.cumsum(\n-                [coord_count // (2 ** i) for i in range(1, split_count)])\n+            split_idx = [min_split_size]\n+            split_size = min_split_size\n+            while coord_count - split_idx[-1] > max_split_size:\n+                split_size *= 2\n+                split_idx.append(split_idx[-1] + min(split_size,\n+                                                     max_split_size))\n             batch_list = np.array_split(coords, split_idx)\n \n         output = np.zeros((0, coords.shape[1]), dtype=coords.dtype)\n", "issue": "Segfault in peak_local_max with large numbed of segments\n## Description\r\n\r\nscikit-image dives to (absolutely uncatchable and untrackable) segfault in peak_local_max.\r\n\r\n## Way to reproduce\r\n```python\r\nimport numpy as np\r\nfrom scipy.ndimage import distance_transform_edt\r\nfrom skimage.feature import peak_local_max\r\n\r\n\r\ndef segment(binary_image):\r\n    distance = distance_transform_edt(binary_image)\r\n    peak_local_max(\r\n        distance, min_distance=100, footprint=np.ones((3, 3)), labels=binary_image,\r\n    )\r\n\r\nfor p in [0.05, 0.95, 0.001, 0.999]:\r\n    print(p)\r\n    segment(np.random.random([2048, 2048]) < p)\r\n\r\n\r\n```\r\n\r\n\r\n## Version information\r\n```python\r\n# Paste the output of the following python commands\r\nfrom __future__ import print_function\r\nimport sys; print(sys.version)\r\nimport platform; print(platform.platform())\r\nimport skimage; print(f'scikit-image version: {skimage.__version__}')\r\nimport numpy; print(f'numpy version: {numpy.__version__}')\r\n```\r\n\r\n```python\r\n3.8.10 (default, Sep 28 2021, 16:10:42) \r\n[GCC 9.3.0]\r\nLinux-5.10.47-linuxkit-x86_64-with-glibc2.29\r\nscikit-image version: 0.18.3\r\nnumpy version: 1.21.4\r\n```\r\n\n", "before_files": [{"content": "import numpy as np\nfrom scipy.spatial import cKDTree, distance\n\n\ndef _ensure_spacing(coord, spacing, p_norm, max_out):\n    \"\"\"Returns a subset of coord where a minimum spacing is guaranteed.\n\n    Parameters\n    ----------\n    coord : ndarray\n        The coordinates of the considered points.\n    spacing : float\n        the maximum allowed spacing between the points.\n    p_norm : float\n        Which Minkowski p-norm to use. Should be in the range [1, inf].\n        A finite large p may cause a ValueError if overflow can occur.\n        ``inf`` corresponds to the Chebyshev distance and 2 to the\n        Euclidean distance.\n    max_out: int\n        If not None, at most the first ``max_out`` candidates are\n        returned.\n\n    Returns\n    -------\n    output : ndarray\n        A subset of coord where a minimum spacing is guaranteed.\n\n    \"\"\"\n\n    # Use KDtree to find the peaks that are too close to each other\n    tree = cKDTree(coord)\n\n    indices = tree.query_ball_point(coord, r=spacing, p=p_norm)\n    rejected_peaks_indices = set()\n    naccepted = 0\n    for idx, candidates in enumerate(indices):\n        if idx not in rejected_peaks_indices:\n            # keep current point and the points at exactly spacing from it\n            candidates.remove(idx)\n            dist = distance.cdist([coord[idx]],\n                                  coord[candidates],\n                                  distance.minkowski,\n                                  p=p_norm).reshape(-1)\n            candidates = [c for c, d in zip(candidates, dist)\n                          if d < spacing]\n\n            # candidates.remove(keep)\n            rejected_peaks_indices.update(candidates)\n            naccepted += 1\n            if max_out is not None and naccepted >= max_out:\n                break\n\n    # Remove the peaks that are too close to each other\n    output = np.delete(coord, tuple(rejected_peaks_indices), axis=0)\n    if max_out is not None:\n        output = output[:max_out]\n\n    return output\n\n\ndef ensure_spacing(coords, spacing=1, p_norm=np.inf, min_split_size=50,\n                   max_out=None):\n    \"\"\"Returns a subset of coord where a minimum spacing is guaranteed.\n\n    Parameters\n    ----------\n    coords : array_like\n        The coordinates of the considered points.\n    spacing : float\n        the maximum allowed spacing between the points.\n    p_norm : float\n        Which Minkowski p-norm to use. Should be in the range [1, inf].\n        A finite large p may cause a ValueError if overflow can occur.\n        ``inf`` corresponds to the Chebyshev distance and 2 to the\n        Euclidean distance.\n    min_split_size : int\n        Minimum split size used to process ``coord`` by batch to save\n        memory. If None, the memory saving strategy is not applied.\n    max_out : int\n        If not None, only the first ``max_out`` candidates are returned.\n\n    Returns\n    -------\n    output : array_like\n        A subset of coord where a minimum spacing is guaranteed.\n\n    \"\"\"\n\n    output = coords\n    if len(coords):\n\n        coords = np.atleast_2d(coords)\n        if min_split_size is None:\n            batch_list = [coords]\n        else:\n            coord_count = len(coords)\n            split_count = int(np.log2(coord_count / min_split_size)) + 1\n            split_idx = np.cumsum(\n                [coord_count // (2 ** i) for i in range(1, split_count)])\n            batch_list = np.array_split(coords, split_idx)\n\n        output = np.zeros((0, coords.shape[1]), dtype=coords.dtype)\n        for batch in batch_list:\n            output = _ensure_spacing(np.vstack([output, batch]),\n                                     spacing, p_norm, max_out)\n            if max_out is not None and len(output) >= max_out:\n                break\n\n    return output\n", "path": "skimage/_shared/coord.py"}], "after_files": [{"content": "import numpy as np\nfrom scipy.spatial import cKDTree, distance\n\n\ndef _ensure_spacing(coord, spacing, p_norm, max_out):\n    \"\"\"Returns a subset of coord where a minimum spacing is guaranteed.\n\n    Parameters\n    ----------\n    coord : ndarray\n        The coordinates of the considered points.\n    spacing : float\n        the maximum allowed spacing between the points.\n    p_norm : float\n        Which Minkowski p-norm to use. Should be in the range [1, inf].\n        A finite large p may cause a ValueError if overflow can occur.\n        ``inf`` corresponds to the Chebyshev distance and 2 to the\n        Euclidean distance.\n    max_out: int\n        If not None, at most the first ``max_out`` candidates are\n        returned.\n\n    Returns\n    -------\n    output : ndarray\n        A subset of coord where a minimum spacing is guaranteed.\n\n    \"\"\"\n\n    # Use KDtree to find the peaks that are too close to each other\n    tree = cKDTree(coord)\n\n    indices = tree.query_ball_point(coord, r=spacing, p=p_norm)\n    rejected_peaks_indices = set()\n    naccepted = 0\n    for idx, candidates in enumerate(indices):\n        if idx not in rejected_peaks_indices:\n            # keep current point and the points at exactly spacing from it\n            candidates.remove(idx)\n            dist = distance.cdist([coord[idx]],\n                                  coord[candidates],\n                                  distance.minkowski,\n                                  p=p_norm).reshape(-1)\n            candidates = [c for c, d in zip(candidates, dist)\n                          if d < spacing]\n\n            # candidates.remove(keep)\n            rejected_peaks_indices.update(candidates)\n            naccepted += 1\n            if max_out is not None and naccepted >= max_out:\n                break\n\n    # Remove the peaks that are too close to each other\n    output = np.delete(coord, tuple(rejected_peaks_indices), axis=0)\n    if max_out is not None:\n        output = output[:max_out]\n\n    return output\n\n\ndef ensure_spacing(coords, spacing=1, p_norm=np.inf, min_split_size=50,\n                   max_out=None, *, max_split_size=2000):\n    \"\"\"Returns a subset of coord where a minimum spacing is guaranteed.\n\n    Parameters\n    ----------\n    coords : array_like\n        The coordinates of the considered points.\n    spacing : float\n        the maximum allowed spacing between the points.\n    p_norm : float\n        Which Minkowski p-norm to use. Should be in the range [1, inf].\n        A finite large p may cause a ValueError if overflow can occur.\n        ``inf`` corresponds to the Chebyshev distance and 2 to the\n        Euclidean distance.\n    min_split_size : int\n        Minimum split size used to process ``coords`` by batch to save\n        memory. If None, the memory saving strategy is not applied.\n    max_out : int\n        If not None, only the first ``max_out`` candidates are returned.\n    max_split_size : int\n        Maximum split size used to process ``coords`` by batch to save\n        memory. This number was decided by profiling with a large number\n        of points. Too small a number results in too much looping in\n        Python instead of C, slowing down the process, while too large\n        a number results in large memory allocations, slowdowns, and,\n        potentially, in the process being killed -- see gh-6010. See\n        benchmark results `here\n        <https://github.com/scikit-image/scikit-image/pull/6035#discussion_r751518691>`_.\n\n    Returns\n    -------\n    output : array_like\n        A subset of coord where a minimum spacing is guaranteed.\n\n    \"\"\"\n\n    output = coords\n    if len(coords):\n\n        coords = np.atleast_2d(coords)\n        if min_split_size is None:\n            batch_list = [coords]\n        else:\n            coord_count = len(coords)\n            split_idx = [min_split_size]\n            split_size = min_split_size\n            while coord_count - split_idx[-1] > max_split_size:\n                split_size *= 2\n                split_idx.append(split_idx[-1] + min(split_size,\n                                                     max_split_size))\n            batch_list = np.array_split(coords, split_idx)\n\n        output = np.zeros((0, coords.shape[1]), dtype=coords.dtype)\n        for batch in batch_list:\n            output = _ensure_spacing(np.vstack([output, batch]),\n                                     spacing, p_norm, max_out)\n            if max_out is not None and len(output) >= max_out:\n                break\n\n    return output\n", "path": "skimage/_shared/coord.py"}]}