We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
pixel_graph causes TypeError in map_array if mask is not given but edge_dunction is given
### Description:

If you give `edge_function` to `pixel_graph` but don't give `mask,` `mask` stays to be the default `None,` which would cause `TypeError 'The type of an array to be remapped should be an integer.'` in `map_array` even if we make sure the input image is integer.

I believe this is caused by the if statement at the beginning of the function. The if statement at the beginning does not cover the specific case I mentioned above, and the mask would not be changed from None.

The following is the lines I mentioned:

```python
if image.dtype == bool and mask is None:
        mask = image
if mask is None and edge_function is None:
        mask = np.ones_like(image, dtype=bool)
        edge_function = _weighted_abs_diff
```

As the solution, I think separating if statement for `mask` and the one for `edge_function` works:

```python
if image.dtype == bool and mask is None:
        mask = image
if mask is None:
        mask = np.ones_like(image, dtype=bool)
if edge_function is None:
        edge_function = _weighted_abs_diff
```


### Way to reproduce:

```python
import numpy as np
import skimage

image = np.array(
    [[1, 2, 3],
     [4, 5, 6],
     [7, 8, 9]]
) 

func = lambda x, y, z: np.abs(x-y)
adj1 = skimage.graph.pixel_graph(image) # No error
adj2 = skimage.graph.pixel_graph(image, edge_function = func) # TypeError
```

### Version information:

