nettracer3d 0.6.6__py3-none-any.whl → 0.6.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.
- nettracer3d/community_extractor.py +0 -269
- nettracer3d/morphology.py +61 -36
- nettracer3d/nettracer.py +261 -238
- nettracer3d/nettracer_gui.py +321 -185
- nettracer3d/proximity.py +9 -8
- nettracer3d/segmenter.py +12 -18
- nettracer3d/smart_dilate.py +156 -82
- {nettracer3d-0.6.6.dist-info → nettracer3d-0.6.7.dist-info}/METADATA +8 -4
- nettracer3d-0.6.7.dist-info/RECORD +20 -0
- nettracer3d-0.6.6.dist-info/RECORD +0 -20
- {nettracer3d-0.6.6.dist-info → nettracer3d-0.6.7.dist-info}/WHEEL +0 -0
- {nettracer3d-0.6.6.dist-info → nettracer3d-0.6.7.dist-info}/entry_points.txt +0 -0
- {nettracer3d-0.6.6.dist-info → nettracer3d-0.6.7.dist-info}/licenses/LICENSE +0 -0
- {nettracer3d-0.6.6.dist-info → nettracer3d-0.6.7.dist-info}/top_level.txt +0 -0
nettracer3d/nettracer_gui.py
CHANGED
|
@@ -25,6 +25,7 @@ import multiprocessing as mp
|
|
|
25
25
|
from concurrent.futures import ThreadPoolExecutor
|
|
26
26
|
from functools import partial
|
|
27
27
|
from nettracer3d import segmenter
|
|
28
|
+
#from nettracer3d import segmenter_GPU
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
|
|
@@ -147,6 +148,11 @@ class ImageViewerWindow(QMainWindow):
|
|
|
147
148
|
3: None
|
|
148
149
|
} #For storing thresholding information
|
|
149
150
|
|
|
151
|
+
self.radii_dict = {
|
|
152
|
+
0: None,
|
|
153
|
+
1: None
|
|
154
|
+
}
|
|
155
|
+
|
|
150
156
|
self.original_shape = None #For undoing resamples
|
|
151
157
|
|
|
152
158
|
# Create control panel
|
|
@@ -1312,7 +1318,10 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1312
1318
|
info_dict['Centroid'] = my_network.node_centroids[label]
|
|
1313
1319
|
|
|
1314
1320
|
if self.volume_dict[0] is not None:
|
|
1315
|
-
info_dict['Volume'] = self.volume_dict[0][label]
|
|
1321
|
+
info_dict['Volume (Scaled)'] = self.volume_dict[0][label]
|
|
1322
|
+
|
|
1323
|
+
if self.radii_dict[0] is not None:
|
|
1324
|
+
info_dict['Max Radius (Scaled)'] = self.radii_dict[0][label]
|
|
1316
1325
|
|
|
1317
1326
|
|
|
1318
1327
|
elif sort == 'edge':
|
|
@@ -1327,7 +1336,10 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1327
1336
|
info_dict['Centroid'] = my_network.edge_centroids[label]
|
|
1328
1337
|
|
|
1329
1338
|
if self.volume_dict[1] is not None:
|
|
1330
|
-
info_dict['Volume'] = self.volume_dict[1][label]
|
|
1339
|
+
info_dict['Volume (Scaled)'] = self.volume_dict[1][label]
|
|
1340
|
+
|
|
1341
|
+
if self.radii_dict[1] is not None:
|
|
1342
|
+
info_dict['~Radius (Scaled)'] = self.radii_dict[1][label]
|
|
1331
1343
|
|
|
1332
1344
|
self.format_for_upperright_table(info_dict, title = f'Info on Object')
|
|
1333
1345
|
|
|
@@ -2485,7 +2497,7 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2485
2497
|
# Process menu
|
|
2486
2498
|
process_menu = menubar.addMenu("Process")
|
|
2487
2499
|
calculate_menu = process_menu.addMenu("Calculate")
|
|
2488
|
-
calc_all_action = calculate_menu.addAction("Calculate
|
|
2500
|
+
calc_all_action = calculate_menu.addAction("Calculate Connectivity Network (Find Node-Edge-Node Network)")
|
|
2489
2501
|
calc_all_action.triggered.connect(self.show_calc_all_dialog)
|
|
2490
2502
|
calc_prox_action = calculate_menu.addAction("Calculate Proximity Network (connect nodes by distance)")
|
|
2491
2503
|
calc_prox_action.triggered.connect(self.show_calc_prox_dialog)
|
|
@@ -2551,8 +2563,8 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2551
2563
|
idoverlay_action.triggered.connect(self.show_idoverlay_dialog)
|
|
2552
2564
|
coloroverlay_action = overlay_menu.addAction("Color Nodes (or Edges)")
|
|
2553
2565
|
coloroverlay_action.triggered.connect(self.show_coloroverlay_dialog)
|
|
2554
|
-
searchoverlay_action = overlay_menu.addAction("Show Search Regions")
|
|
2555
|
-
searchoverlay_action.triggered.connect(self.show_search_dialog)
|
|
2566
|
+
#searchoverlay_action = overlay_menu.addAction("Show Search Regions")
|
|
2567
|
+
#searchoverlay_action.triggered.connect(self.show_search_dialog)
|
|
2556
2568
|
shuffle_action = overlay_menu.addAction("Shuffle")
|
|
2557
2569
|
shuffle_action.triggered.connect(self.show_shuffle_dialog)
|
|
2558
2570
|
arbitrary_action = image_menu.addAction("Select Objects")
|
|
@@ -2887,6 +2899,20 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2887
2899
|
elif trumper == '-':
|
|
2888
2900
|
for key, value in my_dict.items():
|
|
2889
2901
|
my_dict[key] = value[0]
|
|
2902
|
+
elif trumper == '/':
|
|
2903
|
+
new_dict = {}
|
|
2904
|
+
max_val = max(my_dict.keys()) + 1
|
|
2905
|
+
for key, value in my_dict.items():
|
|
2906
|
+
new_dict[key] = f'{value[0]}'
|
|
2907
|
+
if len(value) > 1:
|
|
2908
|
+
for i in range(1, len(value)):
|
|
2909
|
+
new_dict[max_val] = f'{value[i]}'
|
|
2910
|
+
try:
|
|
2911
|
+
my_network.node_centroids[max_val] = my_network.node_centroids[key]
|
|
2912
|
+
except:
|
|
2913
|
+
pass
|
|
2914
|
+
max_val += 1
|
|
2915
|
+
return new_dict
|
|
2890
2916
|
else:
|
|
2891
2917
|
for thing in my_dict:
|
|
2892
2918
|
val = my_dict[thing]
|
|
@@ -2925,12 +2951,16 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2925
2951
|
'Multiple IDs Detected',
|
|
2926
2952
|
'The node identities appear to contain multiple ids per node in a list.\n'
|
|
2927
2953
|
'If you desire one node ID to trump all others, enter it here.\n'
|
|
2928
|
-
'(Enter "-" to have the first IDs trump all others
|
|
2954
|
+
'(Enter "-" to have the first IDs trump all others)\n'
|
|
2955
|
+
'(Enter "/" to have multi-ID nodes be split into many nodes sharing a centroid)\n'
|
|
2956
|
+
'(Close this window to continue with multi-ID nodes)'
|
|
2929
2957
|
)
|
|
2930
2958
|
if not ok or trump_value.strip() == '':
|
|
2931
2959
|
trump_value = None
|
|
2932
2960
|
elif trump_value.upper() == '-':
|
|
2933
2961
|
trump_value = '-'
|
|
2962
|
+
elif trump_value.upper() == "/":
|
|
2963
|
+
trump_value = '/'
|
|
2934
2964
|
my_network.node_identities = uncork(my_network.node_identities, trump_value)
|
|
2935
2965
|
else:
|
|
2936
2966
|
trump_value = None
|
|
@@ -3038,76 +3068,78 @@ class ImageViewerWindow(QMainWindow):
|
|
|
3038
3068
|
"",
|
|
3039
3069
|
QFileDialog.Option.ShowDirsOnly
|
|
3040
3070
|
)
|
|
3041
|
-
self.reset(nodes = True, network = True, xy_scale = 1, z_scale = 1, edges = True, search_region = True, network_overlay = True, id_overlay = True)
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
my_network.assemble(directory)
|
|
3045
3071
|
|
|
3046
|
-
|
|
3047
|
-
try:
|
|
3048
|
-
self.load_channel(0, my_network.nodes, True)
|
|
3049
|
-
except Exception as e:
|
|
3050
|
-
print(e)
|
|
3051
|
-
try:
|
|
3052
|
-
self.load_channel(1, my_network.edges, True)
|
|
3053
|
-
except Exception as e:
|
|
3054
|
-
print(e)
|
|
3055
|
-
try:
|
|
3056
|
-
self.load_channel(2, my_network.network_overlay, True)
|
|
3057
|
-
except Exception as e:
|
|
3058
|
-
print(e)
|
|
3059
|
-
try:
|
|
3060
|
-
self.load_channel(3, my_network.id_overlay, True)
|
|
3061
|
-
except Exception as e:
|
|
3062
|
-
print(e)
|
|
3072
|
+
if directory != "":
|
|
3063
3073
|
|
|
3064
|
-
|
|
3065
|
-
for channel in self.channel_data:
|
|
3066
|
-
if channel is not None:
|
|
3067
|
-
self.slice_slider.setEnabled(True)
|
|
3068
|
-
self.slice_slider.setMinimum(0)
|
|
3069
|
-
self.slice_slider.setMaximum(channel.shape[0] - 1)
|
|
3070
|
-
self.slice_slider.setValue(0)
|
|
3071
|
-
self.current_slice = 0
|
|
3072
|
-
break
|
|
3074
|
+
self.reset(network = True, xy_scale = 1, z_scale = 1, edges = True, network_overlay = True, id_overlay = True)
|
|
3073
3075
|
|
|
3074
|
-
|
|
3075
|
-
# Create empty DataFrame for network table if network_lists is None
|
|
3076
|
-
if not hasattr(my_network, 'network_lists') or my_network.network_lists is None:
|
|
3077
|
-
empty_df = pd.DataFrame(columns=['Node 1A', 'Node 1B', 'Edge 1C'])
|
|
3078
|
-
model = PandasModel(empty_df)
|
|
3079
|
-
self.network_table.setModel(model)
|
|
3080
|
-
else:
|
|
3081
|
-
model = PandasModel(my_network.network_lists)
|
|
3082
|
-
self.network_table.setModel(model)
|
|
3083
|
-
# Adjust column widths to content
|
|
3084
|
-
for column in range(model.columnCount(None)):
|
|
3085
|
-
self.network_table.resizeColumnToContents(column)
|
|
3076
|
+
my_network.assemble(directory)
|
|
3086
3077
|
|
|
3087
|
-
|
|
3078
|
+
# Load image channels
|
|
3088
3079
|
try:
|
|
3089
|
-
self.
|
|
3080
|
+
self.load_channel(0, my_network.nodes, True)
|
|
3090
3081
|
except Exception as e:
|
|
3091
|
-
print(
|
|
3092
|
-
|
|
3093
|
-
if hasattr(my_network, 'edge_centroids') and my_network.edge_centroids is not None:
|
|
3082
|
+
print(e)
|
|
3094
3083
|
try:
|
|
3095
|
-
self.
|
|
3084
|
+
self.load_channel(1, my_network.edges, True)
|
|
3096
3085
|
except Exception as e:
|
|
3097
|
-
print(
|
|
3098
|
-
|
|
3099
|
-
if hasattr(my_network, 'node_identities') and my_network.node_identities is not None:
|
|
3086
|
+
print(e)
|
|
3100
3087
|
try:
|
|
3101
|
-
self.
|
|
3088
|
+
self.load_channel(2, my_network.network_overlay, True)
|
|
3102
3089
|
except Exception as e:
|
|
3103
|
-
print(
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
if hasattr(my_network, 'communities') and my_network.communities is not None:
|
|
3090
|
+
print(e)
|
|
3107
3091
|
try:
|
|
3108
|
-
self.
|
|
3092
|
+
self.load_channel(3, my_network.id_overlay, True)
|
|
3109
3093
|
except Exception as e:
|
|
3110
|
-
print(
|
|
3094
|
+
print(e)
|
|
3095
|
+
|
|
3096
|
+
# Update slider range based on new data
|
|
3097
|
+
for channel in self.channel_data:
|
|
3098
|
+
if channel is not None:
|
|
3099
|
+
self.slice_slider.setEnabled(True)
|
|
3100
|
+
self.slice_slider.setMinimum(0)
|
|
3101
|
+
self.slice_slider.setMaximum(channel.shape[0] - 1)
|
|
3102
|
+
self.slice_slider.setValue(0)
|
|
3103
|
+
self.current_slice = 0
|
|
3104
|
+
break
|
|
3105
|
+
|
|
3106
|
+
# Display network_lists in the network table
|
|
3107
|
+
# Create empty DataFrame for network table if network_lists is None
|
|
3108
|
+
if not hasattr(my_network, 'network_lists') or my_network.network_lists is None:
|
|
3109
|
+
empty_df = pd.DataFrame(columns=['Node 1A', 'Node 1B', 'Edge 1C'])
|
|
3110
|
+
model = PandasModel(empty_df)
|
|
3111
|
+
self.network_table.setModel(model)
|
|
3112
|
+
else:
|
|
3113
|
+
model = PandasModel(my_network.network_lists)
|
|
3114
|
+
self.network_table.setModel(model)
|
|
3115
|
+
# Adjust column widths to content
|
|
3116
|
+
for column in range(model.columnCount(None)):
|
|
3117
|
+
self.network_table.resizeColumnToContents(column)
|
|
3118
|
+
|
|
3119
|
+
if hasattr(my_network, 'node_centroids') and my_network.node_centroids is not None:
|
|
3120
|
+
try:
|
|
3121
|
+
self.format_for_upperright_table(my_network.node_centroids, 'NodeID', ['Z', 'Y', 'X'], 'Node Centroids')
|
|
3122
|
+
except Exception as e:
|
|
3123
|
+
print(f"Error loading node centroid table: {e}")
|
|
3124
|
+
|
|
3125
|
+
if hasattr(my_network, 'edge_centroids') and my_network.edge_centroids is not None:
|
|
3126
|
+
try:
|
|
3127
|
+
self.format_for_upperright_table(my_network.edge_centroids, 'EdgeID', ['Z', 'Y', 'X'], 'Edge Centroids')
|
|
3128
|
+
except Exception as e:
|
|
3129
|
+
print(f"Error loading edge centroid table: {e}")
|
|
3130
|
+
|
|
3131
|
+
if hasattr(my_network, 'node_identities') and my_network.node_identities is not None:
|
|
3132
|
+
try:
|
|
3133
|
+
self.format_for_upperright_table(my_network.node_identities, 'NodeID', 'Identity', 'Node Identities')
|
|
3134
|
+
except Exception as e:
|
|
3135
|
+
print(f"Error loading node identity table: {e}")
|
|
3136
|
+
|
|
3137
|
+
|
|
3138
|
+
if hasattr(my_network, 'communities') and my_network.communities is not None:
|
|
3139
|
+
try:
|
|
3140
|
+
self.format_for_upperright_table(my_network.communities, 'NodeID', 'Community', 'Node Communities')
|
|
3141
|
+
except Exception as e:
|
|
3142
|
+
print(f"Error loading node community table: {e}")
|
|
3111
3143
|
|
|
3112
3144
|
except Exception as e:
|
|
3113
3145
|
QMessageBox.critical(
|
|
@@ -3471,9 +3503,11 @@ class ImageViewerWindow(QMainWindow):
|
|
|
3471
3503
|
|
|
3472
3504
|
if edges:
|
|
3473
3505
|
self.delete_channel(1, False)
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3506
|
+
try:
|
|
3507
|
+
if search_region:
|
|
3508
|
+
my_network.search_region = None
|
|
3509
|
+
except:
|
|
3510
|
+
pass
|
|
3477
3511
|
|
|
3478
3512
|
if network_overlay:
|
|
3479
3513
|
self.delete_channel(2, False)
|
|
@@ -3483,8 +3517,7 @@ class ImageViewerWindow(QMainWindow):
|
|
|
3483
3517
|
|
|
3484
3518
|
|
|
3485
3519
|
|
|
3486
|
-
def save_network_3d(self, asbool
|
|
3487
|
-
|
|
3520
|
+
def save_network_3d(self, asbool=True):
|
|
3488
3521
|
try:
|
|
3489
3522
|
if asbool: # Save As
|
|
3490
3523
|
# First let user select parent directory
|
|
@@ -3494,25 +3527,28 @@ class ImageViewerWindow(QMainWindow):
|
|
|
3494
3527
|
"",
|
|
3495
3528
|
QFileDialog.Option.ShowDirsOnly
|
|
3496
3529
|
)
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3530
|
+
if not parent_dir: # If user canceled the directory selection
|
|
3531
|
+
return # Exit the method early
|
|
3532
|
+
|
|
3533
|
+
# Prompt user for new folder name
|
|
3534
|
+
new_folder_name, ok = QInputDialog.getText(
|
|
3535
|
+
self,
|
|
3536
|
+
"New Folder",
|
|
3537
|
+
"Enter name for new output folder:"
|
|
3538
|
+
)
|
|
3505
3539
|
|
|
3540
|
+
# Check if user canceled the folder name dialog
|
|
3541
|
+
if not ok or not new_folder_name:
|
|
3542
|
+
return # Exit the method early
|
|
3543
|
+
|
|
3506
3544
|
else: # Save
|
|
3507
3545
|
parent_dir = None # Let the backend handle default save location
|
|
3508
3546
|
|
|
3509
3547
|
# Call appropriate save method
|
|
3510
|
-
if
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
my_network.dump(name = 'my_network')
|
|
3515
|
-
|
|
3548
|
+
if asbool:
|
|
3549
|
+
my_network.dump(parent_dir=parent_dir, name=new_folder_name)
|
|
3550
|
+
else:
|
|
3551
|
+
my_network.dump(name='my_network')
|
|
3516
3552
|
|
|
3517
3553
|
except Exception as e:
|
|
3518
3554
|
QMessageBox.critical(
|
|
@@ -3689,6 +3725,11 @@ class ImageViewerWindow(QMainWindow):
|
|
|
3689
3725
|
else:
|
|
3690
3726
|
# Regular channel processing with colormap
|
|
3691
3727
|
# Calculate brightness/contrast limits from entire volume
|
|
3728
|
+
if self.min_max[channel][0] == None:
|
|
3729
|
+
self.min_max[channel][0] = np.min(channel)
|
|
3730
|
+
if self.min_max[channel][1] == None:
|
|
3731
|
+
self.min_max[channel][1] = np.max(channel)
|
|
3732
|
+
|
|
3692
3733
|
img_min = self.min_max[channel][0]
|
|
3693
3734
|
img_max = self.min_max[channel][1]
|
|
3694
3735
|
|
|
@@ -3699,6 +3740,9 @@ class ImageViewerWindow(QMainWindow):
|
|
|
3699
3740
|
else:
|
|
3700
3741
|
vmin = img_min + (img_max - img_min) * self.channel_brightness[channel]['min']
|
|
3701
3742
|
vmax = img_min + (img_max - img_min) * self.channel_brightness[channel]['max']
|
|
3743
|
+
|
|
3744
|
+
|
|
3745
|
+
|
|
3702
3746
|
|
|
3703
3747
|
# Normalize the image safely
|
|
3704
3748
|
if vmin == vmax:
|
|
@@ -3827,7 +3871,8 @@ class ImageViewerWindow(QMainWindow):
|
|
|
3827
3871
|
self.canvas.draw()
|
|
3828
3872
|
|
|
3829
3873
|
except:
|
|
3830
|
-
|
|
3874
|
+
import traceback
|
|
3875
|
+
print(traceback.format_exc())
|
|
3831
3876
|
|
|
3832
3877
|
def update_display_slice(self, channel, preserve_zoom=None):
|
|
3833
3878
|
"""Ultra minimal update that only changes the paint channel's data"""
|
|
@@ -4675,10 +4720,10 @@ class PropertiesDialog(QDialog):
|
|
|
4675
4720
|
self.id_overlay.setChecked(self.check_checked(my_network.id_overlay))
|
|
4676
4721
|
layout.addRow("Overlay 2 Status", self.id_overlay)
|
|
4677
4722
|
|
|
4678
|
-
self.search_region = QPushButton("search region")
|
|
4679
|
-
self.search_region.setCheckable(True)
|
|
4680
|
-
self.search_region.setChecked(self.check_checked(my_network.search_region))
|
|
4681
|
-
layout.addRow("Node Search Region Status", self.search_region)
|
|
4723
|
+
#self.search_region = QPushButton("search region")
|
|
4724
|
+
#self.search_region.setCheckable(True)
|
|
4725
|
+
#self.search_region.setChecked(self.check_checked(my_network.search_region))
|
|
4726
|
+
#layout.addRow("Node Search Region Status", self.search_region)
|
|
4682
4727
|
|
|
4683
4728
|
self.network = QPushButton("Network")
|
|
4684
4729
|
self.network.setCheckable(True)
|
|
@@ -4717,10 +4762,10 @@ class PropertiesDialog(QDialog):
|
|
|
4717
4762
|
edges = not self.edges.isChecked()
|
|
4718
4763
|
network_overlay = not self.network_overlay.isChecked()
|
|
4719
4764
|
id_overlay = not self.id_overlay.isChecked()
|
|
4720
|
-
search_region = not self.search_region.isChecked()
|
|
4765
|
+
#search_region = not self.search_region.isChecked()
|
|
4721
4766
|
network = not self.network.isChecked()
|
|
4722
4767
|
|
|
4723
|
-
self.parent().reset(nodes = nodes, edges = edges, network_overlay = network_overlay, id_overlay = id_overlay,
|
|
4768
|
+
self.parent().reset(nodes = nodes, edges = edges, network_overlay = network_overlay, id_overlay = id_overlay, network = network, xy_scale = xy_scale, z_scale = z_scale)
|
|
4724
4769
|
|
|
4725
4770
|
self.accept()
|
|
4726
4771
|
|
|
@@ -5670,6 +5715,11 @@ class NeighborIdentityDialog(QDialog):
|
|
|
5670
5715
|
self.search = QLineEdit("")
|
|
5671
5716
|
layout.addRow("Search Radius (Ignore if using network):", self.search)
|
|
5672
5717
|
|
|
5718
|
+
self.fastdil = QPushButton("Fast Dilate")
|
|
5719
|
+
self.fastdil.setCheckable(True)
|
|
5720
|
+
self.fastdil.setChecked(False)
|
|
5721
|
+
layout.addRow("(If not using network) Use Fast Dilation (Higher speed, less accurate with search regions much larger than nodes):", self.fastdil)
|
|
5722
|
+
|
|
5673
5723
|
# Add Run button
|
|
5674
5724
|
run_button = QPushButton("Get Neighborhood Identity Distribution")
|
|
5675
5725
|
run_button.clicked.connect(self.neighborids)
|
|
@@ -5690,8 +5740,10 @@ class NeighborIdentityDialog(QDialog):
|
|
|
5690
5740
|
|
|
5691
5741
|
search = float(self.search.text()) if self.search.text().strip() else 0
|
|
5692
5742
|
|
|
5743
|
+
fastdil = self.fastdil.isChecked()
|
|
5744
|
+
|
|
5693
5745
|
|
|
5694
|
-
result, result2, title1, title2, densities = my_network.neighborhood_identities(root = root, directory = directory, mode = mode, search = search)
|
|
5746
|
+
result, result2, title1, title2, densities = my_network.neighborhood_identities(root = root, directory = directory, mode = mode, search = search, fastdil = fastdil)
|
|
5695
5747
|
|
|
5696
5748
|
self.parent().format_for_upperright_table(result, 'Node Identity', 'Amount', title = title1)
|
|
5697
5749
|
self.parent().format_for_upperright_table(result2, 'Node Identity', 'Proportion', title = title2)
|
|
@@ -5761,10 +5813,10 @@ class RadDialog(QDialog):
|
|
|
5761
5813
|
layout = QFormLayout(self)
|
|
5762
5814
|
|
|
5763
5815
|
# GPU checkbox (default False)
|
|
5764
|
-
self.GPU = QPushButton("GPU")
|
|
5765
|
-
self.GPU.setCheckable(True)
|
|
5766
|
-
self.GPU.setChecked(False)
|
|
5767
|
-
layout.addRow("Use GPU:", self.GPU)
|
|
5816
|
+
#self.GPU = QPushButton("GPU")
|
|
5817
|
+
#self.GPU.setCheckable(True)
|
|
5818
|
+
#self.GPU.setChecked(False)
|
|
5819
|
+
#layout.addRow("Use GPU:", self.GPU)
|
|
5768
5820
|
|
|
5769
5821
|
|
|
5770
5822
|
# Add Run button
|
|
@@ -5775,17 +5827,18 @@ class RadDialog(QDialog):
|
|
|
5775
5827
|
def rads(self):
|
|
5776
5828
|
|
|
5777
5829
|
try:
|
|
5778
|
-
GPU = self.GPU.isChecked()
|
|
5830
|
+
#GPU = self.GPU.isChecked() # <- I can never get these to be faster than parallel CPU *shrugs*
|
|
5779
5831
|
|
|
5780
5832
|
active_data = self.parent().channel_data[self.parent().active_channel]
|
|
5781
5833
|
|
|
5782
|
-
radii = n3d.estimate_object_radii(active_data, gpu=
|
|
5834
|
+
radii = n3d.estimate_object_radii(active_data, gpu=False, xy_scale = my_network.xy_scale, z_scale = my_network.z_scale)
|
|
5783
5835
|
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5836
|
+
if self.parent().active_channel == 0:
|
|
5837
|
+
self.parent().radii_dict[0] = radii
|
|
5838
|
+
elif self.parent().active_channel == 1:
|
|
5839
|
+
self.parent().radii_dict[1] = radii
|
|
5787
5840
|
|
|
5788
|
-
self.parent().format_for_upperright_table(radii, title = '
|
|
5841
|
+
self.parent().format_for_upperright_table(radii, title = 'Largest Radii of Objects', metric='ObjectID', value='Largest Radius (Scaled)')
|
|
5789
5842
|
|
|
5790
5843
|
self.accept()
|
|
5791
5844
|
|
|
@@ -5818,6 +5871,11 @@ class InteractionDialog(QDialog):
|
|
|
5818
5871
|
self.mode_selector.setCurrentIndex(0) # Default to Mode 1
|
|
5819
5872
|
layout.addRow("Execution Mode:", self.mode_selector)
|
|
5820
5873
|
|
|
5874
|
+
self.fastdil = QPushButton("Fast Dilate")
|
|
5875
|
+
self.fastdil.setCheckable(True)
|
|
5876
|
+
self.fastdil.setChecked(False)
|
|
5877
|
+
layout.addRow("(If not using network) Use Fast Dilation (Higher speed, less accurate with search regions much larger than nodes):", self.fastdil)
|
|
5878
|
+
|
|
5821
5879
|
# Add Run button
|
|
5822
5880
|
run_button = QPushButton("Calculate")
|
|
5823
5881
|
run_button.clicked.connect(self.interaction)
|
|
@@ -5833,8 +5891,11 @@ class InteractionDialog(QDialog):
|
|
|
5833
5891
|
node_search = float(self.node_search.text()) if self.node_search.text() else 0
|
|
5834
5892
|
except ValueError:
|
|
5835
5893
|
node_search = 0
|
|
5894
|
+
|
|
5895
|
+
|
|
5896
|
+
fastdil = self.fastdil.isChecked()
|
|
5836
5897
|
|
|
5837
|
-
result = my_network.interactions(search = node_search, cores = accepted_mode)
|
|
5898
|
+
result = my_network.interactions(search = node_search, cores = accepted_mode, fastdil = fastdil)
|
|
5838
5899
|
|
|
5839
5900
|
self.parent().format_for_upperright_table(result, 'Node ID', ['Volume of Nearby Edge (Scaled)', 'Volume of Search Region'], title = 'Node/Edge Interactions')
|
|
5840
5901
|
|
|
@@ -5842,6 +5903,9 @@ class InteractionDialog(QDialog):
|
|
|
5842
5903
|
|
|
5843
5904
|
except Exception as e:
|
|
5844
5905
|
|
|
5906
|
+
import traceback
|
|
5907
|
+
print(traceback.format_exc())
|
|
5908
|
+
|
|
5845
5909
|
print(f"Error finding interactions: {e}")
|
|
5846
5910
|
|
|
5847
5911
|
|
|
@@ -6198,6 +6262,15 @@ class ResizeDialog(QDialog):
|
|
|
6198
6262
|
undo_button.clicked.connect(lambda: self.run_resize(undo = True))
|
|
6199
6263
|
layout.addRow(undo_button)
|
|
6200
6264
|
|
|
6265
|
+
if my_network.xy_scale != my_network.z_scale:
|
|
6266
|
+
norm_button_upsize = QPushButton(f"Normalize Scaling with Upsample")
|
|
6267
|
+
norm_button_upsize.clicked.connect(lambda: self.run_resize(upsize = True, special = True))
|
|
6268
|
+
layout.addRow(norm_button_upsize)
|
|
6269
|
+
|
|
6270
|
+
norm_button_downsize = QPushButton("Normalize Scaling with Downsample")
|
|
6271
|
+
norm_button_downsize.clicked.connect(lambda: self.run_resize(upsize = False, special = True))
|
|
6272
|
+
layout.addRow(norm_button_downsize)
|
|
6273
|
+
|
|
6201
6274
|
run_button = QPushButton("Run Resize")
|
|
6202
6275
|
run_button.clicked.connect(self.run_resize)
|
|
6203
6276
|
layout.addRow(run_button)
|
|
@@ -6209,7 +6282,7 @@ class ResizeDialog(QDialog):
|
|
|
6209
6282
|
self.xsize.setText("1")
|
|
6210
6283
|
self.ysize.setText("1")
|
|
6211
6284
|
|
|
6212
|
-
def run_resize(self, undo = False):
|
|
6285
|
+
def run_resize(self, undo = False, upsize = True, special = False):
|
|
6213
6286
|
try:
|
|
6214
6287
|
self.parent().resizing = True
|
|
6215
6288
|
# Get parameters
|
|
@@ -6224,7 +6297,27 @@ class ResizeDialog(QDialog):
|
|
|
6224
6297
|
return
|
|
6225
6298
|
|
|
6226
6299
|
resize = resize if resize is not None else (zsize, ysize, xsize)
|
|
6227
|
-
|
|
6300
|
+
|
|
6301
|
+
if special:
|
|
6302
|
+
if upsize:
|
|
6303
|
+
if (my_network.z_scale > my_network.xy_scale):
|
|
6304
|
+
# Z dimension needs to be stretched
|
|
6305
|
+
resize = [my_network.z_scale/my_network.xy_scale, 1, 1] # Scale factor for [z, y, x]
|
|
6306
|
+
cardinal = my_network.xy_scale
|
|
6307
|
+
elif (my_network.xy_scale > my_network.z_scale):
|
|
6308
|
+
# XY dimensions need to be stretched
|
|
6309
|
+
resize = [1, my_network.xy_scale/my_network.z_scale, my_network.xy_scale/my_network.z_scale] # Scale factor for [z, y, x]
|
|
6310
|
+
cardinal = my_network.z_scale
|
|
6311
|
+
else:
|
|
6312
|
+
if (my_network.z_scale > my_network.xy_scale):
|
|
6313
|
+
# XY dimension needs to be shrunk
|
|
6314
|
+
resize = [1, my_network.xy_scale/my_network.z_scale, my_network.xy_scale/my_network.z_scale] # Scale factor for [z, y, x]
|
|
6315
|
+
cardinal = my_network.z_scale
|
|
6316
|
+
elif (my_network.xy_scale > my_network.z_scale):
|
|
6317
|
+
# Z dimensions need to be shrunk
|
|
6318
|
+
resize = [my_network.z_scale/my_network.xy_scale, 1, 1] # Scale factor for [z, y, x]
|
|
6319
|
+
cardinal = my_network.xy_scale
|
|
6320
|
+
|
|
6228
6321
|
# Get the shape from whichever array exists
|
|
6229
6322
|
array_shape = None
|
|
6230
6323
|
if my_network.nodes is not None:
|
|
@@ -6306,14 +6399,18 @@ class ResizeDialog(QDialog):
|
|
|
6306
6399
|
self.parent().slice_slider.setMaximum(channel.shape[0] - 1)
|
|
6307
6400
|
break
|
|
6308
6401
|
|
|
6309
|
-
if
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6402
|
+
if not special:
|
|
6403
|
+
if isinstance(resize, (int, float)):
|
|
6404
|
+
my_network.xy_scale = my_network.xy_scale/resize
|
|
6405
|
+
my_network.z_scale = my_network.z_scale/resize
|
|
6406
|
+
print("xy_scales and z_scales have been adjusted per resample. Check image -> properties to manually reset them to 1 if desired.")
|
|
6407
|
+
else:
|
|
6408
|
+
my_network.xy_scale = my_network.xy_scale/resize[1]
|
|
6409
|
+
my_network.z_scale = my_network.z_scale/resize[0]
|
|
6410
|
+
print("xy_scales and z_scales have been adjusted per resample. Check image -> properties to manually reset them to 1 if desired. Note that xy_scale will not correspond if you made your XY plane a non-square.")
|
|
6313
6411
|
else:
|
|
6314
|
-
my_network.xy_scale =
|
|
6315
|
-
my_network.z_scale =
|
|
6316
|
-
print("xy_scales and z_scales have been adjusted per resample. Check image -> properties to manually reset them to 1 if desired. Note that xy_scale will not correspond if you made your XY plane a non-square.")
|
|
6412
|
+
my_network.xy_scale = cardinal
|
|
6413
|
+
my_network.z_scale = cardinal
|
|
6317
6414
|
|
|
6318
6415
|
try:
|
|
6319
6416
|
if my_network.node_centroids is not None:
|
|
@@ -6587,9 +6684,14 @@ class ThresholdDialog(QDialog):
|
|
|
6587
6684
|
|
|
6588
6685
|
# Add ML button
|
|
6589
6686
|
ML = QPushButton("Machine Learning")
|
|
6590
|
-
ML.clicked.connect(self.start_ml)
|
|
6687
|
+
ML.clicked.connect(lambda: self.start_ml(GPU = False))
|
|
6591
6688
|
layout.addRow(ML)
|
|
6592
6689
|
|
|
6690
|
+
# Add ML button
|
|
6691
|
+
#ML2 = QPushButton("Machine Learning (GPU)")
|
|
6692
|
+
#ML2.clicked.connect(lambda: self.start_ml(GPU = True))
|
|
6693
|
+
#layout.addRow(ML2)
|
|
6694
|
+
|
|
6593
6695
|
|
|
6594
6696
|
def thresh_mode(self):
|
|
6595
6697
|
|
|
@@ -6614,7 +6716,7 @@ class ThresholdDialog(QDialog):
|
|
|
6614
6716
|
except:
|
|
6615
6717
|
pass
|
|
6616
6718
|
|
|
6617
|
-
def start_ml(self):
|
|
6719
|
+
def start_ml(self, GPU = False):
|
|
6618
6720
|
|
|
6619
6721
|
|
|
6620
6722
|
if self.parent().channel_data[2] is not None or self.parent().channel_data[3] is not None or self.parent().highlight_overlay is not None:
|
|
@@ -6630,10 +6732,16 @@ class ThresholdDialog(QDialog):
|
|
|
6630
6732
|
)
|
|
6631
6733
|
return
|
|
6632
6734
|
|
|
6735
|
+
try:
|
|
6736
|
+
import cupy as cp
|
|
6737
|
+
except:
|
|
6738
|
+
print("Cupy import failed, using CPU version")
|
|
6739
|
+
GPU = False
|
|
6740
|
+
|
|
6633
6741
|
if self.parent().mini_overlay_data is not None:
|
|
6634
6742
|
self.parent().mini_overlay_data = None
|
|
6635
6743
|
|
|
6636
|
-
self.parent().machine_window = MachineWindow(self.parent())
|
|
6744
|
+
self.parent().machine_window = MachineWindow(self.parent(), GPU = GPU)
|
|
6637
6745
|
self.parent().machine_window.show() # Non-modal window
|
|
6638
6746
|
self.accept()
|
|
6639
6747
|
|
|
@@ -6650,7 +6758,7 @@ class ThresholdDialog(QDialog):
|
|
|
6650
6758
|
|
|
6651
6759
|
class MachineWindow(QMainWindow):
|
|
6652
6760
|
|
|
6653
|
-
def __init__(self, parent=None):
|
|
6761
|
+
def __init__(self, parent=None, GPU = False):
|
|
6654
6762
|
super().__init__(parent)
|
|
6655
6763
|
|
|
6656
6764
|
self.setWindowTitle("Threshold")
|
|
@@ -6743,11 +6851,8 @@ class MachineWindow(QMainWindow):
|
|
|
6743
6851
|
# Group 2: Processing Options (GPU)
|
|
6744
6852
|
processing_group = QGroupBox("Processing Options")
|
|
6745
6853
|
processing_layout = QHBoxLayout()
|
|
6746
|
-
|
|
6747
|
-
self.GPU
|
|
6748
|
-
self.GPU.setChecked(False)
|
|
6749
|
-
self.GPU.clicked.connect(self.toggle_GPU)
|
|
6750
|
-
self.use_gpu = False
|
|
6854
|
+
|
|
6855
|
+
self.use_gpu = GPU
|
|
6751
6856
|
self.two = QPushButton("Train By 2D Slice Patterns")
|
|
6752
6857
|
self.two.setCheckable(True)
|
|
6753
6858
|
self.two.setChecked(False)
|
|
@@ -6796,7 +6901,8 @@ class MachineWindow(QMainWindow):
|
|
|
6796
6901
|
|
|
6797
6902
|
# Add all groups to main layout
|
|
6798
6903
|
main_layout.addWidget(drawing_group)
|
|
6799
|
-
|
|
6904
|
+
if not GPU:
|
|
6905
|
+
main_layout.addWidget(processing_group)
|
|
6800
6906
|
main_layout.addWidget(training_group)
|
|
6801
6907
|
main_layout.addWidget(segmentation_group)
|
|
6802
6908
|
|
|
@@ -6806,10 +6912,16 @@ class MachineWindow(QMainWindow):
|
|
|
6806
6912
|
self.trained = False
|
|
6807
6913
|
self.previewing = False
|
|
6808
6914
|
|
|
6915
|
+
if not GPU:
|
|
6916
|
+
self.segmenter = segmenter.InteractiveSegmenter(active_data, use_gpu=False)
|
|
6917
|
+
else:
|
|
6918
|
+
self.segmenter = segmenter_GPU.InteractiveSegmenter(active_data)
|
|
6809
6919
|
|
|
6810
|
-
self.segmenter = segmenter.InteractiveSegmenter(active_data, use_gpu=True)
|
|
6811
6920
|
self.segmentation_worker = None
|
|
6812
6921
|
|
|
6922
|
+
self.fore_button.click()
|
|
6923
|
+
self.fore_button.click()
|
|
6924
|
+
|
|
6813
6925
|
def toggle_lock(self):
|
|
6814
6926
|
|
|
6815
6927
|
self.mem_lock = self.lock_button.isChecked()
|
|
@@ -6835,12 +6947,6 @@ class MachineWindow(QMainWindow):
|
|
|
6835
6947
|
pass
|
|
6836
6948
|
|
|
6837
6949
|
|
|
6838
|
-
|
|
6839
|
-
def toggle_GPU(self):
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
self.use_gpu = self.GPU.isChecked()
|
|
6843
|
-
|
|
6844
6950
|
def toggle_two(self):
|
|
6845
6951
|
if self.two.isChecked():
|
|
6846
6952
|
# If button two is checked, ensure button three is unchecked
|
|
@@ -6918,6 +7024,8 @@ class MachineWindow(QMainWindow):
|
|
|
6918
7024
|
self.trained = True
|
|
6919
7025
|
except Exception as e:
|
|
6920
7026
|
print("Error training. Perhaps you forgot both foreground and background markers? I need both!")
|
|
7027
|
+
import traceback
|
|
7028
|
+
traceback.print_exc()
|
|
6921
7029
|
except MemoryError:
|
|
6922
7030
|
QMessageBox.critical(
|
|
6923
7031
|
self,
|
|
@@ -7580,6 +7688,12 @@ class SmartDilateDialog(QDialog):
|
|
|
7580
7688
|
self.GPU.setChecked(False)
|
|
7581
7689
|
layout.addRow("Use GPU:", self.GPU)
|
|
7582
7690
|
|
|
7691
|
+
# dt checkbox (default False)
|
|
7692
|
+
self.predt = QPushButton("Pre-DT")
|
|
7693
|
+
self.predt.setCheckable(True)
|
|
7694
|
+
self.predt.setChecked(False)
|
|
7695
|
+
layout.addRow("Use Distance Transform for Predilation (Better at Large Dilations):", self.predt)
|
|
7696
|
+
|
|
7583
7697
|
self.down_factor = QLineEdit("")
|
|
7584
7698
|
layout.addRow("Internal Downsample for GPU (if needed):", self.down_factor)
|
|
7585
7699
|
|
|
@@ -7594,11 +7708,12 @@ class SmartDilateDialog(QDialog):
|
|
|
7594
7708
|
|
|
7595
7709
|
GPU = self.GPU.isChecked()
|
|
7596
7710
|
down_factor = float(self.down_factor.text()) if self.down_factor.text().strip() else None
|
|
7711
|
+
predt = not self.predt.isChecked()
|
|
7597
7712
|
active_data, amount, xy_scale, z_scale = self.params
|
|
7598
7713
|
|
|
7599
7714
|
dilate_xy, dilate_z = n3d.dilation_length_to_pixels(xy_scale, z_scale, amount, amount)
|
|
7600
7715
|
|
|
7601
|
-
result = sdl.smart_dilate(active_data, dilate_xy, dilate_z, GPU = GPU, predownsample = down_factor)
|
|
7716
|
+
result = sdl.smart_dilate(active_data, dilate_xy, dilate_z, GPU = GPU, predownsample = down_factor, fast_dil = predt, use_dt_dil_amount = amount, xy_scale = xy_scale, z_scale = z_scale)
|
|
7602
7717
|
|
|
7603
7718
|
self.parent().load_channel(self.parent().active_channel, result, True)
|
|
7604
7719
|
self.accept()
|
|
@@ -7634,7 +7749,7 @@ class DilateDialog(QDialog):
|
|
|
7634
7749
|
|
|
7635
7750
|
# Add mode selection dropdown
|
|
7636
7751
|
self.mode_selector = QComboBox()
|
|
7637
|
-
self.mode_selector.addItems(["Binary
|
|
7752
|
+
self.mode_selector.addItems(["Pseudo3D Binary Kernels (For Fast, small dilations)", "Preserve Labels (slower)", "Distance Transform-Based (Slower but more accurate at larger dilations)"])
|
|
7638
7753
|
self.mode_selector.setCurrentIndex(0) # Default to Mode 1
|
|
7639
7754
|
layout.addRow("Execution Mode:", self.mode_selector)
|
|
7640
7755
|
|
|
@@ -7684,18 +7799,17 @@ class DilateDialog(QDialog):
|
|
|
7684
7799
|
return
|
|
7685
7800
|
|
|
7686
7801
|
if accepted_mode == 2:
|
|
7687
|
-
|
|
7802
|
+
result = n3d.dilate_3D_dt(active_data, amount, xy_scaling = xy_scale, z_scaling = z_scale)
|
|
7688
7803
|
else:
|
|
7689
|
-
recursive = False
|
|
7690
7804
|
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7805
|
+
# Call dilate method with parameters
|
|
7806
|
+
result = n3d.dilate(
|
|
7807
|
+
active_data,
|
|
7808
|
+
amount,
|
|
7809
|
+
xy_scale = xy_scale,
|
|
7810
|
+
z_scale = z_scale)
|
|
7811
|
+
|
|
7812
|
+
result = result * 255
|
|
7699
7813
|
|
|
7700
7814
|
# Update both the display data and the network object
|
|
7701
7815
|
self.parent().load_channel(self.parent().active_channel, result, True)
|
|
@@ -7739,6 +7853,12 @@ class ErodeDialog(QDialog):
|
|
|
7739
7853
|
self.z_scale = QLineEdit(z_scale)
|
|
7740
7854
|
layout.addRow("z_scale:", self.z_scale)
|
|
7741
7855
|
|
|
7856
|
+
# Add mode selection dropdown
|
|
7857
|
+
self.mode_selector = QComboBox()
|
|
7858
|
+
self.mode_selector.addItems(["Pseudo3D Binary Kernels (For Fast, small erosions)", "Distance Transform-Based (Slower but more accurate at larger dilations)"])
|
|
7859
|
+
self.mode_selector.setCurrentIndex(0) # Default to Mode 1
|
|
7860
|
+
layout.addRow("Execution Mode:", self.mode_selector)
|
|
7861
|
+
|
|
7742
7862
|
# Add Run button
|
|
7743
7863
|
run_button = QPushButton("Run Erode")
|
|
7744
7864
|
run_button.clicked.connect(self.run_erode)
|
|
@@ -7769,6 +7889,8 @@ class ErodeDialog(QDialog):
|
|
|
7769
7889
|
z_scale = float(self.z_scale.text()) if self.z_scale.text() else 1
|
|
7770
7890
|
except ValueError:
|
|
7771
7891
|
z_scale = 1
|
|
7892
|
+
|
|
7893
|
+
mode = self.mode_selector.currentIndex()
|
|
7772
7894
|
|
|
7773
7895
|
# Get the active channel data from parent
|
|
7774
7896
|
active_data = self.parent().channel_data[self.parent().active_channel]
|
|
@@ -7781,6 +7903,7 @@ class ErodeDialog(QDialog):
|
|
|
7781
7903
|
amount,
|
|
7782
7904
|
xy_scale = xy_scale,
|
|
7783
7905
|
z_scale = z_scale,
|
|
7906
|
+
mode = mode
|
|
7784
7907
|
)
|
|
7785
7908
|
|
|
7786
7909
|
|
|
@@ -8182,8 +8305,7 @@ class WatershedDialog(QDialog):
|
|
|
8182
8305
|
self.accept()
|
|
8183
8306
|
|
|
8184
8307
|
except Exception as e:
|
|
8185
|
-
|
|
8186
|
-
print(traceback.format_exc())
|
|
8308
|
+
|
|
8187
8309
|
QMessageBox.critical(
|
|
8188
8310
|
self,
|
|
8189
8311
|
"Error",
|
|
@@ -8343,20 +8465,9 @@ class GenNodesDialog(QDialog):
|
|
|
8343
8465
|
layout = QFormLayout(self)
|
|
8344
8466
|
self.called = called
|
|
8345
8467
|
|
|
8346
|
-
self.
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
self.comp_dil = QLineEdit("0")
|
|
8350
|
-
layout.addRow("Voxel distance to merge nearby nodes (Int - compensates for multi-branch identification along thick branch regions):", self.comp_dil)
|
|
8351
|
-
|
|
8352
|
-
self.max_vol = QLineEdit("0")
|
|
8353
|
-
layout.addRow("Maximum Voxel Volume of Vertices to Retain (int - Compensates for skeleton looping - occurs before any node merging - the smallest objects are always 27 voxels):", self.max_vol)
|
|
8354
|
-
|
|
8355
|
-
# auto checkbox (default True)
|
|
8356
|
-
self.auto = QPushButton("Auto")
|
|
8357
|
-
self.auto.setCheckable(True)
|
|
8358
|
-
self.auto.setChecked(False)
|
|
8359
|
-
layout.addRow("Attempt to Auto Correct Skeleton Looping:", self.auto)
|
|
8468
|
+
self.directory = QLineEdit()
|
|
8469
|
+
self.directory.setPlaceholderText("Leave empty to save in active dir")
|
|
8470
|
+
layout.addRow("Output Directory:", self.directory)
|
|
8360
8471
|
|
|
8361
8472
|
if not down_factor:
|
|
8362
8473
|
down_factor = None
|
|
@@ -8371,16 +8482,33 @@ class GenNodesDialog(QDialog):
|
|
|
8371
8482
|
self.down_factor = down_factor[0]
|
|
8372
8483
|
self.cubic = down_factor[1]
|
|
8373
8484
|
|
|
8374
|
-
self.
|
|
8375
|
-
|
|
8376
|
-
|
|
8485
|
+
self.branch_removal = QLineEdit("0")
|
|
8486
|
+
layout.addRow("Skeleton Voxel Branch Length to Remove (int) (Compensates for spines off medial axis):", self.branch_removal)
|
|
8487
|
+
|
|
8488
|
+
self.max_vol = QLineEdit("0")
|
|
8489
|
+
layout.addRow("Maximum Voxel Volume of Vertices to Retain (int - Compensates for skeleton looping - occurs before any node merging - the smallest objects are always 27 voxels):", self.max_vol)
|
|
8490
|
+
|
|
8491
|
+
self.comp_dil = QLineEdit("0")
|
|
8492
|
+
layout.addRow("Voxel distance to merge nearby nodes (Int - compensates for multi-branch identification along thick branch regions):", self.comp_dil)
|
|
8493
|
+
|
|
8494
|
+
self.fast_dil = QPushButton("Fast-Dil")
|
|
8495
|
+
self.fast_dil.setCheckable(True)
|
|
8496
|
+
self.fast_dil.setChecked(True)
|
|
8497
|
+
layout.addRow("(If using above) Use Fast Dilation (Higher speed, less accurate with search regions much larger than nodes):", self.fast_dil)
|
|
8498
|
+
|
|
8499
|
+
# auto checkbox (default True)
|
|
8500
|
+
self.auto = QPushButton("Auto")
|
|
8501
|
+
self.auto.setCheckable(True)
|
|
8502
|
+
self.auto.setChecked(True)
|
|
8503
|
+
layout.addRow("Attempt to Auto Correct Skeleton Looping:", self.auto)
|
|
8504
|
+
|
|
8377
8505
|
|
|
8378
8506
|
# retain checkbox (default True)
|
|
8379
8507
|
if not called:
|
|
8380
8508
|
self.retain = QPushButton("Retain")
|
|
8381
8509
|
self.retain.setCheckable(True)
|
|
8382
8510
|
self.retain.setChecked(True)
|
|
8383
|
-
layout.addRow("Retain Original Edges? (Will be moved to overlay 2):", self.retain)
|
|
8511
|
+
#layout.addRow("Retain Original Edges? (Will be moved to overlay 2):", self.retain)
|
|
8384
8512
|
else:
|
|
8385
8513
|
self.retain = False
|
|
8386
8514
|
|
|
@@ -8436,6 +8564,8 @@ class GenNodesDialog(QDialog):
|
|
|
8436
8564
|
|
|
8437
8565
|
auto = self.auto.isChecked()
|
|
8438
8566
|
|
|
8567
|
+
fastdil = self.fast_dil.isChecked()
|
|
8568
|
+
|
|
8439
8569
|
|
|
8440
8570
|
if auto:
|
|
8441
8571
|
my_network.edges = n3d.skeletonize(my_network.edges)
|
|
@@ -8449,7 +8579,8 @@ class GenNodesDialog(QDialog):
|
|
|
8449
8579
|
comp_dil=comp_dil,
|
|
8450
8580
|
down_factor=down_factor,
|
|
8451
8581
|
order = order,
|
|
8452
|
-
return_skele = True
|
|
8582
|
+
return_skele = True,
|
|
8583
|
+
fastdil = fastdil
|
|
8453
8584
|
|
|
8454
8585
|
)
|
|
8455
8586
|
|
|
@@ -8476,7 +8607,7 @@ class GenNodesDialog(QDialog):
|
|
|
8476
8607
|
|
|
8477
8608
|
self.parent().load_channel(0, channel_data = result, data = True)
|
|
8478
8609
|
|
|
8479
|
-
if retain:
|
|
8610
|
+
if retain and self.called:
|
|
8480
8611
|
self.parent().load_channel(3, channel_data = my_network.edges, data = True)
|
|
8481
8612
|
|
|
8482
8613
|
|
|
@@ -8487,6 +8618,9 @@ class GenNodesDialog(QDialog):
|
|
|
8487
8618
|
|
|
8488
8619
|
except Exception as e:
|
|
8489
8620
|
|
|
8621
|
+
import traceback
|
|
8622
|
+
print(traceback.format_exc())
|
|
8623
|
+
|
|
8490
8624
|
|
|
8491
8625
|
QMessageBox.critical(
|
|
8492
8626
|
self,
|
|
@@ -8581,7 +8715,7 @@ class BranchDialog(QDialog):
|
|
|
8581
8715
|
|
|
8582
8716
|
temp_network = n3d.Network_3D(nodes = output)
|
|
8583
8717
|
|
|
8584
|
-
temp_network.morph_proximity(search =
|
|
8718
|
+
temp_network.morph_proximity(search = [3,3], fastdil = True) #Detect network of nearby branches
|
|
8585
8719
|
|
|
8586
8720
|
temp_network.community_partition(weighted = False, style = 1, dostats = False) #Find communities with louvain, unweighted params
|
|
8587
8721
|
|
|
@@ -9013,8 +9147,6 @@ class CentroidDialog(QDialog):
|
|
|
9013
9147
|
class CalcAllDialog(QDialog):
|
|
9014
9148
|
# Class variables to store previous settings
|
|
9015
9149
|
prev_directory = ""
|
|
9016
|
-
prev_xy_scale = "1"
|
|
9017
|
-
prev_z_scale = "1"
|
|
9018
9150
|
prev_search = ""
|
|
9019
9151
|
prev_diledge = ""
|
|
9020
9152
|
prev_down_factor = ""
|
|
@@ -9024,7 +9156,7 @@ class CalcAllDialog(QDialog):
|
|
|
9024
9156
|
prev_gpu = True
|
|
9025
9157
|
prev_label_nodes = True
|
|
9026
9158
|
prev_inners = True
|
|
9027
|
-
|
|
9159
|
+
prev_fastdil = False
|
|
9028
9160
|
prev_overlays = False
|
|
9029
9161
|
prev_updates = True
|
|
9030
9162
|
|
|
@@ -9041,10 +9173,10 @@ class CalcAllDialog(QDialog):
|
|
|
9041
9173
|
layout.addRow("Output Directory:", self.directory)
|
|
9042
9174
|
|
|
9043
9175
|
# Load previous values for all inputs
|
|
9044
|
-
self.xy_scale = QLineEdit(
|
|
9176
|
+
self.xy_scale = QLineEdit(f'{my_network.xy_scale}')
|
|
9045
9177
|
layout.addRow("xy_scale:", self.xy_scale)
|
|
9046
9178
|
|
|
9047
|
-
self.z_scale = QLineEdit(
|
|
9179
|
+
self.z_scale = QLineEdit(f'{my_network.z_scale}')
|
|
9048
9180
|
layout.addRow("z_scale:", self.z_scale)
|
|
9049
9181
|
|
|
9050
9182
|
self.search = QLineEdit(self.prev_search)
|
|
@@ -9087,10 +9219,10 @@ class CalcAllDialog(QDialog):
|
|
|
9087
9219
|
self.inners.setChecked(self.prev_inners)
|
|
9088
9220
|
layout.addRow("Use Inner Edges:", self.inners)
|
|
9089
9221
|
|
|
9090
|
-
self.
|
|
9091
|
-
self.
|
|
9092
|
-
self.
|
|
9093
|
-
layout.addRow("
|
|
9222
|
+
self.fastdil = QPushButton("Fast Dilate")
|
|
9223
|
+
self.fastdil.setCheckable(True)
|
|
9224
|
+
self.fastdil.setChecked(self.prev_fastdil)
|
|
9225
|
+
layout.addRow("Use Fast Dilation (Higher speed, less accurate with search regions much larger than nodes):", self.fastdil)
|
|
9094
9226
|
|
|
9095
9227
|
self.overlays = QPushButton("Overlays")
|
|
9096
9228
|
self.overlays.setCheckable(True)
|
|
@@ -9161,7 +9293,7 @@ class CalcAllDialog(QDialog):
|
|
|
9161
9293
|
gpu = self.gpu.isChecked()
|
|
9162
9294
|
label_nodes = self.label_nodes.isChecked()
|
|
9163
9295
|
inners = self.inners.isChecked()
|
|
9164
|
-
|
|
9296
|
+
fastdil = self.fastdil.isChecked()
|
|
9165
9297
|
overlays = self.overlays.isChecked()
|
|
9166
9298
|
update = self.update.isChecked()
|
|
9167
9299
|
|
|
@@ -9184,13 +9316,11 @@ class CalcAllDialog(QDialog):
|
|
|
9184
9316
|
GPU=gpu,
|
|
9185
9317
|
label_nodes=label_nodes,
|
|
9186
9318
|
inners=inners,
|
|
9187
|
-
|
|
9319
|
+
fast_dil=fastdil
|
|
9188
9320
|
)
|
|
9189
9321
|
|
|
9190
9322
|
# Store current values as previous values
|
|
9191
9323
|
CalcAllDialog.prev_directory = self.directory.text()
|
|
9192
|
-
CalcAllDialog.prev_xy_scale = self.xy_scale.text()
|
|
9193
|
-
CalcAllDialog.prev_z_scale = self.z_scale.text()
|
|
9194
9324
|
CalcAllDialog.prev_search = self.search.text()
|
|
9195
9325
|
CalcAllDialog.prev_diledge = self.diledge.text()
|
|
9196
9326
|
CalcAllDialog.prev_down_factor = self.down_factor.text()
|
|
@@ -9200,22 +9330,22 @@ class CalcAllDialog(QDialog):
|
|
|
9200
9330
|
CalcAllDialog.prev_gpu = self.gpu.isChecked()
|
|
9201
9331
|
CalcAllDialog.prev_label_nodes = self.label_nodes.isChecked()
|
|
9202
9332
|
CalcAllDialog.prev_inners = self.inners.isChecked()
|
|
9203
|
-
CalcAllDialog.
|
|
9333
|
+
CalcAllDialog.prev_fastdil = self.fastdil.isChecked()
|
|
9204
9334
|
CalcAllDialog.prev_overlays = self.overlays.isChecked()
|
|
9205
9335
|
CalcAllDialog.prev_updates = self.update.isChecked()
|
|
9206
9336
|
|
|
9207
9337
|
|
|
9208
9338
|
# Update both the display data and the network object
|
|
9209
9339
|
if update:
|
|
9210
|
-
self.parent().
|
|
9211
|
-
self.parent().
|
|
9340
|
+
self.parent().load_channel(0, my_network.nodes, True)
|
|
9341
|
+
self.parent().load_channel(1, my_network.edges, True)
|
|
9212
9342
|
else:
|
|
9213
9343
|
my_network.nodes = temp_nodes.copy()
|
|
9214
9344
|
del temp_nodes
|
|
9215
9345
|
my_network.edges = temp_edges.copy()
|
|
9216
9346
|
del temp_edges
|
|
9217
|
-
self.parent().
|
|
9218
|
-
self.parent().
|
|
9347
|
+
self.parent().load_channel(0, my_network.nodes, True)
|
|
9348
|
+
self.parent().load_channel(1, my_network.edges, True)
|
|
9219
9349
|
|
|
9220
9350
|
|
|
9221
9351
|
# Then handle overlays
|
|
@@ -9228,8 +9358,8 @@ class CalcAllDialog(QDialog):
|
|
|
9228
9358
|
my_network.id_overlay = my_network.draw_node_indices(directory=directory)
|
|
9229
9359
|
|
|
9230
9360
|
# Update channel data
|
|
9231
|
-
self.parent().
|
|
9232
|
-
self.parent().
|
|
9361
|
+
self.parent().load_channel(2, my_network.network_overlay, True)
|
|
9362
|
+
self.parent().load_channel(3, my_network.id_overlay, True)
|
|
9233
9363
|
|
|
9234
9364
|
# Enable the overlay channel buttons
|
|
9235
9365
|
self.parent().channel_buttons[2].setEnabled(True)
|
|
@@ -9315,6 +9445,11 @@ class ProxDialog(QDialog):
|
|
|
9315
9445
|
self.mode_selector.setCurrentIndex(0) # Default to Mode 1
|
|
9316
9446
|
layout.addRow("Execution Mode:", self.mode_selector)
|
|
9317
9447
|
|
|
9448
|
+
self.fastdil = QPushButton("Fast Dilate")
|
|
9449
|
+
self.fastdil.setCheckable(True)
|
|
9450
|
+
self.fastdil.setChecked(False)
|
|
9451
|
+
layout.addRow("(If using morphological) Use Fast Dilation (Higher speed, less accurate with search regions much larger than nodes):", self.fastdil)
|
|
9452
|
+
|
|
9318
9453
|
if my_network.node_identities is not None:
|
|
9319
9454
|
self.id_selector = QComboBox()
|
|
9320
9455
|
# Add all options from id dictionary
|
|
@@ -9378,7 +9513,8 @@ class ProxDialog(QDialog):
|
|
|
9378
9513
|
except ValueError:
|
|
9379
9514
|
search = None
|
|
9380
9515
|
|
|
9381
|
-
overlays = self.overlays.isChecked()
|
|
9516
|
+
overlays = self.overlays.isChecked()
|
|
9517
|
+
fastdil = self.fastdil.isChecked()
|
|
9382
9518
|
|
|
9383
9519
|
my_network.xy_scale = xy_scale
|
|
9384
9520
|
my_network.z_scale = z_scale
|
|
@@ -9389,7 +9525,7 @@ class ProxDialog(QDialog):
|
|
|
9389
9525
|
my_network.nodes, _ = n3d.label_objects(my_network.nodes)
|
|
9390
9526
|
if my_network.node_centroids is None:
|
|
9391
9527
|
self.parent().show_centroid_dialog()
|
|
9392
|
-
my_network.morph_proximity(search = search, targets = targets)
|
|
9528
|
+
my_network.morph_proximity(search = search, targets = targets, fastdil = fastdil)
|
|
9393
9529
|
|
|
9394
9530
|
self.parent().load_channel(0, channel_data = my_network.nodes, data = True)
|
|
9395
9531
|
elif mode == 0:
|