We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
New canny implementation silently fails with integer images.
### Description:

The new `skimage.feature.canny` implementation silently fails if given an integer image.  This worked on `scikit-image<=0.19`, and no longer works with `scikit-image=0.20`.  The documentation says that any dtype should work:
```
    image : 2D array
        Grayscale input image to detect edges on; can be of any dtype.
```


### Way to reproduce:

```
from skimage.feature import canny
import numpy as np

im = np.zeros((100, 100))
im[0: 50, 0: 50] = 1.0
print("Edge pixels with float input: ", canny(im, low_threshold=0, high_threshold=1).sum())
print("Edge pixels with int input: ", canny(im.astype(np.int64), low_threshold=0, high_threshold=1).sum())
```
This prints on new skimage (0.20):
```
Edge pixels with float input:  182
Edge pixels with int input:  0
```
And on old skimage (0.19):
```
Edge pixels with float input:  144
Edge pixels with int input:  144
```

As I write this test case I also need to ask ... why did the number of pixels change?

### Version information:

```Shell
3.10.10 | packaged by conda-forge | (main, Mar 24 2023, 20:08:06) [GCC 11.3.0]
Linux-3.10.0-1160.88.1.el7.x86_64-x86_64-with-glibc2.17
scikit-image version: 0.20.0
numpy version: 1.23.5
```


