nettracer3d 0.7.4__py3-none-any.whl → 0.7.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nettracer3d might be problematic. Click here for more details.
- nettracer3d/community_extractor.py +17 -39
- nettracer3d/modularity.py +41 -264
- nettracer3d/nettracer.py +7 -94
- nettracer3d/nettracer_gui.py +422 -205
- 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.6.dist-info}/METADATA +3 -6
- nettracer3d-0.7.6.dist-info/RECORD +21 -0
- {nettracer3d-0.7.4.dist-info → nettracer3d-0.7.6.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.6.dist-info}/entry_points.txt +0 -0
- {nettracer3d-0.7.4.dist-info → nettracer3d-0.7.6.dist-info}/licenses/LICENSE +0 -0
- {nettracer3d-0.7.4.dist-info → nettracer3d-0.7.6.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, max_val = 0):
|
|
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 + max_val
|
|
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,26 @@ 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
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
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
|
+
if (highlighted_nodes==non_highlighted).all():
|
|
1517
|
+
max_val = 0
|
|
1518
|
+
else:
|
|
1519
|
+
max_val = np.max(non_highlighted)
|
|
1520
|
+
|
|
1521
|
+
# Process highlighted part
|
|
1522
|
+
processed_highlights = separate_nontouching_objects(highlighted_nodes, max_val)
|
|
1523
|
+
|
|
1524
|
+
# Combine back with non-highlighted parts
|
|
1525
|
+
my_network.nodes = non_highlighted + processed_highlights
|
|
1475
1526
|
|
|
1476
1527
|
self.load_channel(0, my_network.nodes, True)
|
|
1477
1528
|
|
|
1478
1529
|
# Handle edges
|
|
1479
1530
|
if len(self.clicked_values['edges']) > 0:
|
|
1531
|
+
|
|
1480
1532
|
self.create_highlight_overlay(edge_indices=self.clicked_values['edges'])
|
|
1481
|
-
max_val = np.max(my_network.edges) + 1
|
|
1482
1533
|
|
|
1483
1534
|
# Create a boolean mask for highlighted values
|
|
1484
1535
|
self.highlight_overlay = self.highlight_overlay != 0
|
|
@@ -1486,63 +1537,31 @@ class ImageViewerWindow(QMainWindow):
|
|
|
1486
1537
|
# Create array with just the highlighted values
|
|
1487
1538
|
highlighted_edges = self.highlight_overlay * my_network.edges
|
|
1488
1539
|
|
|
1489
|
-
# Get
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
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
|
|
1540
|
+
# Get non-highlighted part of the array
|
|
1541
|
+
non_highlighted = my_network.edges * (~self.highlight_overlay)
|
|
1542
|
+
|
|
1543
|
+
if (highlighted_nodes==non_highlighted).all():
|
|
1544
|
+
max_val = 0
|
|
1545
|
+
else:
|
|
1546
|
+
max_val = np.max(non_highlighted)
|
|
1532
1547
|
|
|
1533
|
-
|
|
1548
|
+
# Process highlighted part
|
|
1549
|
+
processed_highlights = separate_nontouching_objects(highlighted_edges, max_val)
|
|
1534
1550
|
|
|
1551
|
+
# Combine back with non-highlighted parts
|
|
1552
|
+
my_network.edges = non_highlighted + processed_highlights
|
|
1553
|
+
|
|
1554
|
+
self.load_channel(1, my_network.edges, True)
|
|
1555
|
+
|
|
1535
1556
|
self.highlight_overlay = None
|
|
1536
1557
|
self.update_display()
|
|
1537
|
-
print("Network is not updated automatically, please recompute if necessary. Identities are not automatically updated.")
|
|
1558
|
+
print("Network is not updated automatically, please recompute if necessary - this method has a high chance of disrupting the network. Identities are not automatically updated.")
|
|
1538
1559
|
self.show_centroid_dialog()
|
|
1539
1560
|
except Exception as e:
|
|
1540
1561
|
print(f"Error separating: {e}")
|
|
1541
1562
|
|
|
1542
1563
|
|
|
1543
1564
|
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
1565
|
def handle_delete(self):
|
|
1547
1566
|
|
|
1548
1567
|
try:
|
|
@@ -2684,6 +2703,8 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2684
2703
|
stats_menu = analysis_menu.addMenu("Stats")
|
|
2685
2704
|
allstats_action = stats_menu.addAction("Calculate Generic Network Stats")
|
|
2686
2705
|
allstats_action.triggered.connect(self.stats)
|
|
2706
|
+
histos_action = stats_menu.addAction("Calculate Generic Network Histograms")
|
|
2707
|
+
histos_action.triggered.connect(self.histos)
|
|
2687
2708
|
radial_action = stats_menu.addAction("Radial Distribution Analysis")
|
|
2688
2709
|
radial_action.triggered.connect(self.show_radial_dialog)
|
|
2689
2710
|
degree_dist_action = stats_menu.addAction("Degree Distribution Analysis")
|
|
@@ -2822,6 +2843,108 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2822
2843
|
except Exception as e:
|
|
2823
2844
|
print(f"Error finding stats: {e}")
|
|
2824
2845
|
|
|
2846
|
+
def histos(self):
|
|
2847
|
+
|
|
2848
|
+
"""from networkx documentation"""
|
|
2849
|
+
|
|
2850
|
+
try:
|
|
2851
|
+
|
|
2852
|
+
G = my_network.network
|
|
2853
|
+
|
|
2854
|
+
shortest_path_lengths = dict(nx.all_pairs_shortest_path_length(G))
|
|
2855
|
+
diameter = max(nx.eccentricity(G, sp=shortest_path_lengths).values())
|
|
2856
|
+
# We know the maximum shortest path length (the diameter), so create an array
|
|
2857
|
+
# to store values from 0 up to (and including) diameter
|
|
2858
|
+
path_lengths = np.zeros(diameter + 1, dtype=int)
|
|
2859
|
+
|
|
2860
|
+
|
|
2861
|
+
|
|
2862
|
+
# Extract the frequency of shortest path lengths between two nodes
|
|
2863
|
+
for pls in shortest_path_lengths.values():
|
|
2864
|
+
pl, cnts = np.unique(list(pls.values()), return_counts=True)
|
|
2865
|
+
path_lengths[pl] += cnts
|
|
2866
|
+
|
|
2867
|
+
# Express frequency distribution as a percentage (ignoring path lengths of 0)
|
|
2868
|
+
freq_percent = 100 * path_lengths[1:] / path_lengths[1:].sum()
|
|
2869
|
+
|
|
2870
|
+
# Plot the frequency distribution (ignoring path lengths of 0) as a percentage
|
|
2871
|
+
fig, ax = plt.subplots(figsize=(15, 8))
|
|
2872
|
+
ax.bar(np.arange(1, diameter + 1), height=freq_percent)
|
|
2873
|
+
ax.set_title(
|
|
2874
|
+
"Distribution of shortest path length in G", fontdict={"size": 35}, loc="center"
|
|
2875
|
+
)
|
|
2876
|
+
ax.set_xlabel("Shortest Path Length", fontdict={"size": 22})
|
|
2877
|
+
ax.set_ylabel("Frequency (%)", fontdict={"size": 22})
|
|
2878
|
+
|
|
2879
|
+
plt.show()
|
|
2880
|
+
freq_dict = {freq: length for length, freq in enumerate(freq_percent, start=1)}
|
|
2881
|
+
self.format_for_upperright_table(freq_dict, metric='Frequency (%)', value='Shortest Path Length', title="Distribution of shortest path length in G")
|
|
2882
|
+
|
|
2883
|
+
degree_centrality = nx.centrality.degree_centrality(G)
|
|
2884
|
+
plt.figure(figsize=(15, 8))
|
|
2885
|
+
plt.hist(degree_centrality.values(), bins=25)
|
|
2886
|
+
plt.xticks(ticks=[0, 0.025, 0.05, 0.1, 0.15, 0.2]) # set the x axis ticks
|
|
2887
|
+
plt.title("Degree Centrality Histogram ", fontdict={"size": 35}, loc="center")
|
|
2888
|
+
plt.xlabel("Degree Centrality", fontdict={"size": 20})
|
|
2889
|
+
plt.ylabel("Counts", fontdict={"size": 20})
|
|
2890
|
+
plt.show()
|
|
2891
|
+
self.format_for_upperright_table(degree_centrality, metric='Node', value='Degree Centrality', title="Degree Centrality Table")
|
|
2892
|
+
|
|
2893
|
+
|
|
2894
|
+
betweenness_centrality = nx.centrality.betweenness_centrality(
|
|
2895
|
+
G
|
|
2896
|
+
)
|
|
2897
|
+
plt.figure(figsize=(15, 8))
|
|
2898
|
+
plt.hist(betweenness_centrality.values(), bins=100)
|
|
2899
|
+
plt.xticks(ticks=[0, 0.02, 0.1, 0.2, 0.3, 0.4, 0.5]) # set the x axis ticks
|
|
2900
|
+
plt.title("Betweenness Centrality Histogram ", fontdict={"size": 35}, loc="center")
|
|
2901
|
+
plt.xlabel("Betweenness Centrality", fontdict={"size": 20})
|
|
2902
|
+
plt.ylabel("Counts", fontdict={"size": 20})
|
|
2903
|
+
plt.show()
|
|
2904
|
+
self.format_for_upperright_table(betweenness_centrality, metric='Node', value='Betweenness Centrality', title="Betweenness Centrality Table")
|
|
2905
|
+
|
|
2906
|
+
|
|
2907
|
+
closeness_centrality = nx.centrality.closeness_centrality(
|
|
2908
|
+
G
|
|
2909
|
+
)
|
|
2910
|
+
plt.figure(figsize=(15, 8))
|
|
2911
|
+
plt.hist(closeness_centrality.values(), bins=60)
|
|
2912
|
+
plt.title("Closeness Centrality Histogram ", fontdict={"size": 35}, loc="center")
|
|
2913
|
+
plt.xlabel("Closeness Centrality", fontdict={"size": 20})
|
|
2914
|
+
plt.ylabel("Counts", fontdict={"size": 20})
|
|
2915
|
+
plt.show()
|
|
2916
|
+
self.format_for_upperright_table(closeness_centrality, metric='Node', value='Closeness Centrality', title="Closeness Centrality Table")
|
|
2917
|
+
|
|
2918
|
+
|
|
2919
|
+
eigenvector_centrality = nx.centrality.eigenvector_centrality(
|
|
2920
|
+
G
|
|
2921
|
+
)
|
|
2922
|
+
plt.figure(figsize=(15, 8))
|
|
2923
|
+
plt.hist(eigenvector_centrality.values(), bins=60)
|
|
2924
|
+
plt.xticks(ticks=[0, 0.01, 0.02, 0.04, 0.06, 0.08]) # set the x axis ticks
|
|
2925
|
+
plt.title("Eigenvector Centrality Histogram ", fontdict={"size": 35}, loc="center")
|
|
2926
|
+
plt.xlabel("Eigenvector Centrality", fontdict={"size": 20})
|
|
2927
|
+
plt.ylabel("Counts", fontdict={"size": 20})
|
|
2928
|
+
plt.show()
|
|
2929
|
+
self.format_for_upperright_table(eigenvector_centrality, metric='Node', value='Eigenvector Centrality', title="Eigenvector Centrality Table")
|
|
2930
|
+
|
|
2931
|
+
|
|
2932
|
+
|
|
2933
|
+
clusters = nx.clustering(G)
|
|
2934
|
+
plt.figure(figsize=(15, 8))
|
|
2935
|
+
plt.hist(clusters.values(), bins=50)
|
|
2936
|
+
plt.title("Clustering Coefficient Histogram ", fontdict={"size": 35}, loc="center")
|
|
2937
|
+
plt.xlabel("Clustering Coefficient", fontdict={"size": 20})
|
|
2938
|
+
plt.ylabel("Counts", fontdict={"size": 20})
|
|
2939
|
+
plt.show()
|
|
2940
|
+
self.format_for_upperright_table(clusters, metric='Node', value='Clustering Coefficient', title="Clustering Coefficient Table")
|
|
2941
|
+
|
|
2942
|
+
bridges = list(nx.bridges(G))
|
|
2943
|
+
self.format_for_upperright_table(bridges, metric = 'Node Pair', title="Bridges")
|
|
2944
|
+
|
|
2945
|
+
except Exception as e:
|
|
2946
|
+
print(f"Error generating histograms: {e}")
|
|
2947
|
+
|
|
2825
2948
|
def volumes(self):
|
|
2826
2949
|
|
|
2827
2950
|
|
|
@@ -5756,7 +5879,7 @@ class NetShowDialog(QDialog):
|
|
|
5756
5879
|
|
|
5757
5880
|
# Add mode selection dropdown
|
|
5758
5881
|
self.mode_selector = QComboBox()
|
|
5759
|
-
self.mode_selector.addItems(["Default", "Community Coded
|
|
5882
|
+
self.mode_selector.addItems(["Default", "Community Coded", "Node ID Coded"])
|
|
5760
5883
|
self.mode_selector.setCurrentIndex(0) # Default to Mode 1
|
|
5761
5884
|
layout.addRow("Execution Mode:", self.mode_selector)
|
|
5762
5885
|
|
|
@@ -5778,6 +5901,10 @@ class NetShowDialog(QDialog):
|
|
|
5778
5901
|
|
|
5779
5902
|
def show_network(self):
|
|
5780
5903
|
# Get parameters and run analysis
|
|
5904
|
+
if my_network.communities is None:
|
|
5905
|
+
self.parent().show_partition_dialog()
|
|
5906
|
+
if my_network.communities is None:
|
|
5907
|
+
return
|
|
5781
5908
|
geo = self.geo_layout.isChecked()
|
|
5782
5909
|
if geo:
|
|
5783
5910
|
if my_network.node_centroids is None:
|
|
@@ -5795,12 +5922,6 @@ class NetShowDialog(QDialog):
|
|
|
5795
5922
|
my_network.show_communities_flex(geometric=geo, directory = directory, weighted = weighted, partition = my_network.communities)
|
|
5796
5923
|
self.parent().format_for_upperright_table(my_network.communities, 'NodeID', 'CommunityID')
|
|
5797
5924
|
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
5925
|
my_network.show_identity_network(geometric=geo, directory = directory)
|
|
5805
5926
|
|
|
5806
5927
|
self.accept()
|
|
@@ -5836,6 +5957,9 @@ class PartitionDialog(QDialog):
|
|
|
5836
5957
|
self.stats.setChecked(True)
|
|
5837
5958
|
layout.addRow("Community Stats:", self.stats)
|
|
5838
5959
|
|
|
5960
|
+
self.seed = QLineEdit("")
|
|
5961
|
+
layout.addRow("Seed (int):", self.seed)
|
|
5962
|
+
|
|
5839
5963
|
# Add Run button
|
|
5840
5964
|
run_button = QPushButton("Partition")
|
|
5841
5965
|
run_button.clicked.connect(self.partition)
|
|
@@ -5847,10 +5971,16 @@ class PartitionDialog(QDialog):
|
|
|
5847
5971
|
weighted = self.weighted.isChecked()
|
|
5848
5972
|
dostats = self.stats.isChecked()
|
|
5849
5973
|
|
|
5974
|
+
try:
|
|
5975
|
+
seed = int(self.seed.text()) if self.seed.text() else None
|
|
5976
|
+
except:
|
|
5977
|
+
seed = None
|
|
5978
|
+
|
|
5979
|
+
|
|
5850
5980
|
my_network.communities = None
|
|
5851
5981
|
|
|
5852
5982
|
try:
|
|
5853
|
-
stats = my_network.community_partition(weighted = weighted, style = accepted_mode, dostats = dostats)
|
|
5983
|
+
stats = my_network.community_partition(weighted = weighted, style = accepted_mode, dostats = dostats, seed = seed)
|
|
5854
5984
|
print(f"Discovered communities: {my_network.communities}")
|
|
5855
5985
|
|
|
5856
5986
|
self.parent().format_for_upperright_table(my_network.communities, 'NodeID', 'CommunityID', title = 'Community Partition')
|
|
@@ -6565,21 +6695,21 @@ class MotherDialog(QDialog):
|
|
|
6565
6695
|
|
|
6566
6696
|
overlay = self.overlay.isChecked()
|
|
6567
6697
|
|
|
6698
|
+
if my_network.communities is None:
|
|
6699
|
+
self.parent().show_partition_dialog()
|
|
6700
|
+
if my_network.communities is None:
|
|
6701
|
+
return
|
|
6702
|
+
|
|
6568
6703
|
if my_network.node_centroids is None:
|
|
6569
6704
|
self.parent().show_centroid_dialog()
|
|
6570
6705
|
if my_network.node_centroids is None:
|
|
6571
6706
|
print("Error finding centroids")
|
|
6572
6707
|
overlay = False
|
|
6573
6708
|
|
|
6574
|
-
if my_network.communities is None:
|
|
6575
|
-
self.parent().show_partition_dialog()
|
|
6576
|
-
if my_network.communities is None:
|
|
6577
|
-
return
|
|
6578
|
-
|
|
6579
6709
|
if not overlay:
|
|
6580
|
-
G = my_network.isolate_mothers(self,
|
|
6710
|
+
G = my_network.isolate_mothers(self, ret_nodes = True, called = True)
|
|
6581
6711
|
else:
|
|
6582
|
-
G, result = my_network.isolate_mothers(self,
|
|
6712
|
+
G, result = my_network.isolate_mothers(self, ret_nodes = False, called = True)
|
|
6583
6713
|
self.parent().load_channel(2, channel_data = result, data = True)
|
|
6584
6714
|
|
|
6585
6715
|
degree_dict = {}
|
|
@@ -7513,31 +7643,41 @@ class MachineWindow(QMainWindow):
|
|
|
7513
7643
|
|
|
7514
7644
|
def save_model(self):
|
|
7515
7645
|
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
if
|
|
7526
|
-
|
|
7646
|
+
try:
|
|
7647
|
+
|
|
7648
|
+
filename, _ = QFileDialog.getSaveFileName(
|
|
7649
|
+
self,
|
|
7650
|
+
f"Save Model As",
|
|
7651
|
+
"", # Default directory
|
|
7652
|
+
"numpy data (*.npz);;All Files (*)" # File type filter
|
|
7653
|
+
)
|
|
7654
|
+
|
|
7655
|
+
if filename: # Only proceed if user didn't cancel
|
|
7656
|
+
# If user didn't type an extension, add .tif
|
|
7657
|
+
if not filename.endswith(('.npz')):
|
|
7658
|
+
filename += '.npz'
|
|
7659
|
+
|
|
7660
|
+
self.segmenter.save_model(filename, self.parent().channel_data[2])
|
|
7527
7661
|
|
|
7528
|
-
|
|
7662
|
+
except Exception as e:
|
|
7663
|
+
print(f"Error saving model: {e}")
|
|
7529
7664
|
|
|
7530
7665
|
def load_model(self):
|
|
7531
7666
|
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7667
|
+
try:
|
|
7668
|
+
|
|
7669
|
+
filename, _ = QFileDialog.getOpenFileName(
|
|
7670
|
+
self,
|
|
7671
|
+
f"Load Model",
|
|
7672
|
+
"",
|
|
7673
|
+
"numpy data (*.npz)"
|
|
7674
|
+
)
|
|
7675
|
+
|
|
7676
|
+
self.segmenter.load_model(filename)
|
|
7677
|
+
self.trained = True
|
|
7538
7678
|
|
|
7539
|
-
|
|
7540
|
-
|
|
7679
|
+
except Exception as e:
|
|
7680
|
+
print(f"Error loading model: {e}")
|
|
7541
7681
|
|
|
7542
7682
|
def toggle_two(self):
|
|
7543
7683
|
if self.two.isChecked():
|
|
@@ -7791,8 +7931,6 @@ class MachineWindow(QMainWindow):
|
|
|
7791
7931
|
traceback.print_exc()
|
|
7792
7932
|
|
|
7793
7933
|
def segmentation_finished(self):
|
|
7794
|
-
if not self.use_two:
|
|
7795
|
-
print("Segmentation completed")
|
|
7796
7934
|
|
|
7797
7935
|
current_xlim = self.parent().ax.get_xlim()
|
|
7798
7936
|
current_ylim = self.parent().ax.get_ylim()
|
|
@@ -7994,7 +8132,7 @@ class SegmentationWorker(QThread):
|
|
|
7994
8132
|
current_time - self.last_update >= self.update_interval):
|
|
7995
8133
|
self.chunk_processed.emit()
|
|
7996
8134
|
self.chunks_since_update = 0
|
|
7997
|
-
self.last_update = current_time
|
|
8135
|
+
self.last_update = current_time
|
|
7998
8136
|
|
|
7999
8137
|
self.finished.emit()
|
|
8000
8138
|
|
|
@@ -9132,65 +9270,110 @@ class CentroidNodeDialog(QDialog):
|
|
|
9132
9270
|
|
|
9133
9271
|
class GenNodesDialog(QDialog):
|
|
9134
9272
|
|
|
9135
|
-
def __init__(self, parent=None, down_factor
|
|
9273
|
+
def __init__(self, parent=None, down_factor=None, called=False):
|
|
9136
9274
|
super().__init__(parent)
|
|
9137
9275
|
self.setWindowTitle("Create Nodes from Edge Vertices")
|
|
9138
9276
|
self.setModal(True)
|
|
9139
|
-
|
|
9140
|
-
|
|
9277
|
+
|
|
9278
|
+
# Main layout
|
|
9279
|
+
main_layout = QVBoxLayout(self)
|
|
9141
9280
|
self.called = called
|
|
9142
|
-
|
|
9143
|
-
#
|
|
9144
|
-
#self.directory.setPlaceholderText("Leave empty to save in active dir")
|
|
9145
|
-
#layout.addRow("Output Directory:", self.directory)
|
|
9146
|
-
|
|
9281
|
+
|
|
9282
|
+
# Set down_factor and cubic
|
|
9147
9283
|
if not down_factor:
|
|
9148
9284
|
down_factor = None
|
|
9285
|
+
|
|
9149
9286
|
if down_factor is None:
|
|
9287
|
+
# --- Processing Options Group ---
|
|
9288
|
+
process_group = QGroupBox("Processing Options")
|
|
9289
|
+
process_layout = QGridLayout()
|
|
9290
|
+
|
|
9291
|
+
# Downsample factor
|
|
9150
9292
|
self.down_factor = QLineEdit("0")
|
|
9151
|
-
|
|
9293
|
+
process_layout.addWidget(QLabel("Downsample Factor (Speeds up calculation at the cost of fidelity):"), 0, 0)
|
|
9294
|
+
process_layout.addWidget(self.down_factor, 0, 1)
|
|
9295
|
+
|
|
9296
|
+
# Cubic checkbox
|
|
9152
9297
|
self.cubic = QPushButton("Cubic Downsample")
|
|
9153
9298
|
self.cubic.setCheckable(True)
|
|
9154
9299
|
self.cubic.setChecked(False)
|
|
9155
|
-
|
|
9300
|
+
process_layout.addWidget(QLabel("Use cubic downsample? (Slower but preserves structure better):"), 1, 0)
|
|
9301
|
+
process_layout.addWidget(self.cubic, 1, 1)
|
|
9302
|
+
|
|
9303
|
+
# Fast dilation checkbox
|
|
9304
|
+
self.fast_dil = QPushButton("Fast-Dil")
|
|
9305
|
+
self.fast_dil.setCheckable(True)
|
|
9306
|
+
self.fast_dil.setChecked(True)
|
|
9307
|
+
process_layout.addWidget(QLabel("Use Fast Dilation (Higher speed, less accurate with large search regions):"), 2, 0)
|
|
9308
|
+
process_layout.addWidget(self.fast_dil, 2, 1)
|
|
9309
|
+
|
|
9310
|
+
process_group.setLayout(process_layout)
|
|
9311
|
+
main_layout.addWidget(process_group)
|
|
9156
9312
|
else:
|
|
9157
9313
|
self.down_factor = down_factor[0]
|
|
9158
9314
|
self.cubic = down_factor[1]
|
|
9159
|
-
|
|
9315
|
+
|
|
9316
|
+
# Fast dilation checkbox (still needed even if down_factor is provided)
|
|
9317
|
+
process_group = QGroupBox("Processing Options")
|
|
9318
|
+
process_layout = QGridLayout()
|
|
9319
|
+
|
|
9320
|
+
self.fast_dil = QPushButton("Fast-Dil")
|
|
9321
|
+
self.fast_dil.setCheckable(True)
|
|
9322
|
+
self.fast_dil.setChecked(True)
|
|
9323
|
+
process_layout.addWidget(QLabel("Use Fast Dilation (Higher speed, less accurate with large search regions):"), 0, 0)
|
|
9324
|
+
process_layout.addWidget(self.fast_dil, 0, 1)
|
|
9325
|
+
|
|
9326
|
+
process_group.setLayout(process_layout)
|
|
9327
|
+
main_layout.addWidget(process_group)
|
|
9328
|
+
|
|
9329
|
+
# --- Recommended Corrections Group ---
|
|
9330
|
+
rec_group = QGroupBox("Recommended Corrections")
|
|
9331
|
+
rec_layout = QGridLayout()
|
|
9332
|
+
|
|
9333
|
+
# Branch removal
|
|
9160
9334
|
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)
|
|
9335
|
+
rec_layout.addWidget(QLabel("Skeleton Voxel Branch Length to Remove (Compensates for spines):"), 0, 0)
|
|
9336
|
+
rec_layout.addWidget(self.branch_removal, 0, 1)
|
|
9337
|
+
|
|
9338
|
+
# Auto checkbox
|
|
9175
9339
|
self.auto = QPushButton("Auto")
|
|
9176
9340
|
self.auto.setCheckable(True)
|
|
9177
9341
|
self.auto.setChecked(True)
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9342
|
+
rec_layout.addWidget(QLabel("Attempt to Auto Correct Skeleton Looping:"), 1, 0)
|
|
9343
|
+
rec_layout.addWidget(self.auto, 1, 1)
|
|
9344
|
+
|
|
9345
|
+
rec_group.setLayout(rec_layout)
|
|
9346
|
+
main_layout.addWidget(rec_group)
|
|
9347
|
+
|
|
9348
|
+
# --- Optional Corrections Group ---
|
|
9349
|
+
opt_group = QGroupBox("Optional Corrections")
|
|
9350
|
+
opt_layout = QGridLayout()
|
|
9351
|
+
|
|
9352
|
+
# Max volume
|
|
9353
|
+
self.max_vol = QLineEdit("0")
|
|
9354
|
+
opt_layout.addWidget(QLabel("Maximum Voxel Volume to Retain (Compensates for skeleton looping):"), 0, 0)
|
|
9355
|
+
opt_layout.addWidget(self.max_vol, 0, 1)
|
|
9356
|
+
|
|
9357
|
+
# Component dilation
|
|
9358
|
+
self.comp_dil = QLineEdit("0")
|
|
9359
|
+
opt_layout.addWidget(QLabel("Voxel distance to merge nearby nodes (Compensates for multi-branch regions):"), 1, 0)
|
|
9360
|
+
opt_layout.addWidget(self.comp_dil, 1, 1)
|
|
9361
|
+
|
|
9362
|
+
opt_group.setLayout(opt_layout)
|
|
9363
|
+
main_layout.addWidget(opt_group)
|
|
9364
|
+
|
|
9365
|
+
# Set retain variable but don't add to layout
|
|
9182
9366
|
if not called:
|
|
9183
9367
|
self.retain = QPushButton("Retain")
|
|
9184
9368
|
self.retain.setCheckable(True)
|
|
9185
9369
|
self.retain.setChecked(True)
|
|
9186
|
-
#layout.addRow("Retain Original Edges? (Will be moved to overlay 2):", self.retain)
|
|
9187
9370
|
else:
|
|
9188
9371
|
self.retain = False
|
|
9189
|
-
|
|
9372
|
+
|
|
9190
9373
|
# Add Run button
|
|
9191
9374
|
run_button = QPushButton("Run Node Generation")
|
|
9192
9375
|
run_button.clicked.connect(self.run_gennodes)
|
|
9193
|
-
|
|
9376
|
+
main_layout.addWidget(run_button)
|
|
9194
9377
|
|
|
9195
9378
|
def run_gennodes(self):
|
|
9196
9379
|
|
|
@@ -9311,49 +9494,84 @@ class BranchDialog(QDialog):
|
|
|
9311
9494
|
super().__init__(parent)
|
|
9312
9495
|
self.setWindowTitle("Label Branches (of edges)")
|
|
9313
9496
|
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)
|
|
9497
|
+
|
|
9498
|
+
# Main layout
|
|
9499
|
+
main_layout = QVBoxLayout(self)
|
|
9500
|
+
|
|
9501
|
+
# --- Correction Options Group ---
|
|
9502
|
+
correction_group = QGroupBox("Correction Options")
|
|
9503
|
+
correction_layout = QGridLayout()
|
|
9504
|
+
|
|
9505
|
+
# Branch Fix checkbox
|
|
9330
9506
|
self.fix = QPushButton("Auto-Correct Branches")
|
|
9331
9507
|
self.fix.setCheckable(True)
|
|
9332
9508
|
self.fix.setChecked(False)
|
|
9333
|
-
|
|
9334
|
-
|
|
9509
|
+
correction_layout.addWidget(QLabel("Attempt to auto-correct branch labels:"), 0, 0)
|
|
9510
|
+
correction_layout.addWidget(self.fix, 0, 1)
|
|
9511
|
+
|
|
9512
|
+
# Fix value
|
|
9335
9513
|
self.fix_val = QLineEdit('4')
|
|
9336
|
-
|
|
9337
|
-
|
|
9514
|
+
correction_layout.addWidget(QLabel("Avg Degree of Nearby Branch Communities to Merge (4-6 recommended):"), 1, 0)
|
|
9515
|
+
correction_layout.addWidget(self.fix_val, 1, 1)
|
|
9516
|
+
|
|
9517
|
+
# Seed
|
|
9518
|
+
self.seed = QLineEdit('')
|
|
9519
|
+
correction_layout.addWidget(QLabel("Random seed for auto correction (int - optional):"), 2, 0)
|
|
9520
|
+
correction_layout.addWidget(self.seed, 2, 1)
|
|
9521
|
+
|
|
9522
|
+
correction_group.setLayout(correction_layout)
|
|
9523
|
+
main_layout.addWidget(correction_group)
|
|
9524
|
+
|
|
9525
|
+
# --- Processing Options Group ---
|
|
9526
|
+
processing_group = QGroupBox("Processing Options")
|
|
9527
|
+
processing_layout = QGridLayout()
|
|
9528
|
+
|
|
9529
|
+
# Downsample factor
|
|
9338
9530
|
self.down_factor = QLineEdit("0")
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9531
|
+
processing_layout.addWidget(QLabel("Internal downsample factor (will recompute nodes):"), 0, 0)
|
|
9532
|
+
processing_layout.addWidget(self.down_factor, 0, 1)
|
|
9533
|
+
|
|
9534
|
+
# Cubic checkbox
|
|
9342
9535
|
self.cubic = QPushButton("Cubic Downsample")
|
|
9343
9536
|
self.cubic.setCheckable(True)
|
|
9344
9537
|
self.cubic.setChecked(False)
|
|
9345
|
-
|
|
9346
|
-
|
|
9538
|
+
processing_layout.addWidget(QLabel("Use cubic downsample? (Slower but preserves structure better):"), 1, 0)
|
|
9539
|
+
processing_layout.addWidget(self.cubic, 1, 1)
|
|
9540
|
+
|
|
9541
|
+
processing_group.setLayout(processing_layout)
|
|
9542
|
+
main_layout.addWidget(processing_group)
|
|
9543
|
+
|
|
9544
|
+
# --- Misc Options Group ---
|
|
9545
|
+
misc_group = QGroupBox("Misc Options")
|
|
9546
|
+
misc_layout = QGridLayout()
|
|
9547
|
+
|
|
9548
|
+
# Nodes checkbox
|
|
9549
|
+
self.nodes = QPushButton("Generate Nodes")
|
|
9550
|
+
self.nodes.setCheckable(True)
|
|
9551
|
+
self.nodes.setChecked(True)
|
|
9552
|
+
misc_layout.addWidget(QLabel("Generate nodes from edges? (Skip if already completed):"), 0, 0)
|
|
9553
|
+
misc_layout.addWidget(self.nodes, 0, 1)
|
|
9554
|
+
|
|
9555
|
+
# GPU checkbox
|
|
9556
|
+
self.GPU = QPushButton("GPU")
|
|
9557
|
+
self.GPU.setCheckable(True)
|
|
9558
|
+
self.GPU.setChecked(False)
|
|
9559
|
+
misc_layout.addWidget(QLabel("Use GPU (May downsample large images):"), 1, 0)
|
|
9560
|
+
misc_layout.addWidget(self.GPU, 1, 1)
|
|
9561
|
+
|
|
9562
|
+
misc_group.setLayout(misc_layout)
|
|
9563
|
+
main_layout.addWidget(misc_group)
|
|
9564
|
+
|
|
9347
9565
|
# Add Run button
|
|
9348
9566
|
run_button = QPushButton("Run Branch Label")
|
|
9349
9567
|
run_button.clicked.connect(self.branch_label)
|
|
9350
|
-
|
|
9568
|
+
main_layout.addWidget(run_button)
|
|
9351
9569
|
|
|
9352
|
-
if self.parent().channel_data[0] is not None:
|
|
9570
|
+
if self.parent().channel_data[0] is not None or self.parent().channel_data[3] is not None:
|
|
9353
9571
|
QMessageBox.critical(
|
|
9354
9572
|
self,
|
|
9355
9573
|
"Alert",
|
|
9356
|
-
"The nodes
|
|
9574
|
+
"The nodes and overlay 2 channels will be intermittently overwritten when running this method"
|
|
9357
9575
|
)
|
|
9358
9576
|
|
|
9359
9577
|
def branch_label(self):
|
|
@@ -9370,8 +9588,7 @@ class BranchDialog(QDialog):
|
|
|
9370
9588
|
cubic = self.cubic.isChecked()
|
|
9371
9589
|
fix = self.fix.isChecked()
|
|
9372
9590
|
fix_val = float(self.fix_val.text()) if self.fix_val.text() else None
|
|
9373
|
-
|
|
9374
|
-
|
|
9591
|
+
seed = int(self.seed.text()) if self.seed.text() else None
|
|
9375
9592
|
|
|
9376
9593
|
original_shape = my_network.edges.shape
|
|
9377
9594
|
original_array = copy.deepcopy(my_network.edges)
|
|
@@ -9392,7 +9609,7 @@ class BranchDialog(QDialog):
|
|
|
9392
9609
|
|
|
9393
9610
|
temp_network.morph_proximity(search = [3,3], fastdil = True) #Detect network of nearby branches
|
|
9394
9611
|
|
|
9395
|
-
temp_network.community_partition(weighted = False, style = 1, dostats = False) #Find communities with louvain, unweighted params
|
|
9612
|
+
temp_network.community_partition(weighted = False, style = 1, dostats = False, seed = seed) #Find communities with louvain, unweighted params
|
|
9396
9613
|
|
|
9397
9614
|
targs = n3d.fix_branches(temp_network.nodes, temp_network.network, temp_network.communities, fix_val)
|
|
9398
9615
|
|