nettracer3d 0.6.5__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 +272 -244
- nettracer3d/nettracer_gui.py +345 -192
- nettracer3d/proximity.py +9 -8
- nettracer3d/segmenter.py +250 -181
- nettracer3d/smart_dilate.py +156 -82
- {nettracer3d-0.6.5.dist-info → nettracer3d-0.6.7.dist-info}/METADATA +6 -6
- nettracer3d-0.6.7.dist-info/RECORD +20 -0
- nettracer3d-0.6.5.dist-info/RECORD +0 -20
- {nettracer3d-0.6.5.dist-info → nettracer3d-0.6.7.dist-info}/WHEEL +0 -0
- {nettracer3d-0.6.5.dist-info → nettracer3d-0.6.7.dist-info}/entry_points.txt +0 -0
- {nettracer3d-0.6.5.dist-info → nettracer3d-0.6.7.dist-info}/licenses/LICENSE +0 -0
- {nettracer3d-0.6.5.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
3071
|
|
|
3072
|
+
if directory != "":
|
|
3043
3073
|
|
|
3044
|
-
|
|
3074
|
+
self.reset(network = True, xy_scale = 1, z_scale = 1, edges = True, network_overlay = True, id_overlay = True)
|
|
3045
3075
|
|
|
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)
|
|
3063
|
-
|
|
3064
|
-
# Update slider range based on new data
|
|
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
|
|
3076
|
+
my_network.assemble(directory)
|
|
3073
3077
|
|
|
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)
|
|
3086
|
-
|
|
3087
|
-
if hasattr(my_network, 'node_centroids') and my_network.node_centroids is not None:
|
|
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=
|
|
5783
|
-
|
|
5784
|
-
for key, val in radii.items():
|
|
5834
|
+
radii = n3d.estimate_object_radii(active_data, gpu=False, xy_scale = my_network.xy_scale, z_scale = my_network.z_scale)
|
|
5785
5835
|
|
|
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,21 +6851,18 @@ 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.
|
|
6749
|
-
self.GPU.clicked.connect(self.toggle_GPU)
|
|
6750
|
-
self.use_gpu = False
|
|
6751
|
-
self.two = QPushButton("Train By 2D Slice Patterns (Cheaper - CPU only)")
|
|
6854
|
+
|
|
6855
|
+
self.use_gpu = GPU
|
|
6856
|
+
self.two = QPushButton("Train By 2D Slice Patterns")
|
|
6752
6857
|
self.two.setCheckable(True)
|
|
6753
|
-
self.two.setChecked(
|
|
6858
|
+
self.two.setChecked(False)
|
|
6754
6859
|
self.two.clicked.connect(self.toggle_two)
|
|
6755
|
-
self.use_two =
|
|
6860
|
+
self.use_two = False
|
|
6756
6861
|
self.three = QPushButton("Train by 3D Patterns")
|
|
6757
6862
|
self.three.setCheckable(True)
|
|
6758
|
-
self.three.setChecked(
|
|
6863
|
+
self.three.setChecked(True)
|
|
6759
6864
|
self.three.clicked.connect(self.toggle_three)
|
|
6760
|
-
processing_layout.addWidget(self.GPU)
|
|
6865
|
+
#processing_layout.addWidget(self.GPU) [Decided to hold off on this until its more robust]
|
|
6761
6866
|
processing_layout.addWidget(self.two)
|
|
6762
6867
|
processing_layout.addWidget(self.three)
|
|
6763
6868
|
processing_group.setLayout(processing_layout)
|
|
@@ -6790,13 +6895,14 @@ class MachineWindow(QMainWindow):
|
|
|
6790
6895
|
full_button.clicked.connect(self.segment)
|
|
6791
6896
|
segmentation_layout.addWidget(seg_button)
|
|
6792
6897
|
#segmentation_layout.addWidget(self.pause_button) # <--- for some reason the segmenter preview is still running even when killed, may be regenerating itself somewhere. May or may not actually try to resolve this because this feature isnt that necessary.
|
|
6793
|
-
segmentation_layout.addWidget(self.lock_button)
|
|
6898
|
+
#segmentation_layout.addWidget(self.lock_button) # Also turned this off
|
|
6794
6899
|
segmentation_layout.addWidget(full_button)
|
|
6795
6900
|
segmentation_group.setLayout(segmentation_layout)
|
|
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
|
|
|
@@ -7805,6 +7928,18 @@ class HoleDialog(QDialog):
|
|
|
7805
7928
|
|
|
7806
7929
|
layout = QFormLayout(self)
|
|
7807
7930
|
|
|
7931
|
+
# auto checkbox (default True)
|
|
7932
|
+
self.headon = QPushButton("Head-on")
|
|
7933
|
+
self.headon.setCheckable(True)
|
|
7934
|
+
self.headon.setChecked(False)
|
|
7935
|
+
layout.addRow("Only Use 2D Slicing Dimension:", self.headon)
|
|
7936
|
+
|
|
7937
|
+
# auto checkbox (default True)
|
|
7938
|
+
self.borders = QPushButton("Borders")
|
|
7939
|
+
self.borders.setCheckable(True)
|
|
7940
|
+
self.borders.setChecked(True)
|
|
7941
|
+
layout.addRow("Fill Small Holes Along Borders:", self.borders)
|
|
7942
|
+
|
|
7808
7943
|
# Add Run button
|
|
7809
7944
|
run_button = QPushButton("Run Fill Holes")
|
|
7810
7945
|
run_button.clicked.connect(self.run_holes)
|
|
@@ -7818,10 +7953,15 @@ class HoleDialog(QDialog):
|
|
|
7818
7953
|
active_data = self.parent().channel_data[self.parent().active_channel]
|
|
7819
7954
|
if active_data is None:
|
|
7820
7955
|
raise ValueError("No active image selected")
|
|
7956
|
+
|
|
7957
|
+
borders = self.borders.isChecked()
|
|
7958
|
+
headon = self.headon.isChecked()
|
|
7821
7959
|
|
|
7822
7960
|
# Call dilate method with parameters
|
|
7823
7961
|
result = n3d.fill_holes_3d(
|
|
7824
|
-
active_data
|
|
7962
|
+
active_data,
|
|
7963
|
+
head_on = headon,
|
|
7964
|
+
fill_borders = borders
|
|
7825
7965
|
)
|
|
7826
7966
|
|
|
7827
7967
|
self.parent().load_channel(self.parent().active_channel, result, True)
|
|
@@ -8165,8 +8305,7 @@ class WatershedDialog(QDialog):
|
|
|
8165
8305
|
self.accept()
|
|
8166
8306
|
|
|
8167
8307
|
except Exception as e:
|
|
8168
|
-
|
|
8169
|
-
print(traceback.format_exc())
|
|
8308
|
+
|
|
8170
8309
|
QMessageBox.critical(
|
|
8171
8310
|
self,
|
|
8172
8311
|
"Error",
|
|
@@ -8326,20 +8465,9 @@ class GenNodesDialog(QDialog):
|
|
|
8326
8465
|
layout = QFormLayout(self)
|
|
8327
8466
|
self.called = called
|
|
8328
8467
|
|
|
8329
|
-
self.
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
self.comp_dil = QLineEdit("0")
|
|
8333
|
-
layout.addRow("Voxel distance to merge nearby nodes (Int - compensates for multi-branch identification along thick branch regions):", self.comp_dil)
|
|
8334
|
-
|
|
8335
|
-
self.max_vol = QLineEdit("0")
|
|
8336
|
-
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)
|
|
8337
|
-
|
|
8338
|
-
# auto checkbox (default True)
|
|
8339
|
-
self.auto = QPushButton("Auto")
|
|
8340
|
-
self.auto.setCheckable(True)
|
|
8341
|
-
self.auto.setChecked(False)
|
|
8342
|
-
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)
|
|
8343
8471
|
|
|
8344
8472
|
if not down_factor:
|
|
8345
8473
|
down_factor = None
|
|
@@ -8354,16 +8482,33 @@ class GenNodesDialog(QDialog):
|
|
|
8354
8482
|
self.down_factor = down_factor[0]
|
|
8355
8483
|
self.cubic = down_factor[1]
|
|
8356
8484
|
|
|
8357
|
-
self.
|
|
8358
|
-
|
|
8359
|
-
|
|
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
|
+
|
|
8360
8505
|
|
|
8361
8506
|
# retain checkbox (default True)
|
|
8362
8507
|
if not called:
|
|
8363
8508
|
self.retain = QPushButton("Retain")
|
|
8364
8509
|
self.retain.setCheckable(True)
|
|
8365
8510
|
self.retain.setChecked(True)
|
|
8366
|
-
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)
|
|
8367
8512
|
else:
|
|
8368
8513
|
self.retain = False
|
|
8369
8514
|
|
|
@@ -8419,6 +8564,8 @@ class GenNodesDialog(QDialog):
|
|
|
8419
8564
|
|
|
8420
8565
|
auto = self.auto.isChecked()
|
|
8421
8566
|
|
|
8567
|
+
fastdil = self.fast_dil.isChecked()
|
|
8568
|
+
|
|
8422
8569
|
|
|
8423
8570
|
if auto:
|
|
8424
8571
|
my_network.edges = n3d.skeletonize(my_network.edges)
|
|
@@ -8432,7 +8579,8 @@ class GenNodesDialog(QDialog):
|
|
|
8432
8579
|
comp_dil=comp_dil,
|
|
8433
8580
|
down_factor=down_factor,
|
|
8434
8581
|
order = order,
|
|
8435
|
-
return_skele = True
|
|
8582
|
+
return_skele = True,
|
|
8583
|
+
fastdil = fastdil
|
|
8436
8584
|
|
|
8437
8585
|
)
|
|
8438
8586
|
|
|
@@ -8459,7 +8607,7 @@ class GenNodesDialog(QDialog):
|
|
|
8459
8607
|
|
|
8460
8608
|
self.parent().load_channel(0, channel_data = result, data = True)
|
|
8461
8609
|
|
|
8462
|
-
if retain:
|
|
8610
|
+
if retain and self.called:
|
|
8463
8611
|
self.parent().load_channel(3, channel_data = my_network.edges, data = True)
|
|
8464
8612
|
|
|
8465
8613
|
|
|
@@ -8470,6 +8618,9 @@ class GenNodesDialog(QDialog):
|
|
|
8470
8618
|
|
|
8471
8619
|
except Exception as e:
|
|
8472
8620
|
|
|
8621
|
+
import traceback
|
|
8622
|
+
print(traceback.format_exc())
|
|
8623
|
+
|
|
8473
8624
|
|
|
8474
8625
|
QMessageBox.critical(
|
|
8475
8626
|
self,
|
|
@@ -8564,7 +8715,7 @@ class BranchDialog(QDialog):
|
|
|
8564
8715
|
|
|
8565
8716
|
temp_network = n3d.Network_3D(nodes = output)
|
|
8566
8717
|
|
|
8567
|
-
temp_network.morph_proximity(search =
|
|
8718
|
+
temp_network.morph_proximity(search = [3,3], fastdil = True) #Detect network of nearby branches
|
|
8568
8719
|
|
|
8569
8720
|
temp_network.community_partition(weighted = False, style = 1, dostats = False) #Find communities with louvain, unweighted params
|
|
8570
8721
|
|
|
@@ -8996,8 +9147,6 @@ class CentroidDialog(QDialog):
|
|
|
8996
9147
|
class CalcAllDialog(QDialog):
|
|
8997
9148
|
# Class variables to store previous settings
|
|
8998
9149
|
prev_directory = ""
|
|
8999
|
-
prev_xy_scale = "1"
|
|
9000
|
-
prev_z_scale = "1"
|
|
9001
9150
|
prev_search = ""
|
|
9002
9151
|
prev_diledge = ""
|
|
9003
9152
|
prev_down_factor = ""
|
|
@@ -9007,7 +9156,7 @@ class CalcAllDialog(QDialog):
|
|
|
9007
9156
|
prev_gpu = True
|
|
9008
9157
|
prev_label_nodes = True
|
|
9009
9158
|
prev_inners = True
|
|
9010
|
-
|
|
9159
|
+
prev_fastdil = False
|
|
9011
9160
|
prev_overlays = False
|
|
9012
9161
|
prev_updates = True
|
|
9013
9162
|
|
|
@@ -9024,10 +9173,10 @@ class CalcAllDialog(QDialog):
|
|
|
9024
9173
|
layout.addRow("Output Directory:", self.directory)
|
|
9025
9174
|
|
|
9026
9175
|
# Load previous values for all inputs
|
|
9027
|
-
self.xy_scale = QLineEdit(
|
|
9176
|
+
self.xy_scale = QLineEdit(f'{my_network.xy_scale}')
|
|
9028
9177
|
layout.addRow("xy_scale:", self.xy_scale)
|
|
9029
9178
|
|
|
9030
|
-
self.z_scale = QLineEdit(
|
|
9179
|
+
self.z_scale = QLineEdit(f'{my_network.z_scale}')
|
|
9031
9180
|
layout.addRow("z_scale:", self.z_scale)
|
|
9032
9181
|
|
|
9033
9182
|
self.search = QLineEdit(self.prev_search)
|
|
@@ -9070,10 +9219,10 @@ class CalcAllDialog(QDialog):
|
|
|
9070
9219
|
self.inners.setChecked(self.prev_inners)
|
|
9071
9220
|
layout.addRow("Use Inner Edges:", self.inners)
|
|
9072
9221
|
|
|
9073
|
-
self.
|
|
9074
|
-
self.
|
|
9075
|
-
self.
|
|
9076
|
-
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)
|
|
9077
9226
|
|
|
9078
9227
|
self.overlays = QPushButton("Overlays")
|
|
9079
9228
|
self.overlays.setCheckable(True)
|
|
@@ -9144,7 +9293,7 @@ class CalcAllDialog(QDialog):
|
|
|
9144
9293
|
gpu = self.gpu.isChecked()
|
|
9145
9294
|
label_nodes = self.label_nodes.isChecked()
|
|
9146
9295
|
inners = self.inners.isChecked()
|
|
9147
|
-
|
|
9296
|
+
fastdil = self.fastdil.isChecked()
|
|
9148
9297
|
overlays = self.overlays.isChecked()
|
|
9149
9298
|
update = self.update.isChecked()
|
|
9150
9299
|
|
|
@@ -9167,13 +9316,11 @@ class CalcAllDialog(QDialog):
|
|
|
9167
9316
|
GPU=gpu,
|
|
9168
9317
|
label_nodes=label_nodes,
|
|
9169
9318
|
inners=inners,
|
|
9170
|
-
|
|
9319
|
+
fast_dil=fastdil
|
|
9171
9320
|
)
|
|
9172
9321
|
|
|
9173
9322
|
# Store current values as previous values
|
|
9174
9323
|
CalcAllDialog.prev_directory = self.directory.text()
|
|
9175
|
-
CalcAllDialog.prev_xy_scale = self.xy_scale.text()
|
|
9176
|
-
CalcAllDialog.prev_z_scale = self.z_scale.text()
|
|
9177
9324
|
CalcAllDialog.prev_search = self.search.text()
|
|
9178
9325
|
CalcAllDialog.prev_diledge = self.diledge.text()
|
|
9179
9326
|
CalcAllDialog.prev_down_factor = self.down_factor.text()
|
|
@@ -9183,22 +9330,22 @@ class CalcAllDialog(QDialog):
|
|
|
9183
9330
|
CalcAllDialog.prev_gpu = self.gpu.isChecked()
|
|
9184
9331
|
CalcAllDialog.prev_label_nodes = self.label_nodes.isChecked()
|
|
9185
9332
|
CalcAllDialog.prev_inners = self.inners.isChecked()
|
|
9186
|
-
CalcAllDialog.
|
|
9333
|
+
CalcAllDialog.prev_fastdil = self.fastdil.isChecked()
|
|
9187
9334
|
CalcAllDialog.prev_overlays = self.overlays.isChecked()
|
|
9188
9335
|
CalcAllDialog.prev_updates = self.update.isChecked()
|
|
9189
9336
|
|
|
9190
9337
|
|
|
9191
9338
|
# Update both the display data and the network object
|
|
9192
9339
|
if update:
|
|
9193
|
-
self.parent().
|
|
9194
|
-
self.parent().
|
|
9340
|
+
self.parent().load_channel(0, my_network.nodes, True)
|
|
9341
|
+
self.parent().load_channel(1, my_network.edges, True)
|
|
9195
9342
|
else:
|
|
9196
9343
|
my_network.nodes = temp_nodes.copy()
|
|
9197
9344
|
del temp_nodes
|
|
9198
9345
|
my_network.edges = temp_edges.copy()
|
|
9199
9346
|
del temp_edges
|
|
9200
|
-
self.parent().
|
|
9201
|
-
self.parent().
|
|
9347
|
+
self.parent().load_channel(0, my_network.nodes, True)
|
|
9348
|
+
self.parent().load_channel(1, my_network.edges, True)
|
|
9202
9349
|
|
|
9203
9350
|
|
|
9204
9351
|
# Then handle overlays
|
|
@@ -9211,8 +9358,8 @@ class CalcAllDialog(QDialog):
|
|
|
9211
9358
|
my_network.id_overlay = my_network.draw_node_indices(directory=directory)
|
|
9212
9359
|
|
|
9213
9360
|
# Update channel data
|
|
9214
|
-
self.parent().
|
|
9215
|
-
self.parent().
|
|
9361
|
+
self.parent().load_channel(2, my_network.network_overlay, True)
|
|
9362
|
+
self.parent().load_channel(3, my_network.id_overlay, True)
|
|
9216
9363
|
|
|
9217
9364
|
# Enable the overlay channel buttons
|
|
9218
9365
|
self.parent().channel_buttons[2].setEnabled(True)
|
|
@@ -9298,6 +9445,11 @@ class ProxDialog(QDialog):
|
|
|
9298
9445
|
self.mode_selector.setCurrentIndex(0) # Default to Mode 1
|
|
9299
9446
|
layout.addRow("Execution Mode:", self.mode_selector)
|
|
9300
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
|
+
|
|
9301
9453
|
if my_network.node_identities is not None:
|
|
9302
9454
|
self.id_selector = QComboBox()
|
|
9303
9455
|
# Add all options from id dictionary
|
|
@@ -9361,7 +9513,8 @@ class ProxDialog(QDialog):
|
|
|
9361
9513
|
except ValueError:
|
|
9362
9514
|
search = None
|
|
9363
9515
|
|
|
9364
|
-
overlays = self.overlays.isChecked()
|
|
9516
|
+
overlays = self.overlays.isChecked()
|
|
9517
|
+
fastdil = self.fastdil.isChecked()
|
|
9365
9518
|
|
|
9366
9519
|
my_network.xy_scale = xy_scale
|
|
9367
9520
|
my_network.z_scale = z_scale
|
|
@@ -9372,7 +9525,7 @@ class ProxDialog(QDialog):
|
|
|
9372
9525
|
my_network.nodes, _ = n3d.label_objects(my_network.nodes)
|
|
9373
9526
|
if my_network.node_centroids is None:
|
|
9374
9527
|
self.parent().show_centroid_dialog()
|
|
9375
|
-
my_network.morph_proximity(search = search, targets = targets)
|
|
9528
|
+
my_network.morph_proximity(search = search, targets = targets, fastdil = fastdil)
|
|
9376
9529
|
|
|
9377
9530
|
self.parent().load_channel(0, channel_data = my_network.nodes, data = True)
|
|
9378
9531
|
elif mode == 0:
|