nettracer3d 0.8.6__py3-none-any.whl → 0.8.8__py3-none-any.whl
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/nettracer_gui.py +104 -100
- nettracer3d/painting.py +260 -86
- {nettracer3d-0.8.6.dist-info → nettracer3d-0.8.8.dist-info}/METADATA +2 -2
- {nettracer3d-0.8.6.dist-info → nettracer3d-0.8.8.dist-info}/RECORD +8 -8
- {nettracer3d-0.8.6.dist-info → nettracer3d-0.8.8.dist-info}/WHEEL +0 -0
- {nettracer3d-0.8.6.dist-info → nettracer3d-0.8.8.dist-info}/entry_points.txt +0 -0
- {nettracer3d-0.8.6.dist-info → nettracer3d-0.8.8.dist-info}/licenses/LICENSE +0 -0
- {nettracer3d-0.8.6.dist-info → nettracer3d-0.8.8.dist-info}/top_level.txt +0 -0
nettracer3d/nettracer_gui.py
CHANGED
|
@@ -2172,7 +2172,18 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2172
2172
|
if self.machine_window is not None:
|
|
2173
2173
|
self.machine_window.silence_button()
|
|
2174
2174
|
self.canvas.setCursor(Qt.CursorShape.CrossCursor)
|
|
2175
|
-
if self.
|
|
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
|
+
current_xlim = self.ax.get_xlim()
|
|
2184
|
+
current_ylim = self.ax.get_ylim()
|
|
2185
|
+
self.update_display(preserve_zoom=(current_xlim, current_ylim))
|
|
2186
|
+
if self.pan_mode:
|
|
2176
2187
|
current_xlim = self.ax.get_xlim()
|
|
2177
2188
|
current_ylim = self.ax.get_ylim()
|
|
2178
2189
|
self.update_display(preserve_zoom=(current_xlim, current_ylim))
|
|
@@ -2189,6 +2200,7 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2189
2200
|
self.pan_mode = self.pan_button.isChecked()
|
|
2190
2201
|
if self.pan_mode:
|
|
2191
2202
|
|
|
2203
|
+
|
|
2192
2204
|
self.zoom_button.setChecked(False)
|
|
2193
2205
|
self.pen_button.setChecked(False)
|
|
2194
2206
|
self.zoom_mode = False
|
|
@@ -2196,13 +2208,42 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2196
2208
|
self.threed = False
|
|
2197
2209
|
self.last_change = None
|
|
2198
2210
|
self.brush_mode = False
|
|
2199
|
-
if self.
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2211
|
+
if (hasattr(self, 'virtual_draw_operations') and self.virtual_draw_operations) or \
|
|
2212
|
+
(hasattr(self, 'virtual_erase_operations') and self.virtual_erase_operations) or \
|
|
2213
|
+
(hasattr(self, 'current_operation') and self.current_operation):
|
|
2214
|
+
# Finish current operation first
|
|
2215
|
+
if hasattr(self, 'current_operation') and self.current_operation:
|
|
2216
|
+
self.pm.finish_current_virtual_operation()
|
|
2217
|
+
# Now convert to real data
|
|
2218
|
+
self.pm.convert_virtual_strokes_to_data()
|
|
2203
2219
|
current_xlim = self.ax.get_xlim()
|
|
2204
2220
|
current_ylim = self.ax.get_ylim()
|
|
2205
2221
|
self.update_display(preserve_zoom=(current_xlim, current_ylim))
|
|
2222
|
+
if self.machine_window is not None:
|
|
2223
|
+
self.machine_window.silence_button()
|
|
2224
|
+
self.canvas.setCursor(Qt.CursorShape.OpenHandCursor)
|
|
2225
|
+
|
|
2226
|
+
if self.pan_background_image is None:
|
|
2227
|
+
|
|
2228
|
+
if self.machine_window is not None:
|
|
2229
|
+
if self.machine_window.segmentation_worker is not None:
|
|
2230
|
+
if not self.machine_window.segmentation_worker._paused:
|
|
2231
|
+
self.resume = True
|
|
2232
|
+
self.machine_window.segmentation_worker.pause()
|
|
2233
|
+
|
|
2234
|
+
# Store current channel visibility state
|
|
2235
|
+
self.pre_pan_channel_state = self.channel_visible.copy()
|
|
2236
|
+
|
|
2237
|
+
# Create static background from currently visible channels
|
|
2238
|
+
self.create_pan_background()
|
|
2239
|
+
|
|
2240
|
+
# Hide all channels and show only the background
|
|
2241
|
+
self.channel_visible = [False] * 4
|
|
2242
|
+
self.is_pan_preview = True
|
|
2243
|
+
|
|
2244
|
+
# Update display to show only background
|
|
2245
|
+
self.update_display_pan_mode()
|
|
2246
|
+
|
|
2206
2247
|
else:
|
|
2207
2248
|
current_xlim = self.ax.get_xlim()
|
|
2208
2249
|
current_ylim = self.ax.get_ylim()
|
|
@@ -2229,8 +2270,6 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2229
2270
|
else:
|
|
2230
2271
|
channel = 2
|
|
2231
2272
|
|
|
2232
|
-
self.pm.initiate_paint_session(channel, current_xlim, current_ylim)
|
|
2233
|
-
|
|
2234
2273
|
if self.pan_mode:
|
|
2235
2274
|
current_xlim = self.ax.get_xlim()
|
|
2236
2275
|
current_ylim = self.ax.get_ylim()
|
|
@@ -2242,6 +2281,14 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2242
2281
|
self.zoom_mode = False
|
|
2243
2282
|
self.update_brush_cursor()
|
|
2244
2283
|
else:
|
|
2284
|
+
if (hasattr(self, 'virtual_draw_operations') and self.virtual_draw_operations) or \
|
|
2285
|
+
(hasattr(self, 'virtual_erase_operations') and self.virtual_erase_operations) or \
|
|
2286
|
+
(hasattr(self, 'current_operation') and self.current_operation):
|
|
2287
|
+
# Finish current operation first
|
|
2288
|
+
if hasattr(self, 'current_operation') and self.current_operation:
|
|
2289
|
+
self.pm.finish_current_virtual_operation()
|
|
2290
|
+
# Now convert to real data
|
|
2291
|
+
self.pm.convert_virtual_strokes_to_data()
|
|
2245
2292
|
# Get current zoom and do display update
|
|
2246
2293
|
current_xlim = self.ax.get_xlim()
|
|
2247
2294
|
current_ylim = self.ax.get_ylim()
|
|
@@ -2495,29 +2542,7 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2495
2542
|
|
|
2496
2543
|
self.panning = True
|
|
2497
2544
|
self.pan_start = (event.xdata, event.ydata)
|
|
2498
|
-
|
|
2499
|
-
if self.pan_background_image is None:
|
|
2500
|
-
|
|
2501
|
-
if self.machine_window is not None:
|
|
2502
|
-
if self.machine_window.segmentation_worker is not None:
|
|
2503
|
-
if not self.machine_window.segmentation_worker._paused:
|
|
2504
|
-
self.resume = True
|
|
2505
|
-
self.machine_window.segmentation_worker.pause()
|
|
2506
|
-
|
|
2507
|
-
# Store current channel visibility state
|
|
2508
|
-
self.pre_pan_channel_state = self.channel_visible.copy()
|
|
2509
|
-
|
|
2510
|
-
# Create static background from currently visible channels
|
|
2511
|
-
self.create_pan_background()
|
|
2512
|
-
|
|
2513
|
-
# Hide all channels and show only the background
|
|
2514
|
-
self.channel_visible = [False] * 4
|
|
2515
|
-
self.is_pan_preview = True
|
|
2516
|
-
|
|
2517
|
-
# Update display to show only background
|
|
2518
|
-
self.update_display_pan_mode()
|
|
2519
|
-
else:
|
|
2520
|
-
self.canvas.setCursor(Qt.CursorShape.ClosedHandCursor)
|
|
2545
|
+
self.canvas.setCursor(Qt.CursorShape.ClosedHandCursor)
|
|
2521
2546
|
|
|
2522
2547
|
|
|
2523
2548
|
elif self.brush_mode:
|
|
@@ -2525,21 +2550,16 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2525
2550
|
if event.inaxes != self.ax:
|
|
2526
2551
|
return
|
|
2527
2552
|
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
if self.machine_window is not None and not self.machine_window.segmentation_worker._paused:
|
|
2531
|
-
current_xlim = self.ax.get_xlim() if hasattr(self, 'ax') and self.ax.get_xlim() != (0, 1) else None
|
|
2532
|
-
current_ylim = self.ax.get_ylim() if hasattr(self, 'ax') and self.ax.get_ylim() != (0, 1) else None
|
|
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
|
|
2533
2555
|
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
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)
|
|
2538
2562
|
|
|
2539
|
-
self.pm.initiate_paint_session(channel, current_xlim, current_ylim)
|
|
2540
|
-
except:
|
|
2541
|
-
pass
|
|
2542
|
-
"""
|
|
2543
2563
|
|
|
2544
2564
|
if event.button == 1 or event.button == 3:
|
|
2545
2565
|
if self.machine_window is not None:
|
|
@@ -2555,7 +2575,7 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2555
2575
|
current_ylim = self.ax.get_ylim() if hasattr(self, 'ax') and self.ax.get_ylim() != (0, 1) else None
|
|
2556
2576
|
|
|
2557
2577
|
if event.button == 1 and getattr(self, 'can', False):
|
|
2558
|
-
self.update_display(preserve_zoom=(current_xlim, current_ylim)
|
|
2578
|
+
self.update_display(preserve_zoom=(current_xlim, current_ylim))
|
|
2559
2579
|
self.handle_can(x, y)
|
|
2560
2580
|
return
|
|
2561
2581
|
|
|
@@ -2965,7 +2985,7 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2965
2985
|
if z1 == z2 == self.current_slice:
|
|
2966
2986
|
self.ax.plot([x1, x2], [y1, y2], 'r--', alpha=0.5)
|
|
2967
2987
|
|
|
2968
|
-
self.canvas.setCursor(Qt.CursorShape.ClosedHandCursor)
|
|
2988
|
+
#self.canvas.setCursor(Qt.CursorShape.ClosedHandCursor)
|
|
2969
2989
|
|
|
2970
2990
|
self.canvas.draw_idle()
|
|
2971
2991
|
|
|
@@ -3121,24 +3141,26 @@ class ImageViewerWindow(QMainWindow):
|
|
|
3121
3141
|
|
|
3122
3142
|
if not hasattr(self, 'zoom_changed'):
|
|
3123
3143
|
self.zoom_changed = False
|
|
3124
|
-
|
|
3144
|
+
|
|
3125
3145
|
self.canvas.draw()
|
|
3126
3146
|
|
|
3127
3147
|
# Handle brush mode cleanup with paint session management
|
|
3128
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
|
+
|
|
3129
3154
|
# Finish current operation
|
|
3155
|
+
self.pm.finish_current_stroke()
|
|
3130
3156
|
self.pm.finish_current_virtual_operation()
|
|
3131
3157
|
|
|
3132
3158
|
# Reset last position for next stroke
|
|
3133
|
-
self.last_virtual_pos = None
|
|
3159
|
+
#self.last_virtual_pos = None
|
|
3134
3160
|
|
|
3135
3161
|
# End this stroke but keep session active for continuous painting
|
|
3136
3162
|
self.painting = False
|
|
3137
3163
|
|
|
3138
|
-
current_xlim = self.ax.get_xlim() if hasattr(self, 'ax') and self.ax.get_xlim() != (0, 1) else None
|
|
3139
|
-
current_ylim = self.ax.get_ylim() if hasattr(self, 'ax') and self.ax.get_ylim() != (0, 1) else None
|
|
3140
|
-
|
|
3141
|
-
self.update_display(preserve_zoom=(current_xlim, current_ylim), continue_paint = True)
|
|
3142
3164
|
|
|
3143
3165
|
if self.resume:
|
|
3144
3166
|
self.machine_window.segmentation_worker.resume()
|
|
@@ -3264,7 +3286,6 @@ class ImageViewerWindow(QMainWindow):
|
|
|
3264
3286
|
|
|
3265
3287
|
self.zoom_changed = False # Flag that zoom has changed
|
|
3266
3288
|
|
|
3267
|
-
|
|
3268
3289
|
|
|
3269
3290
|
self.canvas.draw()
|
|
3270
3291
|
|
|
@@ -5110,6 +5131,14 @@ class ImageViewerWindow(QMainWindow):
|
|
|
5110
5131
|
"""Actually perform the slice update after debounce delay."""
|
|
5111
5132
|
if self.pending_slice is not None:
|
|
5112
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()
|
|
5113
5142
|
self.current_slice = slice_value
|
|
5114
5143
|
if self.mini_overlay == True: #If we are rendering the highlight overlay for selected values one at a time.
|
|
5115
5144
|
self.create_mini_overlay(node_indices = self.clicked_values['nodes'], edge_indices = self.clicked_values['edges'])
|
|
@@ -5133,7 +5162,7 @@ class ImageViewerWindow(QMainWindow):
|
|
|
5133
5162
|
|
|
5134
5163
|
|
|
5135
5164
|
|
|
5136
|
-
def update_display(self, preserve_zoom=None, dims = None, called = False, reset_resize = False
|
|
5165
|
+
def update_display(self, preserve_zoom=None, dims = None, called = False, reset_resize = False):
|
|
5137
5166
|
"""Update the display with currently visible channels and highlight overlay."""
|
|
5138
5167
|
try:
|
|
5139
5168
|
self.figure.clear()
|
|
@@ -5163,17 +5192,17 @@ class ImageViewerWindow(QMainWindow):
|
|
|
5163
5192
|
self.restore_channels = []
|
|
5164
5193
|
except:
|
|
5165
5194
|
pass
|
|
5166
|
-
if not continue_paint:
|
|
5167
|
-
self.static_background = None
|
|
5168
5195
|
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
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
|
|
5177
5206
|
|
|
5178
5207
|
# Get active channels and their dimensions
|
|
5179
5208
|
active_channels = [i for i in range(4) if self.channel_data[i] is not None]
|
|
@@ -5404,17 +5433,6 @@ class ImageViewerWindow(QMainWindow):
|
|
|
5404
5433
|
|
|
5405
5434
|
self.canvas.draw()
|
|
5406
5435
|
|
|
5407
|
-
if self.brush_mode and not skip_paint_reinit:
|
|
5408
|
-
# Get current zoom to preserve it
|
|
5409
|
-
current_xlim = self.ax.get_xlim() if hasattr(self, 'ax') and self.ax.get_xlim() != (0, 1) else None
|
|
5410
|
-
current_ylim = self.ax.get_ylim() if hasattr(self, 'ax') and self.ax.get_ylim() != (0, 1) else None
|
|
5411
|
-
|
|
5412
|
-
if self.pen_button.isChecked():
|
|
5413
|
-
channel = self.active_channel
|
|
5414
|
-
else:
|
|
5415
|
-
channel = 2
|
|
5416
|
-
|
|
5417
|
-
self.pm.initiate_paint_session(channel, current_xlim, current_ylim)
|
|
5418
5436
|
|
|
5419
5437
|
except:
|
|
5420
5438
|
import traceback
|
|
@@ -9770,18 +9788,6 @@ class MachineWindow(QMainWindow):
|
|
|
9770
9788
|
|
|
9771
9789
|
self.parent().pm = painting.PaintManager(parent = self.parent())
|
|
9772
9790
|
|
|
9773
|
-
# Start virtual paint session
|
|
9774
|
-
# Get current zoom to preserve it
|
|
9775
|
-
current_xlim = self.parent().ax.get_xlim() if hasattr(self, 'ax') and self.ax.get_xlim() != (0, 1) else None
|
|
9776
|
-
current_ylim = self.parent().ax.get_ylim() if hasattr(self, 'ax') and self.ax.get_ylim() != (0, 1) else None
|
|
9777
|
-
|
|
9778
|
-
if self.parent().pen_button.isChecked():
|
|
9779
|
-
channel = self.parent().active_channel
|
|
9780
|
-
else:
|
|
9781
|
-
channel = 2
|
|
9782
|
-
|
|
9783
|
-
self.parent().pm.initiate_paint_session(channel, current_xlim, current_ylim)
|
|
9784
|
-
|
|
9785
9791
|
self.parent().pan_button.setChecked(False)
|
|
9786
9792
|
self.parent().zoom_button.setChecked(False)
|
|
9787
9793
|
if self.parent().pan_mode:
|
|
@@ -9809,6 +9815,15 @@ class MachineWindow(QMainWindow):
|
|
|
9809
9815
|
# Wait a bit for cleanup
|
|
9810
9816
|
time.sleep(0.1)
|
|
9811
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
|
+
|
|
9812
9827
|
self.previewing = True
|
|
9813
9828
|
try:
|
|
9814
9829
|
try:
|
|
@@ -9852,7 +9867,7 @@ class MachineWindow(QMainWindow):
|
|
|
9852
9867
|
return
|
|
9853
9868
|
else:
|
|
9854
9869
|
self.segmentation_worker = SegmentationWorker(self.parent().highlight_overlay, self.segmenter, self.use_gpu, self.use_two, self.previewing, self, self.mem_lock)
|
|
9855
|
-
self.segmentation_worker.chunk_processed.connect(
|
|
9870
|
+
self.segmentation_worker.chunk_processed.connect(self.update_display) # Just update display
|
|
9856
9871
|
current_xlim = self.parent().ax.get_xlim()
|
|
9857
9872
|
current_ylim = self.parent().ax.get_ylim()
|
|
9858
9873
|
try:
|
|
@@ -9905,7 +9920,7 @@ class MachineWindow(QMainWindow):
|
|
|
9905
9920
|
|
|
9906
9921
|
return changed
|
|
9907
9922
|
|
|
9908
|
-
def update_display(self
|
|
9923
|
+
def update_display(self):
|
|
9909
9924
|
if not hasattr(self, '_last_update'):
|
|
9910
9925
|
self._last_update = 0
|
|
9911
9926
|
|
|
@@ -9932,18 +9947,7 @@ class MachineWindow(QMainWindow):
|
|
|
9932
9947
|
|
|
9933
9948
|
if not self.parent().painting:
|
|
9934
9949
|
# Only update if view limits are valid
|
|
9935
|
-
self.parent().update_display(preserve_zoom=(current_xlim, current_ylim)
|
|
9936
|
-
|
|
9937
|
-
if self.parent().brush_mode:
|
|
9938
|
-
current_xlim = self.parent().ax.get_xlim() if hasattr(self, 'ax') and self.ax.get_xlim() != (0, 1) else None
|
|
9939
|
-
current_ylim = self.parent().ax.get_ylim() if hasattr(self, 'ax') and self.ax.get_ylim() != (0, 1) else None
|
|
9940
|
-
|
|
9941
|
-
if self.parent().pen_button.isChecked():
|
|
9942
|
-
channel = self.parent().active_channel
|
|
9943
|
-
else:
|
|
9944
|
-
channel = 2
|
|
9945
|
-
|
|
9946
|
-
self.parent().pm.initiate_paint_session(channel, current_xlim, current_ylim)
|
|
9950
|
+
self.parent().update_display(preserve_zoom=(current_xlim, current_ylim))
|
|
9947
9951
|
|
|
9948
9952
|
self._last_update = current_time
|
|
9949
9953
|
except Exception as e:
|
nettracer3d/painting.py
CHANGED
|
@@ -7,8 +7,26 @@ import numpy as np
|
|
|
7
7
|
class PaintManager(QMainWindow):
|
|
8
8
|
def __init__(self, parent = None):
|
|
9
9
|
super().__init__(parent)
|
|
10
|
-
|
|
11
10
|
self.resume = False
|
|
11
|
+
|
|
12
|
+
# Initialize stroke tracking storage once
|
|
13
|
+
if parent is not None:
|
|
14
|
+
if not hasattr(parent, 'completed_paint_strokes'):
|
|
15
|
+
parent.completed_paint_strokes = [] # List of individual completed strokes
|
|
16
|
+
if not hasattr(parent, 'current_stroke_points'):
|
|
17
|
+
parent.current_stroke_points = [] # Current stroke being drawn
|
|
18
|
+
if not hasattr(parent, 'current_stroke_type'):
|
|
19
|
+
parent.current_stroke_type = None # 'draw' or 'erase'
|
|
20
|
+
|
|
21
|
+
# Keep the old properties for display purposes
|
|
22
|
+
if not hasattr(parent, 'virtual_draw_operations'):
|
|
23
|
+
parent.virtual_draw_operations = []
|
|
24
|
+
if not hasattr(parent, 'virtual_erase_operations'):
|
|
25
|
+
parent.virtual_erase_operations = []
|
|
26
|
+
if not hasattr(parent, 'current_operation'):
|
|
27
|
+
parent.current_operation = []
|
|
28
|
+
if not hasattr(parent, 'current_operation_type'):
|
|
29
|
+
parent.current_operation_type = None
|
|
12
30
|
|
|
13
31
|
def get_line_points(self, x0, y0, x1, y1):
|
|
14
32
|
"""Get all points in a line between (x0,y0) and (x1,y1) using Bresenham's algorithm."""
|
|
@@ -72,11 +90,35 @@ class PaintManager(QMainWindow):
|
|
|
72
90
|
if not self.parent().channel_visible[channel]:
|
|
73
91
|
self.parent().channel_visible[channel] = True
|
|
74
92
|
|
|
75
|
-
# Initialize
|
|
76
|
-
self.parent()
|
|
77
|
-
|
|
93
|
+
# Initialize stroke tracking storage ONLY if they don't exist
|
|
94
|
+
if not hasattr(self.parent(), 'completed_paint_strokes'):
|
|
95
|
+
self.parent().completed_paint_strokes = []
|
|
96
|
+
if not hasattr(self.parent(), 'current_stroke_points'):
|
|
97
|
+
self.parent().current_stroke_points = []
|
|
98
|
+
if not hasattr(self.parent(), 'current_stroke_type'):
|
|
99
|
+
self.parent().current_stroke_type = None
|
|
100
|
+
|
|
101
|
+
# Initialize display storage ONLY if they don't exist
|
|
102
|
+
if not hasattr(self.parent(), 'virtual_draw_operations'):
|
|
103
|
+
self.parent().virtual_draw_operations = []
|
|
104
|
+
if not hasattr(self.parent(), 'virtual_erase_operations'):
|
|
105
|
+
self.parent().virtual_erase_operations = []
|
|
106
|
+
if not hasattr(self.parent(), 'current_operation'):
|
|
107
|
+
self.parent().current_operation = []
|
|
108
|
+
if not hasattr(self.parent(), 'current_operation_type'):
|
|
109
|
+
self.parent().current_operation_type = None
|
|
110
|
+
|
|
111
|
+
def reset_all_paint_storage(self):
|
|
112
|
+
"""Reset all paint storage - call this when you want to start completely fresh."""
|
|
113
|
+
self.parent().completed_paint_strokes = []
|
|
114
|
+
self.parent().current_stroke_points = []
|
|
115
|
+
self.parent().current_stroke_type = None
|
|
116
|
+
self.parent().virtual_draw_operations = []
|
|
117
|
+
self.parent().virtual_erase_operations = []
|
|
78
118
|
self.parent().current_operation = []
|
|
79
|
-
self.parent().current_operation_type = None
|
|
119
|
+
self.parent().current_operation_type = None
|
|
120
|
+
|
|
121
|
+
|
|
80
122
|
|
|
81
123
|
def add_virtual_paint_point(self, x, y, brush_size, erase=False, foreground=True):
|
|
82
124
|
"""Add a single paint point to the virtual layer."""
|
|
@@ -111,11 +153,19 @@ class PaintManager(QMainWindow):
|
|
|
111
153
|
'threedthresh': getattr(self.parent(), 'threedthresh', 1)
|
|
112
154
|
}
|
|
113
155
|
|
|
114
|
-
#
|
|
156
|
+
# Add to stroke tracking (NEW - separate stroke tracking)
|
|
157
|
+
if self.parent().current_stroke_type != operation_type:
|
|
158
|
+
# Finish previous stroke if switching between draw/erase
|
|
159
|
+
self.finish_current_stroke()
|
|
160
|
+
self.parent().current_stroke_type = operation_type
|
|
161
|
+
|
|
162
|
+
self.parent().current_stroke_points.append(operation_data)
|
|
163
|
+
|
|
164
|
+
# Create visual circle for display
|
|
115
165
|
circle = plt.Circle((x, y), brush_size/2,
|
|
116
166
|
color=paint_color, alpha=alpha, animated=True)
|
|
117
167
|
|
|
118
|
-
# Add to
|
|
168
|
+
# Add to display operations (OLD - for visual display)
|
|
119
169
|
if self.parent().current_operation_type != operation_type:
|
|
120
170
|
# Finish previous operation if switching between draw/erase
|
|
121
171
|
self.finish_current_virtual_operation()
|
|
@@ -128,6 +178,23 @@ class PaintManager(QMainWindow):
|
|
|
128
178
|
|
|
129
179
|
self.parent().ax.add_patch(circle)
|
|
130
180
|
|
|
181
|
+
def finish_current_stroke(self):
|
|
182
|
+
"""Finish the current stroke and add it to completed strokes."""
|
|
183
|
+
if not self.parent().current_stroke_points:
|
|
184
|
+
return
|
|
185
|
+
|
|
186
|
+
# Store the completed stroke with its type
|
|
187
|
+
stroke_data = {
|
|
188
|
+
'points': self.parent().current_stroke_points.copy(),
|
|
189
|
+
'type': self.parent().current_stroke_type
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
self.parent().completed_paint_strokes.append(stroke_data)
|
|
193
|
+
|
|
194
|
+
# Clear current stroke
|
|
195
|
+
self.parent().current_stroke_points = []
|
|
196
|
+
self.parent().current_stroke_type = None
|
|
197
|
+
|
|
131
198
|
def add_virtual_paint_stroke(self, x, y, brush_size, erase=False, foreground=True):
|
|
132
199
|
"""Add a paint stroke - simple visual, interpolation happens during data conversion."""
|
|
133
200
|
# Just add the current point for visual display (no interpolation yet)
|
|
@@ -136,22 +203,119 @@ class PaintManager(QMainWindow):
|
|
|
136
203
|
# Store the last position for data conversion later
|
|
137
204
|
self.parent().last_virtual_pos = (x, y)
|
|
138
205
|
|
|
139
|
-
def
|
|
140
|
-
"""
|
|
206
|
+
def connect_virtual_paint_points(self):
|
|
207
|
+
"""Connect points with lines matching the circle size by converting to screen coordinates."""
|
|
141
208
|
|
|
142
|
-
if not self.parent().current_operation:
|
|
209
|
+
if not hasattr(self.parent(), 'current_operation') or len(self.parent().current_operation) < 2:
|
|
143
210
|
return
|
|
144
|
-
|
|
145
|
-
if self.parent().current_operation_type == 'draw':
|
|
146
|
-
self.parent().virtual_draw_operations.append(self.parent().current_operation)
|
|
147
|
-
elif self.parent().current_operation_type == 'erase':
|
|
148
|
-
self.parent().virtual_erase_operations.append(self.parent().current_operation)
|
|
149
211
|
|
|
150
|
-
|
|
151
|
-
self.parent().
|
|
212
|
+
# Get existing points but DON'T remove them
|
|
213
|
+
existing_points = self.parent().current_operation.copy()
|
|
214
|
+
point_data = [item['data'] for item in existing_points if 'data' in item]
|
|
215
|
+
|
|
216
|
+
if len(point_data) < 2:
|
|
217
|
+
return
|
|
218
|
+
|
|
219
|
+
# Get visual properties and brush size from first point
|
|
220
|
+
first_data = point_data[0]
|
|
221
|
+
brush_size_data = first_data['brush_size'] # This is in data coordinates
|
|
222
|
+
|
|
223
|
+
# Convert brush size from data coordinates to points for linewidth
|
|
224
|
+
# Get the transformation from data to display coordinates
|
|
225
|
+
ax = self.parent().ax
|
|
226
|
+
|
|
227
|
+
# Transform two points to see the scaling
|
|
228
|
+
p1_data = [0, 0]
|
|
229
|
+
p2_data = [brush_size_data, 0] # One brush_size unit away
|
|
230
|
+
|
|
231
|
+
p1_display = ax.transData.transform(p1_data)
|
|
232
|
+
p2_display = ax.transData.transform(p2_data)
|
|
233
|
+
|
|
234
|
+
# Calculate pixels per data unit
|
|
235
|
+
pixels_per_data_unit = abs(p2_display[0] - p1_display[0])
|
|
236
|
+
|
|
237
|
+
# Convert to points (matplotlib uses 72 points per inch, figure.dpi pixels per inch)
|
|
238
|
+
fig = ax.figure
|
|
239
|
+
points_per_pixel = 72.0 / fig.dpi
|
|
240
|
+
brush_size_points = pixels_per_data_unit * points_per_pixel
|
|
241
|
+
|
|
242
|
+
if first_data['erase']:
|
|
243
|
+
line_color = 'black'
|
|
244
|
+
alpha = 0.5
|
|
245
|
+
else:
|
|
246
|
+
if self.parent().machine_window is not None:
|
|
247
|
+
if first_data['foreground']:
|
|
248
|
+
line_color = 'green'
|
|
249
|
+
alpha = 0.7
|
|
250
|
+
else:
|
|
251
|
+
line_color = 'red'
|
|
252
|
+
alpha = 0.7
|
|
253
|
+
else:
|
|
254
|
+
line_color = 'white'
|
|
255
|
+
alpha = 0.7
|
|
256
|
+
|
|
257
|
+
# Create line segments for connections using LineCollection
|
|
258
|
+
from matplotlib.collections import LineCollection
|
|
259
|
+
|
|
260
|
+
segments = []
|
|
261
|
+
for i in range(len(point_data) - 1):
|
|
262
|
+
x1, y1 = point_data[i]['x'], point_data[i]['y']
|
|
263
|
+
x2, y2 = point_data[i+1]['x'], point_data[i+1]['y']
|
|
264
|
+
segments.append([(x1, y1), (x2, y2)])
|
|
265
|
+
|
|
266
|
+
# Create line collection with converted linewidth
|
|
267
|
+
if segments:
|
|
268
|
+
lc = LineCollection(segments,
|
|
269
|
+
colors=line_color,
|
|
270
|
+
alpha=alpha,
|
|
271
|
+
linewidths=brush_size_points, # Now in points, matching circles
|
|
272
|
+
animated=True)
|
|
273
|
+
self.parent().ax.add_collection(lc)
|
|
274
|
+
|
|
275
|
+
# Add the line collection as a visual-only element
|
|
276
|
+
self.parent().current_operation.append({
|
|
277
|
+
'line_collection': lc,
|
|
278
|
+
'is_connection_visual': True
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
def finish_current_virtual_operation(self):
|
|
282
|
+
"""Finish the current operation (draw or erase) and add it to the appropriate list."""
|
|
283
|
+
|
|
284
|
+
if not self.parent().current_operation:
|
|
285
|
+
return
|
|
286
|
+
|
|
287
|
+
# Filter out connection visuals from the operation before storing
|
|
288
|
+
data_items = []
|
|
289
|
+
visual_items = []
|
|
290
|
+
|
|
291
|
+
for item in self.parent().current_operation:
|
|
292
|
+
if item.get('is_connection_visual', False):
|
|
293
|
+
visual_items.append(item)
|
|
294
|
+
else:
|
|
295
|
+
data_items.append(item)
|
|
296
|
+
|
|
297
|
+
# Only store the data items for this specific stroke
|
|
298
|
+
if data_items:
|
|
299
|
+
if self.parent().current_operation_type == 'draw':
|
|
300
|
+
self.parent().virtual_draw_operations.append(data_items)
|
|
301
|
+
elif self.parent().current_operation_type == 'erase':
|
|
302
|
+
self.parent().virtual_erase_operations.append(data_items)
|
|
303
|
+
|
|
304
|
+
# Clean up visual items that are connection-only
|
|
305
|
+
for item in visual_items:
|
|
306
|
+
try:
|
|
307
|
+
if 'line_collection' in item:
|
|
308
|
+
item['line_collection'].remove()
|
|
309
|
+
elif 'line' in item:
|
|
310
|
+
item['line'].remove()
|
|
311
|
+
except:
|
|
312
|
+
pass
|
|
313
|
+
|
|
314
|
+
self.parent().current_operation = []
|
|
315
|
+
self.parent().current_operation_type = None
|
|
152
316
|
|
|
153
317
|
def update_virtual_paint_display(self):
|
|
154
|
-
"""Update display with virtual paint strokes -
|
|
318
|
+
"""Update display with virtual paint strokes - handles different object types."""
|
|
155
319
|
if not hasattr(self.parent(), 'static_background') or self.parent().static_background is None:
|
|
156
320
|
return
|
|
157
321
|
|
|
@@ -162,108 +326,118 @@ class PaintManager(QMainWindow):
|
|
|
162
326
|
for operation_list in [self.parent().virtual_draw_operations, self.parent().virtual_erase_operations]:
|
|
163
327
|
for operation in operation_list:
|
|
164
328
|
for item in operation:
|
|
165
|
-
self.
|
|
329
|
+
self._draw_virtual_item(item)
|
|
166
330
|
|
|
167
331
|
# Draw current operation being painted
|
|
168
332
|
if hasattr(self.parent(), 'current_operation'):
|
|
169
333
|
for item in self.parent().current_operation:
|
|
170
|
-
self.
|
|
334
|
+
self._draw_virtual_item(item)
|
|
171
335
|
|
|
172
336
|
# Blit everything at once
|
|
173
337
|
self.parent().canvas.blit(self.parent().ax.bbox)
|
|
174
338
|
|
|
339
|
+
def _draw_virtual_item(self, item):
|
|
340
|
+
"""Helper method to draw different types of virtual paint items."""
|
|
341
|
+
try:
|
|
342
|
+
# Skip items that are marked as visual-only connections
|
|
343
|
+
if item.get('is_connection_visual', False):
|
|
344
|
+
if 'line' in item:
|
|
345
|
+
self.parent().ax.draw_artist(item['line'])
|
|
346
|
+
elif 'line_collection' in item:
|
|
347
|
+
self.parent().ax.draw_artist(item['line_collection'])
|
|
348
|
+
elif 'circle' in item:
|
|
349
|
+
self.parent().ax.draw_artist(item['circle'])
|
|
350
|
+
elif 'line' in item:
|
|
351
|
+
self.parent().ax.draw_artist(item['line'])
|
|
352
|
+
elif 'line_collection' in item:
|
|
353
|
+
self.parent().ax.draw_artist(item['line_collection'])
|
|
354
|
+
except Exception as e:
|
|
355
|
+
# Skip items that can't be drawn (might have been removed)
|
|
356
|
+
pass
|
|
357
|
+
|
|
175
358
|
def convert_virtual_strokes_to_data(self):
|
|
176
|
-
"""Convert
|
|
359
|
+
"""Convert each stroke separately to actual array data using ONLY the new stroke tracking system."""
|
|
177
360
|
|
|
178
|
-
#
|
|
179
|
-
|
|
180
|
-
last_pos = None
|
|
181
|
-
for item in operation:
|
|
182
|
-
data = item['data']
|
|
183
|
-
current_pos = (data['x'], data['y'])
|
|
184
|
-
|
|
185
|
-
if last_pos is not None:
|
|
186
|
-
points = self.get_line_points(last_pos[0], last_pos[1], current_pos[0], current_pos[1])
|
|
187
|
-
for px, py in points:
|
|
188
|
-
self.paint_at_position_vectorized(
|
|
189
|
-
px, py,
|
|
190
|
-
erase=False,
|
|
191
|
-
channel=data['channel'],
|
|
192
|
-
brush_size=data['brush_size'],
|
|
193
|
-
threed=data['threed'], # Add this
|
|
194
|
-
threedthresh=data['threedthresh'], # Add this
|
|
195
|
-
foreground=data['foreground'],
|
|
196
|
-
machine_window=self.parent().machine_window
|
|
197
|
-
)
|
|
198
|
-
else:
|
|
199
|
-
self.paint_at_position_vectorized(
|
|
200
|
-
data['x'], data['y'],
|
|
201
|
-
erase=False,
|
|
202
|
-
channel=data['channel'],
|
|
203
|
-
brush_size=data['brush_size'],
|
|
204
|
-
threed=data['threed'], # Add this
|
|
205
|
-
threedthresh=data['threedthresh'], # Add this
|
|
206
|
-
foreground=data['foreground'],
|
|
207
|
-
machine_window=self.parent().machine_window
|
|
208
|
-
)
|
|
209
|
-
|
|
210
|
-
last_pos = current_pos
|
|
211
|
-
try:
|
|
212
|
-
item['circle'].remove()
|
|
213
|
-
except:
|
|
214
|
-
pass
|
|
361
|
+
# Finish the current stroke first
|
|
362
|
+
self.finish_current_stroke()
|
|
215
363
|
|
|
216
|
-
#
|
|
217
|
-
for
|
|
364
|
+
# Process ONLY the completed_paint_strokes (ignore old display operations)
|
|
365
|
+
for stroke in self.parent().completed_paint_strokes:
|
|
366
|
+
stroke_points = stroke['points']
|
|
367
|
+
stroke_type = stroke['type']
|
|
368
|
+
|
|
369
|
+
if len(stroke_points) == 0:
|
|
370
|
+
continue
|
|
371
|
+
|
|
372
|
+
# Apply interpolation within this stroke only
|
|
218
373
|
last_pos = None
|
|
219
|
-
for
|
|
220
|
-
|
|
221
|
-
current_pos = (data['x'], data['y'])
|
|
374
|
+
for point_data in stroke_points:
|
|
375
|
+
current_pos = (point_data['x'], point_data['y'])
|
|
222
376
|
|
|
223
377
|
if last_pos is not None:
|
|
378
|
+
# Interpolate between consecutive points in this stroke
|
|
224
379
|
points = self.get_line_points(last_pos[0], last_pos[1], current_pos[0], current_pos[1])
|
|
225
380
|
for px, py in points:
|
|
226
381
|
self.paint_at_position_vectorized(
|
|
227
382
|
px, py,
|
|
228
|
-
erase=
|
|
229
|
-
channel=
|
|
230
|
-
brush_size=
|
|
231
|
-
threed=
|
|
232
|
-
threedthresh=
|
|
233
|
-
foreground=
|
|
383
|
+
erase=point_data['erase'],
|
|
384
|
+
channel=point_data['channel'],
|
|
385
|
+
brush_size=point_data['brush_size'],
|
|
386
|
+
threed=point_data['threed'],
|
|
387
|
+
threedthresh=point_data['threedthresh'],
|
|
388
|
+
foreground=point_data['foreground'],
|
|
234
389
|
machine_window=self.parent().machine_window
|
|
235
390
|
)
|
|
236
391
|
else:
|
|
392
|
+
# First point in stroke
|
|
237
393
|
self.paint_at_position_vectorized(
|
|
238
|
-
|
|
239
|
-
erase=
|
|
240
|
-
channel=
|
|
241
|
-
brush_size=
|
|
242
|
-
threed=
|
|
243
|
-
threedthresh=
|
|
244
|
-
foreground=
|
|
394
|
+
point_data['x'], point_data['y'],
|
|
395
|
+
erase=point_data['erase'],
|
|
396
|
+
channel=point_data['channel'],
|
|
397
|
+
brush_size=point_data['brush_size'],
|
|
398
|
+
threed=point_data['threed'],
|
|
399
|
+
threedthresh=point_data['threedthresh'],
|
|
400
|
+
foreground=point_data['foreground'],
|
|
245
401
|
machine_window=self.parent().machine_window
|
|
246
402
|
)
|
|
247
403
|
|
|
248
404
|
last_pos = current_pos
|
|
249
|
-
try:
|
|
250
|
-
item['circle'].remove()
|
|
251
|
-
except:
|
|
252
|
-
pass
|
|
253
405
|
|
|
254
|
-
# Clean up
|
|
255
|
-
self.parent().virtual_draw_operations
|
|
256
|
-
|
|
406
|
+
# Clean up ALL visual elements (both old and new systems)
|
|
407
|
+
for operation_list in [self.parent().virtual_draw_operations, self.parent().virtual_erase_operations]:
|
|
408
|
+
for operation in operation_list:
|
|
409
|
+
for item in operation:
|
|
410
|
+
try:
|
|
411
|
+
if 'circle' in item:
|
|
412
|
+
item['circle'].remove()
|
|
413
|
+
elif 'line_collection' in item:
|
|
414
|
+
item['line_collection'].remove()
|
|
415
|
+
elif 'line' in item:
|
|
416
|
+
item['line'].remove()
|
|
417
|
+
except:
|
|
418
|
+
pass
|
|
419
|
+
|
|
257
420
|
if hasattr(self.parent(), 'current_operation'):
|
|
258
421
|
for item in self.parent().current_operation:
|
|
259
422
|
try:
|
|
260
|
-
|
|
423
|
+
if 'circle' in item:
|
|
424
|
+
item['circle'].remove()
|
|
425
|
+
elif 'line_collection' in item:
|
|
426
|
+
item['line_collection'].remove()
|
|
427
|
+
elif 'line' in item:
|
|
428
|
+
item['line'].remove()
|
|
261
429
|
except:
|
|
262
430
|
pass
|
|
263
|
-
|
|
431
|
+
|
|
432
|
+
# Reset all storage for next paint session
|
|
433
|
+
self.parent().completed_paint_strokes = []
|
|
434
|
+
self.parent().current_stroke_points = []
|
|
435
|
+
self.parent().current_stroke_type = None
|
|
436
|
+
self.parent().virtual_draw_operations = []
|
|
437
|
+
self.parent().virtual_erase_operations = []
|
|
438
|
+
self.parent().current_operation = []
|
|
264
439
|
self.parent().current_operation_type = None
|
|
265
440
|
|
|
266
|
-
|
|
267
441
|
def end_virtual_paint_session(self):
|
|
268
442
|
"""Convert virtual paint to actual array modifications when exiting paint mode."""
|
|
269
443
|
if not hasattr(self.parent(), 'virtual_paint_strokes'):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nettracer3d
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.8
|
|
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.
|
|
113
|
+
-- Version 0.8.8 Updates --
|
|
114
114
|
|
|
115
115
|
* See Documentation Once Updated
|
|
@@ -6,20 +6,20 @@ nettracer3d/modularity.py,sha256=O9OeKbjD3v6gSFz9K2GzP6LsxlpQaPfeJbM1pyIEigw,217
|
|
|
6
6
|
nettracer3d/morphology.py,sha256=jyDjYzrZ4LvI5jOyw8DLsxmo-i5lpqHsejYpW7Tq7Mo,19786
|
|
7
7
|
nettracer3d/neighborhoods.py,sha256=VWubD5CBu9aNPhUea7FbAk9aTOq0FLKR9y-1VT7YkAc,39677
|
|
8
8
|
nettracer3d/nettracer.py,sha256=TEV-nDmkcGP3UjWEor1LtEwm5mFBQu2nB0VRyz9Lt08,253649
|
|
9
|
-
nettracer3d/nettracer_gui.py,sha256=
|
|
9
|
+
nettracer3d/nettracer_gui.py,sha256=fD03qJU3W5VHW4OeHZg0SnCu4k6km5_vxPoTbd1OAmg,540421
|
|
10
10
|
nettracer3d/network_analysis.py,sha256=kBzsVaq4dZkMe0k-VGvQIUvM-tK0ZZ8bvb-wtsugZRQ,46150
|
|
11
11
|
nettracer3d/network_draw.py,sha256=F7fw6Pcf4qWOhdKwLmhwqWdschbDlHzwCVolQC9imeU,14117
|
|
12
12
|
nettracer3d/node_draw.py,sha256=kZcR1PekLg0riioNeGcALIXQyZ5PtHA_9MT6z7Zovdk,10401
|
|
13
|
-
nettracer3d/painting.py,sha256=
|
|
13
|
+
nettracer3d/painting.py,sha256=K_dwngivw80r-Yyg4btKMsWGn566ZE9PnrQl986uxJE,23497
|
|
14
14
|
nettracer3d/proximity.py,sha256=bTaucn_InQ-v1GIk8ug-dXvDhIO59rnBMl5nIwAmNyw,35335
|
|
15
15
|
nettracer3d/run.py,sha256=xYeaAc8FCx8MuzTGyL3NR3mK7WZzffAYAH23bNRZYO4,127
|
|
16
16
|
nettracer3d/segmenter.py,sha256=O3xjCimPwoL8LM1w4cKVTB7saY-UptFuYC8qOIo3iWg,61637
|
|
17
17
|
nettracer3d/segmenter_GPU.py,sha256=3CJLXCiySZP2dJbkpfBoXwAYbV4TnvIYAm6oQv-T-y4,63479
|
|
18
18
|
nettracer3d/simple_network.py,sha256=dkG4jpc4zzdeuoaQobgGfL3PNo6N8dGKQ5hEEubFIvA,9947
|
|
19
19
|
nettracer3d/smart_dilate.py,sha256=TvRUh6B4q4zIdCO1BWH-xgTdND5OUNmo99eyxG9oIAU,27145
|
|
20
|
-
nettracer3d-0.8.
|
|
21
|
-
nettracer3d-0.8.
|
|
22
|
-
nettracer3d-0.8.
|
|
23
|
-
nettracer3d-0.8.
|
|
24
|
-
nettracer3d-0.8.
|
|
25
|
-
nettracer3d-0.8.
|
|
20
|
+
nettracer3d-0.8.8.dist-info/licenses/LICENSE,sha256=jnNT-yBeIAKAHpYthPvLeqCzJ6nSurgnKmloVnfsjCI,764
|
|
21
|
+
nettracer3d-0.8.8.dist-info/METADATA,sha256=t1q1vmtqmJ1h1L67QdYCWDnGBB5XH6ZyQXhBc7gJN_o,7008
|
|
22
|
+
nettracer3d-0.8.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
23
|
+
nettracer3d-0.8.8.dist-info/entry_points.txt,sha256=Nx1rr_0QhJXDBHAQg2vcqCzLMKBzSHfwy3xwGkueVyc,53
|
|
24
|
+
nettracer3d-0.8.8.dist-info/top_level.txt,sha256=zsYy9rZwirfCEOubolhee4TyzqBAL5gSUeFMzhFTX8c,12
|
|
25
|
+
nettracer3d-0.8.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|