--- 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/feature/_canny.py`
Content:
```
1 """
2 canny.py - Canny Edge detector
3 
4 Reference: Canny, J., A Computational Approach To Edge Detection, IEEE Trans.
5     Pattern Analysis and Machine Intelligence, 8:679-714, 1986
6 """
7 
8 import numpy as np
9 import scipy.ndimage as ndi
10 
11 from ..util.dtype import dtype_limits
12 from .._shared.filters import gaussian
13 from .._shared.utils import _supported_float_type, check_nD
14 from ._canny_cy import _nonmaximum_suppression_bilinear
15 
16 
17 def _preprocess(image, mask, sigma, mode, cval):
18     """Generate a smoothed image and an eroded mask.
19 
20     The image is smoothed using a gaussian filter ignoring masked
21     pixels and the mask is eroded.
22 
23     Parameters
24     ----------
25     image : array
26         Image to be smoothed.
27     mask : array
28         Mask with 1's for significant pixels, 0's for masked pixels.
29     sigma : scalar or sequence of scalars
30         Standard deviation for Gaussian kernel. The standard
31         deviations of the Gaussian filter are given for each axis as a
32         sequence, or as a single number, in which case it is equal for
33         all axes.
34     mode : str, {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}
35         The ``mode`` parameter determines how the array borders are
36         handled, where ``cval`` is the value when mode is equal to
37         'constant'.
38     cval : float, optional
39         Value to fill past edges of input if `mode` is 'constant'.
40 
41     Returns
42     -------
43     smoothed_image : ndarray
44         The smoothed array
45     eroded_mask : ndarray
46         The eroded mask.
47 
48     Notes
49     -----
50     This function calculates the fractional contribution of masked pixels
51     by applying the function to the mask (which gets you the fraction of
52     the pixel data that's due to significant points). We then mask the image
53     and apply the function. The resulting values will be lower by the
54     bleed-over fraction, so you can recalibrate by dividing by the function
55     on the mask to recover the effect of smoothing from just the significant
56     pixels.
57     """
58     gaussian_kwargs = dict(sigma=sigma, mode=mode, cval=cval,
59                            preserve_range=False)
60     compute_bleedover = (mode == 'constant' or mask is not None)
61     float_type = _supported_float_type(image.dtype)
62     if mask is None:
63         if compute_bleedover:
64             mask = np.ones(image.shape, dtype=float_type)
65         masked_image = image
66 
67         eroded_mask = np.ones(image.shape, dtype=bool)
68         eroded_mask[:1, :] = 0
69         eroded_mask[-1:, :] = 0
70         eroded_mask[:, :1] = 0
71         eroded_mask[:, -1:] = 0
72 
73     else:
74         mask = mask.astype(bool, copy=False)
75         masked_image = np.zeros_like(image)
76         masked_image[mask] = image[mask]
77 
78         # Make the eroded mask. Setting the border value to zero will wipe
79         # out the image edges for us.
80         s = ndi.generate_binary_structure(2, 2)
81         eroded_mask = ndi.binary_erosion(mask, s, border_value=0)
82 
83     if compute_bleedover:
84         # Compute the fractional contribution of masked pixels by applying
85         # the function to the mask (which gets you the fraction of the
86         # pixel data that's due to significant points)
87         bleed_over = gaussian(mask.astype(float_type, copy=False),
88                               **gaussian_kwargs) + np.finfo(float_type).eps
89 
90     # Smooth the masked image
91     smoothed_image = gaussian(masked_image, **gaussian_kwargs)
92 
93     # Lower the result by the bleed-over fraction, so you can
94     # recalibrate by dividing by the function on the mask to recover
95     # the effect of smoothing from just the significant pixels.
96     if compute_bleedover:
97         smoothed_image /= bleed_over
98 
99     return smoothed_image, eroded_mask
100 
101 
102 def canny(image, sigma=1., low_threshold=None, high_threshold=None,
103           mask=None, use_quantiles=False, *, mode='constant', cval=0.0):
104     """Edge filter an image using the Canny algorithm.
105 
106     Parameters
107     ----------
108     image : 2D array
109         Grayscale input image to detect edges on; can be of any dtype.
110     sigma : float, optional
111         Standard deviation of the Gaussian filter.
112     low_threshold : float, optional
113         Lower bound for hysteresis thresholding (linking edges).
114         If None, low_threshold is set to 10% of dtype's max.
115     high_threshold : float, optional
116         Upper bound for hysteresis thresholding (linking edges).
117         If None, high_threshold is set to 20% of dtype's max.
118     mask : array, dtype=bool, optional
119         Mask to limit the application of Canny to a certain area.
120     use_quantiles : bool, optional
121         If ``True`` then treat low_threshold and high_threshold as
122         quantiles of the edge magnitude image, rather than absolute
123         edge magnitude values. If ``True`` then the thresholds must be
124         in the range [0, 1].
125     mode : str, {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}
126         The ``mode`` parameter determines how the array borders are
127         handled during Gaussian filtering, where ``cval`` is the value when
128         mode is equal to 'constant'.
129     cval : float, optional
130         Value to fill past edges of input if `mode` is 'constant'.
131 
132     Returns
133     -------
134     output : 2D array (image)
135         The binary edge map.
136 
137     See also
138     --------
139     skimage.filters.sobel
140 
141     Notes
142     -----
143     The steps of the algorithm are as follows:
144 
145     * Smooth the image using a Gaussian with ``sigma`` width.
146 
147     * Apply the horizontal and vertical Sobel operators to get the gradients
148       within the image. The edge strength is the norm of the gradient.
149 
150     * Thin potential edges to 1-pixel wide curves. First, find the normal
151       to the edge at each point. This is done by looking at the
152       signs and the relative magnitude of the X-Sobel and Y-Sobel
153       to sort the points into 4 categories: horizontal, vertical,
154       diagonal and antidiagonal. Then look in the normal and reverse
155       directions to see if the values in either of those directions are
156       greater than the point in question. Use interpolation to get a mix of
157       points instead of picking the one that's the closest to the normal.
158 
159     * Perform a hysteresis thresholding: first label all points above the
160       high threshold as edges. Then recursively label any point above the
161       low threshold that is 8-connected to a labeled point as an edge.
162 
163     References
164     ----------
165     .. [1] Canny, J., A Computational Approach To Edge Detection, IEEE Trans.
166            Pattern Analysis and Machine Intelligence, 8:679-714, 1986
167            :DOI:`10.1109/TPAMI.1986.4767851`
168     .. [2] William Green's Canny tutorial
169            https://en.wikipedia.org/wiki/Canny_edge_detector
170 
171     Examples
172     --------
173     >>> from skimage import feature
174     >>> rng = np.random.default_rng()
175     >>> # Generate noisy image of a square
176     >>> im = np.zeros((256, 256))
177     >>> im[64:-64, 64:-64] = 1
178     >>> im += 0.2 * rng.random(im.shape)
179     >>> # First trial with the Canny filter, with the default smoothing
180     >>> edges1 = feature.canny(im)
181     >>> # Increase the smoothing for better results
182     >>> edges2 = feature.canny(im, sigma=3)
183 
184     """
185 
186     # Regarding masks, any point touching a masked point will have a gradient
187     # that is "infected" by the masked point, so it's enough to erode the
188     # mask by one and then mask the output. We also mask out the border points
189     # because who knows what lies beyond the edge of the image?
190 
191     check_nD(image, 2)
192     dtype_max = dtype_limits(image, clip_negative=False)[1]
193 
194     if low_threshold is None:
195         low_threshold = 0.1
196     elif use_quantiles:
197         if not(0.0 <= low_threshold <= 1.0):
198             raise ValueError("Quantile thresholds must be between 0 and 1.")
199     else:
200         low_threshold /= dtype_max
201 
202     if high_threshold is None:
203         high_threshold = 0.2
204     elif use_quantiles:
205         if not(0.0 <= high_threshold <= 1.0):
206             raise ValueError("Quantile thresholds must be between 0 and 1.")
207     else:
208         high_threshold /= dtype_max
209 
210     if high_threshold < low_threshold:
211         raise ValueError("low_threshold should be lower then high_threshold")
212 
213     # Image filtering
214     smoothed, eroded_mask = _preprocess(image, mask, sigma, mode, cval)
215 
216     # Gradient magnitude estimation
217     jsobel = ndi.sobel(smoothed, axis=1)
218     isobel = ndi.sobel(smoothed, axis=0)
219     magnitude = isobel * isobel
220     magnitude += jsobel * jsobel
221     np.sqrt(magnitude, out=magnitude)
222 
223     if use_quantiles:
224         low_threshold, high_threshold = np.percentile(magnitude,
225                                                       [100.0 * low_threshold,
226                                                        100.0 * high_threshold])
227 
228     # Non-maximum suppression
229     low_masked = _nonmaximum_suppression_bilinear(
230         isobel, jsobel, magnitude, eroded_mask, low_threshold
231     )
232 
233     # Double thresholding and edge tracking
234     #
235     # Segment the low-mask, then only keep low-segments that have
236     # some high_mask component in them
237     #
238     low_mask = low_masked > 0
239     strel = np.ones((3, 3), bool)
240     labels, count = ndi.label(low_mask, strel)
241     if count == 0:
242         return low_mask
243 
244     high_mask = low_mask & (low_masked >= high_threshold)
245     nonzero_sums = np.unique(labels[high_mask])
246     good_label = np.zeros((count + 1,), bool)
247     good_label[nonzero_sums] = True
248     output_mask = good_label[labels]
249     return output_mask
250
```
--- 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()

```

