nettracer3d 0.8.5__tar.gz → 0.8.7__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 (31) hide show
  1. {nettracer3d-0.8.5/src/nettracer3d.egg-info → nettracer3d-0.8.7}/PKG-INFO +2 -2
  2. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/README.md +1 -1
  3. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/pyproject.toml +1 -1
  4. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d/nettracer_gui.py +95 -65
  5. nettracer3d-0.8.7/src/nettracer3d/painting.py +549 -0
  6. {nettracer3d-0.8.5 → nettracer3d-0.8.7/src/nettracer3d.egg-info}/PKG-INFO +2 -2
  7. nettracer3d-0.8.5/src/nettracer3d/painting.py +0 -373
  8. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/LICENSE +0 -0
  9. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/setup.cfg +0 -0
  10. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d/__init__.py +0 -0
  11. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d/cellpose_manager.py +0 -0
  12. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d/community_extractor.py +0 -0
  13. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d/excelotron.py +0 -0
  14. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d/modularity.py +0 -0
  15. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d/morphology.py +0 -0
  16. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d/neighborhoods.py +0 -0
  17. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d/nettracer.py +0 -0
  18. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d/network_analysis.py +0 -0
  19. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d/network_draw.py +0 -0
  20. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d/node_draw.py +0 -0
  21. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d/proximity.py +0 -0
  22. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d/run.py +0 -0
  23. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d/segmenter.py +0 -0
  24. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d/segmenter_GPU.py +0 -0
  25. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d/simple_network.py +0 -0
  26. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d/smart_dilate.py +0 -0
  27. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d.egg-info/SOURCES.txt +0 -0
  28. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d.egg-info/dependency_links.txt +0 -0
  29. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d.egg-info/entry_points.txt +0 -0
  30. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/src/nettracer3d.egg-info/requires.txt +0 -0
  31. {nettracer3d-0.8.5 → nettracer3d-0.8.7}/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.5
3
+ Version: 0.8.7
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,6 @@ 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.5 Updates --
113
+ -- Version 0.8.7 Updates --
114
114
 
115
115
  * See Documentation Once Updated
@@ -65,6 +65,6 @@ 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.5 Updates --
68
+ -- Version 0.8.7 Updates --
69
69
 
70
70
  * See Documentation Once Updated
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nettracer3d"
3
- version = "0.8.5"
3
+ version = "0.8.7"
4
4
  authors = [
5
5
  { name="Liam McLaughlin", email="liamm@wustl.edu" },
6
6
  ]
@@ -464,6 +464,8 @@ class ImageViewerWindow(QMainWindow):
464
464
  self.paint_batch = []
465
465
  self.last_paint_pos = None
466
466
 
467
+ self.resume = False
468
+
467
469
  def start_left_scroll(self):
468
470
  """Start scrolling left when left arrow is pressed."""
469
471
  # Single increment first
@@ -2170,6 +2172,15 @@ class ImageViewerWindow(QMainWindow):
2170
2172
  if self.machine_window is not None:
2171
2173
  self.machine_window.silence_button()
2172
2174
  self.canvas.setCursor(Qt.CursorShape.CrossCursor)
2175
+ if (hasattr(self, 'virtual_draw_operations') and self.virtual_draw_operations) or \
2176
+ (hasattr(self, 'virtual_erase_operations') and self.virtual_erase_operations) or \
2177
+ (hasattr(self, 'current_operation') and self.current_operation):
2178
+ # Finish current operation first
2179
+ if hasattr(self, 'current_operation') and self.current_operation:
2180
+ self.pm.finish_current_virtual_operation()
2181
+ # Now convert to real data
2182
+ self.pm.convert_virtual_strokes_to_data()
2183
+ self.update_display()
2173
2184
  if self.pan_mode or self.brush_mode:
2174
2185
  current_xlim = self.ax.get_xlim()
2175
2186
  current_ylim = self.ax.get_ylim()
@@ -2194,6 +2205,15 @@ class ImageViewerWindow(QMainWindow):
2194
2205
  self.threed = False
2195
2206
  self.last_change = None
2196
2207
  self.brush_mode = False
