nettracer3d 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ NetTracer3D is freely available for academic and nonprofit use and can be obtained from Liam McLaughlin (boom2449@gmail.com) OR at pip (pip install nettracer3d), provided that the following citation is included in any abstract, paper, or presentation utilizing NetTracer3D.
2
+
3
+ Three Dimensional Multiscalar Neurovascular Nephron Connectivity Map of the Human Kidney Across the Lifespan
4
+ Liam McLaughlin, Bo Zhang, Siddharth Sharma, Amanda L. Knoten, Madhurima Kaushal, Jeffrey M. Purkerson, Heidy Huyck, Gloria S. Pryhuber, Joseph P. Gaut, Sanjay Jain
5
+ bioRxiv 2024.07.29.605633; doi: https://doi.org/10.1101/2024.07.29.605633
6
+
7
+ Commercial use is available for a fee. Copyright © is held by Washington University. Please direct all commercial requests for licensing, information, and limited evaluation copies to Washington University's Office of Technology Management at OTM@wustl.edu.
@@ -0,0 +1,37 @@
1
+ Metadata-Version: 2.1
2
+ Name: nettracer3d
3
+ Version: 0.0.1
4
+ Summary: Scripts for intialzing and analyzing networks from segmentations of three dimensional images.
5
+ Author-email: Liam McLaughlin <boom2449@gmail.com>
6
+ Project-URL: User_Manual, https://drive.google.com/drive/folders/1fTkz3n4LN9_VxKRKC8lVQSlrz_wq0bVn?usp=drive_link
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: Other/Proprietary License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.8
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: numpy
14
+ Requires-Dist: scipy
15
+ Requires-Dist: scikit-image
16
+ Requires-Dist: Pillow
17
+ Requires-Dist: matplotlib
18
+ Requires-Dist: networkx
19
+ Requires-Dist: opencv-python
20
+ Requires-Dist: openpyxl
21
+ Requires-Dist: pandas
22
+ Requires-Dist: plotly
23
+ Requires-Dist: python-louvain
24
+ Requires-Dist: tifffile
25
+ Provides-Extra: cuda11
26
+ Requires-Dist: cupy-cuda11x; extra == "cuda11"
27
+ Provides-Extra: cuda12
28
+ Requires-Dist: cupy-cuda12x; extra == "cuda12"
29
+
30
+ NetTracer3D is a python package developed for 3D analysis of microscopic images in the .tif file format, such as lightsheet images, although it can also be used for 2D analysis if the user converts the 2D .tif file into a 3D stack first by appending the image together twice. NetTracer3D does not function on images directly, but rather on binary/labelled segmentations of image signal, and general requires one segmentation to serve as objects in a network (nodes) and one segmentation to serve as a connective media (edges), although it is possible to make networks from only edges.
31
+
32
+ Please see: https://drive.google.com/drive/folders/1fTkz3n4LN9_VxKRKC8lVQSlrz_wq0bVn?usp=drive_link
33
+ for a user manual that provides documentation and detailed information.
34
+
35
+ NetTracer3D is free to use/fork for academic/nonprofit use so long as citation is provided, and is available for commercial use at a fee (see license file for information).
36
+
37
+ NetTracer3D was developed by Liam McLaughlin while working under Dr. Sanjay Jain at Washington University School of Medicine.
@@ -0,0 +1,8 @@
1
+ NetTracer3D is a python package developed for 3D analysis of microscopic images in the .tif file format, such as lightsheet images, although it can also be used for 2D analysis if the user converts the 2D .tif file into a 3D stack first by appending the image together twice. NetTracer3D does not function on images directly, but rather on binary/labelled segmentations of image signal, and general requires one segmentation to serve as objects in a network (nodes) and one segmentation to serve as a connective media (edges), although it is possible to make networks from only edges.
2
+
3
+ Please see: https://drive.google.com/drive/folders/1fTkz3n4LN9_VxKRKC8lVQSlrz_wq0bVn?usp=drive_link
4
+ for a user manual that provides documentation and detailed information.
5
+
6
+ NetTracer3D is free to use/fork for academic/nonprofit use so long as citation is provided, and is available for commercial use at a fee (see license file for information).
7
+
8
+ NetTracer3D was developed by Liam McLaughlin while working under Dr. Sanjay Jain at Washington University School of Medicine.
@@ -0,0 +1,35 @@
1
+ [project]
2
+ name = "nettracer3d"
3
+ version = "0.0.1"
4
+ authors = [
5
+ { name="Liam McLaughlin", email="boom2449@gmail.com" },
6
+ ]
7
+ description = "Scripts for intialzing and analyzing networks from segmentations of three dimensional images."
8
+ dependencies = [
9
+ "numpy",
10
+ "scipy",
11
+ "scikit-image",
12
+ "Pillow",
13
+ "matplotlib",
14
+ "networkx",
15
+ "opencv-python",
16
+ "openpyxl",
17
+ "pandas",
18
+ "plotly",
19
+ "python-louvain",
20
+ "tifffile",
21
+ ]
22
+ readme = "README.md"
23
+ requires-python = ">=3.8"
24
+ classifiers = [
25
+ "Programming Language :: Python :: 3",
26
+ "License :: Other/Proprietary License",
27
+ "Operating System :: OS Independent",
28
+ ]
29
+
30
+ [project.optional-dependencies]
31
+ CUDA11 = ["cupy-cuda11x"]
32
+ CUDA12 = ["cupy-cuda12x"]
33
+
34
+ [project.urls]
35
+ User_Manual = "https://drive.google.com/drive/folders/1fTkz3n4LN9_VxKRKC8lVQSlrz_wq0bVn?usp=drive_link"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,595 @@
1
+ import pandas as pd
2
+ import networkx as nx
3
+ import tifffile
4
+ import numpy as np
5
+ from networkx.algorithms import community
6
+ from scipy import ndimage
7
+ from scipy.ndimage import zoom
8
+ from networkx.algorithms import community
9
+ from community import community_louvain
10
+ import node_draw
11
+
12
+
13
+ def binarize(image):
14
+ """Convert an array from numerical values to boolean mask"""
15
+ image = image != 0
16
+
17
+ image = image.astype(np.uint8)
18
+
19
+ return image
20
+
21
+ def upsample_with_padding(data, factor, original_shape):
22
+ # Upsample the input binary array while adding padding to match the original shape
23
+
24
+ # Get the dimensions of the original and upsampled arrays
25
+ original_shape = np.array(original_shape)
26
+ binary_array = zoom(data, factor, order=0)
27
+ upsampled_shape = np.array(binary_array.shape)
28
+
29
+ # Calculate the positive differences in dimensions
30
+ difference_dims = original_shape - upsampled_shape
31
+
32
+ # Calculate the padding amounts for each dimension
33
+ padding_dims = np.maximum(difference_dims, 0)
34
+ padding_before = padding_dims // 2
35
+ padding_after = padding_dims - padding_before
36
+
37
+ # Pad the binary array along each dimension
38
+ padded_array = np.pad(binary_array, [(padding_before[0], padding_after[0]),
39
+ (padding_before[1], padding_after[1]),
40
+ (padding_before[2], padding_after[2])], mode='constant', constant_values=0)
41
+
42
+ # Calculate the subtraction amounts for each dimension
43
+ sub_dims = np.maximum(-difference_dims, 0)
44
+ sub_before = sub_dims // 2
45
+ sub_after = sub_dims - sub_before
46
+
47
+ # Remove planes from the beginning and end
48
+ if sub_dims[0] == 0:
49
+ trimmed_planes = padded_array
50
+ else:
51
+ trimmed_planes = padded_array[sub_before[0]:-sub_after[0], :, :]
52
+
53
+ # Remove rows from the beginning and end
54
+ if sub_dims[1] == 0:
55
+ trimmed_rows = trimmed_planes
56
+ else:
57
+ trimmed_rows = trimmed_planes[:, sub_before[1]:-sub_after[1], :]
58
+
59
+ # Remove columns from the beginning and end
60
+ if sub_dims[2] == 0:
61
+ trimmed_array = trimmed_rows
62
+ else:
63
+ trimmed_array = trimmed_rows[:, :, sub_before[2]:-sub_after[2]]
64
+
65
+ return trimmed_array
66
+
67
+ def weighted_network(excel_file_path):
68
+ """creates a network where the edges have weights proportional to the number of connections they make between the same structure"""
69
+ # Read the Excel file into a pandas DataFrame
70
+ master_list = read_excel_to_lists(excel_file_path)
71
+
72
+ # Create a graph
73
+ G = nx.Graph()
74
+
75
+ # Create a dictionary to store edge weights based on node pairs
76
+ edge_weights = {}
77
+
78
+ nodes_a = master_list[0]
79
+ nodes_b = master_list[1]
80
+
81
+ # Iterate over the DataFrame rows and update edge weights
82
+ for i in range(len(nodes_a)):
83
+ node1, node2 = nodes_a[i], nodes_b[i]
84
+ edge = (node1, node2) if node1 < node2 else (node2, node1) # Ensure consistent order
85
+ edge_weights[edge] = edge_weights.get(edge, 0) + 1
86
+
87
+ # Add edges to the graph with weights
88
+ for edge, weight in edge_weights.items():
89
+ G.add_edge(edge[0], edge[1], weight=weight)
90
+
91
+ return G, edge_weights
92
+
93
+ def compute_centroid(binary_stack, label):
94
+ """
95
+ Finds centroid of labelled object in array
96
+ """
97
+ indices = np.argwhere(binary_stack == label)
98
+ centroid = np.round(np.mean(indices, axis=0)).astype(int)
99
+
100
+ return centroid
101
+
102
+
103
+
104
+ def get_border_nodes(partition, G):
105
+ # Find nodes that border nodes in other communities
106
+ border_nodes = set()
107
+ for edge in G.edges():
108
+ if partition[edge[0]] != partition[edge[1]]:
109
+ border_nodes.add(edge[0])
110
+ border_nodes.add(edge[1])
111
+
112
+ return border_nodes
113
+
114
+ def downsample(data, factor):
115
+ # Downsample the input data by a specified factor
116
+ return zoom(data, 1/factor, order=0)
117
+
118
+ def labels_to_boolean(label_array, labels_list):
119
+ # Use np.isin to create a boolean array with a single operation
120
+ boolean_array = np.isin(label_array, labels_list)
121
+
122
+ return boolean_array
123
+
124
+ def read_excel_to_lists(file_path, sheet_name=0):
125
+ """Convert a pd dataframe to lists"""
126
+ # Read the Excel file into a DataFrame without headers
127
+ df = pd.read_excel(file_path, header=None, sheet_name=sheet_name)
128
+
129
+ df = df.drop(0)
130
+
131
+ # Initialize an empty list to store the lists of values
132
+ data_lists = []
133
+
134
+ # Iterate over each column in the DataFrame
135
+ for column_name, column_data in df.items():
136
+ # Convert the column values to a list and append to the data_lists
137
+ data_lists.append(column_data.tolist())
138
+
139
+ master_list = [[], [], []]
140
+
141
+
142
+ for i in range(0, len(data_lists), 3):
143
+
144
+ master_list[0].extend(data_lists[i])
145
+ master_list[1].extend(data_lists[i+1])
146
+
147
+ try:
148
+ master_list[2].extend(data_lists[i+2])
149
+ except IndexError:
150
+ pass
151
+
152
+ return master_list
153
+
154
+
155
+ def open_network(excel_file_path):
156
+ """opens an unweighted network from the network excel file"""
157
+
158
+ # Read the Excel file into a pandas DataFrame
159
+ master_list = read_excel_to_lists(excel_file_path)
160
+
161
+ # Create a graph
162
+ G = nx.Graph()
163
+
164
+ nodes_a = master_list[0]
165
+ nodes_b = master_list[1]
166
+
167
+ # Add edges to the graph
168
+ for i in range(len(nodes_a)):
169
+ G.add_edge(nodes_a[i], nodes_b[i])
170
+
171
+ return G
172
+
173
+
174
+
175
+ def isolate_full_edges(nodes, edges, directory = None):
176
+ """requires smart_dilate output to function properly"""
177
+
178
+ print("Isolating full edges")
179
+
180
+ if type(nodes) == str:
181
+ nodes = tifffile.imread(nodes)
182
+
183
+ if type(edges) == str:
184
+ edges = tifffile.imread(edges)
185
+
186
+ node_bools = binarize(nodes)
187
+
188
+ del nodes
189
+
190
+ real_edges = node_bools * edges
191
+
192
+ del node_bools
193
+
194
+ # Flatten the 3D array to a 1D array
195
+ real_edges = real_edges.flatten()
196
+
197
+ # Find unique values
198
+ real_edges = np.unique(real_edges)
199
+
200
+ edge_masks = labels_to_boolean(edges, real_edges)
201
+
202
+ del real_edges
203
+
204
+ edge_labels_1 = edge_masks * edges
205
+
206
+ del edge_masks
207
+
208
+ edge_labels_1 = binarize(edge_labels_1)
209
+
210
+ edge_labels_1 = edge_labels_1 * 255
211
+
212
+ if directory is None:
213
+
214
+ tifffile.imsave(f"full_edges_for_component.tif", edge_labels_1)
215
+ print("Full edge labels saved to full_edges_for_component.tif")
216
+ else:
217
+ tifffile.imsave(f"{directory}/full_edges_for_component.tif", edge_labels_1)
218
+ print(f"Full edge labels saved to {directory}/full_edges_for_component.tif")
219
+
220
+
221
+ def isolate_edges(edges, network, iso_network, netlists = None, directory = None):
222
+
223
+ print("Isolating edges")
224
+
225
+ if netlists is None:
226
+
227
+ master_list = read_excel_to_lists(network)
228
+ else:
229
+ master_list = netlists
230
+
231
+ if directory is None:
232
+ comp_list = read_excel_to_lists(iso_network)
233
+ else:
234
+ comp_list = read_excel_to_lists(f"{directory}/{iso_network}")
235
+
236
+ edge_list = []
237
+
238
+ node_1 = master_list[0]
239
+
240
+ node_2 = master_list[1]
241
+
242
+ edges_list = master_list[2]
243
+
244
+ nodes_1 = comp_list[0]
245
+
246
+ nodes_2 = comp_list[1]
247
+
248
+ compare_list = []
249
+
250
+ iso_list = []
251
+
252
+ output_list = []
253
+
254
+ for i in range(len(nodes_1)):
255
+ item = [nodes_1[i], nodes_2[i]]
256
+ iso_list.append(item)
257
+
258
+ for i in range(len(node_1)):
259
+
260
+ item = [node_1[i], node_2[i]]
261
+
262
+ compare_list.append(item)
263
+
264
+ for i in range(len(iso_list)):
265
+
266
+ for k, item in enumerate(compare_list):
267
+ if item == iso_list[i] or [item[1], item[0]] == iso_list[i]:
268
+ add_item = [nodes_1[i], nodes_2[i], edges_list[k]]
269
+ if add_item in output_list:
270
+ break
271
+ else:
272
+ output_list.append(add_item)
273
+
274
+ edge_list.append(edges_list[k])
275
+
276
+ # Convert to a DataFrame
277
+ edges_df = pd.DataFrame(output_list, columns=["Node A", "Node B", "Edge C"])
278
+
279
+ if directory is None:
280
+ # Save to an Excel file
281
+ edges_df.to_excel(f"{iso_network}", index=False)
282
+ print(f"Isolated network file saved to {iso_network}")
283
+ else:
284
+ edges_df.to_excel(f"{directory}/{iso_network}", index=False)
285
+ print(f"Isolated network file saved to {directory}/{iso_network}")
286
+
287
+
288
+ mask2 = labels_to_boolean(edges, edge_list)
289
+
290
+ mask2 = mask2 * edges
291
+
292
+ # Convert boolean values to 0 and 255
293
+ #mask2 = mask2.astype(np.uint8) * 255
294
+
295
+ if directory is None:
296
+
297
+ tifffile.imwrite(f"edges_for_{iso_network}.tif", mask2)
298
+ print(f"Computational edge mask saved to edges_for_{iso_network}.tif")
299
+ else:
300
+ tifffile.imwrite(f"{directory}/edges_for_{iso_network}.tif", mask2)
301
+ print(f"Computational edge mask saved to {directory}/edges_for_{iso_network}.tif")
302
+
303
+
304
+ return output_list, mask2
305
+
306
+
307
+ def isolate_connected_component(nodes, excel, key=None, edge_file = None, netlists = None, search_region = None, directory = None):
308
+
309
+ structure_3d = np.ones((3, 3, 3), dtype=int)
310
+
311
+ if edge_file is not None and type(edge_file) == str:
312
+ edge_file = tifffile.imread(edge_file)
313
+
314
+ if type(nodes) == str:
315
+ nodes = tifffile.imread(nodes)
316
+ if len(np.unique(nodes)) == 2:
317
+ nodes, num_nodes = ndimage.label(nodes, structure=structure_3d)
318
+
319
+ if type(excel) == str:
320
+ G = open_network(excel)
321
+ else:
322
+ G = excel
323
+
324
+ if key is None:
325
+ print("Isolating nodes of largest connected component")
326
+ edges_df, mask, searchers = isolate_largest_connected(nodes, G, search_region = search_region, directory = directory)
327
+ if edge_file is not None:
328
+ output_list, mask2 = isolate_edges(edge_file, excel, 'largest_connected_component.xlsx', netlists = netlists, directory = directory)
329
+ #isolate_full_edges(nodes, edge_file, 'largest_connected_component')
330
+ return output_list, mask, mask2, searchers
331
+ else:
332
+
333
+ return edges_df, mask, searchers
334
+
335
+ else:
336
+ print("Isolating nodes of connected component containing specified key")
337
+ edges_df, mask, searchers = isolate_key_connected(nodes, G, key, search_region = search_region, directory = directory)
338
+ if edge_file is not None:
339
+ output_list, mask2 = isolate_edges(edge_file, excel, f'connected_component_containing_{key}_node.xlsx', netlists = netlists, directory = directory)
340
+ #isolate_full_edges(nodes, edge_file, 'connected_component_containing_specific_node')
341
+ return output_list, mask, mask2, searchers
342
+ else:
343
+ return edges_df, mask, searchers
344
+
345
+
346
+ def _isolate_connected(G, key = None):
347
+
348
+ if key is None:
349
+ connected_components = list(nx.connected_components(G))
350
+ Gcc = sorted(nx.connected_components(G), key=len, reverse=True)
351
+ G0 = G.subgraph(Gcc[0])
352
+ return G0
353
+
354
+ else:
355
+ # Get the connected component containing the specific node label
356
+ connected_component = nx.node_connected_component(G, key)
357
+
358
+ G0 = G.subgraph(connected_component)
359
+ return G0
360
+
361
+
362
+
363
+ def isolate_largest_connected(masks, G, directory = None, search_region = None):
364
+ # Read the Excel file into a pandas DataFrame
365
+
366
+ # Get a list of connected components
367
+ connected_components = list(nx.connected_components(G))
368
+ Gcc = sorted(nx.connected_components(G), key=len, reverse=True)
369
+ G0 = G.subgraph(Gcc[0])
370
+
371
+ # Extract the edges of the largest connected component
372
+ edges_largest_component = list(G0.edges)
373
+
374
+ # Convert to a DataFrame
375
+ edges_df = pd.DataFrame(edges_largest_component, columns=["Node A", "Node B"])
376
+
377
+ if directory is None:
378
+ # Save to an Excel file
379
+ edges_df.to_excel("largest_connected_component.xlsx", index=False)
380
+ print("Largest component nodes saved to largest_connected_component.xlsx")
381
+ else:
382
+ edges_df.to_excel(f"{directory}/largest_connected_component.xlsx", index=False)
383
+ print(f"Largest component nodes saved to {directory}/largest_connected_component.xlsx")
384
+
385
+ nodes_in_largest_component = list(G0)
386
+
387
+ mask2 = labels_to_boolean(masks, nodes_in_largest_component)
388
+
389
+ mask2 = mask2 * masks
390
+
391
+ if search_region is not None:
392
+ searchers = labels_to_boolean(search_region, nodes_in_largest_component)
393
+ searchers = searchers * search_region
394
+ else:
395
+ searchers = None
396
+
397
+ if directory is None:
398
+ tifffile.imwrite("largest_connected_component.tif", mask2)
399
+ print(f"Largest connected component image saved to largest_connected_component.tif")
400
+
401
+ else:
402
+ tifffile.imwrite(f"{directory}/largest_connected_component.tif", mask2)
403
+ print(f"Largest connected component image saved to {directory}/largest_connected_component.tif")
404
+
405
+ return edges_largest_component, mask2, searchers
406
+
407
+
408
+
409
+ def isolate_key_connected(masks, G, key, search_region = None, directory = None):
410
+
411
+
412
+ # Get the connected component containing the specific node label
413
+ connected_component = nx.node_connected_component(G, key)
414
+
415
+ G0 = G.subgraph(connected_component)
416
+
417
+ # Extract the edges of the largest connected component
418
+ edges_component = list(G0.edges)
419
+
420
+ # Convert to a DataFrame
421
+ edges_df = pd.DataFrame(edges_component, columns=["Node A", "Node B"])
422
+
423
+
424
+ if directory is None:
425
+ # Save to an Excel file
426
+ edges_df.to_excel(f"connected_component_containing_{key}_node.xlsx", index=False)
427
+ print(f"Nodes containing {key} saved to connected_component_containing_{key}_node.xlsx")
428
+ else:
429
+ edges_df.to_excel(f"{directory}/connected_component_containing_{key}_node.xlsx", index=False)
430
+ print(f"Nodes containing {key} saved to {directory}/connected_component_containing_{key}_node.xlsx")
431
+
432
+ # Convert the set of nodes to a list
433
+ nodes_in_component = list(connected_component)
434
+
435
+ mask2 = labels_to_boolean(masks, nodes_in_component)
436
+
437
+ mask2 = mask2 * masks
438
+
439
+ # Convert boolean values to 0 and 255
440
+ #mask2 = mask2.astype(np.uint8) * 255
441
+
442
+ if search_region is not None:
443
+ searchers = labels_to_boolean(search_region, nodes_in_component)
444
+ searchers = searchers * search_region
445
+ else:
446
+ searchers = None
447
+
448
+ if directory is None:
449
+ tifffile.imwrite(f"connected_component_containing_{key}_node.tif", mask2)
450
+ print(f"Connected component containing node {key} saved to connected_component_containing_{key}_node.tif")
451
+
452
+ else:
453
+ tifffile.imwrite(f"{directory}/connected_component_containing_{key}_node.tif", mask2)
454
+ print(f"Connected component containing node {key} saved to {directory}/connected_component_containing_{key}_node.tif")
455
+
456
+ return nodes_in_component, mask2, searchers
457
+
458
+
459
+ def extract_mothers(nodes, excel_file_path, centroid_dic = None, directory = None, louvain = True):
460
+
461
+ if type(nodes) == str:
462
+ nodes = tifffile.imread(nodes)
463
+
464
+ if np.unique(nodes) < 3:
465
+ structure_3d = np.ones((3, 3, 3), dtype=int)
466
+ nodes, num_nodes = ndimage.label(nodes, structure=structure_3d)
467
+
468
+ if type(excel_file_path) == str:
469
+ G, edge_weights = weighted_network(excel_file_path)
470
+ else:
471
+ G = excel_file_path
472
+
473
+ if louvain:
474
+ # Apply the Louvain algorithm for community detection
475
+ partition = community_louvain.best_partition(G)
476
+ else:
477
+ some_communities = list(nx.community.label_propagation_communities(G))
478
+ partition = {}
479
+ for i, community in enumerate(some_communities):
480
+ for node in community:
481
+ partition[node] = i + 1
482
+
483
+ my_nodes = get_border_nodes(partition, G)
484
+
485
+ mother_nodes = list(my_nodes)
486
+
487
+ if centroid_dic is None:
488
+ for item in nodes.shape:
489
+ if item < 5:
490
+ down_factor = 1
491
+ break
492
+ else:
493
+ down_factor = 5
494
+
495
+ smalls2 = downsample(nodes, down_factor)
496
+
497
+ centroid_dic = {}
498
+
499
+ for item in mother_nodes:
500
+ centroid = compute_centroid(smalls2, item)
501
+ centroid_dic[item] = centroid
502
+
503
+ mother_dict = {}
504
+
505
+
506
+ for node in mother_nodes:
507
+ mother_dict[node] = G.degree(node)
508
+
509
+ #mask2 = labels_to_boolean(nodes, mother_nodes)
510
+
511
+ smalls = labels_to_boolean(nodes, mother_nodes)
512
+
513
+ # Convert boolean values to 0 and 255
514
+ mask = smalls * nodes
515
+
516
+ labels = node_draw.degree_draw(mother_dict, centroid_dic, smalls)
517
+
518
+ # Convert dictionary to DataFrame with keys as index and values as a column
519
+ df = pd.DataFrame.from_dict(mother_dict, orient='index', columns=['Degree'])
520
+
521
+ # Rename the index to 'Node ID'
522
+ df.index.name = 'Node ID'
523
+
524
+ if directory is None:
525
+
526
+ # Save DataFrame to Excel file
527
+ df.to_excel('mothers.xlsx', engine='openpyxl')
528
+ print("Mother list saved to mothers.xlsx")
529
+ else:
530
+ df.to_excel(f'{directory}/mothers.xlsx', engine='openpyxl')
531
+ print(f"Mother list saved to {directory}/mothers.xlsx")
532
+
533
+ if directory is None:
534
+
535
+ tifffile.imwrite("mother_nodes.tif", mask)
536
+ print("Mother nodes saved to mother_nodes.tif")
537
+ tifffile.imwrite("mother_degree_labels.tif", labels)
538
+ print(f"Mother degree labels saved to mother_degree_labels.tif")
539
+
540
+ else:
541
+ tifffile.imwrite(f"{directory}/mother_nodes.tif", mask)
542
+ print(f"Mother nodes saved to {directory}/mother_nodes.tif")
543
+ tifffile.imwrite(f"{directory}/mother_degree_labels.tif", labels)
544
+ print(f"Mother degree labels saved to {directory}/mother_degree_labels.tif")
545
+
546
+
547
+ smalls = node_draw.degree_infect(mother_dict, mask)
548
+
549
+ if directory is None:
550
+
551
+ tifffile.imwrite("mother_degree_labels_grayscale.tif", smalls)
552
+ print("Mother graycale degree labels saved to mother_degree_labels_grayscale.tif")
553
+
554
+ else:
555
+ tifffile.imwrite(f"{directory}/mother_degree_labels_grayscale.tif", smalls)
556
+ print(f"Mother graycale degree labels saved to {directory}/mother_degree_labels_grayscale.tif")
557
+
558
+
559
+ return mother_nodes
560
+
561
+
562
+
563
+
564
+
565
+
566
+ if __name__ == "__main__":
567
+
568
+ # Read the Excel file into a pandas DataFrame
569
+ excel_file_path = input("Excel file?: ")
570
+ masks = input("watershedded, dilated glom mask?: ")
571
+ masks = tifffile.imread(masks)
572
+ masks = masks.astype(np.uint16)
573
+
574
+ G = open_network(excel_file_path)
575
+
576
+ # Get a list of connected components
577
+ connected_components = list(nx.connected_components(G))
578
+
579
+ largest_component = max(connected_components, key=len)
580
+
581
+
582
+ # Choose a specific connected component (let's say, the first one)
583
+ #selected_component = connected_components[0]
584
+
585
+ # Convert the set of nodes to a list
586
+ #nodes_in_component = list(selected_component)
587
+
588
+ nodes_in_largest_component = list(largest_component)
589
+
590
+ mask2 = labels_to_boolean(masks, nodes_in_largest_component)
591
+
592
+ # Convert boolean values to 0 and 255
593
+ mask2 = mask2.astype(np.uint8) * 255
594
+
595
+ tifffile.imwrite("isolated_community.tif", mask2)