nettracer3d 0.8.5__py3-none-any.whl → 0.8.7__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 +95 -65
- nettracer3d/painting.py +266 -90
- {nettracer3d-0.8.5.dist-info → nettracer3d-0.8.7.dist-info}/METADATA +2 -2
- {nettracer3d-0.8.5.dist-info → nettracer3d-0.8.7.dist-info}/RECORD +8 -8
- {nettracer3d-0.8.5.dist-info → nettracer3d-0.8.7.dist-info}/WHEEL +0 -0
- {nettracer3d-0.8.5.dist-info → nettracer3d-0.8.7.dist-info}/entry_points.txt +0 -0
- {nettracer3d-0.8.5.dist-info → nettracer3d-0.8.7.dist-info}/licenses/LICENSE +0 -0
- {nettracer3d-0.8.5.dist-info → nettracer3d-0.8.7.dist-info}/top_level.txt +0 -0
nettracer3d/nettracer_gui.py
CHANGED
|
@@ -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)
|
|
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
|
|
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.
|
|
5125
|
-
|
|
5126
|
-
|
|
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
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
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(
|
|
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
|
|
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)
|
|
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
|
nettracer3d/painting.py
CHANGED
|
@@ -7,6 +7,26 @@ import numpy as np
|
|
|
7
7
|
class PaintManager(QMainWindow):
|
|
8
8
|
def __init__(self, parent = None):
|
|
9
9
|
super().__init__(parent)
|
|
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
|
|
10
30
|
|
|
11
31
|
def get_line_points(self, x0, y0, x1, y1):
|
|
12
32
|
"""Get all points in a line between (x0,y0) and (x1,y1) using Bresenham's algorithm."""
|
|
@@ -44,7 +64,8 @@ class PaintManager(QMainWindow):
|
|
|
44
64
|
|
|
45
65
|
if self.parent().machine_window is not None:
|
|
46
66
|
if self.parent().machine_window.segmentation_worker is not None:
|
|
47
|
-
|
|
67
|
+
if not self.parent().machine_window.segmentation_worker._paused:
|
|
68
|
+
self.resume = True
|
|
48
69
|
self.parent().machine_window.segmentation_worker.pause()
|
|
49
70
|
|
|
50
71
|
|
|
@@ -54,10 +75,9 @@ class PaintManager(QMainWindow):
|
|
|
54
75
|
# Capture the background once
|
|
55
76
|
self.parent().static_background = self.parent().canvas.copy_from_bbox(self.parent().ax.bbox)
|
|
56
77
|
|
|
57
|
-
if self.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
self.parent().machine_window.segmentation_worker.resume()
|
|
78
|
+
if self.resume:
|
|
79
|
+
self.parent().machine_window.segmentation_worker.resume()
|
|
80
|
+
self.resume = False
|
|
61
81
|
|
|
62
82
|
|
|
63
83
|
|
|
@@ -70,11 +90,35 @@ class PaintManager(QMainWindow):
|
|
|
70
90
|
if not self.parent().channel_visible[channel]:
|
|
71
91
|
self.parent().channel_visible[channel] = True
|
|
72
92
|
|
|
73
|
-
# Initialize
|
|
74
|
-
self.parent()
|
|
75
|
-
|
|
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 = []
|
|
76
118
|
self.parent().current_operation = []
|
|
77
|
-
self.parent().current_operation_type = None
|
|
119
|
+
self.parent().current_operation_type = None
|
|
120
|
+
|
|
121
|
+
|
|
78
122
|
|
|
79
123
|
def add_virtual_paint_point(self, x, y, brush_size, erase=False, foreground=True):
|
|
80
124
|
"""Add a single paint point to the virtual layer."""
|
|
@@ -109,11 +153,19 @@ class PaintManager(QMainWindow):
|
|
|
109
153
|
'threedthresh': getattr(self.parent(), 'threedthresh', 1)
|
|
110
154
|
}
|
|
111
155
|
|
|
112
|
-
#
|
|
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
|
|
113
165
|
circle = plt.Circle((x, y), brush_size/2,
|
|
114
166
|
color=paint_color, alpha=alpha, animated=True)
|
|
115
167
|
|
|
116
|
-
# Add to
|
|
168
|
+
# Add to display operations (OLD - for visual display)
|
|
117
169
|
if self.parent().current_operation_type != operation_type:
|
|
118
170
|
# Finish previous operation if switching between draw/erase
|
|
119
171
|
self.finish_current_virtual_operation()
|
|
@@ -126,6 +178,23 @@ class PaintManager(QMainWindow):
|
|
|
126
178
|
|
|
127
179
|
self.parent().ax.add_patch(circle)
|
|
128
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
|
+
|
|
129
198
|
def add_virtual_paint_stroke(self, x, y, brush_size, erase=False, foreground=True):
|
|
130
199
|
"""Add a paint stroke - simple visual, interpolation happens during data conversion."""
|
|
131
200
|
# Just add the current point for visual display (no interpolation yet)
|
|
@@ -134,22 +203,119 @@ class PaintManager(QMainWindow):
|
|
|
134
203
|
# Store the last position for data conversion later
|
|
135
204
|
self.parent().last_virtual_pos = (x, y)
|
|
136
205
|
|
|
137
|
-
def
|
|
138
|
-
"""
|
|
206
|
+
def connect_virtual_paint_points(self):
|
|
207
|
+
"""Connect points with lines matching the circle size by converting to screen coordinates."""
|
|
139
208
|
|
|
140
|
-
if not self.parent().current_operation:
|
|
209
|
+
if not hasattr(self.parent(), 'current_operation') or len(self.parent().current_operation) < 2:
|
|
141
210
|
return
|
|
142
|
-
|
|
143
|
-
if self.parent().current_operation_type == 'draw':
|
|
144
|
-
self.parent().virtual_draw_operations.append(self.parent().current_operation)
|
|
145
|
-
elif self.parent().current_operation_type == 'erase':
|
|
146
|
-
self.parent().virtual_erase_operations.append(self.parent().current_operation)
|
|
147
211
|
|
|
148
|
-
|
|
149
|
-
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
|
|
150
316
|
|
|
151
317
|
def update_virtual_paint_display(self):
|
|
152
|
-
"""Update display with virtual paint strokes -
|
|
318
|
+
"""Update display with virtual paint strokes - handles different object types."""
|
|
153
319
|
if not hasattr(self.parent(), 'static_background') or self.parent().static_background is None:
|
|
154
320
|
return
|
|
155
321
|
|
|
@@ -160,108 +326,118 @@ class PaintManager(QMainWindow):
|
|
|
160
326
|
for operation_list in [self.parent().virtual_draw_operations, self.parent().virtual_erase_operations]:
|
|
161
327
|
for operation in operation_list:
|
|
162
328
|
for item in operation:
|
|
163
|
-
self.
|
|
329
|
+
self._draw_virtual_item(item)
|
|
164
330
|
|
|
165
331
|
# Draw current operation being painted
|
|
166
332
|
if hasattr(self.parent(), 'current_operation'):
|
|
167
333
|
for item in self.parent().current_operation:
|
|
168
|
-
self.
|
|
334
|
+
self._draw_virtual_item(item)
|
|
169
335
|
|
|
170
336
|
# Blit everything at once
|
|
171
337
|
self.parent().canvas.blit(self.parent().ax.bbox)
|
|
172
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
|
+
|
|
173
358
|
def convert_virtual_strokes_to_data(self):
|
|
174
|
-
"""Convert
|
|
359
|
+
"""Convert each stroke separately to actual array data using ONLY the new stroke tracking system."""
|
|
175
360
|
|
|
176
|
-
#
|
|
177
|
-
|
|
178
|
-
last_pos = None
|
|
179
|
-
for item in operation:
|
|
180
|
-
data = item['data']
|
|
181
|
-
current_pos = (data['x'], data['y'])
|
|
182
|
-
|
|
183
|
-
if last_pos is not None:
|
|
184
|
-
points = self.get_line_points(last_pos[0], last_pos[1], current_pos[0], current_pos[1])
|
|
185
|
-
for px, py in points:
|
|
186
|
-
self.paint_at_position_vectorized(
|
|
187
|
-
px, py,
|
|
188
|
-
erase=False,
|
|
189
|
-
channel=data['channel'],
|
|
190
|
-
brush_size=data['brush_size'],
|
|
191
|
-
threed=data['threed'], # Add this
|
|
192
|
-
threedthresh=data['threedthresh'], # Add this
|
|
193
|
-
foreground=data['foreground'],
|
|
194
|
-
machine_window=self.parent().machine_window
|
|
195
|
-
)
|
|
196
|
-
else:
|
|
197
|
-
self.paint_at_position_vectorized(
|
|
198
|
-
data['x'], data['y'],
|
|
199
|
-
erase=False,
|
|
200
|
-
channel=data['channel'],
|
|
201
|
-
brush_size=data['brush_size'],
|
|
202
|
-
threed=data['threed'], # Add this
|
|
203
|
-
threedthresh=data['threedthresh'], # Add this
|
|
204
|
-
foreground=data['foreground'],
|
|
205
|
-
machine_window=self.parent().machine_window
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
last_pos = current_pos
|
|
209
|
-
try:
|
|
210
|
-
item['circle'].remove()
|
|
211
|
-
except:
|
|
212
|
-
pass
|
|
361
|
+
# Finish the current stroke first
|
|
362
|
+
self.finish_current_stroke()
|
|
213
363
|
|
|
214
|
-
#
|
|
215
|
-
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
|
|
216
373
|
last_pos = None
|
|
217
|
-
for
|
|
218
|
-
|
|
219
|
-
current_pos = (data['x'], data['y'])
|
|
374
|
+
for point_data in stroke_points:
|
|
375
|
+
current_pos = (point_data['x'], point_data['y'])
|
|
220
376
|
|
|
221
377
|
if last_pos is not None:
|
|
378
|
+
# Interpolate between consecutive points in this stroke
|
|
222
379
|
points = self.get_line_points(last_pos[0], last_pos[1], current_pos[0], current_pos[1])
|
|
223
380
|
for px, py in points:
|
|
224
381
|
self.paint_at_position_vectorized(
|
|
225
382
|
px, py,
|
|
226
|
-
erase=
|
|
227
|
-
channel=
|
|
228
|
-
brush_size=
|
|
229
|
-
threed=
|
|
230
|
-
threedthresh=
|
|
231
|
-
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'],
|
|
232
389
|
machine_window=self.parent().machine_window
|
|
233
390
|
)
|
|
234
391
|
else:
|
|
392
|
+
# First point in stroke
|
|
235
393
|
self.paint_at_position_vectorized(
|
|
236
|
-
|
|
237
|
-
erase=
|
|
238
|
-
channel=
|
|
239
|
-
brush_size=
|
|
240
|
-
threed=
|
|
241
|
-
threedthresh=
|
|
242
|
-
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'],
|
|
243
401
|
machine_window=self.parent().machine_window
|
|
244
402
|
)
|
|
245
403
|
|
|
246
404
|
last_pos = current_pos
|
|
247
|
-
try:
|
|
248
|
-
item['circle'].remove()
|
|
249
|
-
except:
|
|
250
|
-
pass
|
|
251
405
|
|
|
252
|
-
# Clean up
|
|
253
|
-
self.parent().virtual_draw_operations
|
|
254
|
-
|
|
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
|
+
|
|
255
420
|
if hasattr(self.parent(), 'current_operation'):
|
|
256
421
|
for item in self.parent().current_operation:
|
|
257
422
|
try:
|
|
258
|
-
|
|
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()
|
|
259
429
|
except:
|
|
260
430
|
pass
|
|
261
|
-
|
|
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 = []
|
|
262
439
|
self.parent().current_operation_type = None
|
|
263
440
|
|
|
264
|
-
|
|
265
441
|
def end_virtual_paint_session(self):
|
|
266
442
|
"""Convert virtual paint to actual array modifications when exiting paint mode."""
|
|
267
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.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.
|
|
113
|
+
-- Version 0.8.7 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=yrWkpWVt7IvgGFkvc8tbMWy8t6O8P_2ulOKW-eOe9B4,540425
|
|
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.7.dist-info/licenses/LICENSE,sha256=jnNT-yBeIAKAHpYthPvLeqCzJ6nSurgnKmloVnfsjCI,764
|
|
21
|
+
nettracer3d-0.8.7.dist-info/METADATA,sha256=6Q68XlohpUOpHNK6V7doYlQ4R9twu5D9qoJNwoyfs48,7008
|
|
22
|
+
nettracer3d-0.8.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
23
|
+
nettracer3d-0.8.7.dist-info/entry_points.txt,sha256=Nx1rr_0QhJXDBHAQg2vcqCzLMKBzSHfwy3xwGkueVyc,53
|
|
24
|
+
nettracer3d-0.8.7.dist-info/top_level.txt,sha256=zsYy9rZwirfCEOubolhee4TyzqBAL5gSUeFMzhFTX8c,12
|
|
25
|
+
nettracer3d-0.8.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|