diff --git a/skimage/feature/_canny.py b/skimage/feature/_canny.py
--- a/skimage/feature/_canny.py
+++ b/skimage/feature/_canny.py
@@ -55,8 +55,12 @@
     on the mask to recover the effect of smoothing from just the significant
     pixels.
     """
-    gaussian_kwargs = dict(sigma=sigma, mode=mode, cval=cval,
-                           preserve_range=False)
+    gaussian_kwargs = dict(
+        sigma=sigma,
+        mode=mode,
+        cval=cval,
+        preserve_range=False
+    )
     compute_bleedover = (mode == 'constant' or mask is not None)
     float_type = _supported_float_type(image.dtype)
     if mask is None:
@@ -188,6 +192,9 @@
     # mask by one and then mask the output. We also mask out the border points
     # because who knows what lies beyond the edge of the image?
 
+    if np.issubdtype(image.dtype, np.int64) or np.issubdtype(image.dtype, np.uint64):
+        raise ValueError("64-bit integer images are not supported")
+
     check_nD(image, 2)
     dtype_max = dtype_limits(image, clip_negative=False)[1]

{"golden_diff": "diff --git a/skimage/feature/_canny.py b/skimage/feature/_canny.py\n--- a/skimage/feature/_canny.py\n+++ b/skimage/feature/_canny.py\n@@ -55,8 +55,12 @@\n     on the mask to recover the effect of smoothing from just the significant\n     pixels.\n     \"\"\"\n-    gaussian_kwargs = dict(sigma=sigma, mode=mode, cval=cval,\n-                           preserve_range=False)\n+    gaussian_kwargs = dict(\n+        sigma=sigma,\n+        mode=mode,\n+        cval=cval,\n+        preserve_range=False\n+    )\n     compute_bleedover = (mode == 'constant' or mask is not None)\n     float_type = _supported_float_type(image.dtype)\n     if mask is None:\n@@ -188,6 +192,9 @@\n     # mask by one and then mask the output. We also mask out the border points\n     # because who knows what lies beyond the edge of the image?\n \n+    if np.issubdtype(image.dtype, np.int64) or np.issubdtype(image.dtype, np.uint64):\n+        raise ValueError(\"64-bit integer images are not supported\")\n+\n     check_nD(image, 2)\n     dtype_max = dtype_limits(image, clip_negative=False)[1]\n", "issue": "New canny implementation silently fails with integer images.\n### Description:\n\nThe new `skimage.feature.canny` implementation silently fails if given an integer image.  This worked on `scikit-image<=0.19`, and no longer works with `scikit-image=0.20`.  The documentation says that any dtype should work:\r\n```\r\n    image : 2D array\r\n        Grayscale input image to detect edges on; can be of any dtype.\r\n```\r\n\n\n### Way to reproduce:\n\n```\r\nfrom skimage.feature import canny\r\nimport numpy as np\r\n\r\nim = np.zeros((100, 100))\r\nim[0: 50, 0: 50] = 1.0\r\nprint(\"Edge pixels with float input: \", canny(im, low_threshold=0, high_threshold=1).sum())\r\nprint(\"Edge pixels with int input: \", canny(im.astype(np.int64), low_threshold=0, high_threshold=1).sum())\r\n```\r\nThis prints on new skimage (0.20):\r\n```\r\nEdge pixels with float input:  182\r\nEdge pixels with int input:  0\r\n```\r\nAnd on old skimage (0.19):\r\n```\r\nEdge pixels with float input:  144\r\nEdge pixels with int input:  144\r\n```\r\n\r\nAs I write this test case I also need to ask ... why did the number of pixels change?\n\n### Version information:\n\n```Shell\n3.10.10 | packaged by conda-forge | (main, Mar 24 2023, 20:08:06) [GCC 11.3.0]\r\nLinux-3.10.0-1160.88.1.el7.x86_64-x86_64-with-glibc2.17\r\nscikit-image version: 0.20.0\r\nnumpy version: 1.23.5\n```\n\n", "before_files": [{"content": "\"\"\"\ncanny.py - Canny Edge detector\n\nReference: Canny, J., A Computational Approach To Edge Detection, IEEE Trans.\n    Pattern Analysis and Machine Intelligence, 8:679-714, 1986\n\"\"\"\n\nimport numpy as np\nimport scipy.ndimage as ndi\n\nfrom ..util.dtype import dtype_limits\nfrom .._shared.filters import gaussian\nfrom .._shared.utils import _supported_float_type, check_nD\nfrom ._canny_cy import _nonmaximum_suppression_bilinear\n\n\ndef _preprocess(image, mask, sigma, mode, cval):\n    \"\"\"Generate a smoothed image and an eroded mask.\n\n    The image is smoothed using a gaussian filter ignoring masked\n    pixels and the mask is eroded.\n\n    Parameters\n    ----------\n    image : array\n        Image to be smoothed.\n    mask : array\n        Mask with 1's for significant pixels, 0's for masked pixels.\n    sigma : scalar or sequence of scalars\n        Standard deviation for Gaussian kernel. The standard\n        deviations of the Gaussian filter are given for each axis as a\n        sequence, or as a single number, in which case it is equal for\n        all axes.\n    mode : str, {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}\n        The ``mode`` parameter determines how the array borders are\n        handled, where ``cval`` is the value when mode is equal to\n        'constant'.\n    cval : float, optional\n        Value to fill past edges of input if `mode` is 'constant'.\n\n    Returns\n    -------\n    smoothed_image : ndarray\n        The smoothed array\n    eroded_mask : ndarray\n        The eroded mask.\n\n    Notes\n    -----\n    This function calculates the fractional contribution of masked pixels\n    by applying the function to the mask (which gets you the fraction of\n    the pixel data that's due to significant points). We then mask the image\n    and apply the function. The resulting values will be lower by the\n    bleed-over fraction, so you can recalibrate by dividing by the function\n    on the mask to recover the effect of smoothing from just the significant\n    pixels.\n    \"\"\"\n    gaussian_kwargs = dict(sigma=sigma, mode=mode, cval=cval,\n                           preserve_range=False)\n    compute_bleedover = (mode == 'constant' or mask is not None)\n    float_type = _supported_float_type(image.dtype)\n    if mask is None:\n        if compute_bleedover:\n            mask = np.ones(image.shape, dtype=float_type)\n        masked_image = image\n\n        eroded_mask = np.ones(image.shape, dtype=bool)\n        eroded_mask[:1, :] = 0\n        eroded_mask[-1:, :] = 0\n        eroded_mask[:, :1] = 0\n        eroded_mask[:, -1:] = 0\n\n    else:\n        mask = mask.astype(bool, copy=False)\n        masked_image = np.zeros_like(image)\n        masked_image[mask] = image[mask]\n\n        # Make the eroded mask. Setting the border value to zero will wipe\n        # out the image edges for us.\n        s = ndi.generate_binary_structure(2, 2)\n        eroded_mask = ndi.binary_erosion(mask, s, border_value=0)\n\n    if compute_bleedover:\n        # Compute the fractional contribution of masked pixels by applying\n        # the function to the mask (which gets you the fraction of the\n        # pixel data that's due to significant points)\n        bleed_over = gaussian(mask.astype(float_type, copy=False),\n                              **gaussian_kwargs) + np.finfo(float_type).eps\n\n    # Smooth the masked image\n    smoothed_image = gaussian(masked_image, **gaussian_kwargs)\n\n    # Lower the result by the bleed-over fraction, so you can\n    # recalibrate by dividing by the function on the mask to recover\n    # the effect of smoothing from just the significant pixels.\n    if compute_bleedover:\n        smoothed_image /= bleed_over\n\n    return smoothed_image, eroded_mask\n\n\ndef canny(image, sigma=1., low_threshold=None, high_threshold=None,\n          mask=None, use_quantiles=False, *, mode='constant', cval=0.0):\n    \"\"\"Edge filter an image using the Canny algorithm.\n\n    Parameters\n    ----------\n    image : 2D array\n        Grayscale input image to detect edges on; can be of any dtype.\n    sigma : float, optional\n        Standard deviation of the Gaussian filter.\n    low_threshold : float, optional\n        Lower bound for hysteresis thresholding (linking edges).\n        If None, low_threshold is set to 10% of dtype's max.\n    high_threshold : float, optional\n        Upper bound for hysteresis thresholding (linking edges).\n        If None, high_threshold is set to 20% of dtype's max.\n    mask : array, dtype=bool, optional\n        Mask to limit the application of Canny to a certain area.\n    use_quantiles : bool, optional\n        If ``True`` then treat low_threshold and high_threshold as\n        quantiles of the edge magnitude image, rather than absolute\n        edge magnitude values. If ``True`` then the thresholds must be\n        in the range [0, 1].\n    mode : str, {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}\n        The ``mode`` parameter determines how the array borders are\n        handled during Gaussian filtering, where ``cval`` is the value when\n        mode is equal to 'constant'.\n    cval : float, optional\n        Value to fill past edges of input if `mode` is 'constant'.\n\n    Returns\n    -------\n    output : 2D array (image)\n        The binary edge map.\n\n    See also\n    --------\n    skimage.filters.sobel\n\n    Notes\n    -----\n    The steps of the algorithm are as follows:\n\n    * Smooth the image using a Gaussian with ``sigma`` width.\n\n    * Apply the horizontal and vertical Sobel operators to get the gradients\n      within the image. The edge strength is the norm of the gradient.\n\n    * Thin potential edges to 1-pixel wide curves. First, find the normal\n      to the edge at each point. This is done by looking at the\n      signs and the relative magnitude of the X-Sobel and Y-Sobel\n      to sort the points into 4 categories: horizontal, vertical,\n      diagonal and antidiagonal. Then look in the normal and reverse\n      directions to see if the values in either of those directions are\n      greater than the point in question. Use interpolation to get a mix of\n      points instead of picking the one that's the closest to the normal.\n\n    * Perform a hysteresis thresholding: first label all points above the\n      high threshold as edges. Then recursively label any point above the\n      low threshold that is 8-connected to a labeled point as an edge.\n\n    References\n    ----------\n    .. [1] Canny, J., A Computational Approach To Edge Detection, IEEE Trans.\n           Pattern Analysis and Machine Intelligence, 8:679-714, 1986\n           :DOI:`10.1109/TPAMI.1986.4767851`\n    .. [2] William Green's Canny tutorial\n           https://en.wikipedia.org/wiki/Canny_edge_detector\n\n    Examples\n    --------\n    >>> from skimage import feature\n    >>> rng = np.random.default_rng()\n    >>> # Generate noisy image of a square\n    >>> im = np.zeros((256, 256))\n    >>> im[64:-64, 64:-64] = 1\n    >>> im += 0.2 * rng.random(im.shape)\n    >>> # First trial with the Canny filter, with the default smoothing\n    >>> edges1 = feature.canny(im)\n    >>> # Increase the smoothing for better results\n    >>> edges2 = feature.canny(im, sigma=3)\n\n    \"\"\"\n\n    # Regarding masks, any point touching a masked point will have a gradient\n    # that is \"infected\" by the masked point, so it's enough to erode the\n    # mask by one and then mask the output. We also mask out the border points\n    # because who knows what lies beyond the edge of the image?\n\n    check_nD(image, 2)\n    dtype_max = dtype_limits(image, clip_negative=False)[1]\n\n    if low_threshold is None:\n        low_threshold = 0.1\n    elif use_quantiles:\n        if not(0.0 <= low_threshold <= 1.0):\n            raise ValueError(\"Quantile thresholds must be between 0 and 1.\")\n    else:\n        low_threshold /= dtype_max\n\n    if high_threshold is None:\n        high_threshold = 0.2\n    elif use_quantiles:\n        if not(0.0 <= high_threshold <= 1.0):\n            raise ValueError(\"Quantile thresholds must be between 0 and 1.\")\n    else:\n        high_threshold /= dtype_max\n\n    if high_threshold < low_threshold:\n        raise ValueError(\"low_threshold should be lower then high_threshold\")\n\n    # Image filtering\n    smoothed, eroded_mask = _preprocess(image, mask, sigma, mode, cval)\n\n    # Gradient magnitude estimation\n    jsobel = ndi.sobel(smoothed, axis=1)\n    isobel = ndi.sobel(smoothed, axis=0)\n    magnitude = isobel * isobel\n    magnitude += jsobel * jsobel\n    np.sqrt(magnitude, out=magnitude)\n\n    if use_quantiles:\n        low_threshold, high_threshold = np.percentile(magnitude,\n                                                      [100.0 * low_threshold,\n                                                       100.0 * high_threshold])\n\n    # Non-maximum suppression\n    low_masked = _nonmaximum_suppression_bilinear(\n        isobel, jsobel, magnitude, eroded_mask, low_threshold\n    )\n\n    # Double thresholding and edge tracking\n    #\n    # Segment the low-mask, then only keep low-segments that have\n    # some high_mask component in them\n    #\n    low_mask = low_masked > 0\n    strel = np.ones((3, 3), bool)\n    labels, count = ndi.label(low_mask, strel)\n    if count == 0:\n        return low_mask\n\n    high_mask = low_mask & (low_masked >= high_threshold)\n    nonzero_sums = np.unique(labels[high_mask])\n    good_label = np.zeros((count + 1,), bool)\n    good_label[nonzero_sums] = True\n    output_mask = good_label[labels]\n    return output_mask\n", "path": "skimage/feature/_canny.py"}], "after_files": [{"content": "\"\"\"\ncanny.py - Canny Edge detector\n\nReference: Canny, J., A Computational Approach To Edge Detection, IEEE Trans.\n    Pattern Analysis and Machine Intelligence, 8:679-714, 1986\n\"\"\"\n\nimport numpy as np\nimport scipy.ndimage as ndi\n\nfrom ..util.dtype import dtype_limits\nfrom .._shared.filters import gaussian\nfrom .._shared.utils import _supported_float_type, check_nD\nfrom ._canny_cy import _nonmaximum_suppression_bilinear\n\n\ndef _preprocess(image, mask, sigma, mode, cval):\n    \"\"\"Generate a smoothed image and an eroded mask.\n\n    The image is smoothed using a gaussian filter ignoring masked\n    pixels and the mask is eroded.\n\n    Parameters\n    ----------\n    image : array\n        Image to be smoothed.\n    mask : array\n        Mask with 1's for significant pixels, 0's for masked pixels.\n    sigma : scalar or sequence of scalars\n        Standard deviation for Gaussian kernel. The standard\n        deviations of the Gaussian filter are given for each axis as a\n        sequence, or as a single number, in which case it is equal for\n        all axes.\n    mode : str, {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}\n        The ``mode`` parameter determines how the array borders are\n        handled, where ``cval`` is the value when mode is equal to\n        'constant'.\n    cval : float, optional\n        Value to fill past edges of input if `mode` is 'constant'.\n\n    Returns\n    -------\n    smoothed_image : ndarray\n        The smoothed array\n    eroded_mask : ndarray\n        The eroded mask.\n\n    Notes\n    -----\n    This function calculates the fractional contribution of masked pixels\n    by applying the function to the mask (which gets you the fraction of\n    the pixel data that's due to significant points). We then mask the image\n    and apply the function. The resulting values will be lower by the\n    bleed-over fraction, so you can recalibrate by dividing by the function\n    on the mask to recover the effect of smoothing from just the significant\n    pixels.\n    \"\"\"\n    gaussian_kwargs = dict(\n        sigma=sigma,\n        mode=mode,\n        cval=cval,\n        preserve_range=False\n    )\n    compute_bleedover = (mode == 'constant' or mask is not None)\n    float_type = _supported_float_type(image.dtype)\n    if mask is None:\n        if compute_bleedover:\n            mask = np.ones(image.shape, dtype=float_type)\n        masked_image = image\n\n        eroded_mask = np.ones(image.shape, dtype=bool)\n        eroded_mask[:1, :] = 0\n        eroded_mask[-1:, :] = 0\n        eroded_mask[:, :1] = 0\n        eroded_mask[:, -1:] = 0\n\n    else:\n        mask = mask.astype(bool, copy=False)\n        masked_image = np.zeros_like(image)\n        masked_image[mask] = image[mask]\n\n        # Make the eroded mask. Setting the border value to zero will wipe\n        # out the image edges for us.\n        s = ndi.generate_binary_structure(2, 2)\n        eroded_mask = ndi.binary_erosion(mask, s, border_value=0)\n\n    if compute_bleedover:\n        # Compute the fractional contribution of masked pixels by applying\n        # the function to the mask (which gets you the fraction of the\n        # pixel data that's due to significant points)\n        bleed_over = gaussian(mask.astype(float_type, copy=False),\n                              **gaussian_kwargs) + np.finfo(float_type).eps\n\n    # Smooth the masked image\n    smoothed_image = gaussian(masked_image, **gaussian_kwargs)\n\n    # Lower the result by the bleed-over fraction, so you can\n    # recalibrate by dividing by the function on the mask to recover\n    # the effect of smoothing from just the significant pixels.\n    if compute_bleedover:\n        smoothed_image /= bleed_over\n\n    return smoothed_image, eroded_mask\n\n\ndef canny(image, sigma=1., low_threshold=None, high_threshold=None,\n          mask=None, use_quantiles=False, *, mode='constant', cval=0.0):\n    \"\"\"Edge filter an image using the Canny algorithm.\n\n    Parameters\n    ----------\n    image : 2D array\n        Grayscale input image to detect edges on; can be of any dtype.\n    sigma : float, optional\n        Standard deviation of the Gaussian filter.\n    low_threshold : float, optional\n        Lower bound for hysteresis thresholding (linking edges).\n        If None, low_threshold is set to 10% of dtype's max.\n    high_threshold : float, optional\n        Upper bound for hysteresis thresholding (linking edges).\n        If None, high_threshold is set to 20% of dtype's max.\n    mask : array, dtype=bool, optional\n        Mask to limit the application of Canny to a certain area.\n    use_quantiles : bool, optional\n        If ``True`` then treat low_threshold and high_threshold as\n        quantiles of the edge magnitude image, rather than absolute\n        edge magnitude values. If ``True`` then the thresholds must be\n        in the range [0, 1].\n    mode : str, {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}\n        The ``mode`` parameter determines how the array borders are\n        handled during Gaussian filtering, where ``cval`` is the value when\n        mode is equal to 'constant'.\n    cval : float, optional\n        Value to fill past edges of input if `mode` is 'constant'.\n\n    Returns\n    -------\n    output : 2D array (image)\n        The binary edge map.\n\n    See also\n    --------\n    skimage.filters.sobel\n\n    Notes\n    -----\n    The steps of the algorithm are as follows:\n\n    * Smooth the image using a Gaussian with ``sigma`` width.\n\n    * Apply the horizontal and vertical Sobel operators to get the gradients\n      within the image. The edge strength is the norm of the gradient.\n\n    * Thin potential edges to 1-pixel wide curves. First, find the normal\n      to the edge at each point. This is done by looking at the\n      signs and the relative magnitude of the X-Sobel and Y-Sobel\n      to sort the points into 4 categories: horizontal, vertical,\n      diagonal and antidiagonal. Then look in the normal and reverse\n      directions to see if the values in either of those directions are\n      greater than the point in question. Use interpolation to get a mix of\n      points instead of picking the one that's the closest to the normal.\n\n    * Perform a hysteresis thresholding: first label all points above the\n      high threshold as edges. Then recursively label any point above the\n      low threshold that is 8-connected to a labeled point as an edge.\n\n    References\n    ----------\n    .. [1] Canny, J., A Computational Approach To Edge Detection, IEEE Trans.\n           Pattern Analysis and Machine Intelligence, 8:679-714, 1986\n           :DOI:`10.1109/TPAMI.1986.4767851`\n    .. [2] William Green's Canny tutorial\n           https://en.wikipedia.org/wiki/Canny_edge_detector\n\n    Examples\n    --------\n    >>> from skimage import feature\n    >>> rng = np.random.default_rng()\n    >>> # Generate noisy image of a square\n    >>> im = np.zeros((256, 256))\n    >>> im[64:-64, 64:-64] = 1\n    >>> im += 0.2 * rng.random(im.shape)\n    >>> # First trial with the Canny filter, with the default smoothing\n    >>> edges1 = feature.canny(im)\n    >>> # Increase the smoothing for better results\n    >>> edges2 = feature.canny(im, sigma=3)\n\n    \"\"\"\n\n    # Regarding masks, any point touching a masked point will have a gradient\n    # that is \"infected\" by the masked point, so it's enough to erode the\n    # mask by one and then mask the output. We also mask out the border points\n    # because who knows what lies beyond the edge of the image?\n\n    if np.issubdtype(image.dtype, np.int64) or np.issubdtype(image.dtype, np.uint64):\n        raise ValueError(\"64-bit integer images are not supported\")\n\n    check_nD(image, 2)\n    dtype_max = dtype_limits(image, clip_negative=False)[1]\n\n    if low_threshold is None:\n        low_threshold = 0.1\n    elif use_quantiles:\n        if not(0.0 <= low_threshold <= 1.0):\n            raise ValueError(\"Quantile thresholds must be between 0 and 1.\")\n    else:\n        low_threshold /= dtype_max\n\n    if high_threshold is None:\n        high_threshold = 0.2\n    elif use_quantiles:\n        if not(0.0 <= high_threshold <= 1.0):\n            raise ValueError(\"Quantile thresholds must be between 0 and 1.\")\n    else:\n        high_threshold /= dtype_max\n\n    if high_threshold < low_threshold:\n        raise ValueError(\"low_threshold should be lower then high_threshold\")\n\n    # Image filtering\n    smoothed, eroded_mask = _preprocess(image, mask, sigma, mode, cval)\n\n    # Gradient magnitude estimation\n    jsobel = ndi.sobel(smoothed, axis=1)\n    isobel = ndi.sobel(smoothed, axis=0)\n    magnitude = isobel * isobel\n    magnitude += jsobel * jsobel\n    np.sqrt(magnitude, out=magnitude)\n\n    if use_quantiles:\n        low_threshold, high_threshold = np.percentile(magnitude,\n                                                      [100.0 * low_threshold,\n                                                       100.0 * high_threshold])\n\n    # Non-maximum suppression\n    low_masked = _nonmaximum_suppression_bilinear(\n        isobel, jsobel, magnitude, eroded_mask, low_threshold\n    )\n\n    # Double thresholding and edge tracking\n    #\n    # Segment the low-mask, then only keep low-segments that have\n    # some high_mask component in them\n    #\n    low_mask = low_masked > 0\n    strel = np.ones((3, 3), bool)\n    labels, count = ndi.label(low_mask, strel)\n    if count == 0:\n        return low_mask\n\n    high_mask = low_mask & (low_masked >= high_threshold)\n    nonzero_sums = np.unique(labels[high_mask])\n    good_label = np.zeros((count + 1,), bool)\n    good_label[nonzero_sums] = True\n    output_mask = good_label[labels]\n    return output_mask\n", "path": "skimage/feature/_canny.py"}]}