2208
+ if (hasattr(self, 'virtual_draw_operations') and self.virtual_draw_operations) or \
2209
+ (hasattr(self, 'virtual_erase_operations') and self.virtual_erase_operations) or \
2210
+ (hasattr(self, 'current_operation') and self.current_operation):
2211
+ # Finish current operation first
2212
+ if hasattr(self, 'current_operation') and self.current_operation:
2213
+ self.pm.finish_current_virtual_operation()
2214
+ # Now convert to real data
2215
+ self.pm.convert_virtual_strokes_to_data()
2216
+ self.update_display()
2197
2217
  if self.machine_window is not None:
2198
2218
  self.machine_window.silence_button()
2199
2219
  self.canvas.setCursor(Qt.CursorShape.OpenHandCursor)
@@ -2227,8 +2247,6 @@ class ImageViewerWindow(QMainWindow):
2227
2247
  else:
2228
2248
  channel = 2
2229
2249
 
2230
- self.pm.initiate_paint_session(channel, current_xlim, current_ylim)
2231
-
2232
2250
  if self.pan_mode:
2233
2251
  current_xlim = self.ax.get_xlim()
2234
2252
  current_ylim = self.ax.get_ylim()
@@ -2240,6 +2258,15 @@ class ImageViewerWindow(QMainWindow):
2240
2258
  self.zoom_mode = False
2241
2259
  self.update_brush_cursor()
2242
2260
  else:
2261
+ if (hasattr(self, 'virtual_draw_operations') and self.virtual_draw_operations) or \
2262
+ (hasattr(self, 'virtual_erase_operations') and self.virtual_erase_operations) or \
2263
+ (hasattr(self, 'current_operation') and self.current_operation):
2264
+ # Finish current operation first
2265
+ if hasattr(self, 'current_operation') and self.current_operation:
2266
+ self.pm.finish_current_virtual_operation()
2267
+ # Now convert to real data
2268
+ self.pm.convert_virtual_strokes_to_data()
2269
+ self.update_display()
2243
2270
  # Get current zoom and do display update
2244
2271
  current_xlim = self.ax.get_xlim()
2245
2272
  current_ylim = self.ax.get_ylim()
@@ -2498,6 +2525,8 @@ class ImageViewerWindow(QMainWindow):
2498
2525
 
2499
2526
  if self.machine_window is not None:
2500
2527
  if self.machine_window.segmentation_worker is not None:
2528
+ if not self.machine_window.segmentation_worker._paused:
2529
+ self.resume = True
2501
2530
  self.machine_window.segmentation_worker.pause()
2502
2531
 
2503
2532
  # Store current channel visibility state
@@ -2520,10 +2549,23 @@ class ImageViewerWindow(QMainWindow):
2520
2549
  """Handle brush mode with virtual painting."""
2521
2550
  if event.inaxes != self.ax:
2522
2551
  return
2552
+
2553
+ current_xlim = self.ax.get_xlim() if hasattr(self, 'ax') and self.ax.get_xlim() != (0, 1) else None
2554
+ current_ylim = self.ax.get_ylim() if hasattr(self, 'ax') and self.ax.get_ylim() != (0, 1) else None
2555
+
2556
+ if self.pen_button.isChecked():
2557
+ channel = self.active_channel
2558
+ else:
2559
+ channel = 2
2560
+
2561
+ self.pm.initiate_paint_session(channel, current_xlim, current_ylim)
2562
+
2523
2563
 
2524
2564
  if event.button == 1 or event.button == 3:
2525
2565
  if self.machine_window is not None:
2526
2566
  if self.machine_window.segmentation_worker is not None:
2567
+ if not self.machine_window.segmentation_worker._paused:
2568
+ self.resume = True
2527
2569
  self.machine_window.segmentation_worker.pause()
2528
2570
 
2529
2571
  x, y = int(event.xdata), int(event.ydata)
@@ -2533,7 +2575,7 @@ class ImageViewerWindow(QMainWindow):
2533
2575
  current_ylim = self.ax.get_ylim() if hasattr(self, 'ax') and self.ax.get_ylim() != (0, 1) else None
2534
2576
 
2535
2577
  if event.button == 1 and getattr(self, 'can', False):
