nettracer3d 0.9.4__py3-none-any.whl → 0.9.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.
Potentially problematic release.
This version of nettracer3d might be problematic. Click here for more details.
- nettracer3d/community_extractor.py +88 -25
- nettracer3d/nettracer.py +93 -113
- nettracer3d/nettracer_gui.py +457 -200
- {nettracer3d-0.9.4.dist-info → nettracer3d-0.9.5.dist-info}/METADATA +5 -3
- {nettracer3d-0.9.4.dist-info → nettracer3d-0.9.5.dist-info}/RECORD +9 -9
- {nettracer3d-0.9.4.dist-info → nettracer3d-0.9.5.dist-info}/WHEEL +0 -0
- {nettracer3d-0.9.4.dist-info → nettracer3d-0.9.5.dist-info}/entry_points.txt +0 -0
- {nettracer3d-0.9.4.dist-info → nettracer3d-0.9.5.dist-info}/licenses/LICENSE +0 -0
- {nettracer3d-0.9.4.dist-info → nettracer3d-0.9.5.dist-info}/top_level.txt +0 -0
|
@@ -549,54 +549,117 @@ def convert_node_colors_to_names(node_to_color: Dict[int, Tuple[int, int, int]],
|
|
|
549
549
|
|
|
550
550
|
num_entries = len(node_to_color)
|
|
551
551
|
|
|
552
|
-
# Calculate
|
|
553
|
-
|
|
554
|
-
|
|
552
|
+
# Calculate text widths to determine optimal figure size
|
|
553
|
+
sorted_nodes = sorted(node_to_color.keys())
|
|
554
|
+
|
|
555
|
+
# Create a temporary figure to measure text widths
|
|
556
|
+
temp_fig, temp_ax = plt.subplots(figsize=(1, 1))
|
|
557
|
+
|
|
558
|
+
max_node_width = 0
|
|
559
|
+
max_color_width = 0
|
|
560
|
+
|
|
561
|
+
for node in sorted_nodes:
|
|
562
|
+
color_name = node_to_names[node]
|
|
563
|
+
|
|
564
|
+
# Measure node ID text width
|
|
565
|
+
node_text = temp_ax.text(0, 0, str(node), fontsize=12, fontweight='bold')
|
|
566
|
+
node_bbox = node_text.get_window_extent(renderer=temp_fig.canvas.get_renderer())
|
|
567
|
+
node_width = node_bbox.width
|
|
568
|
+
max_node_width = max(max_node_width, node_width)
|
|
569
|
+
|
|
570
|
+
# Measure color name text width
|
|
571
|
+
color_text = temp_ax.text(0, 0, color_name.replace('_', ' ').title(), fontsize=11)
|
|
572
|
+
color_bbox = color_text.get_window_extent(renderer=temp_fig.canvas.get_renderer())
|
|
573
|
+
color_width = color_bbox.width
|
|
574
|
+
max_color_width = max(max_color_width, color_width)
|
|
575
|
+
|
|
576
|
+
plt.close(temp_fig)
|
|
577
|
+
|
|
578
|
+
# Convert pixel widths to figure units (approximate conversion)
|
|
579
|
+
# This is a rough conversion - matplotlib uses 72 DPI by default
|
|
580
|
+
dpi = 72
|
|
581
|
+
max_node_width_fig = max_node_width / dpi
|
|
582
|
+
max_color_width_fig = max_color_width / dpi
|
|
583
|
+
|
|
584
|
+
# Calculate optimal figure dimensions
|
|
585
|
+
entry_height = 0.6 # Reduced for tighter spacing
|
|
586
|
+
margin = 0.3
|
|
587
|
+
swatch_width = 0.8
|
|
588
|
+
spacing = 0.2
|
|
589
|
+
|
|
590
|
+
# Calculate total width needed
|
|
591
|
+
total_width = (margin + max_node_width_fig + spacing +
|
|
592
|
+
swatch_width + spacing + max_color_width_fig + margin)
|
|
555
593
|
|
|
556
|
-
#
|
|
557
|
-
|
|
558
|
-
|
|
594
|
+
# Ensure minimum width for readability
|
|
595
|
+
total_width = max(total_width, 4.0)
|
|
596
|
+
|
|
597
|
+
# Calculate total height
|
|
598
|
+
title_height = 0.8
|
|
599
|
+
total_height = num_entries * entry_height + title_height + 2 * margin
|
|
600
|
+
|
|
601
|
+
# Create the actual figure with calculated dimensions
|
|
602
|
+
fig, ax = plt.subplots(figsize=(total_width, total_height))
|
|
603
|
+
|
|
604
|
+
# Set axis limits to match our calculated dimensions
|
|
605
|
+
ax.set_xlim(0, total_width)
|
|
559
606
|
ax.set_ylim(0, total_height)
|
|
560
607
|
ax.axis('off')
|
|
561
608
|
|
|
562
609
|
# Title
|
|
563
|
-
ax.text(
|
|
564
|
-
fontsize=
|
|
565
|
-
|
|
566
|
-
# Sort nodes for consistent display
|
|
567
|
-
sorted_nodes = sorted(node_to_color.keys())
|
|
610
|
+
ax.text(total_width/2, total_height - margin - 0.2, 'Color Legend',
|
|
611
|
+
fontsize=14, fontweight='bold', ha='center', va='top')
|
|
568
612
|
|
|
569
613
|
# Create legend entries
|
|
570
614
|
for i, node in enumerate(sorted_nodes):
|
|
571
|
-
y_pos = total_height - (i + 1) * entry_height
|
|
615
|
+
y_pos = total_height - title_height - margin - (i + 1) * entry_height + entry_height/2
|
|
572
616
|
rgb = node_to_color[node]
|
|
573
617
|
color_name = node_to_names[node]
|
|
574
618
|
|
|
575
619
|
# Normalize RGB values for matplotlib (0-1 range)
|
|
576
620
|
norm_rgb = tuple(c/255.0 for c in rgb)
|
|
577
621
|
|
|
578
|
-
#
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
622
|
+
# Position calculations
|
|
623
|
+
node_x = margin
|
|
624
|
+
swatch_x = margin + max_node_width_fig + spacing
|
|
625
|
+
color_x = swatch_x + swatch_width + spacing
|
|
582
626
|
|
|
583
|
-
# Node ID (
|
|
584
|
-
ax.text(
|
|
627
|
+
# Node ID (left-aligned)
|
|
628
|
+
ax.text(node_x, y_pos, str(node), fontsize=12, fontweight='bold',
|
|
585
629
|
va='center', ha='left')
|
|
586
630
|
|
|
587
|
-
#
|
|
588
|
-
|
|
631
|
+
# Draw color swatch
|
|
632
|
+
swatch_y = y_pos - entry_height/4
|
|
633
|
+
swatch = Rectangle((swatch_x, swatch_y), swatch_width, entry_height/2,
|
|
634
|
+
facecolor=norm_rgb, edgecolor='black', linewidth=1)
|
|
635
|
+
ax.add_patch(swatch)
|
|
636
|
+
|
|
637
|
+
# Color name
|
|
638
|
+
formatted_name = color_name.replace('_', ' ').title()
|
|
639
|
+
# Truncate very long color names to prevent layout issues
|
|
640
|
+
if len(formatted_name) > 25:
|
|
641
|
+
formatted_name = formatted_name[:22] + "..."
|
|
642
|
+
|
|
643
|
+
ax.text(color_x, y_pos, formatted_name,
|
|
589
644
|
fontsize=11, va='center', ha='left')
|
|
590
645
|
|
|
591
|
-
# Add border around the legend
|
|
592
|
-
|
|
593
|
-
|
|
646
|
+
# Add a subtle border around the entire legend
|
|
647
|
+
border_margin = 0.1
|
|
648
|
+
border = Rectangle((border_margin, border_margin),
|
|
649
|
+
total_width - 2*border_margin,
|
|
650
|
+
total_height - 2*border_margin,
|
|
651
|
+
fill=False, edgecolor='lightgray', linewidth=1.5)
|
|
594
652
|
ax.add_patch(border)
|
|
595
653
|
|
|
596
|
-
|
|
654
|
+
# Remove any extra whitespace
|
|
655
|
+
plt.tight_layout(pad=0.1)
|
|
656
|
+
|
|
657
|
+
# Adjust the figure to eliminate whitespace
|
|
658
|
+
ax.margins(0)
|
|
659
|
+
fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
|
|
597
660
|
|
|
598
661
|
if save_path:
|
|
599
|
-
plt.savefig(save_path, dpi=300, bbox_inches='tight')
|
|
662
|
+
plt.savefig(save_path, dpi=300, bbox_inches='tight', pad_inches=0.05)
|
|
600
663
|
|
|
601
664
|
plt.show()
|
|
602
665
|
|
nettracer3d/nettracer.py
CHANGED
|
@@ -1267,31 +1267,6 @@ def dilate_2D(array, search, scaling = 1):
|
|
|
1267
1267
|
|
|
1268
1268
|
return inv
|
|
1269
1269
|
|
|
1270
|
-
def erode_2D(array, search, scaling=1):
|
|
1271
|
-
"""
|
|
1272
|
-
Erode a 2D array using distance transform method.
|
|
1273
|
-
|
|
1274
|
-
Parameters:
|
|
1275
|
-
array -- Input 2D binary array
|
|
1276
|
-
search -- Distance within which to erode
|
|
1277
|
-
scaling -- Scaling factor (default: 1)
|
|
1278
|
-
|
|
1279
|
-
Returns:
|
|
1280
|
-
Eroded 2D array
|
|
1281
|
-
"""
|
|
1282
|
-
# For erosion, we work directly with the foreground
|
|
1283
|
-
# No need to invert the array
|
|
1284
|
-
|
|
1285
|
-
# Compute distance transform on the foreground
|
|
1286
|
-
dt = smart_dilate.compute_distance_transform_distance(array)
|
|
1287
|
-
|
|
1288
|
-
# Apply scaling
|
|
1289
|
-
dt = dt * scaling
|
|
1290
|
-
|
|
1291
|
-
# Threshold to keep only points that are at least 'search' distance from the boundary
|
|
1292
|
-
eroded = dt >= search
|
|
1293
|
-
|
|
1294
|
-
return eroded
|
|
1295
1270
|
|
|
1296
1271
|
def dilate_3D_dt(array, search_distance, xy_scaling=1.0, z_scaling=1.0):
|
|
1297
1272
|
"""
|
|
@@ -1353,7 +1328,42 @@ def dilate_3D_dt(array, search_distance, xy_scaling=1.0, z_scaling=1.0):
|
|
|
1353
1328
|
|
|
1354
1329
|
return inv.astype(np.uint8)
|
|
1355
1330
|
|
|
1356
|
-
def
|
|
1331
|
+
def erode_2D(array, search, scaling=1, preserve_labels = False):
|
|
1332
|
+
"""
|
|
1333
|
+
Erode a 2D array using distance transform method.
|
|
1334
|
+
|
|
1335
|
+
Parameters:
|
|
1336
|
+
array -- Input 2D binary array
|
|
1337
|
+
search -- Distance within which to erode
|
|
1338
|
+
scaling -- Scaling factor (default: 1)
|
|
1339
|
+
|
|
1340
|
+
Returns:
|
|
1341
|
+
Eroded 2D array
|
|
1342
|
+
"""
|
|
1343
|
+
# For erosion, we work directly with the foreground
|
|
1344
|
+
# No need to invert the array
|
|
1345
|
+
|
|
1346
|
+
if preserve_labels:
|
|
1347
|
+
from skimage.segmentation import find_boundaries
|
|
1348
|
+
borders = find_boundaries(array, mode='thick')
|
|
1349
|
+
mask = array * invert_array(borders)
|
|
1350
|
+
mask = smart_dilate.compute_distance_transform_distance(mask)
|
|
1351
|
+
mask = mask * scaling
|
|
1352
|
+
mask = mask >= search
|
|
1353
|
+
array = mask * array
|
|
1354
|
+
else:
|
|
1355
|
+
# Compute distance transform on the foreground
|
|
1356
|
+
dt = smart_dilate.compute_distance_transform_distance(array)
|
|
1357
|
+
|
|
1358
|
+
# Apply scaling
|
|
1359
|
+
dt = dt * scaling
|
|
1360
|
+
|
|
1361
|
+
# Threshold to keep only points that are at least 'search' distance from the boundary
|
|
1362
|
+
array = dt > search
|
|
1363
|
+
|
|
1364
|
+
return array
|
|
1365
|
+
|
|
1366
|
+
def erode_3D_dt(array, search_distance, xy_scaling=1.0, z_scaling=1.0, preserve_labels = False):
|
|
1357
1367
|
"""
|
|
1358
1368
|
Erode a 3D array using distance transform method. DT erosion produces perfect results
|
|
1359
1369
|
with Euclidean geometry, but may be slower for large arrays.
|
|
@@ -1371,43 +1381,24 @@ def erode_3D_dt(array, search_distance, xy_scaling=1.0, z_scaling=1.0):
|
|
|
1371
1381
|
|
|
1372
1382
|
if array.shape[0] == 1:
|
|
1373
1383
|
# Handle 2D case
|
|
1374
|
-
return erode_2D(array, search_distance, scaling=xy_scaling)
|
|
1375
|
-
|
|
1376
|
-
# For erosion, we work directly with the foreground (no inversion needed)
|
|
1377
|
-
|
|
1378
|
-
"""
|
|
1379
|
-
# Determine which dimension needs resampling
|
|
1380
|
-
if (z_scaling > xy_scaling):
|
|
1381
|
-
# Z dimension needs to be stretched
|
|
1382
|
-
zoom_factor = [z_scaling/xy_scaling, 1, 1] # Scale factor for [z, y, x]
|
|
1383
|
-
rev_factor = [xy_scaling/z_scaling, 1, 1]
|
|
1384
|
-
cardinal = xy_scaling
|
|
1385
|
-
elif (xy_scaling > z_scaling):
|
|
1386
|
-
# XY dimensions need to be stretched
|
|
1387
|
-
zoom_factor = [1, xy_scaling/z_scaling, xy_scaling/z_scaling] # Scale factor for [z, y, x]
|
|
1388
|
-
rev_factor = [1, z_scaling/xy_scaling, z_scaling/xy_scaling] # Scale factor for [z, y, x]
|
|
1389
|
-
cardinal = z_scaling
|
|
1390
|
-
else:
|
|
1391
|
-
# Already uniform scaling, no need to resample
|
|
1392
|
-
zoom_factor = None
|
|
1393
|
-
rev_factor = None
|
|
1394
|
-
cardinal = xy_scaling
|
|
1384
|
+
return erode_2D(array, search_distance, scaling=xy_scaling, preserve_labels = True)
|
|
1395
1385
|
|
|
1396
|
-
# Resample the mask if needed
|
|
1397
|
-
if zoom_factor:
|
|
1398
|
-
array = ndimage.zoom(array, zoom_factor, order=0) # Use order=0 for binary masks
|
|
1399
|
-
"""
|
|
1400
|
-
|
|
1401
|
-
print("Computing a distance transform for a perfect erosion...")
|
|
1402
1386
|
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1387
|
+
if preserve_labels:
|
|
1388
|
+
|
|
1389
|
+
|
|
1390
|
+
from skimage.segmentation import find_boundaries
|
|
1391
|
+
|
|
1392
|
+
borders = find_boundaries(array, mode='thick')
|
|
1393
|
+
mask = array * invert_array(borders)
|
|
1394
|
+
mask = smart_dilate.compute_distance_transform_distance(mask, sampling = [z_scaling, xy_scaling, xy_scaling])
|
|
1395
|
+
mask = mask >= search_distance
|
|
1396
|
+
array = mask * array
|
|
1397
|
+
else:
|
|
1398
|
+
array = smart_dilate.compute_distance_transform_distance(array, sampling = [z_scaling, xy_scaling, xy_scaling])
|
|
1399
|
+
# Threshold the distance transform to get eroded result
|
|
1400
|
+
# For erosion, we keep only the points that are at least search_distance from the boundary
|
|
1401
|
+
array = array > search_distance
|
|
1411
1402
|
|
|
1412
1403
|
# Resample back to original dimensions if needed
|
|
1413
1404
|
#if rev_factor:
|
|
@@ -1574,7 +1565,7 @@ def dilate_3D_old(tiff_array, dilated_x=3, dilated_y=3, dilated_z=3):
|
|
|
1574
1565
|
|
|
1575
1566
|
|
|
1576
1567
|
def erode_3D(tiff_array, eroded_x, eroded_y, eroded_z):
|
|
1577
|
-
"""Internal method to erode an array in 3D. Erosion this way is
|
|
1568
|
+
"""Internal method to erode an array in 3D. Erosion this way is faster than using a distance transform although the latter is theoretically more accurate.
|
|
1578
1569
|
Arguments are an array, and the desired pixel erosion amounts in X, Y, Z."""
|
|
1579
1570
|
|
|
1580
1571
|
if tiff_array.shape[0] == 1:
|
|
@@ -2124,15 +2115,15 @@ def dilate(arrayimage, amount, xy_scale = 1, z_scale = 1, directory = None, fast
|
|
|
2124
2115
|
|
|
2125
2116
|
return arrayimage
|
|
2126
2117
|
|
|
2127
|
-
def erode(arrayimage, amount, xy_scale = 1, z_scale = 1, mode = 0):
|
|
2128
|
-
if len(np.unique(arrayimage)) > 2: #binarize
|
|
2118
|
+
def erode(arrayimage, amount, xy_scale = 1, z_scale = 1, mode = 0, preserve_labels = False):
|
|
2119
|
+
if not preserve_labels and len(np.unique(arrayimage)) > 2: #binarize
|
|
2129
2120
|
arrayimage = binarize(arrayimage)
|
|
2130
2121
|
erode_xy, erode_z = dilation_length_to_pixels(xy_scale, z_scale, amount, amount)
|
|
2131
2122
|
|
|
2132
2123
|
if mode == 0:
|
|
2133
2124
|
arrayimage = (erode_3D(arrayimage, erode_xy, erode_xy, erode_z)) * 255
|
|
2134
2125
|
else:
|
|
2135
|
-
arrayimage = erode_3D_dt(arrayimage, amount, xy_scaling=xy_scale, z_scaling=z_scale)
|
|
2126
|
+
arrayimage = erode_3D_dt(arrayimage, amount, xy_scaling=xy_scale, z_scaling=z_scale, preserve_labels = preserve_labels)
|
|
2136
2127
|
|
|
2137
2128
|
if np.max(arrayimage) == 1:
|
|
2138
2129
|
arrayimage = arrayimage * 255
|
|
@@ -3847,7 +3838,7 @@ class Network_3D:
|
|
|
3847
3838
|
|
|
3848
3839
|
self._search_region = self._nodes
|
|
3849
3840
|
|
|
3850
|
-
def calculate_edges(self, binary_edges, diledge = None, inners = True,
|
|
3841
|
+
def calculate_edges(self, binary_edges, diledge = None, inners = True, search = None, remove_edgetrunk = 0, GPU = True, fast_dil = False, skeletonized = False):
|
|
3851
3842
|
"""
|
|
3852
3843
|
Method to calculate the edges that are used to directly connect nodes. May be done with or without the search region, however using search_region is recommended.
|
|
3853
3844
|
The search_region property must be set to use the search region, otherwise the nodes property must be set. Sets the edges property
|
|
@@ -3856,7 +3847,6 @@ class Network_3D:
|
|
|
3856
3847
|
so some amount of dilation is recommended if there are any, but not so much to create overconnectivity. This is a value that needs to be tuned by the user.
|
|
3857
3848
|
:param inners: (Optional - Val = True; boolean). Will use inner edges if True, will not if False. Inner edges are parts of the edge mask that exist within search regions. If search regions overlap,
|
|
3858
3849
|
any edges that exist within the overlap will only assert connectivity if 'inners' is True.
|
|
3859
|
-
:param hash_inner_edges: (Optional - Val = True; boolean). If False, all search regions that contain an edge object connecting multiple nodes will be assigned as connected.
|
|
3860
3850
|
If True, an extra processing step is used to sort the correct connectivity amongst these search_regions. Can only be computed when search_regions property is set.
|
|
3861
3851
|
:param search: (Optional - Val = None; int). Amount for nodes to search for connections, assuming the search_regions are not being used. Assigning a value to this param will utilize the secondary algorithm and not the search_regions.
|
|
3862
3852
|
:param remove_edgetrunk: (Optional - Val = 0; int). Amount of times to remove the 'Trunk' from the edges. A trunk in this case is the largest (by vol) edge object remaining after nodes have broken up the edges.
|
|
@@ -3909,11 +3899,7 @@ class Network_3D:
|
|
|
3909
3899
|
labelled_edges, num_edge = label_objects(outer_edges)
|
|
3910
3900
|
|
|
3911
3901
|
if inners:
|
|
3912
|
-
|
|
3913
|
-
if search is None and hash_inner_edges is True:
|
|
3914
|
-
inner_edges = hash_inners(self._search_region, binary_edges, GPU = GPU)
|
|
3915
|
-
else:
|
|
3916
|
-
inner_edges = establish_inner_edges(search_region, binary_edges)
|
|
3902
|
+
inner_edges = hash_inners(self._search_region, binary_edges, GPU = GPU)
|
|
3917
3903
|
|
|
3918
3904
|
del binary_edges
|
|
3919
3905
|
|
|
@@ -4045,7 +4031,7 @@ class Network_3D:
|
|
|
4045
4031
|
self._network_lists = network_analysis.read_excel_to_lists(df)
|
|
4046
4032
|
self._network, net_weights = network_analysis.weighted_network(df)
|
|
4047
4033
|
|
|
4048
|
-
def calculate_all(self, nodes, edges, xy_scale = 1, z_scale = 1, down_factor = None, search = None, diledge = None, inners = True,
|
|
4034
|
+
def calculate_all(self, nodes, edges, xy_scale = 1, z_scale = 1, down_factor = None, search = None, diledge = None, inners = True, remove_trunk = 0, ignore_search_region = False, other_nodes = None, label_nodes = True, directory = None, GPU = True, fast_dil = True, skeletonize = False, GPU_downsample = None):
|
|
4049
4035
|
"""
|
|
4050
4036
|
Method to calculate and save to mem all properties of a Network_3D object. In general, after initializing a Network_3D object, this method should be called on the node and edge masks that will be used to calculate the network.
|
|
4051
4037
|
:param nodes: (Mandatory; String or ndarray). Filepath to segmented nodes mask or a numpy array containing the same.
|
|
@@ -4058,7 +4044,6 @@ class Network_3D:
|
|
|
4058
4044
|
so some amount of dilation is recommended if there are any, but not so much to create overconnectivity. This is a value that needs to be tuned by the user.
|
|
4059
4045
|
:param inners: (Optional - Val = True; boolean). Will use inner edges if True, will not if False. Inner edges are parts of the edge mask that exist within search regions. If search regions overlap,
|
|
4060
4046
|
any edges that exist within the overlap will only assert connectivity if 'inners' is True.
|
|
4061
|
-
:param hash_inners: (Optional - Val = True; boolean). If False, all search regions that contain an edge object connecting multiple nodes will be assigned as connected.
|
|
4062
4047
|
If True, an extra processing step is used to sort the correct connectivity amongst these search_regions. Can only be computed when search_regions property is set.
|
|
4063
4048
|
:param remove_trunk: (Optional - Val = 0; int). Amount of times to remove the 'Trunk' from the edges. A trunk in this case is the largest (by vol) edge object remaining after nodes have broken up the edges.
|
|
4064
4049
|
Any 'Trunks' removed will be absent for connection calculations.
|
|
@@ -4120,7 +4105,7 @@ class Network_3D:
|
|
|
4120
4105
|
except:
|
|
4121
4106
|
pass
|
|
4122
4107
|
|
|
4123
|
-
self.calculate_edges(edges, diledge = diledge, inners = inners,
|
|
4108
|
+
self.calculate_edges(edges, diledge = diledge, inners = inners, search = search, remove_edgetrunk = remove_trunk, GPU = GPU, fast_dil = fast_dil, skeletonized = skeletonize) #Will have to be moved out if the second method becomes more directly implemented
|
|
4124
4109
|
else:
|
|
4125
4110
|
self._edges, _ = label_objects(edges)
|
|
4126
4111
|
|
|
@@ -5104,13 +5089,16 @@ class Network_3D:
|
|
|
5104
5089
|
|
|
5105
5090
|
|
|
5106
5091
|
for node in G.nodes():
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5092
|
+
try:
|
|
5093
|
+
nodeid = node_identities[node]
|
|
5094
|
+
neighbors = list(G.neighbors(node))
|
|
5095
|
+
for subnode in neighbors:
|
|
5096
|
+
subnodeid = node_identities[subnode]
|
|
5097
|
+
if subnodeid == root:
|
|
5098
|
+
neighborhood_dict[nodeid] += 1
|
|
5099
|
+
break
|
|
5100
|
+
except:
|
|
5101
|
+
pass
|
|
5114
5102
|
|
|
5115
5103
|
title1 = f'Neighborhood Distribution of Nodes in Network from Nodes: {root}'
|
|
5116
5104
|
title2 = f'Neighborhood Distribution of Nodes in Network from Nodes {root} as a proportion of total nodes of that ID'
|
|
@@ -5219,34 +5207,22 @@ class Network_3D:
|
|
|
5219
5207
|
bounds = (min_coords, max_coords)
|
|
5220
5208
|
dim_list = max_coords - min_coords
|
|
5221
5209
|
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
"""
|
|
5236
|
-
|
|
5237
|
-
for centroid in roots:
|
|
5238
|
-
|
|
5239
|
-
if ((centroid[2] - min_coords[0]) > dim_list[0] * factor) and ((max_coords[0] - centroid[2]) > dim_list[0] * factor) and ((centroid[1] - min_coords[1]) > dim_list[1] * factor) and ((max_coords[1] - centroid[1]) > dim_list[1] * factor) and ((centroid[0] - min_coords[2]) > dim_list[2] * factor) and ((max_coords[2] - centroid[0]) > dim_list[2] * factor):
|
|
5210
|
+
for centroid in roots:
|
|
5211
|
+
# Assuming centroid is [z, y, x] based on your indexing
|
|
5212
|
+
z, y, x = centroid[0], centroid[1], centroid[2]
|
|
5213
|
+
|
|
5214
|
+
# Check x-dimension
|
|
5215
|
+
x_ok = (x - min_coords[0]) > dim_list[0] * factor and (max_coords[0] - x) > dim_list[0] * factor
|
|
5216
|
+
# Check y-dimension
|
|
5217
|
+
y_ok = (y - min_coords[1]) > dim_list[1] * factor and (max_coords[1] - y) > dim_list[1] * factor
|
|
5218
|
+
|
|
5219
|
+
if dim == 3: # 3D case
|
|
5220
|
+
# Check z-dimension
|
|
5221
|
+
z_ok = (z - min_coords[2]) > dim_list[2] * factor and (max_coords[2] - z) > dim_list[2] * factor
|
|
5222
|
+
if x_ok and y_ok and z_ok:
|
|
5240
5223
|
new_list.append(centroid)
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
#if ((centroid[2] - min_coords[0]) > dim_list[0] * factor) and ((max_coords[0] - centroid[2]) > dim_list[0] * factor) and ((centroid[1] - min_coords[1]) > dim_list[1] * factor) and ((max_coords[1] - centroid[1]) > dim_list[1] * factor) and ((centroid[0] - min_coords[2]) > dim_list[2] * factor) and ((max_coords[2] - centroid[0]) > dim_list[2] * factor):
|
|
5244
|
-
#new_list.append(centroid)
|
|
5245
|
-
#print(f"dim_list: {dim_list}, centroid: {centroid}, min_coords: {min_coords}, max_coords: {max_coords}")
|
|
5246
|
-
else:
|
|
5247
|
-
for centroid in roots:
|
|
5248
|
-
|
|
5249
|
-
if ((centroid[2] - min_coords[0]) > dim_list[0] * factor) and ((max_coords[0] - centroid[2]) > dim_list[0] * factor) and ((centroid[1] - min_coords[1]) > dim_list[1] * factor) and ((max_coords[1] - centroid[1]) > dim_list[1] * factor):
|
|
5224
|
+
else: # 2D case
|
|
5225
|
+
if x_ok and y_ok:
|
|
5250
5226
|
new_list.append(centroid)
|
|
5251
5227
|
|
|
5252
5228
|
else:
|
|
@@ -5299,8 +5275,6 @@ class Network_3D:
|
|
|
5299
5275
|
print(f"Utilizing {len(roots)} root points. Note that low n values are unstable.")
|
|
5300
5276
|
is_subset = True
|
|
5301
5277
|
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
5278
|
roots = proximity.convert_centroids_to_array(roots, xy_scale = self.xy_scale, z_scale = self.z_scale)
|
|
5305
5279
|
|
|
5306
5280
|
n_subset = len(targs)
|
|
@@ -5608,7 +5582,10 @@ class Network_3D:
|
|
|
5608
5582
|
for iden in idens:
|
|
5609
5583
|
counter[id_dict[iden]] += 1
|
|
5610
5584
|
except:
|
|
5611
|
-
|
|
5585
|
+
try:
|
|
5586
|
+
counter[id_dict[self.node_identities[node]]] += 1 # Keep them as arrays
|
|
5587
|
+
except:
|
|
5588
|
+
pass
|
|
5612
5589
|
|
|
5613
5590
|
for i in range(len(counter)): # Translate them into proportions out of 1
|
|
5614
5591
|
|
|
@@ -5645,7 +5622,10 @@ class Network_3D:
|
|
|
5645
5622
|
for iden in idents:
|
|
5646
5623
|
iden_tracker[iden] += 1
|
|
5647
5624
|
except:
|
|
5648
|
-
|
|
5625
|
+
try:
|
|
5626
|
+
iden_tracker[self.node_identities[node]] += 1
|
|
5627
|
+
except:
|
|
5628
|
+
pass
|
|
5649
5629
|
|
|
5650
5630
|
i = 0
|
|
5651
5631
|
|