nettracer3d 0.6.8__py3-none-any.whl → 0.7.0__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/modularity.py +23 -24
- nettracer3d/morphology.py +14 -9
- nettracer3d/nettracer.py +271 -44
- nettracer3d/nettracer_gui.py +502 -64
- nettracer3d/proximity.py +376 -16
- nettracer3d/segmenter.py +87 -4
- nettracer3d/smart_dilate.py +23 -24
- {nettracer3d-0.6.8.dist-info → nettracer3d-0.7.0.dist-info}/METADATA +42 -9
- nettracer3d-0.7.0.dist-info/RECORD +20 -0
- {nettracer3d-0.6.8.dist-info → nettracer3d-0.7.0.dist-info}/WHEEL +1 -1
- nettracer3d-0.6.8.dist-info/RECORD +0 -20
- {nettracer3d-0.6.8.dist-info → nettracer3d-0.7.0.dist-info}/entry_points.txt +0 -0
- {nettracer3d-0.6.8.dist-info → nettracer3d-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {nettracer3d-0.6.8.dist-info → nettracer3d-0.7.0.dist-info}/top_level.txt +0 -0
nettracer3d/nettracer_gui.py
CHANGED
|
@@ -25,7 +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
|
+
#from nettracer3d import segmenter_GPU <--- couldn't get this faster than CPU ¯\_(ツ)_/¯
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
|
|
@@ -818,6 +818,8 @@ class ImageViewerWindow(QMainWindow):
|
|
|
818
818
|
link_nodes.triggered.connect(self.handle_link)
|
|
819
819
|
delink_nodes = highlight_menu.addAction("Split Nodes")
|
|
820
820
|
delink_nodes.triggered.connect(self.handle_split)
|
|
821
|
+
override_obj = highlight_menu.addAction("Override Channel with Selection")
|
|
822
|
+
override_obj.triggered.connect(self.handle_override)
|
|
821
823
|
context_menu.addMenu(highlight_menu)
|
|
822
824
|
|
|
823
825
|
# Create measure menu
|
|
@@ -1244,7 +1246,7 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1244
1246
|
except:
|
|
1245
1247
|
pass
|
|
1246
1248
|
|
|
1247
|
-
print(f"Found {len(filtered_df)} direct connections between nodes of ID {sort} and their neighbors (of any ID)")
|
|
1249
|
+
#print(f"Found {len(filtered_df)} direct connections between nodes of ID {sort} and their neighbors (of any ID)")
|
|
1248
1250
|
|
|
1249
1251
|
if self.channel_data[0].shape[0] * self.channel_data[0].shape[1] * self.channel_data[0].shape[2] > self.mini_thresh:
|
|
1250
1252
|
self.mini_overlay = True
|
|
@@ -1662,7 +1664,9 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1662
1664
|
print(f"An error has occurred: {e}")
|
|
1663
1665
|
|
|
1664
1666
|
|
|
1665
|
-
|
|
1667
|
+
def handle_override(self):
|
|
1668
|
+
dialog = OverrideDialog(self)
|
|
1669
|
+
dialog.exec()
|
|
1666
1670
|
|
|
1667
1671
|
|
|
1668
1672
|
|
|
@@ -1970,6 +1974,28 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1970
1974
|
points.append((x, y))
|
|
1971
1975
|
return points
|
|
1972
1976
|
|
|
1977
|
+
def get_current_mouse_position(self):
|
|
1978
|
+
# Get the main application's current mouse position
|
|
1979
|
+
cursor_pos = QCursor.pos()
|
|
1980
|
+
|
|
1981
|
+
# Convert global screen coordinates to canvas widget coordinates
|
|
1982
|
+
canvas_pos = self.canvas.mapFromGlobal(cursor_pos)
|
|
1983
|
+
|
|
1984
|
+
# Check if the position is within the canvas bounds
|
|
1985
|
+
if not (0 <= canvas_pos.x() < self.canvas.width() and
|
|
1986
|
+
0 <= canvas_pos.y() < self.canvas.height()):
|
|
1987
|
+
return 0, 0 # Mouse is outside of the matplotlib canvas
|
|
1988
|
+
|
|
1989
|
+
# Convert from canvas widget coordinates to matplotlib data coordinates
|
|
1990
|
+
x = canvas_pos.x()
|
|
1991
|
+
y = canvas_pos.y()
|
|
1992
|
+
|
|
1993
|
+
# Transform display coordinates to data coordinates
|
|
1994
|
+
inv = self.ax.transData.inverted()
|
|
1995
|
+
data_coords = inv.transform((x, y))
|
|
1996
|
+
|
|
1997
|
+
return data_coords[0], data_coords[1]
|
|
1998
|
+
|
|
1973
1999
|
def on_mouse_press(self, event):
|
|
1974
2000
|
"""Handle mouse press events."""
|
|
1975
2001
|
if event.inaxes != self.ax:
|
|
@@ -2661,8 +2687,8 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2661
2687
|
degree_dist_action.triggered.connect(self.show_degree_dist_dialog)
|
|
2662
2688
|
neighbor_id_action = stats_menu.addAction("Identity Distribution of Neighbors")
|
|
2663
2689
|
neighbor_id_action.triggered.connect(self.show_neighbor_id_dialog)
|
|
2664
|
-
|
|
2665
|
-
|
|
2690
|
+
ripley_action = stats_menu.addAction("Clustering Analysis")
|
|
2691
|
+
ripley_action.triggered.connect(self.show_ripley_dialog)
|
|
2666
2692
|
vol_action = stats_menu.addAction("Calculate Volumes")
|
|
2667
2693
|
vol_action.triggered.connect(self.volumes)
|
|
2668
2694
|
rad_action = stats_menu.addAction("Calculate Radii")
|
|
@@ -2681,6 +2707,13 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2681
2707
|
id_code_action = overlay_menu.addAction("Code Identities")
|
|
2682
2708
|
id_code_action.triggered.connect(lambda: self.show_code_dialog(sort = 'Identity'))
|
|
2683
2709
|
|
|
2710
|
+
rand_menu = analysis_menu.addMenu("Randomize")
|
|
2711
|
+
random_action = rand_menu.addAction("Generate Equivalent Random Network")
|
|
2712
|
+
random_action.triggered.connect(self.show_random_dialog)
|
|
2713
|
+
random_nodes = rand_menu.addAction("Scramble Nodes (Centroids)")
|
|
2714
|
+
random_nodes.triggered.connect(self.show_randnode_dialog)
|
|
2715
|
+
|
|
2716
|
+
|
|
2684
2717
|
|
|
2685
2718
|
# Process menu
|
|
2686
2719
|
process_menu = menubar.addMenu("Process")
|
|
@@ -2958,8 +2991,11 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2958
2991
|
|
|
2959
2992
|
def show_type_dialog(self):
|
|
2960
2993
|
"""Show the type dialog"""
|
|
2961
|
-
|
|
2962
|
-
|
|
2994
|
+
try:
|
|
2995
|
+
dialog = TypeDialog(self)
|
|
2996
|
+
dialog.exec()
|
|
2997
|
+
except:
|
|
2998
|
+
pass
|
|
2963
2999
|
|
|
2964
3000
|
def show_skeletonize_dialog(self):
|
|
2965
3001
|
"""show the skeletonize dialog"""
|
|
@@ -3472,8 +3508,8 @@ class ImageViewerWindow(QMainWindow):
|
|
|
3472
3508
|
nii_img = nib.load(filename)
|
|
3473
3509
|
# Get data and transpose to match TIFF orientation
|
|
3474
3510
|
# If X needs to become Z, we move axis 2 (X) to position 0 (Z)
|
|
3475
|
-
|
|
3476
|
-
self.channel_data[channel_index] = np.transpose(
|
|
3511
|
+
arraydata = nii_img.get_fdata()
|
|
3512
|
+
self.channel_data[channel_index] = np.transpose(arraydata, (2, 1, 0))
|
|
3477
3513
|
|
|
3478
3514
|
elif file_extension in ['jpg', 'jpeg', 'png']:
|
|
3479
3515
|
from PIL import Image
|
|
@@ -4128,10 +4164,18 @@ class ImageViewerWindow(QMainWindow):
|
|
|
4128
4164
|
dialog = NeighborIdentityDialog(self)
|
|
4129
4165
|
dialog.exec()
|
|
4130
4166
|
|
|
4167
|
+
def show_ripley_dialog(self):
|
|
4168
|
+
dialog = RipleyDialog(self)
|
|
4169
|
+
dialog.exec()
|
|
4170
|
+
|
|
4131
4171
|
def show_random_dialog(self):
|
|
4132
4172
|
dialog = RandomDialog(self)
|
|
4133
4173
|
dialog.exec()
|
|
4134
4174
|
|
|
4175
|
+
def show_randnode_dialog(self):
|
|
4176
|
+
dialog = RandNodeDialog(self)
|
|
4177
|
+
dialog.exec()
|
|
4178
|
+
|
|
4135
4179
|
def show_rad_dialog(self):
|
|
4136
4180
|
dialog = RadDialog(self)
|
|
4137
4181
|
dialog.exec()
|
|
@@ -4908,10 +4952,10 @@ class PropertiesDialog(QDialog):
|
|
|
4908
4952
|
self.id_overlay.setChecked(self.check_checked(my_network.id_overlay))
|
|
4909
4953
|
layout.addRow("Overlay 2 Status", self.id_overlay)
|
|
4910
4954
|
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4955
|
+
self.search_region = QPushButton("search region")
|
|
4956
|
+
self.search_region.setCheckable(True)
|
|
4957
|
+
self.search_region.setChecked(self.check_checked(my_network.search_region))
|
|
4958
|
+
layout.addRow("Node Search Region Status", self.search_region)
|
|
4915
4959
|
|
|
4916
4960
|
self.network = QPushButton("Network")
|
|
4917
4961
|
self.network.setCheckable(True)
|
|
@@ -4950,10 +4994,10 @@ class PropertiesDialog(QDialog):
|
|
|
4950
4994
|
edges = not self.edges.isChecked()
|
|
4951
4995
|
network_overlay = not self.network_overlay.isChecked()
|
|
4952
4996
|
id_overlay = not self.id_overlay.isChecked()
|
|
4953
|
-
|
|
4997
|
+
search_region = not self.search_region.isChecked()
|
|
4954
4998
|
network = not self.network.isChecked()
|
|
4955
4999
|
|
|
4956
|
-
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)
|
|
5000
|
+
self.parent().reset(nodes = nodes, edges = edges, search_region = search_region, network_overlay = network_overlay, id_overlay = id_overlay, network = network, xy_scale = xy_scale, z_scale = z_scale)
|
|
4957
5001
|
|
|
4958
5002
|
self.accept()
|
|
4959
5003
|
|
|
@@ -5948,16 +5992,128 @@ class NeighborIdentityDialog(QDialog):
|
|
|
5948
5992
|
|
|
5949
5993
|
|
|
5950
5994
|
|
|
5995
|
+
class RipleyDialog(QDialog):
|
|
5996
|
+
|
|
5997
|
+
def __init__(self, parent=None):
|
|
5998
|
+
|
|
5999
|
+
super().__init__(parent)
|
|
6000
|
+
self.setWindowTitle(f"Find Ripley's H Function From Centroids")
|
|
6001
|
+
self.setModal(True)
|
|
6002
|
+
|
|
6003
|
+
layout = QFormLayout(self)
|
|
6004
|
+
|
|
6005
|
+
if my_network.node_identities is not None:
|
|
6006
|
+
self.root = QComboBox()
|
|
6007
|
+
self.root.addItems(list(set(my_network.node_identities.values())))
|
|
6008
|
+
self.root.setCurrentIndex(0)
|
|
6009
|
+
layout.addRow("Root Identity to Search for Neighbors", self.root)
|
|
6010
|
+
else:
|
|
6011
|
+
self.root = None
|
|
6012
|
+
|
|
6013
|
+
if my_network.node_identities is not None:
|
|
6014
|
+
self.targ = QComboBox()
|
|
6015
|
+
self.targ.addItems(list(set(my_network.node_identities.values())))
|
|
6016
|
+
self.targ.setCurrentIndex(0)
|
|
6017
|
+
layout.addRow("Targ Identity to be Searched For", self.targ)
|
|
6018
|
+
else:
|
|
6019
|
+
self.targ = None
|
|
6020
|
+
|
|
6021
|
+
self.distance = QLineEdit("5")
|
|
6022
|
+
layout.addRow("Bucket Distance for Searching For Clusters (automatically scaled by xy and z scales):", self.distance)
|
|
6023
|
+
|
|
6024
|
+
|
|
6025
|
+
self.proportion = QLineEdit("0.5")
|
|
6026
|
+
layout.addRow("Proportion of image to search? (0-1, high vals increase border artifacts): ", self.proportion)
|
|
6027
|
+
|
|
6028
|
+
self.edgecorrect = QPushButton("Border Correction")
|
|
6029
|
+
self.edgecorrect.setCheckable(True)
|
|
6030
|
+
self.edgecorrect.setChecked(False)
|
|
6031
|
+
layout.addRow("Use Border Correction (Extrapolate for points beyond the border):", self.edgecorrect)
|
|
6032
|
+
|
|
6033
|
+
self.ignore = QPushButton("Ignore Border Roots")
|
|
6034
|
+
self.ignore.setCheckable(True)
|
|
6035
|
+
self.ignore.setChecked(False)
|
|
6036
|
+
layout.addRow("Exclude Root Nodes Near Borders?:", self.ignore)
|
|
6037
|
+
|
|
6038
|
+
# Add Run button
|
|
6039
|
+
run_button = QPushButton("Get Ripley's H")
|
|
6040
|
+
run_button.clicked.connect(self.ripley)
|
|
6041
|
+
layout.addWidget(run_button)
|
|
6042
|
+
|
|
6043
|
+
def ripley(self):
|
|
6044
|
+
|
|
6045
|
+
try:
|
|
6046
|
+
|
|
6047
|
+
if my_network.node_centroids is None:
|
|
6048
|
+
self.parent().show_centroid_dialog()
|
|
6049
|
+
|
|
6050
|
+
try:
|
|
6051
|
+
root = self.root.currentText()
|
|
6052
|
+
except:
|
|
6053
|
+
root = None
|
|
6054
|
+
|
|
6055
|
+
try:
|
|
6056
|
+
targ = self.targ.currentText()
|
|
6057
|
+
except:
|
|
6058
|
+
targ = None
|
|
6059
|
+
|
|
6060
|
+
try:
|
|
6061
|
+
distance = float(self.distance.text())
|
|
6062
|
+
except:
|
|
6063
|
+
return
|
|
6064
|
+
|
|
6065
|
+
|
|
6066
|
+
try:
|
|
6067
|
+
proportion = abs(float(self.proportion.text()))
|
|
6068
|
+
except:
|
|
6069
|
+
proportion = 0.5
|
|
6070
|
+
|
|
6071
|
+
if proportion > 1 or proportion <= 0:
|
|
6072
|
+
print("Utilizing proportion = 0.5")
|
|
6073
|
+
proportion = 0.5
|
|
5951
6074
|
|
|
5952
6075
|
|
|
6076
|
+
edgecorrect = self.edgecorrect.isChecked()
|
|
5953
6077
|
|
|
6078
|
+
ignore = self.ignore.isChecked()
|
|
6079
|
+
|
|
6080
|
+
if my_network.nodes is not None:
|
|
6081
|
+
|
|
6082
|
+
if my_network.nodes.shape[0] == 1:
|
|
6083
|
+
bounds = (np.array([0, 0]), np.array([my_network.nodes.shape[2], my_network.nodes.shape[1]]))
|
|
6084
|
+
else:
|
|
6085
|
+
bounds = (np.array([0, 0, 0]), np.array([my_network.nodes.shape[2], my_network.nodes.shape[1], my_network.nodes.shape[0]]))
|
|
6086
|
+
else:
|
|
6087
|
+
bounds = None
|
|
6088
|
+
|
|
6089
|
+
r_vals, k_vals, h_vals = my_network.get_ripley(root, targ, distance, edgecorrect, bounds, ignore, proportion)
|
|
6090
|
+
|
|
6091
|
+
k_dict = dict(zip(r_vals, k_vals))
|
|
6092
|
+
h_dict = dict(zip(r_vals, h_vals))
|
|
6093
|
+
|
|
6094
|
+
|
|
6095
|
+
self.parent().format_for_upperright_table(k_dict, metric='Radius (scaled)', value='L Value', title="Ripley's K")
|
|
6096
|
+
self.parent().format_for_upperright_table(h_dict, metric='Radius (scaled)', value='L Normed', title="Ripley's H")
|
|
6097
|
+
|
|
6098
|
+
|
|
6099
|
+
self.accept()
|
|
6100
|
+
|
|
6101
|
+
except Exception as e:
|
|
6102
|
+
QMessageBox.critical(
|
|
6103
|
+
self,
|
|
6104
|
+
"Error:",
|
|
6105
|
+
f"Failed to preform cluster analysis: {str(e)}"
|
|
6106
|
+
)
|
|
6107
|
+
import traceback
|
|
6108
|
+
print(traceback.format_exc())
|
|
6109
|
+
print(f"Error: {e}")
|
|
5954
6110
|
|
|
5955
6111
|
class RandomDialog(QDialog):
|
|
5956
6112
|
|
|
5957
6113
|
def __init__(self, parent=None):
|
|
5958
6114
|
|
|
5959
6115
|
super().__init__(parent)
|
|
5960
|
-
self.setWindowTitle("
|
|
6116
|
+
self.setWindowTitle("Random Parameters")
|
|
5961
6117
|
self.setModal(True)
|
|
5962
6118
|
|
|
5963
6119
|
layout = QFormLayout(self)
|
|
@@ -5990,6 +6146,79 @@ class RandomDialog(QDialog):
|
|
|
5990
6146
|
|
|
5991
6147
|
self.accept()
|
|
5992
6148
|
|
|
6149
|
+
class RandNodeDialog(QDialog):
|
|
6150
|
+
|
|
6151
|
+
def __init__(self, parent=None):
|
|
6152
|
+
|
|
6153
|
+
super().__init__(parent)
|
|
6154
|
+
self.setWindowTitle("Random Node Parameters")
|
|
6155
|
+
self.setModal(True)
|
|
6156
|
+
layout = QFormLayout(self)
|
|
6157
|
+
|
|
6158
|
+
|
|
6159
|
+
self.mode = QComboBox()
|
|
6160
|
+
self.mode.addItems(["Anywhere", "Within Dimensional Bounds of Nodes", "Within Masked Bounds of Edges", "Within Masked Bounds of Overlay1", "Within Masked Bounds of Overlay2"])
|
|
6161
|
+
self.mode.setCurrentIndex(0)
|
|
6162
|
+
layout.addRow("Mode", self.mode)
|
|
6163
|
+
|
|
6164
|
+
# Add Run button
|
|
6165
|
+
run_button = QPushButton("Get Random Nodes (Will go in Nodes)")
|
|
6166
|
+
run_button.clicked.connect(self.random)
|
|
6167
|
+
layout.addWidget(run_button)
|
|
6168
|
+
|
|
6169
|
+
def random(self):
|
|
6170
|
+
|
|
6171
|
+
try:
|
|
6172
|
+
|
|
6173
|
+
if my_network.node_centroids is None:
|
|
6174
|
+
self.parent().show_centroid_dialog()
|
|
6175
|
+
|
|
6176
|
+
bounds = None
|
|
6177
|
+
mask = None
|
|
6178
|
+
|
|
6179
|
+
mode = self.mode.currentIndex()
|
|
6180
|
+
|
|
6181
|
+
if mode == 0 and not (my_network.nodes is None and my_network.edges is None and my_network.network_overlay is None and my_network.id_overlay is None):
|
|
6182
|
+
pass
|
|
6183
|
+
elif mode == 1 or (my_network.nodes is None and my_network.edges is None and my_network.network_overlay is None and my_network.id_overlay is None):
|
|
6184
|
+
print("HELLO")
|
|
6185
|
+
# Convert string labels to integers if necessary
|
|
6186
|
+
if any(isinstance(k, str) for k in my_network.node_centroids.keys()):
|
|
6187
|
+
label_map = {label: idx for idx, label in enumerate(my_network.node_centroids.keys())}
|
|
6188
|
+
my_network.node_centroids = {label_map[k]: v for k, v in my_network.node_centroids.items()}
|
|
6189
|
+
|
|
6190
|
+
# Convert centroids to array and keep track of labels
|
|
6191
|
+
labels = np.array(list(my_network.node_centroids.keys()), dtype=np.uint32)
|
|
6192
|
+
centroid_points = np.array([my_network.node_centroids[label] for label in labels])
|
|
6193
|
+
|
|
6194
|
+
# Calculate shape if not provided
|
|
6195
|
+
max_coords = centroid_points.max(axis=0)
|
|
6196
|
+
max_shape = tuple(max_coord + 1 for max_coord in max_coords)
|
|
6197
|
+
min_coords = centroid_points.min(axis=0)
|
|
6198
|
+
min_shape = tuple(min_coord + 1 for min_coord in min_coords)
|
|
6199
|
+
bounds = (min_shape, max_shape)
|
|
6200
|
+
else:
|
|
6201
|
+
mask = n3d.binarize(self.parent().channel_data[mode - 1])
|
|
6202
|
+
|
|
6203
|
+
centroids, array = my_network.random_nodes(bounds = bounds, mask = mask)
|
|
6204
|
+
|
|
6205
|
+
if my_network.nodes is not None:
|
|
6206
|
+
try:
|
|
6207
|
+
self.parent().load_channel(0, array, data = True)
|
|
6208
|
+
except:
|
|
6209
|
+
pass
|
|
6210
|
+
|
|
6211
|
+
self.parent().format_for_upperright_table(my_network.node_centroids, 'NodeID', ['Z', 'Y', 'X'], 'Node Centroids')
|
|
6212
|
+
|
|
6213
|
+
except Exception as e:
|
|
6214
|
+
QMessageBox.critical(
|
|
6215
|
+
self,
|
|
6216
|
+
"Error:",
|
|
6217
|
+
f"Failed to randomize: {str(e)}"
|
|
6218
|
+
)
|
|
6219
|
+
print(f"Error: {e}")
|
|
6220
|
+
|
|
6221
|
+
|
|
5993
6222
|
class RadDialog(QDialog):
|
|
5994
6223
|
|
|
5995
6224
|
def __init__(self, parent=None):
|
|
@@ -6001,10 +6230,10 @@ class RadDialog(QDialog):
|
|
|
6001
6230
|
layout = QFormLayout(self)
|
|
6002
6231
|
|
|
6003
6232
|
# GPU checkbox (default False)
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6233
|
+
self.GPU = QPushButton("GPU")
|
|
6234
|
+
self.GPU.setCheckable(True)
|
|
6235
|
+
self.GPU.setChecked(False)
|
|
6236
|
+
layout.addRow("Use GPU:", self.GPU)
|
|
6008
6237
|
|
|
6009
6238
|
|
|
6010
6239
|
# Add Run button
|
|
@@ -6015,7 +6244,7 @@ class RadDialog(QDialog):
|
|
|
6015
6244
|
def rads(self):
|
|
6016
6245
|
|
|
6017
6246
|
try:
|
|
6018
|
-
|
|
6247
|
+
GPU = self.GPU.isChecked() # <- I can never get these to be faster than parallel CPU *shrugs*
|
|
6019
6248
|
|
|
6020
6249
|
active_data = self.parent().channel_data[self.parent().active_channel]
|
|
6021
6250
|
|
|
@@ -6042,7 +6271,7 @@ class InteractionDialog(QDialog):
|
|
|
6042
6271
|
def __init__(self, parent=None):
|
|
6043
6272
|
|
|
6044
6273
|
super().__init__(parent)
|
|
6045
|
-
self.setWindowTitle("
|
|
6274
|
+
self.setWindowTitle("Interaction Parameters")
|
|
6046
6275
|
self.setModal(True)
|
|
6047
6276
|
|
|
6048
6277
|
layout = QFormLayout(self)
|
|
@@ -6062,7 +6291,7 @@ class InteractionDialog(QDialog):
|
|
|
6062
6291
|
self.fastdil = QPushButton("Fast Dilate")
|
|
6063
6292
|
self.fastdil.setCheckable(True)
|
|
6064
6293
|
self.fastdil.setChecked(False)
|
|
6065
|
-
layout.addRow("
|
|
6294
|
+
layout.addRow("Use Fast Dilation (Higher speed, less accurate with search regions much larger than nodes):", self.fastdil)
|
|
6066
6295
|
|
|
6067
6296
|
# Add Run button
|
|
6068
6297
|
run_button = QPushButton("Calculate")
|
|
@@ -6117,7 +6346,7 @@ class DegreeDialog(QDialog):
|
|
|
6117
6346
|
layout.addRow("Execution Mode:", self.mode_selector)
|
|
6118
6347
|
|
|
6119
6348
|
self.mask_limiter = QLineEdit("1")
|
|
6120
|
-
layout.addRow("
|
|
6349
|
+
layout.addRow("Proportion of high degree nodes to keep (ignore if only returning degrees)", self.mask_limiter)
|
|
6121
6350
|
|
|
6122
6351
|
self.down_factor = QLineEdit("1")
|
|
6123
6352
|
layout.addRow("down_factor (for speeding up overlay generation - ignore if only returning degrees:", self.down_factor)
|
|
@@ -6662,6 +6891,116 @@ class ResizeDialog(QDialog):
|
|
|
6662
6891
|
QMessageBox.critical(self, "Error", f"Failed to resize: {str(e)}")
|
|
6663
6892
|
|
|
6664
6893
|
|
|
6894
|
+
class OverrideDialog(QDialog):
|
|
6895
|
+
def __init__(self, parent=None):
|
|
6896
|
+
super().__init__(parent)
|
|
6897
|
+
self.setWindowTitle("Override Parameters")
|
|
6898
|
+
self.setModal(True)
|
|
6899
|
+
|
|
6900
|
+
layout = QFormLayout(self)
|
|
6901
|
+
|
|
6902
|
+
layout.addRow(QLabel("Use Highlight Overlay to Place Data From: "))
|
|
6903
|
+
|
|
6904
|
+
# Add mode selection dropdown
|
|
6905
|
+
self.mode_selector = QComboBox()
|
|
6906
|
+
self.mode_selector.addItems(["Nodes", "Edges"])
|
|
6907
|
+
self.mode_selector.setCurrentIndex(0) # Default to Mode 1
|
|
6908
|
+
layout.addRow("Overrider:", self.mode_selector)
|
|
6909
|
+
|
|
6910
|
+
layout.addRow(QLabel("To Override Corresponding Data In: "))
|
|
6911
|
+
|
|
6912
|
+
# Add mode selection dropdown
|
|
6913
|
+
self.target_selector = QComboBox()
|
|
6914
|
+
self.target_selector.addItems(["Nodes", "Edges", "Overlay 1", "Overlay 2"])
|
|
6915
|
+
self.target_selector.setCurrentIndex(0) # Default to Mode 1
|
|
6916
|
+
layout.addRow("To be overwritten:", self.target_selector)
|
|
6917
|
+
|
|
6918
|
+
layout.addRow(QLabel("Place output in: "))
|
|
6919
|
+
|
|
6920
|
+
# Add mode selection dropdown
|
|
6921
|
+
self.output_selector = QComboBox()
|
|
6922
|
+
self.output_selector.addItems(["Nodes", "Edges", "Overlay 1", "Overlay 2", "Highlight Overlay"])
|
|
6923
|
+
self.output_selector.setCurrentIndex(0) # Default to Mode 1
|
|
6924
|
+
layout.addRow("Output Location:", self.output_selector)
|
|
6925
|
+
|
|
6926
|
+
# Add Run button
|
|
6927
|
+
run_button = QPushButton("Override")
|
|
6928
|
+
run_button.clicked.connect(self.override)
|
|
6929
|
+
layout.addWidget(run_button)
|
|
6930
|
+
|
|
6931
|
+
def override(self):
|
|
6932
|
+
|
|
6933
|
+
try:
|
|
6934
|
+
|
|
6935
|
+
accepted_mode = self.mode_selector.currentIndex()
|
|
6936
|
+
accepted_target = self.target_selector.currentIndex()
|
|
6937
|
+
output_target = self.output_selector.currentIndex()
|
|
6938
|
+
|
|
6939
|
+
if accepted_mode == accepted_target:
|
|
6940
|
+
return
|
|
6941
|
+
|
|
6942
|
+
active_data = self.parent().channel_data[accepted_mode]
|
|
6943
|
+
|
|
6944
|
+
if accepted_mode == 0:
|
|
6945
|
+
self.parent().create_highlight_overlay(node_indices=self.parent().clicked_values['nodes'])
|
|
6946
|
+
else:
|
|
6947
|
+
self.parent().create_highlight_overlay(edge_indices=self.parent().clicked_values['edges'])
|
|
6948
|
+
|
|
6949
|
+
target_data = self.parent().channel_data[accepted_target]
|
|
6950
|
+
|
|
6951
|
+
if target_data is None:
|
|
6952
|
+
target_data = np.zeros_like(active_data)
|
|
6953
|
+
|
|
6954
|
+
|
|
6955
|
+
|
|
6956
|
+
try:
|
|
6957
|
+
|
|
6958
|
+
self.parent().highlight_overlay = self.parent().highlight_overlay > 0 #What we want in override image
|
|
6959
|
+
inv = n3d.invert_boolean(self.parent().highlight_overlay) #what we want to keep in target image
|
|
6960
|
+
|
|
6961
|
+
target_data = target_data * inv #Cut out what we don't want in target image
|
|
6962
|
+
max_val = np.max(target_data) #Ensure non-val overlap
|
|
6963
|
+
other_max = np.max(active_data)
|
|
6964
|
+
true_max = max_val + other_max
|
|
6965
|
+
if true_max < 256:
|
|
6966
|
+
dtype = np.uint8
|
|
6967
|
+
elif true_max < 65536:
|
|
6968
|
+
dtype = np.uint16
|
|
6969
|
+
else:
|
|
6970
|
+
dtype = np.uint32
|
|
6971
|
+
|
|
6972
|
+
active_data = active_data.astype(dtype)
|
|
6973
|
+
|
|
6974
|
+
active_data = active_data + max_val #Transpose override image
|
|
6975
|
+
|
|
6976
|
+
active_data = self.parent().highlight_overlay * active_data #Cut out what we want from old image image
|
|
6977
|
+
|
|
6978
|
+
target_data = target_data.astype(dtype)
|
|
6979
|
+
|
|
6980
|
+
target_data = target_data + active_data #Insert new selection
|
|
6981
|
+
|
|
6982
|
+
if output_target == 4:
|
|
6983
|
+
|
|
6984
|
+
self.parent().highlight_overlay = result
|
|
6985
|
+
|
|
6986
|
+
else:
|
|
6987
|
+
|
|
6988
|
+
|
|
6989
|
+
# Update both the display data and the network object
|
|
6990
|
+
self.parent().load_channel(output_target, channel_data = target_data, data = True)
|
|
6991
|
+
|
|
6992
|
+
self.parent().update_display()
|
|
6993
|
+
|
|
6994
|
+
self.accept()
|
|
6995
|
+
|
|
6996
|
+
except Exception as e:
|
|
6997
|
+
print(f"Error overriding: {e}")
|
|
6998
|
+
|
|
6999
|
+
except Exception as e:
|
|
7000
|
+
print(f"Error overriding: {e}")
|
|
7001
|
+
|
|
7002
|
+
|
|
7003
|
+
|
|
6665
7004
|
class BinarizeDialog(QDialog):
|
|
6666
7005
|
def __init__(self, parent=None):
|
|
6667
7006
|
super().__init__(parent)
|
|
@@ -6923,7 +7262,7 @@ class ThresholdDialog(QDialog):
|
|
|
6923
7262
|
try:
|
|
6924
7263
|
import cupy as cp
|
|
6925
7264
|
except:
|
|
6926
|
-
print("Cupy import failed, using CPU version")
|
|
7265
|
+
#print("Cupy import failed, using CPU version")
|
|
6927
7266
|
GPU = False
|
|
6928
7267
|
|
|
6929
7268
|
if self.parent().mini_overlay_data is not None:
|
|
@@ -6983,8 +7322,12 @@ class MachineWindow(QMainWindow):
|
|
|
6983
7322
|
active_data = self.parent().channel_data[1]
|
|
6984
7323
|
act_channel = 1
|
|
6985
7324
|
|
|
7325
|
+
try:
|
|
7326
|
+
array1 = np.zeros_like(active_data).astype(np.uint8)
|
|
7327
|
+
except:
|
|
7328
|
+
print("No data in nodes channel")
|
|
7329
|
+
return
|
|
6986
7330
|
|
|
6987
|
-
array1 = np.zeros_like(active_data).astype(np.uint8)
|
|
6988
7331
|
array3 = np.zeros_like(active_data).astype(np.uint8)
|
|
6989
7332
|
self.parent().highlight_overlay = array3 #Clear this out for the segmenter to use
|
|
6990
7333
|
|
|
@@ -7065,8 +7408,14 @@ class MachineWindow(QMainWindow):
|
|
|
7065
7408
|
train_quick.clicked.connect(lambda: self.train_model(speed=True))
|
|
7066
7409
|
train_detailed = QPushButton("Train More Detailed Model")
|
|
7067
7410
|
train_detailed.clicked.connect(lambda: self.train_model(speed=False))
|
|
7411
|
+
save = QPushButton("Save Model")
|
|
7412
|
+
save.clicked.connect(self.save_model)
|
|
7413
|
+
load = QPushButton("Load Model")
|
|
7414
|
+
load.clicked.connect(self.load_model)
|
|
7068
7415
|
training_layout.addWidget(train_quick)
|
|
7069
7416
|
training_layout.addWidget(train_detailed)
|
|
7417
|
+
training_layout.addWidget(save)
|
|
7418
|
+
training_layout.addWidget(load)
|
|
7070
7419
|
training_group.setLayout(training_layout)
|
|
7071
7420
|
|
|
7072
7421
|
# Group 4: Segmentation Options
|
|
@@ -7137,6 +7486,33 @@ class MachineWindow(QMainWindow):
|
|
|
7137
7486
|
except:
|
|
7138
7487
|
pass
|
|
7139
7488
|
|
|
7489
|
+
def save_model(self):
|
|
7490
|
+
|
|
7491
|
+
filename, _ = QFileDialog.getSaveFileName(
|
|
7492
|
+
self,
|
|
7493
|
+
f"Save Model As",
|
|
7494
|
+
"", # Default directory
|
|
7495
|
+
"numpy data (*.npz);;All Files (*)" # File type filter
|
|
7496
|
+
)
|
|
7497
|
+
|
|
7498
|
+
if filename: # Only proceed if user didn't cancel
|
|
7499
|
+
# If user didn't type an extension, add .tif
|
|
7500
|
+
if not filename.endswith(('.npz')):
|
|
7501
|
+
filename += '.npz'
|
|
7502
|
+
|
|
7503
|
+
self.segmenter.save_model(filename, self.parent().channel_data[2])
|
|
7504
|
+
|
|
7505
|
+
def load_model(self):
|
|
7506
|
+
|
|
7507
|
+
filename, _ = QFileDialog.getOpenFileName(
|
|
7508
|
+
self,
|
|
7509
|
+
f"Load Model",
|
|
7510
|
+
"",
|
|
7511
|
+
"numpy data (*.npz)"
|
|
7512
|
+
)
|
|
7513
|
+
|
|
7514
|
+
self.segmenter.load_model(filename)
|
|
7515
|
+
self.trained = True
|
|
7140
7516
|
|
|
7141
7517
|
def toggle_two(self):
|
|
7142
7518
|
if self.two.isChecked():
|
|
@@ -7227,6 +7603,7 @@ class MachineWindow(QMainWindow):
|
|
|
7227
7603
|
)
|
|
7228
7604
|
|
|
7229
7605
|
|
|
7606
|
+
|
|
7230
7607
|
def start_segmentation(self):
|
|
7231
7608
|
|
|
7232
7609
|
self.kill_segmentation()
|
|
@@ -7255,7 +7632,11 @@ class MachineWindow(QMainWindow):
|
|
|
7255
7632
|
self.segmentation_worker.finished.connect(self.segmentation_finished)
|
|
7256
7633
|
current_xlim = self.parent().ax.get_xlim()
|
|
7257
7634
|
current_ylim = self.parent().ax.get_ylim()
|
|
7258
|
-
|
|
7635
|
+
try:
|
|
7636
|
+
x, y = self.parent().get_current_mouse_position()
|
|
7637
|
+
except:
|
|
7638
|
+
x, y = 0, 0
|
|
7639
|
+
self.segmenter.update_position(self.parent().current_slice, x, y)
|
|
7259
7640
|
self.segmentation_worker.start()
|
|
7260
7641
|
|
|
7261
7642
|
def confirm_seg_dialog(self):
|
|
@@ -7321,7 +7702,12 @@ class MachineWindow(QMainWindow):
|
|
|
7321
7702
|
current_xlim = self.parent().ax.get_xlim()
|
|
7322
7703
|
current_ylim = self.parent().ax.get_ylim()
|
|
7323
7704
|
|
|
7324
|
-
|
|
7705
|
+
try:
|
|
7706
|
+
x, y = self.parent().get_current_mouse_position()
|
|
7707
|
+
except:
|
|
7708
|
+
x, y = 0, 0
|
|
7709
|
+
self.segmenter.update_position(self.parent().current_slice, x, y)
|
|
7710
|
+
|
|
7325
7711
|
if not self.parent().painting:
|
|
7326
7712
|
# Only update if view limits are valid
|
|
7327
7713
|
self.parent().update_display(preserve_zoom=(current_xlim, current_ylim))
|
|
@@ -7616,6 +8002,8 @@ class ThresholdWindow(QMainWindow):
|
|
|
7616
8002
|
self.bounds = True
|
|
7617
8003
|
self.parent().bounds = True
|
|
7618
8004
|
|
|
8005
|
+
self.chan = self.parent().active_channel
|
|
8006
|
+
|
|
7619
8007
|
|
|
7620
8008
|
# Create matplotlib figure
|
|
7621
8009
|
fig = Figure(figsize=(5, 4))
|
|
@@ -7684,6 +8072,45 @@ class ThresholdWindow(QMainWindow):
|
|
|
7684
8072
|
self.parent().preview = False
|
|
7685
8073
|
self.parent().targs = None
|
|
7686
8074
|
self.parent().bounds = False
|
|
8075
|
+
try: # could probably be refactored but this just handles keeping the highlight elements if the user presses X
|
|
8076
|
+
if self.chan == 0:
|
|
8077
|
+
if not self.bounds:
|
|
8078
|
+
self.parent().clicked_values['nodes'] = self.get_values_in_range_all_vols(self.chan, float(self.min.text()), float(self.max.text()))
|
|
8079
|
+
else:
|
|
8080
|
+
vals = np.unique(self.parent().channel_data[self.chan])
|
|
8081
|
+
self.parent().clicked_values['nodes'] = (vals[(vals >= float(self.min.text())) & (vals <= float(self.max.text()))]).tolist()
|
|
8082
|
+
|
|
8083
|
+
if self.parent().channel_data[0].shape[0] * self.parent().channel_data[0].shape[1] * self.parent().channel_data[0].shape[2] > self.parent().mini_thresh:
|
|
8084
|
+
self.parent().mini_overlay = True
|
|
8085
|
+
self.parent().create_mini_overlay(node_indices = self.parent().clicked_values['nodes'])
|
|
8086
|
+
else:
|
|
8087
|
+
self.parent().create_highlight_overlay(
|
|
8088
|
+
node_indices=self.parent().clicked_values['nodes']
|
|
8089
|
+
)
|
|
8090
|
+
elif self.chan == 1:
|
|
8091
|
+
if not self.bounds:
|
|
8092
|
+
self.parent().clicked_values['edges'] = self.get_values_in_range_all_vols(self.chan, float(self.min.text()), float(self.max.text()))
|
|
8093
|
+
else:
|
|
8094
|
+
vals = np.unique(self.parent().channel_data[self.chan])
|
|
8095
|
+
self.parent().clicked_values['edges'] = (vals[(vals >= float(self.min.text())) & (vals <= float(self.max.text()))]).tolist()
|
|
8096
|
+
|
|
8097
|
+
if self.parent().channel_data[1].shape[0] * self.parent().channel_data[1].shape[1] * self.parent().channel_data[1].shape[2] > self.parent().mini_thresh:
|
|
8098
|
+
self.parent().mini_overlay = True
|
|
8099
|
+
self.parent().create_mini_overlay(edge_indices = self.parent().clicked_values['edges'])
|
|
8100
|
+
else:
|
|
8101
|
+
self.parent().create_highlight_overlay(
|
|
8102
|
+
node_indices=self.parent().clicked_values['edges']
|
|
8103
|
+
)
|
|
8104
|
+
except:
|
|
8105
|
+
pass
|
|
8106
|
+
|
|
8107
|
+
|
|
8108
|
+
def get_values_in_range_all_vols(self, chan, min_val, max_val):
|
|
8109
|
+
output = []
|
|
8110
|
+
for node, vol in self.parent().volume_dict[chan].items():
|
|
8111
|
+
if min_val <= vol <= max_val:
|
|
8112
|
+
output.append(node)
|
|
8113
|
+
return output
|
|
7687
8114
|
|
|
7688
8115
|
def get_values_in_range(self, lst, min_val, max_val):
|
|
7689
8116
|
values = [x for x in lst if min_val <= x <= max_val]
|
|
@@ -7882,10 +8309,10 @@ class SmartDilateDialog(QDialog):
|
|
|
7882
8309
|
layout.addRow("Use GPU:", self.GPU)
|
|
7883
8310
|
|
|
7884
8311
|
# dt checkbox (default False)
|
|
7885
|
-
self.predt = QPushButton("
|
|
8312
|
+
self.predt = QPushButton("Fast Dilation")
|
|
7886
8313
|
self.predt.setCheckable(True)
|
|
7887
8314
|
self.predt.setChecked(False)
|
|
7888
|
-
layout.addRow("Use
|
|
8315
|
+
layout.addRow("Use Fast Dilation (Higher speed, less accurate with search regions much larger than nodes):", self.predt)
|
|
7889
8316
|
|
|
7890
8317
|
self.down_factor = QLineEdit("")
|
|
7891
8318
|
layout.addRow("Internal Downsample for GPU (if needed):", self.down_factor)
|
|
@@ -7901,7 +8328,7 @@ class SmartDilateDialog(QDialog):
|
|
|
7901
8328
|
|
|
7902
8329
|
GPU = self.GPU.isChecked()
|
|
7903
8330
|
down_factor = float(self.down_factor.text()) if self.down_factor.text().strip() else None
|
|
7904
|
-
predt =
|
|
8331
|
+
predt = self.predt.isChecked()
|
|
7905
8332
|
active_data, amount, xy_scale, z_scale = self.params
|
|
7906
8333
|
|
|
7907
8334
|
dilate_xy, dilate_z = n3d.dilation_length_to_pixels(xy_scale, z_scale, amount, amount)
|
|
@@ -8000,7 +8427,8 @@ class DilateDialog(QDialog):
|
|
|
8000
8427
|
active_data,
|
|
8001
8428
|
amount,
|
|
8002
8429
|
xy_scale = xy_scale,
|
|
8003
|
-
z_scale = z_scale
|
|
8430
|
+
z_scale = z_scale,
|
|
8431
|
+
fast_dil = True)
|
|
8004
8432
|
|
|
8005
8433
|
result = result * 255
|
|
8006
8434
|
|
|
@@ -8250,6 +8678,7 @@ class MaskDialog(QDialog):
|
|
|
8250
8678
|
print(f"Error masking: {e}")
|
|
8251
8679
|
|
|
8252
8680
|
|
|
8681
|
+
|
|
8253
8682
|
class TypeDialog(QDialog):
|
|
8254
8683
|
|
|
8255
8684
|
def __init__(self, parent=None):
|
|
@@ -8268,7 +8697,7 @@ class TypeDialog(QDialog):
|
|
|
8268
8697
|
|
|
8269
8698
|
# Add mode selection dropdown
|
|
8270
8699
|
self.mode_selector = QComboBox()
|
|
8271
|
-
self.mode_selector.addItems(["8bit
|
|
8700
|
+
self.mode_selector.addItems(["8bit uint", "16bit uint", "32bit uint", "32bit float", "64bit float"])
|
|
8272
8701
|
self.mode_selector.setCurrentIndex(0) # Default to Mode 1
|
|
8273
8702
|
layout.addRow("Change to?:", self.mode_selector)
|
|
8274
8703
|
|
|
@@ -8279,33 +8708,38 @@ class TypeDialog(QDialog):
|
|
|
8279
8708
|
|
|
8280
8709
|
def run_type(self, active_data):
|
|
8281
8710
|
|
|
8282
|
-
|
|
8711
|
+
try:
|
|
8283
8712
|
|
|
8284
|
-
|
|
8713
|
+
mode = self.mode_selector.currentIndex()
|
|
8285
8714
|
|
|
8286
|
-
|
|
8715
|
+
if mode == 0:
|
|
8287
8716
|
|
|
8288
|
-
|
|
8717
|
+
active_data = active_data.astype(np.uint8)
|
|
8289
8718
|
|
|
8290
|
-
|
|
8719
|
+
elif mode == 1:
|
|
8291
8720
|
|
|
8292
|
-
|
|
8721
|
+
active_data = active_data.astype(np.uint16)
|
|
8293
8722
|
|
|
8294
|
-
|
|
8723
|
+
elif mode == 2:
|
|
8295
8724
|
|
|
8296
|
-
|
|
8725
|
+
active_data = active_data.astype(np.uint32)
|
|
8297
8726
|
|
|
8298
|
-
|
|
8727
|
+
elif mode == 3:
|
|
8299
8728
|
|
|
8300
|
-
|
|
8729
|
+
active_data = active_data.astype(np.float32)
|
|
8301
8730
|
|
|
8302
|
-
|
|
8731
|
+
elif mode == 4:
|
|
8303
8732
|
|
|
8304
|
-
|
|
8733
|
+
active_data = active_data.astype(np.float64)
|
|
8305
8734
|
|
|
8735
|
+
self.parent().load_channel(self.active_chan, active_data, True)
|
|
8306
8736
|
|
|
8307
|
-
|
|
8308
|
-
|
|
8737
|
+
|
|
8738
|
+
print(f"Channel {self.active_chan}) dtype now: {self.parent().channel_data[self.active_chan].dtype}")
|
|
8739
|
+
self.accept()
|
|
8740
|
+
|
|
8741
|
+
except Exception as E:
|
|
8742
|
+
print(f"Error: {e}")
|
|
8309
8743
|
|
|
8310
8744
|
|
|
8311
8745
|
|
|
@@ -8423,7 +8857,7 @@ class WatershedDialog(QDialog):
|
|
|
8423
8857
|
# Predownsample (empty by default)
|
|
8424
8858
|
self.predownsample = QLineEdit()
|
|
8425
8859
|
self.predownsample.setPlaceholderText("Leave empty for None")
|
|
8426
|
-
layout.addRow("
|
|
8860
|
+
layout.addRow("Kernel Obtainment GPU Downsample:", self.predownsample)
|
|
8427
8861
|
|
|
8428
8862
|
# Predownsample2 (empty by default)
|
|
8429
8863
|
self.predownsample2 = QLineEdit()
|
|
@@ -8658,9 +9092,9 @@ class GenNodesDialog(QDialog):
|
|
|
8658
9092
|
layout = QFormLayout(self)
|
|
8659
9093
|
self.called = called
|
|
8660
9094
|
|
|
8661
|
-
self.directory = QLineEdit()
|
|
8662
|
-
self.directory.setPlaceholderText("Leave empty to save in active dir")
|
|
8663
|
-
layout.addRow("Output Directory:", self.directory)
|
|
9095
|
+
#self.directory = QLineEdit()
|
|
9096
|
+
#self.directory.setPlaceholderText("Leave empty to save in active dir")
|
|
9097
|
+
#layout.addRow("Output Directory:", self.directory)
|
|
8664
9098
|
|
|
8665
9099
|
if not down_factor:
|
|
8666
9100
|
down_factor = None
|
|
@@ -8670,7 +9104,7 @@ class GenNodesDialog(QDialog):
|
|
|
8670
9104
|
self.cubic = QPushButton("Cubic Downsample")
|
|
8671
9105
|
self.cubic.setCheckable(True)
|
|
8672
9106
|
self.cubic.setChecked(False)
|
|
8673
|
-
layout.addRow("(if downsampling): Use cubic
|
|
9107
|
+
layout.addRow("(if downsampling): Use cubic downsample? (Slower but can preserve structure better)", self.cubic)
|
|
8674
9108
|
else:
|
|
8675
9109
|
self.down_factor = down_factor[0]
|
|
8676
9110
|
self.cubic = down_factor[1]
|
|
@@ -8714,7 +9148,7 @@ class GenNodesDialog(QDialog):
|
|
|
8714
9148
|
|
|
8715
9149
|
try:
|
|
8716
9150
|
# Get directory (None if empty)
|
|
8717
|
-
directory = self.directory.text() if self.directory.text() else None
|
|
9151
|
+
#directory = self.directory.text() if self.directory.text() else None
|
|
8718
9152
|
|
|
8719
9153
|
# Get branch_removal
|
|
8720
9154
|
try:
|
|
@@ -8860,7 +9294,7 @@ class BranchDialog(QDialog):
|
|
|
8860
9294
|
self.cubic = QPushButton("Cubic Downsample")
|
|
8861
9295
|
self.cubic.setCheckable(True)
|
|
8862
9296
|
self.cubic.setChecked(False)
|
|
8863
|
-
layout.addRow("(if downsampling): Use cubic
|
|
9297
|
+
layout.addRow("(if downsampling): Use cubic downsample? (Slower but can preserve structure better)", self.cubic)
|
|
8864
9298
|
|
|
8865
9299
|
# Add Run button
|
|
8866
9300
|
run_button = QPushButton("Run Branch Label")
|
|
@@ -9283,20 +9717,23 @@ class CentroidDialog(QDialog):
|
|
|
9283
9717
|
my_network.calculate_node_centroids(
|
|
9284
9718
|
down_factor = downsample
|
|
9285
9719
|
)
|
|
9286
|
-
|
|
9720
|
+
if directory:
|
|
9721
|
+
my_network.save_node_centroids(directory = directory)
|
|
9287
9722
|
|
|
9288
9723
|
elif chan == 2:
|
|
9289
9724
|
my_network.calculate_edge_centroids(
|
|
9290
9725
|
down_factor = downsample
|
|
9291
9726
|
)
|
|
9292
|
-
|
|
9727
|
+
if directory:
|
|
9728
|
+
my_network.save_edge_centroids(directory = directory)
|
|
9293
9729
|
|
|
9294
9730
|
elif chan == 0:
|
|
9295
9731
|
try:
|
|
9296
9732
|
my_network.calculate_node_centroids(
|
|
9297
9733
|
down_factor = downsample
|
|
9298
9734
|
)
|
|
9299
|
-
|
|
9735
|
+
if directory:
|
|
9736
|
+
my_network.save_node_centroids(directory = directory)
|
|
9300
9737
|
except:
|
|
9301
9738
|
pass
|
|
9302
9739
|
|
|
@@ -9305,7 +9742,8 @@ class CentroidDialog(QDialog):
|
|
|
9305
9742
|
my_network.calculate_edge_centroids(
|
|
9306
9743
|
down_factor = downsample
|
|
9307
9744
|
)
|
|
9308
|
-
|
|
9745
|
+
if directory:
|
|
9746
|
+
my_network.save_edge_centroids(directory = directory)
|
|
9309
9747
|
|
|
9310
9748
|
except:
|
|
9311
9749
|
pass
|
|
@@ -9346,7 +9784,7 @@ class CalcAllDialog(QDialog):
|
|
|
9346
9784
|
prev_GPU_downsample = ""
|
|
9347
9785
|
prev_other_nodes = ""
|
|
9348
9786
|
prev_remove_trunk = ""
|
|
9349
|
-
prev_gpu =
|
|
9787
|
+
prev_gpu = False
|
|
9350
9788
|
prev_label_nodes = True
|
|
9351
9789
|
prev_inners = True
|
|
9352
9790
|
prev_fastdil = False
|
|
@@ -9378,7 +9816,7 @@ class CalcAllDialog(QDialog):
|
|
|
9378
9816
|
|
|
9379
9817
|
self.diledge = QLineEdit(self.prev_diledge)
|
|
9380
9818
|
self.diledge.setPlaceholderText("Leave empty for None")
|
|
9381
|
-
layout.addRow("Edge Reconnection Distance (
|
|
9819
|
+
layout.addRow("Edge Reconnection Distance (float):", self.diledge)
|
|
9382
9820
|
|
|
9383
9821
|
self.down_factor = QLineEdit(self.prev_down_factor)
|
|
9384
9822
|
self.down_factor.setPlaceholderText("Leave empty for None")
|
|
@@ -9405,7 +9843,7 @@ class CalcAllDialog(QDialog):
|
|
|
9405
9843
|
self.label_nodes = QPushButton("Label")
|
|
9406
9844
|
self.label_nodes.setCheckable(True)
|
|
9407
9845
|
self.label_nodes.setChecked(self.prev_label_nodes)
|
|
9408
|
-
layout.addRow("Label Nodes:", self.label_nodes)
|
|
9846
|
+
layout.addRow("Re-Label Nodes (WARNING - OVERRIDES ANY CURRENT LABELS):", self.label_nodes)
|
|
9409
9847
|
|
|
9410
9848
|
self.inners = QPushButton("Inner Edges")
|
|
9411
9849
|
self.inners.setCheckable(True)
|
|
@@ -9744,13 +10182,13 @@ class ProxDialog(QDialog):
|
|
|
9744
10182
|
return
|
|
9745
10183
|
|
|
9746
10184
|
if populate:
|
|
9747
|
-
my_network.nodes = my_network.kd_network(distance = search, targets = targets)
|
|
10185
|
+
my_network.nodes = my_network.kd_network(distance = search, targets = targets, make_array = True)
|
|
9748
10186
|
self.parent().load_channel(0, channel_data = my_network.nodes, data = True)
|
|
9749
10187
|
else:
|
|
9750
10188
|
my_network.kd_network(distance = search, targets = targets)
|
|
9751
10189
|
|
|
9752
|
-
|
|
9753
|
-
|
|
10190
|
+
if directory is not None:
|
|
10191
|
+
my_network.dump(directory = directory)
|
|
9754
10192
|
|
|
9755
10193
|
|
|
9756
10194
|
# Then handle overlays
|