2536
- self.update_display(preserve_zoom=(current_xlim, current_ylim), skip_paint_reinit = True)
2578
+ self.update_display(preserve_zoom=(current_xlim, current_ylim))
2537
2579
  self.handle_can(x, y)
2538
2580
  return
2539
2581
 
@@ -3099,29 +3141,30 @@ class ImageViewerWindow(QMainWindow):
3099
3141
 
3100
3142
  if not hasattr(self, 'zoom_changed'):
3101
3143
  self.zoom_changed = False
3102
-
3144
+
3103
3145
  self.canvas.draw()
3104
3146
 
3105
3147
  # Handle brush mode cleanup with paint session management
3106
3148
  if self.brush_mode and hasattr(self, 'painting') and self.painting:
3149
+
3150
+ self.pm.connect_virtual_paint_points()
3151
+ self.pm.update_virtual_paint_display()
3152
+
3153
+
3107
3154
  # Finish current operation
3155
+ self.pm.finish_current_stroke()
3108
3156
  self.pm.finish_current_virtual_operation()
3109
3157
 
3110
3158
  # Reset last position for next stroke
3111
- self.last_virtual_pos = None
3159
+ #self.last_virtual_pos = None
3112
3160
 
3113
3161
  # End this stroke but keep session active for continuous painting
3114
3162
  self.painting = False
3115
3163
 
3116
- current_xlim = self.ax.get_xlim() if hasattr(self, 'ax') and self.ax.get_xlim() != (0, 1) else None
3117
- current_ylim = self.ax.get_ylim() if hasattr(self, 'ax') and self.ax.get_ylim() != (0, 1) else None
3118
-
3119
- self.update_display(preserve_zoom=(current_xlim, current_ylim), continue_paint = True)
3120
-
3121
- if self.machine_window is not None:
3122
- if self.machine_window.segmentation_worker is not None:
3123
- self.machine_window.segmentation_worker.resume()
3124
3164
 
3165
+ if self.resume:
3166
+ self.machine_window.segmentation_worker.resume()
3167
+ self.resume = False
3125
3168
 
3126
3169
 
3127
3170
  def highlight_value_in_tables(self, clicked_value):
@@ -3243,7 +3286,6 @@ class ImageViewerWindow(QMainWindow):
3243
3286
 
3244
3287
  self.zoom_changed = False # Flag that zoom has changed
3245
3288
 
3246
-
3247
3289
 
3248
3290
  self.canvas.draw()
3249
3291
 
@@ -5089,6 +5131,14 @@ class ImageViewerWindow(QMainWindow):
5089
5131
  """Actually perform the slice update after debounce delay."""
5090
5132
  if self.pending_slice is not None:
5091
5133
  slice_value, view_settings = self.pending_slice
5134
+ if (hasattr(self, 'virtual_draw_operations') and self.virtual_draw_operations) or \
5135
+ (hasattr(self, 'virtual_erase_operations') and self.virtual_erase_operations) or \
5136
+ (hasattr(self, 'current_operation') and self.current_operation):
5137
+ # Finish current operation first
5138
+ if hasattr(self, 'current_operation') and self.current_operation:
5139
+ self.pm.finish_current_virtual_operation()
5140
+ # Now convert to real data
5141
+ self.pm.convert_virtual_strokes_to_data()
5092
5142
  self.current_slice = slice_value
5093
5143
  if self.mini_overlay == True: #If we are rendering the highlight overlay for selected values one at a time.
5094
5144
  self.create_mini_overlay(node_indices = self.clicked_values['nodes'], edge_indices = self.clicked_values['edges'])
@@ -5112,7 +5162,7 @@ class ImageViewerWindow(QMainWindow):
5112
5162
 
5113
5163
 
5114
5164
 
5115
- def update_display(self, preserve_zoom=None, dims = None, called = False, reset_resize = False, continue_paint = False, skip_paint_reinit = False):
5165
+ def update_display(self, preserve_zoom=None, dims = None, called = False, reset_resize = False):
5116
5166
  """Update the display with currently visible channels and highlight overlay."""
5117
5167
  try:
5118
5168
  self.figure.clear()
@@ -5121,9 +5171,9 @@ class ImageViewerWindow(QMainWindow):
5121
5171
  self.channel_visible = self.pre_pan_channel_state.copy()
