nettracer3d 0.7.4__py3-none-any.whl → 0.7.5__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 +17 -39
- nettracer3d/modularity.py +41 -264
- nettracer3d/nettracer.py +7 -94
- nettracer3d/nettracer_gui.py +407 -204
- nettracer3d/network_analysis.py +0 -178
- nettracer3d/segmenter.py +9 -56
- nettracer3d/segmenter_GPU.py +856 -181
- nettracer3d/simple_network.py +2 -150
- {nettracer3d-0.7.4.dist-info → nettracer3d-0.7.5.dist-info}/METADATA +7 -5
- nettracer3d-0.7.5.dist-info/RECORD +21 -0
- {nettracer3d-0.7.4.dist-info → nettracer3d-0.7.5.dist-info}/WHEEL +1 -1
- nettracer3d/segmenter - Copy.py +0 -2097
- nettracer3d-0.7.4.dist-info/RECORD +0 -22
- {nettracer3d-0.7.4.dist-info → nettracer3d-0.7.5.dist-info}/entry_points.txt +0 -0
- {nettracer3d-0.7.4.dist-info → nettracer3d-0.7.5.dist-info}/licenses/LICENSE +0 -0
- {nettracer3d-0.7.4.dist-info → nettracer3d-0.7.5.dist-info}/top_level.txt +0 -0
nettracer3d/nettracer_gui.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
import networkx as nx
|
|
3
|
-
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
|
|
3
|
+
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QGridLayout,
|
|
4
4
|
QHBoxLayout, QSlider, QMenuBar, QMenu, QDialog,
|
|
5
5
|
QFormLayout, QLineEdit, QPushButton, QFileDialog,
|
|
6
6
|
QLabel, QComboBox, QMessageBox, QTableView, QInputDialog,
|
|
@@ -1311,22 +1311,40 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1311
1311
|
info_dict['Object Class'] = 'Node'
|
|
1312
1312
|
|
|
1313
1313
|
if my_network.node_identities is not None:
|
|
1314
|
-
|
|
1314
|
+
try:
|
|
1315
|
+
info_dict['ID'] = my_network.node_identities[label]
|
|
1316
|
+
except:
|
|
1317
|
+
pass
|
|
1315
1318
|
|
|
1316
1319
|
if my_network.network is not None:
|
|
1317
|
-
|
|
1320
|
+
try:
|
|
1321
|
+
info_dict['Degree'] = my_network.network.degree(label)
|
|
1322
|
+
except:
|
|
1323
|
+
pass
|
|
1318
1324
|
|
|
1319
1325
|
if my_network.communities is not None:
|
|
1320
|
-
|
|
1326
|
+
try:
|
|
1327
|
+
info_dict['Community'] = my_network.communities[label]
|
|
1328
|
+
except:
|
|
1329
|
+
pass
|
|
1321
1330
|
|
|
1322
1331
|
if my_network.node_centroids is not None:
|
|
1323
|
-
|
|
1332
|
+
try:
|
|
1333
|
+
info_dict['Centroid'] = my_network.node_centroids[label]
|
|
1334
|
+
except:
|
|
1335
|
+
pass
|
|
1324
1336
|
|
|
1325
1337
|
if self.volume_dict[0] is not None:
|
|
1326
|
-
|
|
1338
|
+
try:
|
|
1339
|
+
info_dict['Volume (Scaled)'] = self.volume_dict[0][label]
|
|
1340
|
+
except:
|
|
1341
|
+
pass
|
|
1327
1342
|
|
|
1328
1343
|
if self.radii_dict[0] is not None:
|
|
1329
|
-
|
|
1344
|
+
try:
|
|
1345
|
+
info_dict['Max Radius (Scaled)'] = self.radii_dict[0][label]
|
|
1346
|
+
except:
|
|
1347
|
+
pass
|
|
1330
1348
|
|
|
1331
1349
|
|
|
1332
1350
|
elif sort == 'edge':
|
|
@@ -1338,13 +1356,22 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1338
1356
|
info_dict['Object Class'] = 'Edge'
|
|
1339
1357
|
|
|
1340
1358
|
if my_network.edge_centroids is not None:
|
|
1341
|
-
|
|
1359
|
+
try:
|
|
1360
|
+
info_dict['Centroid'] = my_network.edge_centroids[label]
|
|
1361
|
+
except:
|
|
1362
|
+
pass
|
|
1342
1363
|
|
|
1343
1364
|
if self.volume_dict[1] is not None:
|
|
1344
|
-
|
|
1365
|
+
try:
|
|
1366
|
+
info_dict['Volume (Scaled)'] = self.volume_dict[1][label]
|
|
1367
|
+
except:
|
|
1368
|
+
pass
|
|
1345
1369
|
|
|
1346
1370
|
if self.radii_dict[1] is not None:
|
|
1347
|
-
|
|
1371
|
+
try:
|
|
1372
|
+
info_dict['~Radius (Scaled)'] = self.radii_dict[1][label]
|
|
1373
|
+
except:
|
|
1374
|
+
pass
|
|
1348
1375
|
|
|
1349
1376
|
self.format_for_upperright_table(info_dict, title = f'Info on Object')
|
|
1350
1377
|
|
|
@@ -1353,7 +1380,6 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1353
1380
|
|
|
1354
1381
|
|
|
1355
1382
|
|
|
1356
|
-
|
|
1357
1383
|
def handle_combine(self):
|
|
1358
1384
|
|
|
1359
1385
|
try:
|
|
@@ -1416,12 +1442,67 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1416
1442
|
print(f"An error has occured: {e}")
|
|
1417
1443
|
|
|
1418
1444
|
def handle_seperate(self):
|
|
1419
|
-
|
|
1445
|
+
|
|
1446
|
+
import scipy.ndimage as ndi
|
|
1447
|
+
from scipy.sparse import csr_matrix
|
|
1448
|
+
|
|
1449
|
+
print("Note, this method is a tad slow...")
|
|
1450
|
+
|
|
1451
|
+
def separate_nontouching_objects(input_array):
|
|
1452
|
+
"""
|
|
1453
|
+
Efficiently separate non-touching objects in a labeled array.
|
|
1454
|
+
|
|
1455
|
+
Parameters:
|
|
1456
|
+
-----------
|
|
1457
|
+
input_array : numpy.ndarray
|
|
1458
|
+
Input labeled array where each object has a unique label value > 0
|
|
1459
|
+
|
|
1460
|
+
Returns:
|
|
1461
|
+
--------
|
|
1462
|
+
output_array : numpy.ndarray
|
|
1463
|
+
Array with new labels where non-touching components have different labels
|
|
1464
|
+
"""
|
|
1465
|
+
# Step 1: Perform connected component labeling on the entire binary mask
|
|
1466
|
+
binary_mask = input_array > 0
|
|
1467
|
+
structure = np.ones((3,) * input_array.ndim, dtype=bool) # 3x3x3 connectivity for 3D or 3x3 for 2D
|
|
1468
|
+
labeled_array, num_features = ndi.label(binary_mask, structure=structure)
|
|
1469
|
+
|
|
1470
|
+
# Step 2: Map the original labels to the new connected components
|
|
1471
|
+
# Create a sparse matrix to efficiently store label mappings
|
|
1472
|
+
coords = np.nonzero(input_array)
|
|
1473
|
+
original_values = input_array[coords]
|
|
1474
|
+
new_labels = labeled_array[coords]
|
|
1475
|
+
|
|
1476
|
+
# Create a mapping of (original_label, new_connected_component) pairs
|
|
1477
|
+
label_mapping = {}
|
|
1478
|
+
for orig, new in zip(original_values, new_labels):
|
|
1479
|
+
if orig not in label_mapping:
|
|
1480
|
+
label_mapping[orig] = []
|
|
1481
|
+
if new not in label_mapping[orig]:
|
|
1482
|
+
label_mapping[orig].append(new)
|
|
1483
|
+
|
|
1484
|
+
# Step 3: Create a new output array with unique labels for each connected component
|
|
1485
|
+
output_array = np.zeros_like(input_array)
|
|
1486
|
+
next_label = 1
|
|
1487
|
+
|
|
1488
|
+
# Map of (original_label, connected_component) -> new_unique_label
|
|
1489
|
+
unique_label_map = {}
|
|
1490
|
+
|
|
1491
|
+
for orig_label, cc_list in label_mapping.items():
|
|
1492
|
+
for cc in cc_list:
|
|
1493
|
+
unique_label_map[(orig_label, cc)] = next_label
|
|
1494
|
+
# Create a mask for this original label and connected component
|
|
1495
|
+
mask = (input_array == orig_label) & (labeled_array == cc)
|
|
1496
|
+
# Assign the new unique label
|
|
1497
|
+
output_array[mask] = next_label
|
|
1498
|
+
next_label += 1
|
|
1499
|
+
|
|
1500
|
+
return output_array
|
|
1501
|
+
|
|
1420
1502
|
try:
|
|
1421
1503
|
# Handle nodes
|
|
1422
1504
|
if len(self.clicked_values['nodes']) > 0:
|
|
1423
1505
|
self.create_highlight_overlay(node_indices=self.clicked_values['nodes'])
|
|
1424
|
-
max_val = np.max(my_network.nodes) + 1
|
|
1425
1506
|
|
|
1426
1507
|
# Create a boolean mask for highlighted values
|
|
1427
1508
|
self.highlight_overlay = self.highlight_overlay != 0
|
|
@@ -1429,56 +1510,21 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1429
1510
|
# Create array with just the highlighted values
|
|
1430
1511
|
highlighted_nodes = self.highlight_overlay * my_network.nodes
|
|
1431
1512
|
|
|
1432
|
-
# Get
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
val_mask = my_network.nodes == val
|
|
1441
|
-
|
|
1442
|
-
# Create an array without this value
|
|
1443
|
-
temp = my_network.nodes - (val_mask * val)
|
|
1444
|
-
|
|
1445
|
-
# Label the connected components for this value
|
|
1446
|
-
labeled_mask, num_components = n3d.label_objects(val_mask)
|
|
1447
|
-
|
|
1448
|
-
if num_components > 1:
|
|
1449
|
-
# Set appropriate dtype based on max value
|
|
1450
|
-
if max_val + num_components < 256:
|
|
1451
|
-
dtype = np.uint8
|
|
1452
|
-
elif max_val + num_components < 65536:
|
|
1453
|
-
dtype = np.uint16
|
|
1454
|
-
labeled_mask = labeled_mask.astype(dtype)
|
|
1455
|
-
temp = temp.astype(dtype)
|
|
1456
|
-
else:
|
|
1457
|
-
dtype = np.uint32
|
|
1458
|
-
labeled_mask = labeled_mask.astype(dtype)
|
|
1459
|
-
temp = temp.astype(dtype)
|
|
1460
|
-
|
|
1461
|
-
# Add new labels to the temporary array
|
|
1462
|
-
mask_nonzero = labeled_mask != 0
|
|
1463
|
-
labeled_mask = labeled_mask + max_val - 1 # -1 because we'll restore the first component
|
|
1464
|
-
labeled_mask = labeled_mask * mask_nonzero
|
|
1465
|
-
|
|
1466
|
-
# Restore original value for first component
|
|
1467
|
-
first_component = labeled_mask == max_val
|
|
1468
|
-
labeled_mask = labeled_mask - (first_component * (max_val - val))
|
|
1469
|
-
|
|
1470
|
-
# Add labeled components back to the array
|
|
1471
|
-
my_network.nodes = temp + labeled_mask
|
|
1472
|
-
|
|
1473
|
-
# Update max value for next iteration
|
|
1474
|
-
max_val += num_components - 1 # -1 because we kept one original label
|
|
1513
|
+
# Get non-highlighted part of the array
|
|
1514
|
+
non_highlighted = my_network.nodes * (~self.highlight_overlay)
|
|
1515
|
+
|
|
1516
|
+
# Process highlighted part
|
|
1517
|
+
processed_highlights = separate_nontouching_objects(highlighted_nodes)
|
|
1518
|
+
|
|
1519
|
+
# Combine back with non-highlighted parts
|
|
1520
|
+
my_network.nodes = non_highlighted + processed_highlights
|
|
1475
1521
|
|
|
1476
1522
|
self.load_channel(0, my_network.nodes, True)
|
|
1477
1523
|
|
|
1478
1524
|
# Handle edges
|
|
1479
1525
|
if len(self.clicked_values['edges']) > 0:
|
|
1526
|
+
|
|
1480
1527
|
self.create_highlight_overlay(edge_indices=self.clicked_values['edges'])
|
|
1481
|
-
max_val = np.max(my_network.edges) + 1
|
|
1482
1528
|
|
|
1483
1529
|
# Create a boolean mask for highlighted values
|
|
1484
1530
|
self.highlight_overlay = self.highlight_overlay != 0
|
|
@@ -1486,52 +1532,17 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1486
1532
|
# Create array with just the highlighted values
|
|
1487
1533
|
highlighted_edges = self.highlight_overlay * my_network.edges
|
|
1488
1534
|
|
|
1489
|
-
# Get
|
|
1490
|
-
|
|
1491
|
-
if vals[0] == 0:
|
|
1492
|
-
del vals[0]
|
|
1493
|
-
|
|
1494
|
-
# Process each value separately
|
|
1495
|
-
for val in vals:
|
|
1496
|
-
# Create a mask for this value
|
|
1497
|
-
val_mask = my_network.edges == val
|
|
1498
|
-
|
|
1499
|
-
# Create an array without this value
|
|
1500
|
-
temp = my_network.edges - (val_mask * val)
|
|
1501
|
-
|
|
1502
|
-
# Label the connected components for this value
|
|
1503
|
-
labeled_mask, num_components = n3d.label_objects(val_mask)
|
|
1504
|
-
|
|
1505
|
-
if num_components > 1:
|
|
1506
|
-
# Set appropriate dtype based on max value
|
|
1507
|
-
if max_val + num_components < 256:
|
|
1508
|
-
dtype = np.uint8
|
|
1509
|
-
elif max_val + num_components < 65536:
|
|
1510
|
-
dtype = np.uint16
|
|
1511
|
-
labeled_mask = labeled_mask.astype(dtype)
|
|
1512
|
-
temp = temp.astype(dtype)
|
|
1513
|
-
else:
|
|
1514
|
-
dtype = np.uint32
|
|
1515
|
-
labeled_mask = labeled_mask.astype(dtype)
|
|
1516
|
-
temp = temp.astype(dtype)
|
|
1517
|
-
|
|
1518
|
-
# Add new labels to the temporary array
|
|
1519
|
-
mask_nonzero = labeled_mask != 0
|
|
1520
|
-
labeled_mask = labeled_mask + max_val - 1 # -1 because we'll restore the first component
|
|
1521
|
-
labeled_mask = labeled_mask * mask_nonzero
|
|
1522
|
-
|
|
1523
|
-
# Restore original value for first component
|
|
1524
|
-
first_component = labeled_mask == max_val
|
|
1525
|
-
labeled_mask = labeled_mask - (first_component * (max_val - val))
|
|
1526
|
-
|
|
1527
|
-
# Add labeled components back to the array
|
|
1528
|
-
my_network.edges = temp + labeled_mask
|
|
1529
|
-
|
|
1530
|
-
# Update max value for next iteration
|
|
1531
|
-
max_val += num_components - 1 # -1 because we kept one original label
|
|
1535
|
+
# Get non-highlighted part of the array
|
|
1536
|
+
non_highlighted = my_network.edges * (~self.highlight_overlay)
|
|
1532
1537
|
|
|
1533
|
-
|
|
1538
|
+
# Process highlighted part
|
|
1539
|
+
processed_highlights = separate_nontouching_objects(highlighted_edges)
|
|
1534
1540
|
|
|
1541
|
+
# Combine back with non-highlighted parts
|
|
1542
|
+
my_network.edges = non_highlighted + processed_highlights
|
|
1543
|
+
|
|
1544
|
+
self.load_channel(1, my_network.edges, True)
|
|
1545
|
+
|
|
1535
1546
|
self.highlight_overlay = None
|
|
1536
1547
|
self.update_display()
|
|
1537
1548
|
print("Network is not updated automatically, please recompute if necessary. Identities are not automatically updated.")
|
|
@@ -1541,8 +1552,6 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1541
1552
|
|
|
1542
1553
|
|
|
1543
1554
|
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
1555
|
def handle_delete(self):
|
|
1547
1556
|
|
|
1548
1557
|
try:
|
|
@@ -2684,6 +2693,8 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2684
2693
|
stats_menu = analysis_menu.addMenu("Stats")
|
|
2685
2694
|
allstats_action = stats_menu.addAction("Calculate Generic Network Stats")
|
|
2686
2695
|
allstats_action.triggered.connect(self.stats)
|
|
2696
|
+
histos_action = stats_menu.addAction("Calculate Generic Network Histograms")
|
|
2697
|
+
histos_action.triggered.connect(self.histos)
|
|
2687
2698
|
radial_action = stats_menu.addAction("Radial Distribution Analysis")
|
|
2688
2699
|
radial_action.triggered.connect(self.show_radial_dialog)
|
|
2689
2700
|
degree_dist_action = stats_menu.addAction("Degree Distribution Analysis")
|
|
@@ -2822,6 +2833,104 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2822
2833
|
except Exception as e:
|
|
2823
2834
|
print(f"Error finding stats: {e}")
|
|
2824
2835
|
|
|
2836
|
+
def histos(self):
|
|
2837
|
+
|
|
2838
|
+
"""from networkx documentation"""
|
|
2839
|
+
|
|
2840
|
+
G = my_network.network
|
|
2841
|
+
|
|
2842
|
+
shortest_path_lengths = dict(nx.all_pairs_shortest_path_length(G))
|
|
2843
|
+
diameter = max(nx.eccentricity(G, sp=shortest_path_lengths).values())
|
|
2844
|
+
# We know the maximum shortest path length (the diameter), so create an array
|
|
2845
|
+
# to store values from 0 up to (and including) diameter
|
|
2846
|
+
path_lengths = np.zeros(diameter + 1, dtype=int)
|
|
2847
|
+
|
|
2848
|
+
|
|
2849
|
+
|
|
2850
|
+
# Extract the frequency of shortest path lengths between two nodes
|
|
2851
|
+
for pls in shortest_path_lengths.values():
|
|
2852
|
+
pl, cnts = np.unique(list(pls.values()), return_counts=True)
|
|
2853
|
+
path_lengths[pl] += cnts
|
|
2854
|
+
|
|
2855
|
+
# Express frequency distribution as a percentage (ignoring path lengths of 0)
|
|
2856
|
+
freq_percent = 100 * path_lengths[1:] / path_lengths[1:].sum()
|
|
2857
|
+
|
|
2858
|
+
# Plot the frequency distribution (ignoring path lengths of 0) as a percentage
|
|
2859
|
+
fig, ax = plt.subplots(figsize=(15, 8))
|
|
2860
|
+
ax.bar(np.arange(1, diameter + 1), height=freq_percent)
|
|
2861
|
+
ax.set_title(
|
|
2862
|
+
"Distribution of shortest path length in G", fontdict={"size": 35}, loc="center"
|
|
2863
|
+
)
|
|
2864
|
+
ax.set_xlabel("Shortest Path Length", fontdict={"size": 22})
|
|
2865
|
+
ax.set_ylabel("Frequency (%)", fontdict={"size": 22})
|
|
2866
|
+
|
|
2867
|
+
plt.show()
|
|
2868
|
+
freq_dict = {freq: length for length, freq in enumerate(freq_percent, start=1)}
|
|
2869
|
+
self.format_for_upperright_table(freq_dict, metric='Frequency (%)', value='Shortest Path Length', title="Distribution of shortest path length in G")
|
|
2870
|
+
|
|
2871
|
+
degree_centrality = nx.centrality.degree_centrality(G)
|
|
2872
|
+
plt.figure(figsize=(15, 8))
|
|
2873
|
+
plt.hist(degree_centrality.values(), bins=25)
|
|
2874
|
+
plt.xticks(ticks=[0, 0.025, 0.05, 0.1, 0.15, 0.2]) # set the x axis ticks
|
|
2875
|
+
plt.title("Degree Centrality Histogram ", fontdict={"size": 35}, loc="center")
|
|
2876
|
+
plt.xlabel("Degree Centrality", fontdict={"size": 20})
|
|
2877
|
+
plt.ylabel("Counts", fontdict={"size": 20})
|
|
2878
|
+
plt.show()
|
|
2879
|
+
self.format_for_upperright_table(degree_centrality, metric='Node', value='Degree Centrality', title="Degree Centrality Table")
|
|
2880
|
+
|
|
2881
|
+
|
|
2882
|
+
betweenness_centrality = nx.centrality.betweenness_centrality(
|
|
2883
|
+
G
|
|
2884
|
+
)
|
|
2885
|
+
plt.figure(figsize=(15, 8))
|
|
2886
|
+
plt.hist(betweenness_centrality.values(), bins=100)
|
|
2887
|
+
plt.xticks(ticks=[0, 0.02, 0.1, 0.2, 0.3, 0.4, 0.5]) # set the x axis ticks
|
|
2888
|
+
plt.title("Betweenness Centrality Histogram ", fontdict={"size": 35}, loc="center")
|
|
2889
|
+
plt.xlabel("Betweenness Centrality", fontdict={"size": 20})
|
|
2890
|
+
plt.ylabel("Counts", fontdict={"size": 20})
|
|
2891
|
+
plt.show()
|
|
2892
|
+
self.format_for_upperright_table(betweenness_centrality, metric='Node', value='Betweenness Centrality', title="Betweenness Centrality Table")
|
|
2893
|
+
|
|
2894
|
+
|
|
2895
|
+
closeness_centrality = nx.centrality.closeness_centrality(
|
|
2896
|
+
G
|
|
2897
|
+
)
|
|
2898
|
+
plt.figure(figsize=(15, 8))
|
|
2899
|
+
plt.hist(closeness_centrality.values(), bins=60)
|
|
2900
|
+
plt.title("Closeness Centrality Histogram ", fontdict={"size": 35}, loc="center")
|
|
2901
|
+
plt.xlabel("Closeness Centrality", fontdict={"size": 20})
|
|
2902
|
+
plt.ylabel("Counts", fontdict={"size": 20})
|
|
2903
|
+
plt.show()
|
|
2904
|
+
self.format_for_upperright_table(closeness_centrality, metric='Node', value='Closeness Centrality', title="Closeness Centrality Table")
|
|
2905
|
+
|
|
2906
|
+
|
|
2907
|
+
eigenvector_centrality = nx.centrality.eigenvector_centrality(
|
|
2908
|
+
G
|
|
2909
|
+
)
|
|
2910
|
+
plt.figure(figsize=(15, 8))
|
|
2911
|
+
plt.hist(eigenvector_centrality.values(), bins=60)
|
|
2912
|
+
plt.xticks(ticks=[0, 0.01, 0.02, 0.04, 0.06, 0.08]) # set the x axis ticks
|
|
2913
|
+
plt.title("Eigenvector Centrality Histogram ", fontdict={"size": 35}, loc="center")
|
|
2914
|
+
plt.xlabel("Eigenvector Centrality", fontdict={"size": 20})
|
|
2915
|
+
plt.ylabel("Counts", fontdict={"size": 20})
|
|
2916
|
+
plt.show()
|
|
2917
|
+
self.format_for_upperright_table(eigenvector_centrality, metric='Node', value='Eigenvector Centrality', title="Eigenvector Centrality Table")
|
|
2918
|
+
|
|
2919
|
+
|
|
2920
|
+
|
|
2921
|
+
clusters = nx.clustering(G)
|
|
2922
|
+
plt.figure(figsize=(15, 8))
|
|
2923
|
+
plt.hist(clusters.values(), bins=50)
|
|
2924
|
+
plt.title("Clustering Coefficient Histogram ", fontdict={"size": 35}, loc="center")
|
|
2925
|
+
plt.xlabel("Clustering Coefficient", fontdict={"size": 20})
|
|
2926
|
+
plt.ylabel("Counts", fontdict={"size": 20})
|
|
2927
|
+
plt.show()
|
|
2928
|
+
self.format_for_upperright_table(clusters, metric='Node', value='Clustering Coefficient', title="Clustering Coefficient Table")
|
|
2929
|
+
|
|
2930
|
+
bridges = list(nx.bridges(G))
|
|
2931
|
+
self.format_for_upperright_table(bridges, metric = 'Node Pair', title="Bridges")
|
|
2932
|
+
|
|
2933
|
+
|
|
2825
2934
|
def volumes(self):
|
|
2826
2935
|
|
|
2827
2936
|
|
|
@@ -5756,7 +5865,7 @@ class NetShowDialog(QDialog):
|
|
|
5756
5865
|
|
|
5757
5866
|
# Add mode selection dropdown
|
|
5758
5867
|
self.mode_selector = QComboBox()
|
|
5759
|
-
self.mode_selector.addItems(["Default", "Community Coded
|
|
5868
|
+
self.mode_selector.addItems(["Default", "Community Coded", "Node ID Coded"])
|
|
5760
5869
|
self.mode_selector.setCurrentIndex(0) # Default to Mode 1
|
|
5761
5870
|
layout.addRow("Execution Mode:", self.mode_selector)
|
|
5762
5871
|
|
|
@@ -5778,6 +5887,10 @@ class NetShowDialog(QDialog):
|
|
|
5778
5887
|
|
|
5779
5888
|
def show_network(self):
|
|
5780
5889
|
# Get parameters and run analysis
|
|
5890
|
+
if my_network.communities is None:
|
|
5891
|
+
self.parent().show_partition_dialog()
|
|
5892
|
+
if my_network.communities is None:
|
|
5893
|
+
return
|
|
5781
5894
|
geo = self.geo_layout.isChecked()
|
|
5782
5895
|
if geo:
|
|
5783
5896
|
if my_network.node_centroids is None:
|
|
@@ -5795,12 +5908,6 @@ class NetShowDialog(QDialog):
|
|
|
5795
5908
|
my_network.show_communities_flex(geometric=geo, directory = directory, weighted = weighted, partition = my_network.communities)
|
|
5796
5909
|
self.parent().format_for_upperright_table(my_network.communities, 'NodeID', 'CommunityID')
|
|
5797
5910
|
elif accepted_mode == 2:
|
|
5798
|
-
my_network.show_communities_flex(geometric=geo, directory = directory, weighted = weighted, partition = my_network.communities, style = 0)
|
|
5799
|
-
self.parent().format_for_upperright_table(my_network.communities, 'NodeID', 'CommunityID')
|
|
5800
|
-
elif accepted_mode ==3:
|
|
5801
|
-
my_network.show_communities_flex(geometric=geo, directory = directory, weighted = weighted, partition = my_network.communities, style = 1)
|
|
5802
|
-
self.parent().format_for_upperright_table(my_network.communities, 'NodeID', 'CommunityID')
|
|
5803
|
-
elif accepted_mode == 4:
|
|
5804
5911
|
my_network.show_identity_network(geometric=geo, directory = directory)
|
|
5805
5912
|
|
|
5806
5913
|
self.accept()
|
|
@@ -5836,6 +5943,9 @@ class PartitionDialog(QDialog):
|
|
|
5836
5943
|
self.stats.setChecked(True)
|
|
5837
5944
|
layout.addRow("Community Stats:", self.stats)
|
|
5838
5945
|
|
|
5946
|
+
self.seed = QLineEdit("")
|
|
5947
|
+
layout.addRow("Seed (int):", self.seed)
|
|
5948
|
+
|
|
5839
5949
|
# Add Run button
|
|
5840
5950
|
run_button = QPushButton("Partition")
|
|
5841
5951
|
run_button.clicked.connect(self.partition)
|
|
@@ -5847,10 +5957,16 @@ class PartitionDialog(QDialog):
|
|
|
5847
5957
|
weighted = self.weighted.isChecked()
|
|
5848
5958
|
dostats = self.stats.isChecked()
|
|
5849
5959
|
|
|
5960
|
+
try:
|
|
5961
|
+
seed = int(self.seed.text()) if self.seed.text() else None
|
|
5962
|
+
except:
|
|
5963
|
+
seed = None
|
|
5964
|
+
|
|
5965
|
+
|
|
5850
5966
|
my_network.communities = None
|
|
5851
5967
|
|
|
5852
5968
|
try:
|
|
5853
|
-
stats = my_network.community_partition(weighted = weighted, style = accepted_mode, dostats = dostats)
|
|
5969
|
+
stats = my_network.community_partition(weighted = weighted, style = accepted_mode, dostats = dostats, seed = seed)
|
|
5854
5970
|
print(f"Discovered communities: {my_network.communities}")
|
|
5855
5971
|
|
|
5856
5972
|
self.parent().format_for_upperright_table(my_network.communities, 'NodeID', 'CommunityID', title = 'Community Partition')
|
|
@@ -6565,21 +6681,21 @@ class MotherDialog(QDialog):
|
|
|
6565
6681
|
|
|
6566
6682
|
overlay = self.overlay.isChecked()
|
|
6567
6683
|
|
|
6684
|
+
if my_network.communities is None:
|
|
6685
|
+
self.parent().show_partition_dialog()
|
|
6686
|
+
if my_network.communities is None:
|
|
6687
|
+
return
|
|
6688
|
+
|
|
6568
6689
|
if my_network.node_centroids is None:
|
|
6569
6690
|
self.parent().show_centroid_dialog()
|
|
6570
6691
|
if my_network.node_centroids is None:
|
|
6571
6692
|
print("Error finding centroids")
|
|
6572
6693
|
overlay = False
|
|
6573
6694
|
|
|
6574
|
-
if my_network.communities is None:
|
|
6575
|
-
self.parent().show_partition_dialog()
|
|
6576
|
-
if my_network.communities is None:
|
|
6577
|
-
return
|
|
6578
|
-
|
|
6579
6695
|
if not overlay:
|
|
6580
|
-
G = my_network.isolate_mothers(self,
|
|
6696
|
+
G = my_network.isolate_mothers(self, ret_nodes = True, called = True)
|
|
6581
6697
|
else:
|
|
6582
|
-
G, result = my_network.isolate_mothers(self,
|
|
6698
|
+
G, result = my_network.isolate_mothers(self, ret_nodes = False, called = True)
|
|
6583
6699
|
self.parent().load_channel(2, channel_data = result, data = True)
|
|
6584
6700
|
|
|
6585
6701
|
degree_dict = {}
|
|
@@ -7513,31 +7629,41 @@ class MachineWindow(QMainWindow):
|
|
|
7513
7629
|
|
|
7514
7630
|
def save_model(self):
|
|
7515
7631
|
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
if
|
|
7526
|
-
|
|
7632
|
+
try:
|
|
7633
|
+
|
|
7634
|
+
filename, _ = QFileDialog.getSaveFileName(
|
|
7635
|
+
self,
|
|
7636
|
+
f"Save Model As",
|
|
7637
|
+
"", # Default directory
|
|
7638
|
+
"numpy data (*.npz);;All Files (*)" # File type filter
|
|
7639
|
+
)
|
|
7640
|
+
|
|
7641
|
+
if filename: # Only proceed if user didn't cancel
|
|
7642
|
+
# If user didn't type an extension, add .tif
|
|
7643
|
+
if not filename.endswith(('.npz')):
|
|
7644
|
+
filename += '.npz'
|
|
7527
7645
|
|
|
7528
|
-
|
|
7646
|
+
self.segmenter.save_model(filename, self.parent().channel_data[2])
|
|
7647
|
+
|
|
7648
|
+
except Exception as e:
|
|
7649
|
+
print(f"Error saving model: {e}")
|
|
7529
7650
|
|
|
7530
7651
|
def load_model(self):
|
|
7531
7652
|
|
|
7532
|
-
|
|
7533
|
-
self,
|
|
7534
|
-
f"Load Model",
|
|
7535
|
-
"",
|
|
7536
|
-
"numpy data (*.npz)"
|
|
7537
|
-
)
|
|
7653
|
+
try:
|
|
7538
7654
|
|
|
7539
|
-
|
|
7540
|
-
|
|
7655
|
+
filename, _ = QFileDialog.getOpenFileName(
|
|
7656
|
+
self,
|
|
7657
|
+
f"Load Model",
|
|
7658
|
+
"",
|
|
7659
|
+
"numpy data (*.npz)"
|
|
7660
|
+
)
|
|
7661
|
+
|
|
7662
|
+
self.segmenter.load_model(filename)
|
|
7663
|
+
self.trained = True
|
|
7664
|
+
|
|
7665
|
+
except Exception as e:
|
|
7666
|
+
print(f"Error loading model: {e}")
|
|
7541
7667
|
|
|
7542
7668
|
def toggle_two(self):
|
|
7543
7669
|
if self.two.isChecked():
|
|
@@ -7791,8 +7917,6 @@ class MachineWindow(QMainWindow):
|
|
|
7791
7917
|
traceback.print_exc()
|
|
7792
7918
|
|
|
7793
7919
|
def segmentation_finished(self):
|
|
7794
|
-
if not self.use_two:
|
|
7795
|
-
print("Segmentation completed")
|
|
7796
7920
|
|
|
7797
7921
|
current_xlim = self.parent().ax.get_xlim()
|
|
7798
7922
|
current_ylim = self.parent().ax.get_ylim()
|
|
@@ -7994,7 +8118,7 @@ class SegmentationWorker(QThread):
|
|
|
7994
8118
|
current_time - self.last_update >= self.update_interval):
|
|
7995
8119
|
self.chunk_processed.emit()
|
|
7996
8120
|
self.chunks_since_update = 0
|
|
7997
|
-
self.last_update = current_time
|
|
8121
|
+
self.last_update = current_time
|
|
7998
8122
|
|
|
7999
8123
|
self.finished.emit()
|
|
8000
8124
|
|
|
@@ -9132,65 +9256,110 @@ class CentroidNodeDialog(QDialog):
|
|
|
9132
9256
|
|
|
9133
9257
|
class GenNodesDialog(QDialog):
|
|
9134
9258
|
|
|
9135
|
-
def __init__(self, parent=None, down_factor
|
|
9259
|
+
def __init__(self, parent=None, down_factor=None, called=False):
|
|
9136
9260
|
super().__init__(parent)
|
|
9137
9261
|
self.setWindowTitle("Create Nodes from Edge Vertices")
|
|
9138
9262
|
self.setModal(True)
|
|
9139
|
-
|
|
9140
|
-
|
|
9263
|
+
|
|
9264
|
+
# Main layout
|
|
9265
|
+
main_layout = QVBoxLayout(self)
|
|
9141
9266
|
self.called = called
|
|
9142
|
-
|
|
9143
|
-
#
|
|
9144
|
-
#self.directory.setPlaceholderText("Leave empty to save in active dir")
|
|
9145
|
-
#layout.addRow("Output Directory:", self.directory)
|
|
9146
|
-
|
|
9267
|
+
|
|
9268
|
+
# Set down_factor and cubic
|
|
9147
9269
|
if not down_factor:
|
|
9148
9270
|
down_factor = None
|
|
9271
|
+
|
|
9149
9272
|
if down_factor is None:
|
|
9273
|
+
# --- Processing Options Group ---
|
|
9274
|
+
process_group = QGroupBox("Processing Options")
|
|
9275
|
+
process_layout = QGridLayout()
|
|
9276
|
+
|
|
9277
|
+
# Downsample factor
|
|
9150
9278
|
self.down_factor = QLineEdit("0")
|
|
9151
|
-
|
|
9279
|
+
process_layout.addWidget(QLabel("Downsample Factor (Speeds up calculation at the cost of fidelity):"), 0, 0)
|
|
9280
|
+
process_layout.addWidget(self.down_factor, 0, 1)
|
|
9281
|
+
|
|
9282
|
+
# Cubic checkbox
|
|
9152
9283
|
self.cubic = QPushButton("Cubic Downsample")
|
|
9153
9284
|
self.cubic.setCheckable(True)
|
|
9154
9285
|
self.cubic.setChecked(False)
|
|
9155
|
-
|
|
9286
|
+
process_layout.addWidget(QLabel("Use cubic downsample? (Slower but preserves structure better):"), 1, 0)
|
|
9287
|
+
process_layout.addWidget(self.cubic, 1, 1)
|
|
9288
|
+
|
|
9289
|
+
# Fast dilation checkbox
|
|
9290
|
+
self.fast_dil = QPushButton("Fast-Dil")
|
|
9291
|
+
self.fast_dil.setCheckable(True)
|
|
9292
|
+
self.fast_dil.setChecked(True)
|
|
9293
|
+
process_layout.addWidget(QLabel("Use Fast Dilation (Higher speed, less accurate with large search regions):"), 2, 0)
|
|
9294
|
+
process_layout.addWidget(self.fast_dil, 2, 1)
|
|
9295
|
+
|
|
9296
|
+
process_group.setLayout(process_layout)
|
|
9297
|
+
main_layout.addWidget(process_group)
|
|
9156
9298
|
else:
|
|
9157
9299
|
self.down_factor = down_factor[0]
|
|
9158
9300
|
self.cubic = down_factor[1]
|
|
9159
|
-
|
|
9301
|
+
|
|
9302
|
+
# Fast dilation checkbox (still needed even if down_factor is provided)
|
|
9303
|
+
process_group = QGroupBox("Processing Options")
|
|
9304
|
+
process_layout = QGridLayout()
|
|
9305
|
+
|
|
9306
|
+
self.fast_dil = QPushButton("Fast-Dil")
|
|
9307
|
+
self.fast_dil.setCheckable(True)
|
|
9308
|
+
self.fast_dil.setChecked(True)
|
|
9309
|
+
process_layout.addWidget(QLabel("Use Fast Dilation (Higher speed, less accurate with large search regions):"), 0, 0)
|
|
9310
|
+
process_layout.addWidget(self.fast_dil, 0, 1)
|
|
9311
|
+
|
|
9312
|
+
process_group.setLayout(process_layout)
|
|
9313
|
+
main_layout.addWidget(process_group)
|
|
9314
|
+
|
|
9315
|
+
# --- Recommended Corrections Group ---
|
|
9316
|
+
rec_group = QGroupBox("Recommended Corrections")
|
|
9317
|
+
rec_layout = QGridLayout()
|
|
9318
|
+
|
|
9319
|
+
# Branch removal
|
|
9160
9320
|
self.branch_removal = QLineEdit("0")
|
|
9161
|
-
|
|
9162
|
-
|
|
9163
|
-
|
|
9164
|
-
|
|
9165
|
-
|
|
9166
|
-
self.comp_dil = QLineEdit("0")
|
|
9167
|
-
layout.addRow("Voxel distance to merge nearby nodes (Int - compensates for multi-branch identification along thick branch regions):", self.comp_dil)
|
|
9168
|
-
|
|
9169
|
-
self.fast_dil = QPushButton("Fast-Dil")
|
|
9170
|
-
self.fast_dil.setCheckable(True)
|
|
9171
|
-
self.fast_dil.setChecked(True)
|
|
9172
|
-
layout.addRow("(If using above) Use Fast Dilation (Higher speed, less accurate with search regions much larger than nodes):", self.fast_dil)
|
|
9173
|
-
|
|
9174
|
-
# auto checkbox (default True)
|
|
9321
|
+
rec_layout.addWidget(QLabel("Skeleton Voxel Branch Length to Remove (Compensates for spines):"), 0, 0)
|
|
9322
|
+
rec_layout.addWidget(self.branch_removal, 0, 1)
|
|
9323
|
+
|
|
9324
|
+
# Auto checkbox
|
|
9175
9325
|
self.auto = QPushButton("Auto")
|
|
9176
9326
|
self.auto.setCheckable(True)
|
|
9177
9327
|
self.auto.setChecked(True)
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9328
|
+
rec_layout.addWidget(QLabel("Attempt to Auto Correct Skeleton Looping:"), 1, 0)
|
|
9329
|
+
rec_layout.addWidget(self.auto, 1, 1)
|
|
9330
|
+
|
|
9331
|
+
rec_group.setLayout(rec_layout)
|
|
9332
|
+
main_layout.addWidget(rec_group)
|
|
9333
|
+
|
|
9334
|
+
# --- Optional Corrections Group ---
|
|
9335
|
+
opt_group = QGroupBox("Optional Corrections")
|
|
9336
|
+
opt_layout = QGridLayout()
|
|
9337
|
+
|
|
9338
|
+
# Max volume
|
|
9339
|
+
self.max_vol = QLineEdit("0")
|
|
9340
|
+
opt_layout.addWidget(QLabel("Maximum Voxel Volume to Retain (Compensates for skeleton looping):"), 0, 0)
|
|
9341
|
+
opt_layout.addWidget(self.max_vol, 0, 1)
|
|
9342
|
+
|
|
9343
|
+
# Component dilation
|
|
9344
|
+
self.comp_dil = QLineEdit("0")
|
|
9345
|
+
opt_layout.addWidget(QLabel("Voxel distance to merge nearby nodes (Compensates for multi-branch regions):"), 1, 0)
|
|
9346
|
+
opt_layout.addWidget(self.comp_dil, 1, 1)
|
|
9347
|
+
|
|
9348
|
+
opt_group.setLayout(opt_layout)
|
|
9349
|
+
main_layout.addWidget(opt_group)
|
|
9350
|
+
|
|
9351
|
+
# Set retain variable but don't add to layout
|
|
9182
9352
|
if not called:
|
|
9183
9353
|
self.retain = QPushButton("Retain")
|
|
9184
9354
|
self.retain.setCheckable(True)
|
|
9185
9355
|
self.retain.setChecked(True)
|
|
9186
|
-
#layout.addRow("Retain Original Edges? (Will be moved to overlay 2):", self.retain)
|
|
9187
9356
|
else:
|
|
9188
9357
|
self.retain = False
|
|
9189
|
-
|
|
9358
|
+
|
|
9190
9359
|
# Add Run button
|
|
9191
9360
|
run_button = QPushButton("Run Node Generation")
|
|
9192
9361
|
run_button.clicked.connect(self.run_gennodes)
|
|
9193
|
-
|
|
9362
|
+
main_layout.addWidget(run_button)
|
|
9194
9363
|
|
|
9195
9364
|
def run_gennodes(self):
|
|
9196
9365
|
|
|
@@ -9311,49 +9480,84 @@ class BranchDialog(QDialog):
|
|
|
9311
9480
|
super().__init__(parent)
|
|
9312
9481
|
self.setWindowTitle("Label Branches (of edges)")
|
|
9313
9482
|
self.setModal(True)
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
9319
|
-
|
|
9320
|
-
|
|
9321
|
-
|
|
9322
|
-
|
|
9323
|
-
# GPU checkbox (default False)
|
|
9324
|
-
self.GPU = QPushButton("GPU")
|
|
9325
|
-
self.GPU.setCheckable(True)
|
|
9326
|
-
self.GPU.setChecked(False)
|
|
9327
|
-
layout.addRow("Use GPU (Note this may need to temporarily downsample your large images which may simplify outputs - Only memory errors but not permission errors for accessing VRAM are handled by default - CPU will never try to downsample):", self.GPU)
|
|
9328
|
-
|
|
9329
|
-
# Branch Fix checkbox (default False)
|
|
9483
|
+
|
|
9484
|
+
# Main layout
|
|
9485
|
+
main_layout = QVBoxLayout(self)
|
|
9486
|
+
|
|
9487
|
+
# --- Correction Options Group ---
|
|
9488
|
+
correction_group = QGroupBox("Correction Options")
|
|
9489
|
+
correction_layout = QGridLayout()
|
|
9490
|
+
|
|
9491
|
+
# Branch Fix checkbox
|
|
9330
9492
|
self.fix = QPushButton("Auto-Correct Branches")
|
|
9331
9493
|
self.fix.setCheckable(True)
|
|
9332
9494
|
self.fix.setChecked(False)
|
|
9333
|
-
|
|
9334
|
-
|
|
9495
|
+
correction_layout.addWidget(QLabel("Attempt to auto-correct branch labels:"), 0, 0)
|
|
9496
|
+
correction_layout.addWidget(self.fix, 0, 1)
|
|
9497
|
+
|
|
9498
|
+
# Fix value
|
|
9335
9499
|
self.fix_val = QLineEdit('4')
|
|
9336
|
-
|
|
9337
|
-
|
|
9500
|
+
correction_layout.addWidget(QLabel("Avg Degree of Nearby Branch Communities to Merge (4-6 recommended):"), 1, 0)
|
|
9501
|
+
correction_layout.addWidget(self.fix_val, 1, 1)
|
|
9502
|
+
|
|
9503
|
+
# Seed
|
|
9504
|
+
self.seed = QLineEdit('')
|
|
9505
|
+
correction_layout.addWidget(QLabel("Random seed for auto correction (int - optional):"), 2, 0)
|
|
9506
|
+
correction_layout.addWidget(self.seed, 2, 1)
|
|
9507
|
+
|
|
9508
|
+
correction_group.setLayout(correction_layout)
|
|
9509
|
+
main_layout.addWidget(correction_group)
|
|
9510
|
+
|
|
9511
|
+
# --- Processing Options Group ---
|
|
9512
|
+
processing_group = QGroupBox("Processing Options")
|
|
9513
|
+
processing_layout = QGridLayout()
|
|
9514
|
+
|
|
9515
|
+
# Downsample factor
|
|
9338
9516
|
self.down_factor = QLineEdit("0")
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9517
|
+
processing_layout.addWidget(QLabel("Internal downsample factor (will recompute nodes):"), 0, 0)
|
|
9518
|
+
processing_layout.addWidget(self.down_factor, 0, 1)
|
|
9519
|
+
|
|
9520
|
+
# Cubic checkbox
|
|
9342
9521
|
self.cubic = QPushButton("Cubic Downsample")
|
|
9343
9522
|
self.cubic.setCheckable(True)
|
|
9344
9523
|
self.cubic.setChecked(False)
|
|
9345
|
-
|
|
9346
|
-
|
|
9524
|
+
processing_layout.addWidget(QLabel("Use cubic downsample? (Slower but preserves structure better):"), 1, 0)
|
|
9525
|
+
processing_layout.addWidget(self.cubic, 1, 1)
|
|
9526
|
+
|
|
9527
|
+
processing_group.setLayout(processing_layout)
|
|
9528
|
+
main_layout.addWidget(processing_group)
|
|
9529
|
+
|
|
9530
|
+
# --- Misc Options Group ---
|
|
9531
|
+
misc_group = QGroupBox("Misc Options")
|
|
9532
|
+
misc_layout = QGridLayout()
|
|
9533
|
+
|
|
9534
|
+
# Nodes checkbox
|
|
9535
|
+
self.nodes = QPushButton("Generate Nodes")
|
|
9536
|
+
self.nodes.setCheckable(True)
|
|
9537
|
+
self.nodes.setChecked(True)
|
|
9538
|
+
misc_layout.addWidget(QLabel("Generate nodes from edges? (Skip if already completed):"), 0, 0)
|
|
9539
|
+
misc_layout.addWidget(self.nodes, 0, 1)
|
|
9540
|
+
|
|
9541
|
+
# GPU checkbox
|
|
9542
|
+
self.GPU = QPushButton("GPU")
|
|
9543
|
+
self.GPU.setCheckable(True)
|
|
9544
|
+
self.GPU.setChecked(False)
|
|
9545
|
+
misc_layout.addWidget(QLabel("Use GPU (May downsample large images):"), 1, 0)
|
|
9546
|
+
misc_layout.addWidget(self.GPU, 1, 1)
|
|
9547
|
+
|
|
9548
|
+
misc_group.setLayout(misc_layout)
|
|
9549
|
+
main_layout.addWidget(misc_group)
|
|
9550
|
+
|
|
9347
9551
|
# Add Run button
|
|
9348
9552
|
run_button = QPushButton("Run Branch Label")
|
|
9349
9553
|
run_button.clicked.connect(self.branch_label)
|
|
9350
|
-
|
|
9554
|
+
main_layout.addWidget(run_button)
|
|
9351
9555
|
|
|
9352
|
-
if self.parent().channel_data[0] is not None:
|
|
9556
|
+
if self.parent().channel_data[0] is not None or self.parent().channel_data[3] is not None:
|
|
9353
9557
|
QMessageBox.critical(
|
|
9354
9558
|
self,
|
|
9355
9559
|
"Alert",
|
|
9356
|
-
"The nodes
|
|
9560
|
+
"The nodes and overlay 2 channels will be intermittently overwritten when running this method"
|
|
9357
9561
|
)
|
|
9358
9562
|
|
|
9359
9563
|
def branch_label(self):
|
|
@@ -9370,8 +9574,7 @@ class BranchDialog(QDialog):
|
|
|
9370
9574
|
cubic = self.cubic.isChecked()
|
|
9371
9575
|
fix = self.fix.isChecked()
|
|
9372
9576
|
fix_val = float(self.fix_val.text()) if self.fix_val.text() else None
|
|
9373
|
-
|
|
9374
|
-
|
|
9577
|
+
seed = int(self.seed.text()) if self.seed.text() else None
|
|
9375
9578
|
|
|
9376
9579
|
original_shape = my_network.edges.shape
|
|
9377
9580
|
original_array = copy.deepcopy(my_network.edges)
|
|
@@ -9392,7 +9595,7 @@ class BranchDialog(QDialog):
|
|
|
9392
9595
|
|
|
9393
9596
|
temp_network.morph_proximity(search = [3,3], fastdil = True) #Detect network of nearby branches
|
|
9394
9597
|
|
|
9395
|
-
temp_network.community_partition(weighted = False, style = 1, dostats = False) #Find communities with louvain, unweighted params
|
|
9598
|
+
temp_network.community_partition(weighted = False, style = 1, dostats = False, seed = seed) #Find communities with louvain, unweighted params
|
|
9396
9599
|
|
|
9397
9600
|
targs = n3d.fix_branches(temp_network.nodes, temp_network.network, temp_network.communities, fix_val)
|
|
9398
9601
|
|