nettracer3d 0.8.8__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.

Files changed (30) hide show
  1. {nettracer3d-0.8.8/src/nettracer3d.egg-info → nettracer3d-0.9.0}/PKG-INFO +14 -4
  2. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/README.md +13 -3
  3. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/pyproject.toml +1 -1
  4. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/cellpose_manager.py +22 -11
  5. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/modularity.py +24 -9
  6. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/neighborhoods.py +46 -14
  7. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/nettracer.py +286 -48
  8. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/nettracer_gui.py +735 -236
  9. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/proximity.py +101 -48
  10. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/segmenter.py +294 -186
  11. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/segmenter_GPU.py +274 -158
  12. {nettracer3d-0.8.8 → nettracer3d-0.9.0/src/nettracer3d.egg-info}/PKG-INFO +14 -4
  13. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/LICENSE +0 -0
  14. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/setup.cfg +0 -0
  15. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/__init__.py +0 -0
  16. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/community_extractor.py +0 -0
  17. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/excelotron.py +0 -0
  18. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/morphology.py +0 -0
  19. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/network_analysis.py +0 -0
  20. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/network_draw.py +0 -0
  21. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/node_draw.py +0 -0
  22. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/painting.py +0 -0
  23. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/run.py +0 -0
  24. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/simple_network.py +0 -0
  25. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d/smart_dilate.py +0 -0
  26. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d.egg-info/SOURCES.txt +0 -0
  27. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d.egg-info/dependency_links.txt +0 -0
  28. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d.egg-info/entry_points.txt +0 -0
  29. {nettracer3d-0.8.8 → nettracer3d-0.9.0}/src/nettracer3d.egg-info/requires.txt +0 -0
  30. {nettracer3d-0.8.8 → 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.8.8
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.8.8 Updates --
114
-
115
- * See Documentation Once Updated
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.8.8 Updates --
69
-
70
- * See Documentation Once Updated
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.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nettracer3d"
3
- version = "0.8.8"
3
+ version = "0.9.0"
4
4
  authors = [
5
5
  { name="Liam McLaughlin", email="liamm@wustl.edu" },
6
6
  ]
@@ -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
- self.show_error(f"Failed to launch cellpose GUI: {str(e)}")
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
- print(f"Failed to launch cellpose GUI: {str(e)}")
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
- self.show_info("Cellpose GUI launched!")
69
- else:
70
- print("Cellpose GUI launched!")
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
- self.show_error(f"Failed to start cellpose thread: {str(e)}")
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
- print(f"Failed to start cellpose thread: {str(e)}")
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 = None, geometric=False, directory=None, weighted=True, partition=None, style=0):
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
- community_to_index = {comm: idx for idx, comm in enumerate(unique_communities)}
141
-
142
- # Prepare colors using the number of unique communities
143
- colors = [plt.cm.jet(i / len(unique_communities)) for i in range(len(unique_communities))]
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=[colors[community_to_index[community_id]]],
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=[colors[community_to_index[community_id]]],
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 = [colors[community_to_index[partition[node]]] for node in G.nodes()]
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
- # All same value, use neutral color
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 (like RdBu_r colormap)
653
- normalized = 2 * (intensity - min_val) / (max_val - min_val) - 1
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
- else:
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
- # All same value, use neutral color
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 (like RdBu_r colormap)
877
- normalized = 2 * (intensity - min_val) / (max_val - min_val) - 1
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
- else:
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