```Shell
3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0]
Linux-5.15.120+-x86_64-with-glibc2.35
scikit-image version: 0.19.3
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/graph/_graph.py`
Content:
```
1 import numpy as np
2 from scipy import sparse
3 from scipy.sparse import csgraph
4 from ..morphology._util import _raveled_offsets_and_distances
5 from ..util._map_array import map_array
6 
7 
8 def _weighted_abs_diff(values0, values1, distances):
9     """A default edge function for complete image graphs.
10 
11     A pixel graph on an image with no edge values and no mask is a very
12     boring regular lattice, so we define a default edge weight to be the
13     absolute difference between values *weighted* by the distance
14     between them.
15 
16     Parameters
17     ----------
18     values0 : array
19         The pixel values for each node.
20     values1 : array
21         The pixel values for each neighbor.
22     distances : array
23         The distance between each node and its neighbor.
24 
25     Returns
26     -------
27     edge_values : array of float
28         The computed values: abs(values0 - values1) * distances.
29     """
30     return np.abs(values0 - values1) * distances
31 
32 
33 def pixel_graph(image, *, mask=None, edge_function=None, connectivity=1, spacing=None):
34     """Create an adjacency graph of pixels in an image.
35 
36     Pixels where the mask is True are nodes in the returned graph, and they are
37     connected by edges to their neighbors according to the connectivity
38     parameter. By default, the *value* of an edge when a mask is given, or when
39     the image is itself the mask, is the Euclidean distance between the pixels.
40 
41     However, if an int- or float-valued image is given with no mask, the value
42     of the edges is the absolute difference in intensity between adjacent
43     pixels, weighted by the Euclidean distance.
44 
45     Parameters
46     ----------
47     image : array
48         The input image. If the image is of type bool, it will be used as the
49         mask as well.
50     mask : array of bool
51         Which pixels to use. If None, the graph for the whole image is used.
52     edge_function : callable
53         A function taking an array of pixel values, and an array of neighbor
54         pixel values, and an array of distances, and returning a value for the
55         edge. If no function is given, the value of an edge is just the
56         distance.
57     connectivity : int
58         The square connectivity of the pixel neighborhood: the number of
59         orthogonal steps allowed to consider a pixel a neighbor. See
60         `scipy.ndimage.generate_binary_structure` for details.
61     spacing : tuple of float
62         The spacing between pixels along each axis.
63 
64     Returns
65     -------
66     graph : scipy.sparse.csr_matrix
67         A sparse adjacency matrix in which entry (i, j) is 1 if nodes i and j
68         are neighbors, 0 otherwise.
69     nodes : array of int
70         The nodes of the graph. These correspond to the raveled indices of the
71         nonzero pixels in the mask.
72     """
73     if image.dtype == bool and mask is None:
74         mask = image
75     if mask is None and edge_function is None:
76         mask = np.ones_like(image, dtype=bool)
77         edge_function = _weighted_abs_diff
78 
79     # Strategy: we are going to build the (i, j, data) arrays of a scipy
80     # sparse COO matrix, then convert to CSR (which is fast).
81     # - grab the raveled IDs of the foreground (mask == True) parts of the
82     #   image **in the padded space**.
83     # - broadcast them together with the raveled offsets to their neighbors.
84     #   This gives us for each foreground pixel a list of neighbors (that
85     #   may or may not be selected by the mask). (We also track the *distance*
86     #   to each neighbor.)
87     # - select "valid" entries in the neighbors and distance arrays by indexing
88     #   into the mask, which we can do since these are raveled indices.
89     # - use np.repeat() to repeat each source index according to the number
90     #   of neighbors selected by the mask it has. Each of these repeated
91     #   indices will be lined up with its neighbor, i.e. **this is the i
92     #   array** of the COO format matrix.
93     # - use the mask as a boolean index to get a 1D view of the selected
94     #   neighbors. **This is the j array.**
95     # - by default, the same boolean indexing can be applied to the distances
96     #   to each neighbor, to give the **data array.** Optionally, a
97     #   provided edge function can be computed on the pixel values and the
98     #   distances to give a different value for the edges.
99     # Note, we use map_array to map the raveled coordinates in the padded
100     # image to the ones in the original image, and those are the returned
101     # nodes.
102     padded = np.pad(mask, 1, mode='constant', constant_values=False)
103     nodes_padded = np.flatnonzero(padded)
104     neighbor_offsets_padded, distances_padded = _raveled_offsets_and_distances(
105         padded.shape, connectivity=connectivity, spacing=spacing
106     )
107     neighbors_padded = nodes_padded[:, np.newaxis] + neighbor_offsets_padded
108     neighbor_distances_full = np.broadcast_to(distances_padded, neighbors_padded.shape)
109     nodes = np.flatnonzero(mask)
110     nodes_sequential = np.arange(nodes.size)
111     # neighbors outside the mask get mapped to 0, which is a valid index,
112     # BUT, they will be masked out in the next step.
113     neighbors = map_array(neighbors_padded, nodes_padded, nodes)
114     neighbors_mask = padded.reshape(-1)[neighbors_padded]
115     num_neighbors = np.sum(neighbors_mask, axis=1)
116     indices = np.repeat(nodes, num_neighbors)
117     indices_sequential = np.repeat(nodes_sequential, num_neighbors)
118     neighbor_indices = neighbors[neighbors_mask]
119     neighbor_distances = neighbor_distances_full[neighbors_mask]
120     neighbor_indices_sequential = map_array(neighbor_indices, nodes, nodes_sequential)
121     if edge_function is None:
122         data = neighbor_distances
123     else:
124         image_r = image.reshape(-1)
125         data = edge_function(
126             image_r[indices], image_r[neighbor_indices], neighbor_distances
127         )
128     m = nodes_sequential.size
129     mat = sparse.coo_matrix(
130         (data, (indices_sequential, neighbor_indices_sequential)), shape=(m, m)
131     )
132     graph = mat.tocsr()
133     return graph, nodes
134 
135 
136 def central_pixel(graph, nodes=None, shape=None, partition_size=100):
137     """Find the pixel with the highest closeness centrality.
138 
139     Closeness centrality is the inverse of the total sum of shortest distances
140     from a node to every other node.
141 
142     Parameters
143     ----------
144     graph : scipy.sparse.csr_matrix
145         The sparse matrix representation of the graph.
146     nodes : array of int
147         The raveled index of each node in graph in the image. If not provided,
148         the returned value will be the index in the input graph.
149     shape : tuple of int
150         The shape of the image in which the nodes are embedded. If provided,
151         the returned coordinates are a NumPy multi-index of the same
152         dimensionality as the input shape. Otherwise, the returned coordinate
153         is the raveled index provided in `nodes`.
154     partition_size : int
155         This function computes the shortest path distance between every pair
156         of nodes in the graph. This can result in a very large (N*N) matrix.
157         As a simple performance tweak, the distance values are computed in
158         lots of `partition_size`, resulting in a memory requirement of only
159         partition_size*N.
160 
161     Returns
162     -------
163     position : int or tuple of int
164         If shape is given, the coordinate of the central pixel in the image.
165         Otherwise, the raveled index of that pixel.
166     distances : array of float
167         The total sum of distances from each node to each other reachable
168         node.
169     """
170     if nodes is None:
171         nodes = np.arange(graph.shape[0])
172     if partition_size is None:
173         num_splits = 1
174     else:
175         num_splits = max(2, graph.shape[0] // partition_size)
176     idxs = np.arange(graph.shape[0])
177     total_shortest_path_len_list = []
178     for partition in np.array_split(idxs, num_splits):
179         shortest_paths = csgraph.shortest_path(graph, directed=False, indices=partition)
180         shortest_paths_no_inf = np.nan_to_num(shortest_paths)
181         total_shortest_path_len_list.append(np.sum(shortest_paths_no_inf, axis=1))
182     total_shortest_path_len = np.concatenate(total_shortest_path_len_list)
183     nonzero = np.flatnonzero(total_shortest_path_len)
184     min_sp = np.argmin(total_shortest_path_len[nonzero])
185     raveled_index = nodes[nonzero[min_sp]]
186     if shape is not None:
187         central = np.unravel_index(raveled_index, shape)
188     else:
189         central = raveled_index
190     return central, total_shortest_path_len
191
```
--- 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/graph/_graph.py b/skimage/graph/_graph.py
--- a/skimage/graph/_graph.py
+++ b/skimage/graph/_graph.py
@@ -70,11 +70,20 @@
         The nodes of the graph. These correspond to the raveled indices of the
         nonzero pixels in the mask.
     """
-    if image.dtype == bool and mask is None:
-        mask = image
-    if mask is None and edge_function is None:
-        mask = np.ones_like(image, dtype=bool)
-        edge_function = _weighted_abs_diff
+    if mask is None:
+        if image.dtype == bool:
+            mask = image
+        else:
+            mask = np.ones_like(image, dtype=bool)
+
+    if edge_function is None:
+        if image.dtype == bool:
+
+            def edge_function(x, y, distances):
+                return distances
+
+        else:
+            edge_function = _weighted_abs_diff
 
     # Strategy: we are going to build the (i, j, data) arrays of a scipy
     # sparse COO matrix, then convert to CSR (which is fast).
@@ -118,13 +127,12 @@
     neighbor_indices = neighbors[neighbors_mask]
     neighbor_distances = neighbor_distances_full[neighbors_mask]
     neighbor_indices_sequential = map_array(neighbor_indices, nodes, nodes_sequential)
-    if edge_function is None:
-        data = neighbor_distances
-    else:
-        image_r = image.reshape(-1)
-        data = edge_function(
-            image_r[indices], image_r[neighbor_indices], neighbor_distances
-        )
+
+    image_r = image.reshape(-1)
+    data = edge_function(
+        image_r[indices], image_r[neighbor_indices], neighbor_distances
+    )
+
     m = nodes_sequential.size
     mat = sparse.coo_matrix(
         (data, (indices_sequential, neighbor_indices_sequential)), shape=(m, m)

{"golden_diff": "diff --git a/skimage/graph/_graph.py b/skimage/graph/_graph.py\n--- a/skimage/graph/_graph.py\n+++ b/skimage/graph/_graph.py\n@@ -70,11 +70,20 @@\n         The nodes of the graph. These correspond to the raveled indices of the\n         nonzero pixels in the mask.\n     \"\"\"\n-    if image.dtype == bool and mask is None:\n-        mask = image\n-    if mask is None and edge_function is None:\n-        mask = np.ones_like(image, dtype=bool)\n-        edge_function = _weighted_abs_diff\n+    if mask is None:\n+        if image.dtype == bool:\n+            mask = image\n+        else:\n+            mask = np.ones_like(image, dtype=bool)\n+\n+    if edge_function is None:\n+        if image.dtype == bool:\n+\n+            def edge_function(x, y, distances):\n+                return distances\n+\n+        else:\n+            edge_function = _weighted_abs_diff\n \n     # Strategy: we are going to build the (i, j, data) arrays of a scipy\n     # sparse COO matrix, then convert to CSR (which is fast).\n@@ -118,13 +127,12 @@\n     neighbor_indices = neighbors[neighbors_mask]\n     neighbor_distances = neighbor_distances_full[neighbors_mask]\n     neighbor_indices_sequential = map_array(neighbor_indices, nodes, nodes_sequential)\n-    if edge_function is None:\n-        data = neighbor_distances\n-    else:\n-        image_r = image.reshape(-1)\n-        data = edge_function(\n-            image_r[indices], image_r[neighbor_indices], neighbor_distances\n-        )\n+\n+    image_r = image.reshape(-1)\n+    data = edge_function(\n+        image_r[indices], image_r[neighbor_indices], neighbor_distances\n+    )\n+\n     m = nodes_sequential.size\n     mat = sparse.coo_matrix(\n         (data, (indices_sequential, neighbor_indices_sequential)), shape=(m, m)\n", "issue": "pixel_graph causes TypeError in map_array if mask is not given but edge_dunction is given\n### Description:\n\nIf you give `edge_function` to `pixel_graph` but don't give `mask,` `mask` stays to be the default `None,` which would cause `TypeError 'The type of an array to be remapped should be an integer.'` in `map_array` even if we make sure the input image is integer.\r\n\r\nI believe this is caused by the if statement at the beginning of the function. The if statement at the beginning does not cover the specific case I mentioned above, and the mask would not be changed from None.\r\n\r\nThe following is the lines I mentioned:\r\n\r\n```python\r\nif image.dtype == bool and mask is None:\r\n        mask = image\r\nif mask is None and edge_function is None:\r\n        mask = np.ones_like(image, dtype=bool)\r\n        edge_function = _weighted_abs_diff\r\n```\r\n\r\nAs the solution, I think separating if statement for `mask` and the one for `edge_function` works:\r\n\r\n```python\r\nif image.dtype == bool and mask is None:\r\n        mask = image\r\nif mask is None:\r\n        mask = np.ones_like(image, dtype=bool)\r\nif edge_function is None:\r\n        edge_function = _weighted_abs_diff\r\n```\r\n\n\n### Way to reproduce:\n\n```python\r\nimport numpy as np\r\nimport skimage\r\n\r\nimage = np.array(\r\n    [[1, 2, 3],\r\n     [4, 5, 6],\r\n     [7, 8, 9]]\r\n) \r\n\r\nfunc = lambda x, y, z: np.abs(x-y)\r\nadj1 = skimage.graph.pixel_graph(image) # No error\r\nadj2 = skimage.graph.pixel_graph(image, edge_function = func) # TypeError\r\n```\n\n### Version information:\n\n```Shell\n3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0]\r\nLinux-5.15.120+-x86_64-with-glibc2.35\r\nscikit-image version: 0.19.3\r\nnumpy version: 1.23.5\n```\n\n", "before_files": [{"content": "import numpy as np\nfrom scipy import sparse\nfrom scipy.sparse import csgraph\nfrom ..morphology._util import _raveled_offsets_and_distances\nfrom ..util._map_array import map_array\n\n\ndef _weighted_abs_diff(values0, values1, distances):\n    \"\"\"A default edge function for complete image graphs.\n\n    A pixel graph on an image with no edge values and no mask is a very\n    boring regular lattice, so we define a default edge weight to be the\n    absolute difference between values *weighted* by the distance\n    between them.\n\n    Parameters\n    ----------\n    values0 : array\n        The pixel values for each node.\n    values1 : array\n        The pixel values for each neighbor.\n    distances : array\n        The distance between each node and its neighbor.\n\n    Returns\n    -------\n    edge_values : array of float\n        The computed values: abs(values0 - values1) * distances.\n    \"\"\"\n    return np.abs(values0 - values1) * distances\n\n\ndef pixel_graph(image, *, mask=None, edge_function=None, connectivity=1, spacing=None):\n    \"\"\"Create an adjacency graph of pixels in an image.\n\n    Pixels where the mask is True are nodes in the returned graph, and they are\n    connected by edges to their neighbors according to the connectivity\n    parameter. By default, the *value* of an edge when a mask is given, or when\n    the image is itself the mask, is the Euclidean distance between the pixels.\n\n    However, if an int- or float-valued image is given with no mask, the value\n    of the edges is the absolute difference in intensity between adjacent\n    pixels, weighted by the Euclidean distance.\n\n    Parameters\n    ----------\n    image : array\n        The input image. If the image is of type bool, it will be used as the\n        mask as well.\n    mask : array of bool\n        Which pixels to use. If None, the graph for the whole image is used.\n    edge_function : callable\n        A function taking an array of pixel values, and an array of neighbor\n        pixel values, and an array of distances, and returning a value for the\n        edge. If no function is given, the value of an edge is just the\n        distance.\n    connectivity : int\n        The square connectivity of the pixel neighborhood: the number of\n        orthogonal steps allowed to consider a pixel a neighbor. See\n        `scipy.ndimage.generate_binary_structure` for details.\n    spacing : tuple of float\n        The spacing between pixels along each axis.\n\n    Returns\n    -------\n    graph : scipy.sparse.csr_matrix\n        A sparse adjacency matrix in which entry (i, j) is 1 if nodes i and j\n        are neighbors, 0 otherwise.\n    nodes : array of int\n        The nodes of the graph. These correspond to the raveled indices of the\n        nonzero pixels in the mask.\n    \"\"\"\n    if image.dtype == bool and mask is None:\n        mask = image\n    if mask is None and edge_function is None:\n        mask = np.ones_like(image, dtype=bool)\n        edge_function = _weighted_abs_diff\n\n    # Strategy: we are going to build the (i, j, data) arrays of a scipy\n    # sparse COO matrix, then convert to CSR (which is fast).\n    # - grab the raveled IDs of the foreground (mask == True) parts of the\n    #   image **in the padded space**.\n    # - broadcast them together with the raveled offsets to their neighbors.\n    #   This gives us for each foreground pixel a list of neighbors (that\n    #   may or may not be selected by the mask). (We also track the *distance*\n    #   to each neighbor.)\n    # - select \"valid\" entries in the neighbors and distance arrays by indexing\n    #   into the mask, which we can do since these are raveled indices.\n    # - use np.repeat() to repeat each source index according to the number\n    #   of neighbors selected by the mask it has. Each of these repeated\n    #   indices will be lined up with its neighbor, i.e. **this is the i\n    #   array** of the COO format matrix.\n    # - use the mask as a boolean index to get a 1D view of the selected\n    #   neighbors. **This is the j array.**\n    # - by default, the same boolean indexing can be applied to the distances\n    #   to each neighbor, to give the **data array.** Optionally, a\n    #   provided edge function can be computed on the pixel values and the\n    #   distances to give a different value for the edges.\n    # Note, we use map_array to map the raveled coordinates in the padded\n    # image to the ones in the original image, and those are the returned\n    # nodes.\n    padded = np.pad(mask, 1, mode='constant', constant_values=False)\n    nodes_padded = np.flatnonzero(padded)\n    neighbor_offsets_padded, distances_padded = _raveled_offsets_and_distances(\n        padded.shape, connectivity=connectivity, spacing=spacing\n    )\n    neighbors_padded = nodes_padded[:, np.newaxis] + neighbor_offsets_padded\n    neighbor_distances_full = np.broadcast_to(distances_padded, neighbors_padded.shape)\n    nodes = np.flatnonzero(mask)\n    nodes_sequential = np.arange(nodes.size)\n    # neighbors outside the mask get mapped to 0, which is a valid index,\n    # BUT, they will be masked out in the next step.\n    neighbors = map_array(neighbors_padded, nodes_padded, nodes)\n    neighbors_mask = padded.reshape(-1)[neighbors_padded]\n    num_neighbors = np.sum(neighbors_mask, axis=1)\n    indices = np.repeat(nodes, num_neighbors)\n    indices_sequential = np.repeat(nodes_sequential, num_neighbors)\n    neighbor_indices = neighbors[neighbors_mask]\n    neighbor_distances = neighbor_distances_full[neighbors_mask]\n    neighbor_indices_sequential = map_array(neighbor_indices, nodes, nodes_sequential)\n    if edge_function is None:\n        data = neighbor_distances\n    else:\n        image_r = image.reshape(-1)\n        data = edge_function(\n            image_r[indices], image_r[neighbor_indices], neighbor_distances\n        )\n    m = nodes_sequential.size\n    mat = sparse.coo_matrix(\n        (data, (indices_sequential, neighbor_indices_sequential)), shape=(m, m)\n    )\n    graph = mat.tocsr()\n    return graph, nodes\n\n\ndef central_pixel(graph, nodes=None, shape=None, partition_size=100):\n    \"\"\"Find the pixel with the highest closeness centrality.\n\n    Closeness centrality is the inverse of the total sum of shortest distances\n    from a node to every other node.\n\n    Parameters\n    ----------\n    graph : scipy.sparse.csr_matrix\n        The sparse matrix representation of the graph.\n    nodes : array of int\n        The raveled index of each node in graph in the image. If not provided,\n        the returned value will be the index in the input graph.\n    shape : tuple of int\n        The shape of the image in which the nodes are embedded. If provided,\n        the returned coordinates are a NumPy multi-index of the same\n        dimensionality as the input shape. Otherwise, the returned coordinate\n        is the raveled index provided in `nodes`.\n    partition_size : int\n        This function computes the shortest path distance between every pair\n        of nodes in the graph. This can result in a very large (N*N) matrix.\n        As a simple performance tweak, the distance values are computed in\n        lots of `partition_size`, resulting in a memory requirement of only\n        partition_size*N.\n\n    Returns\n    -------\n    position : int or tuple of int\n        If shape is given, the coordinate of the central pixel in the image.\n        Otherwise, the raveled index of that pixel.\n    distances : array of float\n        The total sum of distances from each node to each other reachable\n        node.\n    \"\"\"\n    if nodes is None:\n        nodes = np.arange(graph.shape[0])\n    if partition_size is None:\n        num_splits = 1\n    else:\n        num_splits = max(2, graph.shape[0] // partition_size)\n    idxs = np.arange(graph.shape[0])\n    total_shortest_path_len_list = []\n    for partition in np.array_split(idxs, num_splits):\n        shortest_paths = csgraph.shortest_path(graph, directed=False, indices=partition)\n        shortest_paths_no_inf = np.nan_to_num(shortest_paths)\n        total_shortest_path_len_list.append(np.sum(shortest_paths_no_inf, axis=1))\n    total_shortest_path_len = np.concatenate(total_shortest_path_len_list)\n    nonzero = np.flatnonzero(total_shortest_path_len)\n    min_sp = np.argmin(total_shortest_path_len[nonzero])\n    raveled_index = nodes[nonzero[min_sp]]\n    if shape is not None:\n        central = np.unravel_index(raveled_index, shape)\n    else:\n        central = raveled_index\n    return central, total_shortest_path_len\n", "path": "skimage/graph/_graph.py"}], "after_files": [{"content": "import numpy as np\nfrom scipy import sparse\nfrom scipy.sparse import csgraph\nfrom ..morphology._util import _raveled_offsets_and_distances\nfrom ..util._map_array import map_array\n\n\ndef _weighted_abs_diff(values0, values1, distances):\n    \"\"\"A default edge function for complete image graphs.\n\n    A pixel graph on an image with no edge values and no mask is a very\n    boring regular lattice, so we define a default edge weight to be the\n    absolute difference between values *weighted* by the distance\n    between them.\n\n    Parameters\n    ----------\n    values0 : array\n        The pixel values for each node.\n    values1 : array\n        The pixel values for each neighbor.\n    distances : array\n        The distance between each node and its neighbor.\n\n    Returns\n    -------\n    edge_values : array of float\n        The computed values: abs(values0 - values1) * distances.\n    \"\"\"\n    return np.abs(values0 - values1) * distances\n\n\ndef pixel_graph(image, *, mask=None, edge_function=None, connectivity=1, spacing=None):\n    \"\"\"Create an adjacency graph of pixels in an image.\n\n    Pixels where the mask is True are nodes in the returned graph, and they are\n    connected by edges to their neighbors according to the connectivity\n    parameter. By default, the *value* of an edge when a mask is given, or when\n    the image is itself the mask, is the Euclidean distance between the pixels.\n\n    However, if an int- or float-valued image is given with no mask, the value\n    of the edges is the absolute difference in intensity between adjacent\n    pixels, weighted by the Euclidean distance.\n\n    Parameters\n    ----------\n    image : array\n        The input image. If the image is of type bool, it will be used as the\n        mask as well.\n    mask : array of bool\n        Which pixels to use. If None, the graph for the whole image is used.\n    edge_function : callable\n        A function taking an array of pixel values, and an array of neighbor\n        pixel values, and an array of distances, and returning a value for the\n        edge. If no function is given, the value of an edge is just the\n        distance.\n    connectivity : int\n        The square connectivity of the pixel neighborhood: the number of\n        orthogonal steps allowed to consider a pixel a neighbor. See\n        `scipy.ndimage.generate_binary_structure` for details.\n    spacing : tuple of float\n        The spacing between pixels along each axis.\n\n    Returns\n    -------\n    graph : scipy.sparse.csr_matrix\n        A sparse adjacency matrix in which entry (i, j) is 1 if nodes i and j\n        are neighbors, 0 otherwise.\n    nodes : array of int\n        The nodes of the graph. These correspond to the raveled indices of the\n        nonzero pixels in the mask.\n    \"\"\"\n    if mask is None:\n        if image.dtype == bool:\n            mask = image\n        else:\n            mask = np.ones_like(image, dtype=bool)\n\n    if edge_function is None:\n        if image.dtype == bool:\n\n            def edge_function(x, y, distances):\n                return distances\n\n        else:\n            edge_function = _weighted_abs_diff\n\n    # Strategy: we are going to build the (i, j, data) arrays of a scipy\n    # sparse COO matrix, then convert to CSR (which is fast).\n    # - grab the raveled IDs of the foreground (mask == True) parts of the\n    #   image **in the padded space**.\n    # - broadcast them together with the raveled offsets to their neighbors.\n    #   This gives us for each foreground pixel a list of neighbors (that\n    #   may or may not be selected by the mask). (We also track the *distance*\n    #   to each neighbor.)\n    # - select \"valid\" entries in the neighbors and distance arrays by indexing\n    #   into the mask, which we can do since these are raveled indices.\n    # - use np.repeat() to repeat each source index according to the number\n    #   of neighbors selected by the mask it has. Each of these repeated\n    #   indices will be lined up with its neighbor, i.e. **this is the i\n    #   array** of the COO format matrix.\n    # - use the mask as a boolean index to get a 1D view of the selected\n    #   neighbors. **This is the j array.**\n    # - by default, the same boolean indexing can be applied to the distances\n    #   to each neighbor, to give the **data array.** Optionally, a\n    #   provided edge function can be computed on the pixel values and the\n    #   distances to give a different value for the edges.\n    # Note, we use map_array to map the raveled coordinates in the padded\n    # image to the ones in the original image, and those are the returned\n    # nodes.\n    padded = np.pad(mask, 1, mode='constant', constant_values=False)\n    nodes_padded = np.flatnonzero(padded)\n    neighbor_offsets_padded, distances_padded = _raveled_offsets_and_distances(\n        padded.shape, connectivity=connectivity, spacing=spacing\n    )\n    neighbors_padded = nodes_padded[:, np.newaxis] + neighbor_offsets_padded\n    neighbor_distances_full = np.broadcast_to(distances_padded, neighbors_padded.shape)\n    nodes = np.flatnonzero(mask)\n    nodes_sequential = np.arange(nodes.size)\n    # neighbors outside the mask get mapped to 0, which is a valid index,\n    # BUT, they will be masked out in the next step.\n    neighbors = map_array(neighbors_padded, nodes_padded, nodes)\n    neighbors_mask = padded.reshape(-1)[neighbors_padded]\n    num_neighbors = np.sum(neighbors_mask, axis=1)\n    indices = np.repeat(nodes, num_neighbors)\n    indices_sequential = np.repeat(nodes_sequential, num_neighbors)\n    neighbor_indices = neighbors[neighbors_mask]\n    neighbor_distances = neighbor_distances_full[neighbors_mask]\n    neighbor_indices_sequential = map_array(neighbor_indices, nodes, nodes_sequential)\n\n    image_r = image.reshape(-1)\n    data = edge_function(\n        image_r[indices], image_r[neighbor_indices], neighbor_distances\n    )\n\n    m = nodes_sequential.size\n    mat = sparse.coo_matrix(\n        (data, (indices_sequential, neighbor_indices_sequential)), shape=(m, m)\n    )\n    graph = mat.tocsr()\n    return graph, nodes\n\n\ndef central_pixel(graph, nodes=None, shape=None, partition_size=100):\n    \"\"\"Find the pixel with the highest closeness centrality.\n\n    Closeness centrality is the inverse of the total sum of shortest distances\n    from a node to every other node.\n\n    Parameters\n    ----------\n    graph : scipy.sparse.csr_matrix\n        The sparse matrix representation of the graph.\n    nodes : array of int\n        The raveled index of each node in graph in the image. If not provided,\n        the returned value will be the index in the input graph.\n    shape : tuple of int\n        The shape of the image in which the nodes are embedded. If provided,\n        the returned coordinates are a NumPy multi-index of the same\n        dimensionality as the input shape. Otherwise, the returned coordinate\n        is the raveled index provided in `nodes`.\n    partition_size : int\n        This function computes the shortest path distance between every pair\n        of nodes in the graph. This can result in a very large (N*N) matrix.\n        As a simple performance tweak, the distance values are computed in\n        lots of `partition_size`, resulting in a memory requirement of only\n        partition_size*N.\n\n    Returns\n    -------\n    position : int or tuple of int\n        If shape is given, the coordinate of the central pixel in the image.\n        Otherwise, the raveled index of that pixel.\n    distances : array of float\n        The total sum of distances from each node to each other reachable\n        node.\n    \"\"\"\n    if nodes is None:\n        nodes = np.arange(graph.shape[0])\n    if partition_size is None:\n        num_splits = 1\n    else:\n        num_splits = max(2, graph.shape[0] // partition_size)\n    idxs = np.arange(graph.shape[0])\n    total_shortest_path_len_list = []\n    for partition in np.array_split(idxs, num_splits):\n        shortest_paths = csgraph.shortest_path(graph, directed=False, indices=partition)\n        shortest_paths_no_inf = np.nan_to_num(shortest_paths)\n        total_shortest_path_len_list.append(np.sum(shortest_paths_no_inf, axis=1))\n    total_shortest_path_len = np.concatenate(total_shortest_path_len_list)\n    nonzero = np.flatnonzero(total_shortest_path_len)\n    min_sp = np.argmin(total_shortest_path_len[nonzero])\n    raveled_index = nodes[nonzero[min_sp]]\n    if shape is not None:\n        central = np.unravel_index(raveled_index, shape)\n    else:\n        central = raveled_index\n    return central, total_shortest_path_len\n", "path": "skimage/graph/_graph.py"}]}