5122
5172
  self.is_pan_preview = False
5123
5173
  self.pan_background_image = None
5124
- if self.machine_window is not None:
5125
- if self.machine_window.segmentation_worker is not None:
5126
- self.machine_window.segmentation_worker.resume()
5174
+ if self.resume:
5175
+ self.machine_window.segmentation_worker.resume()
5176
+ self.resume = False
5127
5177
  if self.static_background is not None:
5128
5178
  # NEW: Convert virtual strokes to real data before cleanup
5129
5179
  if (hasattr(self, 'virtual_draw_operations') and self.virtual_draw_operations) or \
@@ -5142,17 +5192,17 @@ class ImageViewerWindow(QMainWindow):
5142
5192
  self.restore_channels = []
5143
5193
  except:
5144
5194
  pass
5145
- if not continue_paint:
5146
- self.static_background = None
5147
5195
 
5148
- if self.machine_window is None:
5149
- try:
5150
- self.channel_data[4][self.current_slice, :, :] = n3d.overlay_arrays_simple(self.channel_data[self.temp_chan][self.current_slice, :, :], self.channel_data[4][self.current_slice, :, :])
5151
- self.load_channel(self.temp_chan, self.channel_data[4], data = True, end_paint = True)
5152
- self.channel_data[4] = None
5153
- self.channel_visible[4] = False
5154
- except:
5155
- pass
5196
+ self.static_background = None
5197
+
5198
+ if self.machine_window is None:
5199
+ try:
5200
+ self.channel_data[4][self.current_slice, :, :] = n3d.overlay_arrays_simple(self.channel_data[self.temp_chan][self.current_slice, :, :], self.channel_data[4][self.current_slice, :, :])
5201
+ self.load_channel(self.temp_chan, self.channel_data[4], data = True, end_paint = True)
5202
+ self.channel_data[4] = None
5203
+ self.channel_visible[4] = False
5204
+ except:
5205
+ pass
5156
5206
 
5157
5207
  # Get active channels and their dimensions
5158
5208
  active_channels = [i for i in range(4) if self.channel_data[i] is not None]
@@ -5383,17 +5433,6 @@ class ImageViewerWindow(QMainWindow):
5383
5433
 
5384
5434
  self.canvas.draw()
5385
5435
 
5386
- if self.brush_mode and not skip_paint_reinit:
5387
- # Get current zoom to preserve it
5388
- current_xlim = self.ax.get_xlim() if hasattr(self, 'ax') and self.ax.get_xlim() != (0, 1) else None
5389
- current_ylim = self.ax.get_ylim() if hasattr(self, 'ax') and self.ax.get_ylim() != (0, 1) else None
5390
-
5391
- if self.pen_button.isChecked():
5392
- channel = self.active_channel
5393
- else:
5394
- channel = 2
5395
-
5396
- self.pm.initiate_paint_session(channel, current_xlim, current_ylim)
5397
5436
 
5398
5437
  except:
5399
5438
  import traceback
@@ -9605,6 +9644,8 @@ class MachineWindow(QMainWindow):
9605
9644
  self.fore_button.click()
9606
9645
  self.fore_button.click()
9607
9646
 
9647
+ self.num_chunks = 0
9648
+
9608
9649
  except:
9609
9650
  return
9610
9651
 
@@ -9747,18 +9788,6 @@ class MachineWindow(QMainWindow):
9747
9788
 
9748
9789
  self.parent().pm = painting.PaintManager(parent = self.parent())
9749
9790
 
9750
- # Start virtual paint session
9751
- # Get current zoom to preserve it
9752
- current_xlim = self.parent().ax.get_xlim() if hasattr(self, 'ax') and self.ax.get_xlim() != (0, 1) else None
9753
- current_ylim = self.parent().ax.get_ylim() if hasattr(self, 'ax') and self.ax.get_ylim() != (0, 1) else None
9754
-
9755
- if self.parent().pen_button.isChecked():
9756
- channel = self.parent().active_channel
9757
- else:
9758
- channel = 2
9759
-
9760
- self.parent().pm.initiate_paint_session(channel, current_xlim, current_ylim)
9761
-
9762
9791
  self.parent().pan_button.setChecked(False)
