nettracer3d 0.8.9__tar.gz → 0.9.0__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.
Potentially problematic release.
This version of nettracer3d might be problematic. Click here for more details.
- {nettracer3d-0.8.9/src/nettracer3d.egg-info → nettracer3d-0.9.0}/PKG-INFO +14 -4
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/README.md +13 -3
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/pyproject.toml +1 -1
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/cellpose_manager.py +22 -11
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/modularity.py +24 -9
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/neighborhoods.py +46 -14
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/nettracer.py +286 -48
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/nettracer_gui.py +580 -201
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/proximity.py +101 -48
- {nettracer3d-0.8.9 → nettracer3d-0.9.0/src/nettracer3d.egg-info}/PKG-INFO +14 -4
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/LICENSE +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/setup.cfg +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/__init__.py +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/community_extractor.py +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/excelotron.py +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/morphology.py +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/network_analysis.py +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/network_draw.py +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/node_draw.py +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/painting.py +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/run.py +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/segmenter.py +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/segmenter_GPU.py +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/simple_network.py +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d/smart_dilate.py +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d.egg-info/SOURCES.txt +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d.egg-info/dependency_links.txt +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d.egg-info/entry_points.txt +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d.egg-info/requires.txt +0 -0
- {nettracer3d-0.8.9 → nettracer3d-0.9.0}/src/nettracer3d.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nettracer3d
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: Scripts for intializing and analyzing networks from segmentations of three dimensional images.
|
|
5
5
|
Author-email: Liam McLaughlin <liamm@wustl.edu>
|
|
6
6
|
Project-URL: Documentation, https://nettracer3d.readthedocs.io/en/latest/
|
|
@@ -110,6 +110,16 @@ McLaughlin, L., Zhang, B., Sharma, S. et al. Three dimensional multiscalar neuro
|
|
|
110
110
|
|
|
111
111
|
NetTracer3D was developed by Liam McLaughlin while working under Dr. Sanjay Jain at Washington University School of Medicine.
|
|
112
112
|
|
|
113
|
-
-- Version 0.
|
|
114
|
-
|
|
115
|
-
*
|
|
113
|
+
-- Version 0.9.0 Updates --
|
|
114
|
+
* Note that this includes updates for 0.8.3 - 0.9.0
|
|
115
|
+
* Bug Fixes
|
|
116
|
+
* Updated network histogram statistics menu, and moved degree distribution here
|
|
117
|
+
* Added gray watershed
|
|
118
|
+
* Updated binary watershed
|
|
119
|
+
* Improved branch labelling method
|
|
120
|
+
* Updated branch removal in skeletonization method to no longer trim branches that do not reach their node.
|
|
121
|
+
* Added default branchpoint and branch-adjacency calculation options.
|
|
122
|
+
* Improved speed of painting and panning.
|
|
123
|
+
* Enabled the nearest neighbor method to handle non-centroid objects, for the first neighbor at least. And updated it to actually predict theoretical clustering when coloring the heatmap.
|
|
124
|
+
* Improved segmenter.
|
|
125
|
+
* Added centroids UMAP method.
|
|
@@ -65,6 +65,16 @@ McLaughlin, L., Zhang, B., Sharma, S. et al. Three dimensional multiscalar neuro
|
|
|
65
65
|
|
|
66
66
|
NetTracer3D was developed by Liam McLaughlin while working under Dr. Sanjay Jain at Washington University School of Medicine.
|
|
67
67
|
|
|
68
|
-
-- Version 0.
|
|
69
|
-
|
|
70
|
-
*
|
|
68
|
+
-- Version 0.9.0 Updates --
|
|
69
|
+
* Note that this includes updates for 0.8.3 - 0.9.0
|
|
70
|
+
* Bug Fixes
|
|
71
|
+
* Updated network histogram statistics menu, and moved degree distribution here
|
|
72
|
+
* Added gray watershed
|
|
73
|
+
* Updated binary watershed
|
|
74
|
+
* Improved branch labelling method
|
|
75
|
+
* Updated branch removal in skeletonization method to no longer trim branches that do not reach their node.
|
|
76
|
+
* Added default branchpoint and branch-adjacency calculation options.
|
|
77
|
+
* Improved speed of painting and panning.
|
|
78
|
+
* Enabled the nearest neighbor method to handle non-centroid objects, for the first neighbor at least. And updated it to actually predict theoretical clustering when coloring the heatmap.
|
|
79
|
+
* Improved segmenter.
|
|
80
|
+
* Added centroids UMAP method.
|
|
@@ -16,14 +16,15 @@ class CellposeGUILauncher:
|
|
|
16
16
|
"""
|
|
17
17
|
self.parent_widget = parent_widget
|
|
18
18
|
self.cellpose_process = None
|
|
19
|
-
|
|
20
|
-
def launch_cellpose_gui(self, image_path=None, working_directory=None):
|
|
19
|
+
|
|
20
|
+
def launch_cellpose_gui(self, image_path=None, working_directory=None, use_3d=False):
|
|
21
21
|
"""
|
|
22
22
|
Launch cellpose GUI in a separate thread.
|
|
23
23
|
|
|
24
24
|
Args:
|
|
25
25
|
image_path (str, optional): Path to image file to load automatically
|
|
26
26
|
working_directory (str, optional): Directory to start cellpose in
|
|
27
|
+
use_3d (bool, optional): Whether to launch cellpose 3D version (default: False)
|
|
27
28
|
|
|
28
29
|
Returns:
|
|
29
30
|
bool: True if launch was initiated successfully
|
|
@@ -34,6 +35,10 @@ class CellposeGUILauncher:
|
|
|
34
35
|
# Build command
|
|
35
36
|
cmd = [sys.executable, "-m", "cellpose"]
|
|
36
37
|
|
|
38
|
+
# Add 3D flag if requested
|
|
39
|
+
if use_3d:
|
|
40
|
+
cmd.append("--Zstack")
|
|
41
|
+
|
|
37
42
|
# Add image path if provided
|
|
38
43
|
if image_path and Path(image_path).exists():
|
|
39
44
|
cmd.extend(["--image_path", str(image_path)])
|
|
@@ -55,29 +60,35 @@ class CellposeGUILauncher:
|
|
|
55
60
|
except Exception as e:
|
|
56
61
|
if self.parent_widget:
|
|
57
62
|
# Show error in main thread
|
|
58
|
-
|
|
63
|
+
version_str = "3D " if use_3d else ""
|
|
64
|
+
self.show_error(f"Failed to launch cellpose {version_str}GUI: {str(e)}")
|
|
59
65
|
else:
|
|
60
|
-
|
|
66
|
+
version_str = "3D " if use_3d else ""
|
|
67
|
+
print(f"Failed to launch cellpose {version_str}GUI: {str(e)}")
|
|
61
68
|
|
|
62
69
|
try:
|
|
63
70
|
# Start cellpose in separate thread
|
|
64
71
|
thread = threading.Thread(target=run_cellpose, daemon=True)
|
|
65
72
|
thread.start()
|
|
66
73
|
|
|
67
|
-
if self.parent_widget:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
74
|
+
#if self.parent_widget:
|
|
75
|
+
#version_str = "3D " if use_3d else ""
|
|
76
|
+
#self.show_info(f"Cellpose {version_str}GUI launched!")
|
|
77
|
+
#else:
|
|
78
|
+
#version_str = "3D " if use_3d else ""
|
|
79
|
+
#print(f"Cellpose {version_str}GUI launched!")
|
|
71
80
|
|
|
72
81
|
return True
|
|
73
82
|
|
|
74
83
|
except Exception as e:
|
|
75
84
|
if self.parent_widget:
|
|
76
|
-
|
|
85
|
+
version_str = "3D " if use_3d else ""
|
|
86
|
+
self.show_error(f"Failed to start cellpose {version_str}thread: {str(e)}")
|
|
77
87
|
else:
|
|
78
|
-
|
|
88
|
+
version_str = "3D " if use_3d else ""
|
|
89
|
+
print(f"Failed to start cellpose {version_str}thread: {str(e)}")
|
|
79
90
|
return False
|
|
80
|
-
|
|
91
|
+
|
|
81
92
|
def launch_with_directory(self, directory_path):
|
|
82
93
|
"""
|
|
83
94
|
Launch cellpose GUI with a specific directory.
|
|
@@ -103,7 +103,7 @@ def read_excel_to_lists(file_path, sheet_name=0):
|
|
|
103
103
|
|
|
104
104
|
|
|
105
105
|
|
|
106
|
-
def show_communities_flex(G, master_list, normalized_weights, geo_info
|
|
106
|
+
def show_communities_flex(G, master_list, normalized_weights, geo_info=None, geometric=False, directory=None, weighted=True, partition=None, style=0):
|
|
107
107
|
|
|
108
108
|
if normalized_weights is None:
|
|
109
109
|
G, edge_weights = network_analysis.weighted_network(master_list)
|
|
@@ -137,10 +137,25 @@ def show_communities_flex(G, master_list, normalized_weights, geo_info = None, g
|
|
|
137
137
|
|
|
138
138
|
# Create a mapping of community IDs to sequential indices
|
|
139
139
|
unique_communities = sorted(set(partition.values()))
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
#
|
|
143
|
-
|
|
140
|
+
|
|
141
|
+
# Use the same color generation method as the overlay system
|
|
142
|
+
# Get community sizes for sorting (largest first)
|
|
143
|
+
from collections import Counter
|
|
144
|
+
community_sizes = Counter(partition.values())
|
|
145
|
+
sorted_communities = sorted(unique_communities, key=lambda x: community_sizes[x], reverse=True)
|
|
146
|
+
|
|
147
|
+
from . import community_extractor
|
|
148
|
+
|
|
149
|
+
# Generate distinct colors using the same method as assign_community_colors
|
|
150
|
+
colors_rgb = community_extractor.generate_distinct_colors(len(unique_communities))
|
|
151
|
+
|
|
152
|
+
# Create community to color mapping (same order as the overlay system)
|
|
153
|
+
community_to_color = {comm: colors_rgb[i] for i, comm in enumerate(sorted_communities)}
|
|
154
|
+
|
|
155
|
+
# Convert RGB tuples to matplotlib format (0-1 range)
|
|
156
|
+
colors_matplotlib = {}
|
|
157
|
+
for comm, rgb in community_to_color.items():
|
|
158
|
+
colors_matplotlib[comm] = tuple(c/255.0 for c in rgb)
|
|
144
159
|
|
|
145
160
|
if weighted:
|
|
146
161
|
G = nx.Graph()
|
|
@@ -156,7 +171,7 @@ def show_communities_flex(G, master_list, normalized_weights, geo_info = None, g
|
|
|
156
171
|
for community_id, nodes in communities.items():
|
|
157
172
|
node_sizes_list = [z_pos[node] for node in nodes]
|
|
158
173
|
nx.draw_networkx_nodes(G, pos, nodelist=nodes,
|
|
159
|
-
node_color=[
|
|
174
|
+
node_color=[colors_matplotlib[community_id]],
|
|
160
175
|
node_size=node_sizes_list, alpha=0.8)
|
|
161
176
|
|
|
162
177
|
# Draw edges with normalized weights
|
|
@@ -172,7 +187,7 @@ def show_communities_flex(G, master_list, normalized_weights, geo_info = None, g
|
|
|
172
187
|
# Draw the nodes, coloring them according to their community
|
|
173
188
|
for community_id, nodes in communities.items():
|
|
174
189
|
nx.draw_networkx_nodes(G, pos, nodelist=nodes,
|
|
175
|
-
node_color=[
|
|
190
|
+
node_color=[colors_matplotlib[community_id]],
|
|
176
191
|
node_size=100, alpha=0.8)
|
|
177
192
|
|
|
178
193
|
# Draw edges with normalized weights
|
|
@@ -183,8 +198,8 @@ def show_communities_flex(G, master_list, normalized_weights, geo_info = None, g
|
|
|
183
198
|
nx.draw_networkx_labels(G, pos)
|
|
184
199
|
|
|
185
200
|
else:
|
|
186
|
-
# Create node color list based on partition and mapping
|
|
187
|
-
node_colors = [
|
|
201
|
+
# Create node color list based on partition and the same color mapping
|
|
202
|
+
node_colors = [colors_matplotlib[partition[node]] for node in G.nodes()]
|
|
188
203
|
|
|
189
204
|
if geometric:
|
|
190
205
|
pos, z_pos = simple_network.geometric_positions(geo_info[0], geo_info[1])
|
|
@@ -644,25 +644,41 @@ def create_community_heatmap(community_intensity, node_community, node_centroids
|
|
|
644
644
|
|
|
645
645
|
# Create colormap function (RdBu_r - red for high, blue for low, yellow/white for middle)
|
|
646
646
|
def intensity_to_rgb(intensity, min_val, max_val):
|
|
647
|
-
"""Convert intensity value to RGB using RdBu_r colormap logic"""
|
|
647
|
+
"""Convert intensity value to RGB using RdBu_r colormap logic, centered at 0"""
|
|
648
|
+
|
|
649
|
+
# Handle edge case where all values are the same
|
|
648
650
|
if max_val == min_val:
|
|
649
|
-
|
|
651
|
+
if intensity == 0:
|
|
652
|
+
return np.array([255, 255, 255], dtype=np.uint8) # White for 0
|
|
653
|
+
elif intensity > 0:
|
|
654
|
+
return np.array([255, 200, 200], dtype=np.uint8) # Light red for positive
|
|
655
|
+
else:
|
|
656
|
+
return np.array([200, 200, 255], dtype=np.uint8) # Light blue for negative
|
|
657
|
+
|
|
658
|
+
# Find the maximum absolute value for symmetric scaling around 0
|
|
659
|
+
max_abs = max(abs(min_val), abs(max_val))
|
|
660
|
+
|
|
661
|
+
# If max_abs is 0, everything is 0, so return white
|
|
662
|
+
if max_abs == 0:
|
|
650
663
|
return np.array([255, 255, 255], dtype=np.uint8) # White
|
|
651
664
|
|
|
652
|
-
# Normalize to -1 to 1 range
|
|
653
|
-
normalized =
|
|
665
|
+
# Normalize intensity to -1 to 1 range, centered at 0
|
|
666
|
+
normalized = intensity / max_abs
|
|
654
667
|
normalized = np.clip(normalized, -1, 1)
|
|
655
668
|
|
|
656
669
|
if normalized > 0:
|
|
657
|
-
# Positive values: white to red
|
|
670
|
+
# Positive values: white to red (intensity 0 = white, max positive = red)
|
|
658
671
|
r = 255
|
|
659
672
|
g = int(255 * (1 - normalized))
|
|
660
673
|
b = int(255 * (1 - normalized))
|
|
661
|
-
|
|
662
|
-
# Negative values: white to blue
|
|
674
|
+
elif normalized < 0:
|
|
675
|
+
# Negative values: white to blue (intensity 0 = white, max negative = blue)
|
|
663
676
|
r = int(255 * (1 + normalized))
|
|
664
677
|
g = int(255 * (1 + normalized))
|
|
665
678
|
b = 255
|
|
679
|
+
else:
|
|
680
|
+
# Exactly 0: white
|
|
681
|
+
r, g, b = 255, 255, 255
|
|
666
682
|
|
|
667
683
|
return np.array([r, g, b], dtype=np.uint8)
|
|
668
684
|
|
|
@@ -868,25 +884,41 @@ def create_node_heatmap(node_intensity, node_centroids, shape=None, is_3d=True,
|
|
|
868
884
|
|
|
869
885
|
# Create colormap function (RdBu_r - red for high, blue for low, yellow/white for middle)
|
|
870
886
|
def intensity_to_rgb(intensity, min_val, max_val):
|
|
871
|
-
"""Convert intensity value to RGB using RdBu_r colormap logic"""
|
|
887
|
+
"""Convert intensity value to RGB using RdBu_r colormap logic, centered at 0"""
|
|
888
|
+
|
|
889
|
+
# Handle edge case where all values are the same
|
|
872
890
|
if max_val == min_val:
|
|
873
|
-
|
|
891
|
+
if intensity == 0:
|
|
892
|
+
return np.array([255, 255, 255], dtype=np.uint8) # White for 0
|
|
893
|
+
elif intensity > 0:
|
|
894
|
+
return np.array([255, 200, 200], dtype=np.uint8) # Light red for positive
|
|
895
|
+
else:
|
|
896
|
+
return np.array([200, 200, 255], dtype=np.uint8) # Light blue for negative
|
|
897
|
+
|
|
898
|
+
# Find the maximum absolute value for symmetric scaling around 0
|
|
899
|
+
max_abs = max(abs(min_val), abs(max_val))
|
|
900
|
+
|
|
901
|
+
# If max_abs is 0, everything is 0, so return white
|
|
902
|
+
if max_abs == 0:
|
|
874
903
|
return np.array([255, 255, 255], dtype=np.uint8) # White
|
|
875
904
|
|
|
876
|
-
# Normalize to -1 to 1 range
|
|
877
|
-
normalized =
|
|
905
|
+
# Normalize intensity to -1 to 1 range, centered at 0
|
|
906
|
+
normalized = intensity / max_abs
|
|
878
907
|
normalized = np.clip(normalized, -1, 1)
|
|
879
908
|
|
|
880
909
|
if normalized > 0:
|
|
881
|
-
# Positive values: white to red
|
|
910
|
+
# Positive values: white to red (intensity 0 = white, max positive = red)
|
|
882
911
|
r = 255
|
|
883
912
|
g = int(255 * (1 - normalized))
|
|
884
913
|
b = int(255 * (1 - normalized))
|
|
885
|
-
|
|
886
|
-
# Negative values: white to blue
|
|
914
|
+
elif normalized < 0:
|
|
915
|
+
# Negative values: white to blue (intensity 0 = white, max negative = blue)
|
|
887
916
|
r = int(255 * (1 + normalized))
|
|
888
917
|
g = int(255 * (1 + normalized))
|
|
889
918
|
b = 255
|
|
919
|
+
else:
|
|
920
|
+
# Exactly 0: white
|
|
921
|
+
r, g, b = 255, 255, 255
|
|
890
922
|
|
|
891
923
|
return np.array([r, g, b], dtype=np.uint8)
|
|
892
924
|
|