9763
9792
  self.parent().zoom_button.setChecked(False)
9764
9793
  if self.parent().pan_mode:
@@ -9786,6 +9815,15 @@ class MachineWindow(QMainWindow):
9786
9815
  # Wait a bit for cleanup
9787
9816
  time.sleep(0.1)
9788
9817
 
9818
+ if (hasattr(self.parent(), 'virtual_draw_operations') and self.parent().virtual_draw_operations) or \
9819
+ (hasattr(self.parent(), 'virtual_erase_operations') and self.parent().virtual_erase_operations) or \
9820
+ (hasattr(self.parent(), 'current_operation') and self.parent().current_operation):
9821
+ # Finish current operation first
9822
+ if hasattr(self.parent(), 'current_operation') and self.parent().current_operation:
9823
+ self.parent().pm.finish_current_virtual_operation()
9824
+ # Now convert to real data
9825
+ self.parent().pm.convert_virtual_strokes_to_data()
9826
+
9789
9827
  self.previewing = True
9790
9828
  try:
9791
9829
  try:
@@ -9829,7 +9867,7 @@ class MachineWindow(QMainWindow):
9829
9867
  return
9830
9868
  else:
9831
9869
  self.segmentation_worker = SegmentationWorker(self.parent().highlight_overlay, self.segmenter, self.use_gpu, self.use_two, self.previewing, self, self.mem_lock)
9832
- self.segmentation_worker.chunk_processed.connect(lambda: self.update_display(skip_paint_reinit = True)) # Just update display
9870
+ self.segmentation_worker.chunk_processed.connect(self.update_display) # Just update display
9833
9871
  current_xlim = self.parent().ax.get_xlim()
9834
9872
  current_ylim = self.parent().ax.get_ylim()
9835
9873
  try:
@@ -9882,7 +9920,7 @@ class MachineWindow(QMainWindow):
9882
9920
 
9883
9921
  return changed
9884
9922
 
9885
- def update_display(self, skip_paint_reinit = False):
9923
+ def update_display(self):
9886
9924
  if not hasattr(self, '_last_update'):
9887
9925
  self._last_update = 0
9888
9926
 
@@ -9892,6 +9930,7 @@ class MachineWindow(QMainWindow):
9892
9930
 
9893
9931
  self._last_z = current_z
9894
9932
 
9933
+ self.num_chunks += 1
9895
9934
 
9896
9935
  current_time = time.time()
9897
9936
  if current_time - self._last_update >= 1: # Match worker's interval
@@ -9908,18 +9947,7 @@ class MachineWindow(QMainWindow):
9908
9947
 
9909
9948
  if not self.parent().painting:
9910
9949
  # Only update if view limits are valid
9911
- self.parent().update_display(preserve_zoom=(current_xlim, current_ylim), skip_paint_reinit = skip_paint_reinit)
9912
-
9913
- if self.parent().brush_mode:
9914
- current_xlim = self.parent().ax.get_xlim() if hasattr(self, 'ax') and self.ax.get_xlim() != (0, 1) else None
9915
- current_ylim = self.parent().ax.get_ylim() if hasattr(self, 'ax') and self.ax.get_ylim() != (0, 1) else None
9916
-
9917
- if self.parent().pen_button.isChecked():
9918
- channel = self.parent().active_channel
9919
- else:
9920
- channel = 2
9921
-
9922
- self.parent().pm.initiate_paint_session(channel, current_xlim, current_ylim)
9950
+ self.parent().update_display(preserve_zoom=(current_xlim, current_ylim))
9923
9951
 
9924
9952
  self._last_update = current_time
9925
9953
  except Exception as e:
@@ -10110,6 +10138,8 @@ class SegmentationWorker(QThread):
10110
10138
  current_time = time.time()
10111
10139
  if (self.chunks_since_update >= self.chunks_per_update and
10112
10140
  current_time - self.last_update >= self.update_interval):
10141
+ if self.machine_window.parent().shape[1] * self.machine_window.parent().shape[2] > 3000 * 3000: #arbitrary throttle for large arrays.
10142
+ self.msleep(3000)
10113
10143
  self.chunk_processed.emit()
10114
10144
  self.chunks_since_update = 0
10115
10145
  self.last_update = current_time