nettracer3d 0.6.5__py3-none-any.whl → 0.6.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nettracer3d/community_extractor.py +0 -269
- nettracer3d/morphology.py +61 -36
- nettracer3d/nettracer.py +272 -244
- nettracer3d/nettracer_gui.py +345 -192
- nettracer3d/proximity.py +9 -8
- nettracer3d/segmenter.py +250 -181
- nettracer3d/smart_dilate.py +156 -82
- {nettracer3d-0.6.5.dist-info → nettracer3d-0.6.7.dist-info}/METADATA +6 -6
- nettracer3d-0.6.7.dist-info/RECORD +20 -0
- nettracer3d-0.6.5.dist-info/RECORD +0 -20
- {nettracer3d-0.6.5.dist-info → nettracer3d-0.6.7.dist-info}/WHEEL +0 -0
- {nettracer3d-0.6.5.dist-info → nettracer3d-0.6.7.dist-info}/entry_points.txt +0 -0
- {nettracer3d-0.6.5.dist-info → nettracer3d-0.6.7.dist-info}/licenses/LICENSE +0 -0
- {nettracer3d-0.6.5.dist-info → nettracer3d-0.6.7.dist-info}/top_level.txt +0 -0
nettracer3d/nettracer.py
CHANGED
|
@@ -547,7 +547,7 @@ def remove_branches(skeleton, length):
|
|
|
547
547
|
return image_copy
|
|
548
548
|
|
|
549
549
|
|
|
550
|
-
def estimate_object_radii(labeled_array, gpu=False, n_jobs=None):
|
|
550
|
+
def estimate_object_radii(labeled_array, gpu=False, n_jobs=None, xy_scale = 1, z_scale = 1):
|
|
551
551
|
"""
|
|
552
552
|
Estimate the radii of labeled objects in a 3D numpy array.
|
|
553
553
|
Dispatches to appropriate implementation based on parameters.
|
|
@@ -579,9 +579,9 @@ def estimate_object_radii(labeled_array, gpu=False, n_jobs=None):
|
|
|
579
579
|
gpu = False
|
|
580
580
|
|
|
581
581
|
if gpu:
|
|
582
|
-
return morphology.estimate_object_radii_gpu(labeled_array)
|
|
582
|
+
return morphology.estimate_object_radii_gpu(labeled_array, xy_scale = xy_scale, z_scale = z_scale)
|
|
583
583
|
else:
|
|
584
|
-
return morphology.estimate_object_radii_cpu(labeled_array, n_jobs)
|
|
584
|
+
return morphology.estimate_object_radii_cpu(labeled_array, n_jobs, xy_scale = xy_scale, z_scale = z_scale)
|
|
585
585
|
|
|
586
586
|
|
|
587
587
|
def break_and_label_skeleton(skeleton, peaks = 1, branch_removal = 0, comp_dil = 0, max_vol = 0, directory = None, return_skele = False, nodes = None):
|
|
@@ -744,9 +744,9 @@ def z_project(array3d, method='max'):
|
|
|
744
744
|
else:
|
|
745
745
|
raise ValueError("Method must be one of: 'max', 'mean', 'min', 'sum', 'std'")
|
|
746
746
|
|
|
747
|
-
def fill_holes_3d(array):
|
|
747
|
+
def fill_holes_3d(array, head_on = False, fill_borders = True):
|
|
748
748
|
|
|
749
|
-
def process_slice(slice_2d, border_threshold=0.08):
|
|
749
|
+
def process_slice(slice_2d, border_threshold=0.08, fill_borders = True):
|
|
750
750
|
"""
|
|
751
751
|
Process a 2D slice, considering components that touch less than border_threshold
|
|
752
752
|
of any border length as potential holes.
|
|
@@ -757,6 +757,9 @@ def fill_holes_3d(array):
|
|
|
757
757
|
"""
|
|
758
758
|
slice_2d = slice_2d.astype(np.uint8)
|
|
759
759
|
labels, num_features = ndimage.label(slice_2d)
|
|
760
|
+
|
|
761
|
+
if not fill_borders:
|
|
762
|
+
border_threshold = 0 #Testing
|
|
760
763
|
|
|
761
764
|
if num_features == 0:
|
|
762
765
|
return np.zeros_like(slice_2d)
|
|
@@ -781,8 +784,10 @@ def fill_holes_3d(array):
|
|
|
781
784
|
|
|
782
785
|
# Create mask of components that either don't touch borders
|
|
783
786
|
# or touch less than the threshold proportion
|
|
787
|
+
|
|
784
788
|
background_labels = {label for label, prop in border_proportions.items()
|
|
785
789
|
if prop > border_threshold}
|
|
790
|
+
|
|
786
791
|
|
|
787
792
|
holes_mask = ~np.isin(labels, list(background_labels))
|
|
788
793
|
|
|
@@ -802,19 +807,19 @@ def fill_holes_3d(array):
|
|
|
802
807
|
|
|
803
808
|
# Process XY plane
|
|
804
809
|
for z in range(inv_array.shape[0]):
|
|
805
|
-
array_xy[z] = process_slice(inv_array[z])
|
|
810
|
+
array_xy[z] = process_slice(inv_array[z], fill_borders = fill_borders)
|
|
806
811
|
|
|
807
|
-
if array.shape[0] > 3: #only use these dimensions for sufficiently large zstacks
|
|
812
|
+
if (array.shape[0] > 3) and not head_on: #only use these dimensions for sufficiently large zstacks
|
|
808
813
|
|
|
809
814
|
# Process XZ plane
|
|
810
815
|
for y in range(inv_array.shape[1]):
|
|
811
816
|
slice_xz = inv_array[:, y, :]
|
|
812
|
-
array_xz[:, y, :] = process_slice(slice_xz)
|
|
817
|
+
array_xz[:, y, :] = process_slice(slice_xz, fill_borders = fill_borders)
|
|
813
818
|
|
|
814
819
|
# Process YZ plane
|
|
815
820
|
for x in range(inv_array.shape[2]):
|
|
816
821
|
slice_yz = inv_array[:, :, x]
|
|
817
|
-
array_yz[:, :, x] = process_slice(slice_yz)
|
|
822
|
+
array_yz[:, :, x] = process_slice(slice_yz, fill_borders = fill_borders)
|
|
818
823
|
|
|
819
824
|
# Combine results from all three planes
|
|
820
825
|
filled = (array_xy | array_xz | array_yz) * 255
|
|
@@ -857,23 +862,48 @@ def _rescale(array, original_shape, xy_scale, z_scale):
|
|
|
857
862
|
return array
|
|
858
863
|
|
|
859
864
|
|
|
860
|
-
def remove_trunk(edges):
|
|
865
|
+
def remove_trunk(edges, num_iterations=1):
|
|
861
866
|
"""
|
|
862
|
-
|
|
863
|
-
|
|
867
|
+
Removes the largest connected objects from a 3D binary array in-place.
|
|
868
|
+
|
|
869
|
+
Parameters:
|
|
870
|
+
-----------
|
|
871
|
+
edges : ndarray
|
|
872
|
+
3D binary array containing objects to process.
|
|
873
|
+
Will be modified in-place.
|
|
874
|
+
num_iterations : int, optional
|
|
875
|
+
Number of largest objects to remove, default is 1.
|
|
876
|
+
|
|
877
|
+
Returns:
|
|
878
|
+
--------
|
|
879
|
+
ndarray
|
|
880
|
+
Reference to the modified input array.
|
|
864
881
|
"""
|
|
865
|
-
|
|
866
|
-
labeled_array = measure.label(edges)
|
|
867
|
-
|
|
868
|
-
#
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
#
|
|
875
|
-
|
|
876
|
-
|
|
882
|
+
# Label connected components
|
|
883
|
+
labeled_array, num_features = measure.label(edges, background=0, return_num=True)
|
|
884
|
+
|
|
885
|
+
# If there are fewer objects than requested iterations, adjust
|
|
886
|
+
iterations = min(num_iterations, num_features)
|
|
887
|
+
|
|
888
|
+
if iterations == 0 or num_features == 0:
|
|
889
|
+
return edges
|
|
890
|
+
|
|
891
|
+
# Count occurrences of each label
|
|
892
|
+
label_counts = np.bincount(labeled_array.ravel())
|
|
893
|
+
|
|
894
|
+
# Skip background (label 0)
|
|
895
|
+
label_counts = label_counts[1:]
|
|
896
|
+
|
|
897
|
+
# Find indices of largest objects (argsort returns ascending order, so we reverse it)
|
|
898
|
+
largest_indices = np.argsort(label_counts)[::-1][:iterations]
|
|
899
|
+
|
|
900
|
+
# Convert back to actual labels (add 1 because we skipped background)
|
|
901
|
+
largest_labels = largest_indices + 1
|
|
902
|
+
|
|
903
|
+
# Modify the input array in-place
|
|
904
|
+
for label in largest_labels:
|
|
905
|
+
edges[labeled_array == label] = 0
|
|
906
|
+
|
|
877
907
|
return edges
|
|
878
908
|
|
|
879
909
|
def hash_inners(search_region, inner_edges, GPU = True):
|
|
@@ -907,10 +937,155 @@ def dilate_2D(array, search, scaling = 1):
|
|
|
907
937
|
|
|
908
938
|
return inv
|
|
909
939
|
|
|
940
|
+
def erode_2D(array, search, scaling=1):
|
|
941
|
+
"""
|
|
942
|
+
Erode a 2D array using distance transform method.
|
|
943
|
+
|
|
944
|
+
Parameters:
|
|
945
|
+
array -- Input 2D binary array
|
|
946
|
+
search -- Distance within which to erode
|
|
947
|
+
scaling -- Scaling factor (default: 1)
|
|
948
|
+
|
|
949
|
+
Returns:
|
|
950
|
+
Eroded 2D array
|
|
951
|
+
"""
|
|
952
|
+
# For erosion, we work directly with the foreground
|
|
953
|
+
# No need to invert the array
|
|
954
|
+
|
|
955
|
+
# Compute distance transform on the foreground
|
|
956
|
+
dt = smart_dilate.compute_distance_transform_distance(array)
|
|
957
|
+
|
|
958
|
+
# Apply scaling
|
|
959
|
+
dt = dt * scaling
|
|
960
|
+
|
|
961
|
+
# Threshold to keep only points that are at least 'search' distance from the boundary
|
|
962
|
+
eroded = dt >= search
|
|
963
|
+
|
|
964
|
+
return eroded
|
|
965
|
+
|
|
966
|
+
def dilate_3D_dt(array, search_distance, xy_scaling=1.0, z_scaling=1.0):
|
|
967
|
+
"""
|
|
968
|
+
Dilate a 3D array using distance transform method. Dt dilation produces perfect results but only works in euclidean geometry and lags in big arrays.
|
|
969
|
+
|
|
970
|
+
Parameters:
|
|
971
|
+
array -- Input 3D binary array
|
|
972
|
+
search_distance -- Distance within which to dilate
|
|
973
|
+
xy_scaling -- Scaling factor for x and y dimensions (default: 1.0)
|
|
974
|
+
z_scaling -- Scaling factor for z dimension (default: 1.0)
|
|
975
|
+
|
|
976
|
+
Returns:
|
|
977
|
+
Dilated 3D array
|
|
978
|
+
"""
|
|
979
|
+
|
|
980
|
+
if array.shape[0] == 1:
|
|
981
|
+
|
|
982
|
+
return dilate_2D(array, search_distance, scaling = xy_scaling) #Use the 2d method in psueod-3d cases
|
|
983
|
+
|
|
984
|
+
|
|
985
|
+
# Invert the array (find background)
|
|
986
|
+
inv = array < 1
|
|
987
|
+
|
|
988
|
+
del array
|
|
989
|
+
|
|
990
|
+
# Determine which dimension needs resampling
|
|
991
|
+
if (z_scaling > xy_scaling):
|
|
992
|
+
# Z dimension needs to be stretched
|
|
993
|
+
zoom_factor = [z_scaling/xy_scaling, 1, 1] # Scale factor for [z, y, x]
|
|
994
|
+
rev_factor = [xy_scaling/z_scaling, 1, 1]
|
|
995
|
+
cardinal = xy_scaling
|
|
996
|
+
elif (xy_scaling > z_scaling):
|
|
997
|
+
# XY dimensions need to be stretched
|
|
998
|
+
zoom_factor = [1, xy_scaling/z_scaling, xy_scaling/z_scaling] # Scale factor for [z, y, x]
|
|
999
|
+
rev_factor = [1, z_scaling/xy_scaling, z_scaling/xy_scaling] # Scale factor for [z, y, x]
|
|
1000
|
+
cardinal = z_scaling
|
|
1001
|
+
else:
|
|
1002
|
+
# Already uniform scaling, no need to resample
|
|
1003
|
+
zoom_factor = None
|
|
1004
|
+
rev_factor = None
|
|
1005
|
+
cardinal = xy_scaling
|
|
1006
|
+
|
|
1007
|
+
# Resample the mask if needed
|
|
1008
|
+
if zoom_factor:
|
|
1009
|
+
inv = ndimage.zoom(inv, zoom_factor, order=0) # Use order=0 for binary masks
|
|
1010
|
+
|
|
1011
|
+
# Compute distance transform (Euclidean)
|
|
1012
|
+
inv = smart_dilate.compute_distance_transform_distance(inv)
|
|
1013
|
+
|
|
1014
|
+
inv = inv * cardinal
|
|
1015
|
+
|
|
1016
|
+
# Threshold the distance transform to get dilated result
|
|
1017
|
+
inv = inv <= search_distance
|
|
1018
|
+
|
|
1019
|
+
if rev_factor:
|
|
1020
|
+
inv = ndimage.zoom(inv, rev_factor, order=0) # Use order=0 for binary masks
|
|
1021
|
+
|
|
1022
|
+
return inv.astype(np.uint8)
|
|
1023
|
+
|
|
1024
|
+
def erode_3D_dt(array, search_distance, xy_scaling=1.0, z_scaling=1.0):
|
|
1025
|
+
"""
|
|
1026
|
+
Erode a 3D array using distance transform method. DT erosion produces perfect results
|
|
1027
|
+
with Euclidean geometry, but may be slower for large arrays.
|
|
1028
|
+
|
|
1029
|
+
Parameters:
|
|
1030
|
+
array -- Input 3D binary array
|
|
1031
|
+
search_distance -- Distance within which to erode
|
|
1032
|
+
xy_scaling -- Scaling factor for x and y dimensions (default: 1.0)
|
|
1033
|
+
z_scaling -- Scaling factor for z dimension (default: 1.0)
|
|
1034
|
+
GPU -- Whether to use GPU acceleration if available (default: False)
|
|
1035
|
+
|
|
1036
|
+
Returns:
|
|
1037
|
+
Eroded 3D array
|
|
1038
|
+
"""
|
|
1039
|
+
|
|
1040
|
+
if array.shape[0] == 1:
|
|
1041
|
+
# Handle 2D case
|
|
1042
|
+
return erode_2D(array, search_distance, scaling=xy_scaling)
|
|
1043
|
+
|
|
1044
|
+
# For erosion, we work directly with the foreground (no inversion needed)
|
|
1045
|
+
|
|
1046
|
+
# Determine which dimension needs resampling
|
|
1047
|
+
if (z_scaling > xy_scaling):
|
|
1048
|
+
# Z dimension needs to be stretched
|
|
1049
|
+
zoom_factor = [z_scaling/xy_scaling, 1, 1] # Scale factor for [z, y, x]
|
|
1050
|
+
rev_factor = [xy_scaling/z_scaling, 1, 1]
|
|
1051
|
+
cardinal = xy_scaling
|
|
1052
|
+
elif (xy_scaling > z_scaling):
|
|
1053
|
+
# XY dimensions need to be stretched
|
|
1054
|
+
zoom_factor = [1, xy_scaling/z_scaling, xy_scaling/z_scaling] # Scale factor for [z, y, x]
|
|
1055
|
+
rev_factor = [1, z_scaling/xy_scaling, z_scaling/xy_scaling] # Scale factor for [z, y, x]
|
|
1056
|
+
cardinal = z_scaling
|
|
1057
|
+
else:
|
|
1058
|
+
# Already uniform scaling, no need to resample
|
|
1059
|
+
zoom_factor = None
|
|
1060
|
+
rev_factor = None
|
|
1061
|
+
cardinal = xy_scaling
|
|
1062
|
+
|
|
1063
|
+
# Resample the mask if needed
|
|
1064
|
+
if zoom_factor:
|
|
1065
|
+
array = ndimage.zoom(array, zoom_factor, order=0) # Use order=0 for binary masks
|
|
1066
|
+
|
|
1067
|
+
print("Computing a distance transform for a perfect erosion...")
|
|
1068
|
+
|
|
1069
|
+
array = smart_dilate.compute_distance_transform_distance(array)
|
|
1070
|
+
|
|
1071
|
+
# Apply scaling factor
|
|
1072
|
+
array = array * cardinal
|
|
1073
|
+
|
|
1074
|
+
# Threshold the distance transform to get eroded result
|
|
1075
|
+
# For erosion, we keep only the points that are at least search_distance from the boundary
|
|
1076
|
+
array = array >= search_distance
|
|
1077
|
+
|
|
1078
|
+
# Resample back to original dimensions if needed
|
|
1079
|
+
if rev_factor:
|
|
1080
|
+
array = ndimage.zoom(array, rev_factor, order=0) # Use order=0 for binary masks
|
|
1081
|
+
|
|
1082
|
+
return array.astype(np.uint8)
|
|
1083
|
+
|
|
910
1084
|
|
|
911
1085
|
def dilate_3D(tiff_array, dilated_x, dilated_y, dilated_z):
|
|
912
1086
|
"""Internal method to dilate an array in 3D. Dilation this way is much faster than using a distance transform although the latter is theoretically more accurate.
|
|
913
|
-
Arguments are an array, and the desired pixel dilation amounts in X, Y, Z.
|
|
1087
|
+
Arguments are an array, and the desired pixel dilation amounts in X, Y, Z. Uses psuedo-3D kernels (imagine a 3D + sign rather than a cube) to approximate 3D neighborhoods but will miss diagonally located things with larger kernels, if those are needed use the distance transform version.
|
|
1088
|
+
"""
|
|
914
1089
|
|
|
915
1090
|
if tiff_array.shape[0] == 1:
|
|
916
1091
|
return dilate_2D(tiff_array, ((dilated_x - 1) / 2))
|
|
@@ -1036,118 +1211,41 @@ def dilate_3D(tiff_array, dilated_x, dilated_y, dilated_z):
|
|
|
1036
1211
|
|
|
1037
1212
|
return final_result
|
|
1038
1213
|
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
"""Recursive 3D dilation method that handles odd-numbered dilations properly.
|
|
1214
|
+
def dilate_3D_old(tiff_array, dilated_x=3, dilated_y=3, dilated_z=3):
|
|
1215
|
+
"""
|
|
1216
|
+
Dilate a 3D array using scipy.ndimage.binary_dilation with a 3x3x3 cubic kernel.
|
|
1043
1217
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1218
|
+
Arguments:
|
|
1219
|
+
tiff_array -- Input 3D binary array
|
|
1220
|
+
dilated_x -- Fixed at 3 for X dimension
|
|
1221
|
+
dilated_y -- Fixed at 3 for Y dimension
|
|
1222
|
+
dilated_z -- Fixed at 3 for Z dimension
|
|
1048
1223
|
|
|
1049
|
-
|
|
1224
|
+
Returns:
|
|
1225
|
+
Dilated 3D array
|
|
1050
1226
|
"""
|
|
1227
|
+
|
|
1228
|
+
# Handle special case for 2D arrays
|
|
1051
1229
|
if tiff_array.shape[0] == 1:
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
min_dim = min(tiff_array.shape)
|
|
1230
|
+
# Call 2D dilation function if needed
|
|
1231
|
+
return dilate_2D(tiff_array, 1) # For a 3x3 kernel, radius is 1
|
|
1055
1232
|
|
|
1056
|
-
#
|
|
1057
|
-
|
|
1058
|
-
if max_dilation < (0.2 * min_dim):
|
|
1059
|
-
return dilate_3D(tiff_array, dilated_x, dilated_y, dilated_z)
|
|
1060
|
-
elif dilated_x == 1 and dilated_y == 1 and dilated_z == 1: #Also if there is only a single dilation don't do it
|
|
1061
|
-
return dilate_3D(tiff_array, dilated_x, dilated_y, dilated_z)
|
|
1233
|
+
# Create a simple 3x3x3 cubic kernel (all ones)
|
|
1234
|
+
kernel = np.ones((3, 3, 3), dtype=bool)
|
|
1062
1235
|
|
|
1063
|
-
#
|
|
1064
|
-
|
|
1065
|
-
# Start with a reasonable step size based on the largest dilation
|
|
1066
|
-
step_size = min(5, max((max_dilation - 1) // 2 // 3, 1))
|
|
1236
|
+
# Perform binary dilation
|
|
1237
|
+
dilated_array = ndimage.binary_dilation(tiff_array.astype(bool), structure=kernel)
|
|
1067
1238
|
|
|
1068
|
-
|
|
1069
|
-
if step_size == 1 or (dilated_x <= 1 and dilated_y <= 1 and dilated_z <= 1):
|
|
1070
|
-
def create_circular_kernel(diameter):
|
|
1071
|
-
radius = diameter/2
|
|
1072
|
-
size = radius
|
|
1073
|
-
size = int(np.ceil(size))
|
|
1074
|
-
y, x = np.ogrid[-radius:radius+1, -radius:radius+1]
|
|
1075
|
-
distance = np.sqrt(x**2 + y**2)
|
|
1076
|
-
kernel = distance <= radius
|
|
1077
|
-
return kernel.astype(np.uint8)
|
|
1078
|
-
|
|
1079
|
-
def create_ellipsoidal_kernel(long_axis, short_axis):
|
|
1080
|
-
semi_major, semi_minor = long_axis / 2, short_axis / 2
|
|
1081
|
-
size_y = int(np.ceil(semi_minor))
|
|
1082
|
-
size_x = int(np.ceil(semi_major))
|
|
1083
|
-
y, x = np.ogrid[-semi_minor:semi_minor+1, -semi_major:semi_major+1]
|
|
1084
|
-
ellipse = (x**2 / semi_major**2) + (y**2 / semi_minor**2) <= 1
|
|
1085
|
-
return ellipse.astype(np.uint8)
|
|
1086
|
-
|
|
1087
|
-
def process_slice(z):
|
|
1088
|
-
tiff_slice = tiff_array[z].astype(np.uint8)
|
|
1089
|
-
dilated_slice = cv2.dilate(tiff_slice, kernel, iterations=1)
|
|
1090
|
-
return z, dilated_slice
|
|
1091
|
-
|
|
1092
|
-
def process_slice_other(y):
|
|
1093
|
-
tiff_slice = tiff_array[:, y, :].astype(np.uint8)
|
|
1094
|
-
dilated_slice = cv2.dilate(tiff_slice, kernel, iterations=1)
|
|
1095
|
-
return y, dilated_slice
|
|
1096
|
-
|
|
1097
|
-
# Create empty arrays for the dilated results
|
|
1098
|
-
dilated_xy = np.zeros_like(tiff_array, dtype=np.uint8)
|
|
1099
|
-
dilated_xz = np.zeros_like(tiff_array, dtype=np.uint8)
|
|
1100
|
-
|
|
1101
|
-
# Create kernels for final dilation
|
|
1102
|
-
kernel = create_circular_kernel(dilated_x)
|
|
1103
|
-
|
|
1104
|
-
# Process XY plane
|
|
1105
|
-
num_cores = mp.cpu_count()
|
|
1106
|
-
with ThreadPoolExecutor(max_workers=num_cores) as executor:
|
|
1107
|
-
futures = {executor.submit(process_slice, z): z for z in range(tiff_array.shape[0])}
|
|
1108
|
-
for future in as_completed(futures):
|
|
1109
|
-
z, dilated_slice = future.result()
|
|
1110
|
-
dilated_xy[z] = dilated_slice
|
|
1239
|
+
return dilated_array.astype(np.uint8)
|
|
1111
1240
|
|
|
1112
|
-
# Process XZ plane
|
|
1113
|
-
kernel = create_ellipsoidal_kernel(dilated_x, dilated_z)
|
|
1114
|
-
with ThreadPoolExecutor(max_workers=num_cores) as executor:
|
|
1115
|
-
futures = {executor.submit(process_slice_other, y): y for y in range(tiff_array.shape[1])}
|
|
1116
|
-
for future in as_completed(futures):
|
|
1117
|
-
y, dilated_slice = future.result()
|
|
1118
|
-
dilated_xz[:, y, :] = dilated_slice
|
|
1119
|
-
|
|
1120
|
-
return dilated_xy | dilated_xz
|
|
1121
|
-
|
|
1122
|
-
# Calculate current iteration's dilation sizes (must be odd numbers)
|
|
1123
|
-
current_x_steps = min((dilated_x - 1) // 2, step_size)
|
|
1124
|
-
current_y_steps = min((dilated_y - 1) // 2, step_size)
|
|
1125
|
-
current_z_steps = min((dilated_z - 1) // 2, step_size)
|
|
1126
|
-
|
|
1127
|
-
current_x_dilation = current_x_steps * 2 + 1
|
|
1128
|
-
current_y_dilation = current_y_steps * 2 + 1
|
|
1129
|
-
current_z_dilation = current_z_steps * 2 + 1
|
|
1130
|
-
|
|
1131
|
-
# Perform current iteration's dilation
|
|
1132
|
-
current_result = dilate_3D_recursive(tiff_array, current_x_dilation, current_y_dilation, current_z_dilation, step_size=1)
|
|
1133
|
-
|
|
1134
|
-
# Calculate remaining dilation needed
|
|
1135
|
-
# For X and Y, use the circle radius (current_x_steps)
|
|
1136
|
-
# For Z, use the ellipse short axis (current_z_steps)
|
|
1137
|
-
remaining_x = max(1, dilated_x - (current_x_steps * 2))
|
|
1138
|
-
remaining_y = max(1, dilated_y - (current_y_steps * 2))
|
|
1139
|
-
remaining_z = max(1, dilated_z - (current_z_steps * 2))
|
|
1140
|
-
|
|
1141
|
-
# If no more dilation needed, return current result
|
|
1142
|
-
if remaining_x == 1 and remaining_y == 1 and remaining_z == 1:
|
|
1143
|
-
return current_result
|
|
1144
|
-
|
|
1145
|
-
# Recursive call with remaining dilation and decreased step size
|
|
1146
|
-
return dilate_3D_recursive(current_result, remaining_x, remaining_y, remaining_z, step_size=max(1, step_size - 1))
|
|
1147
1241
|
|
|
1148
1242
|
def erode_3D(tiff_array, eroded_x, eroded_y, eroded_z):
|
|
1149
1243
|
"""Internal method to erode an array in 3D. Erosion this way is much faster than using a distance transform although the latter is theoretically more accurate.
|
|
1150
1244
|
Arguments are an array, and the desired pixel erosion amounts in X, Y, Z."""
|
|
1245
|
+
|
|
1246
|
+
if tiff_array.shape[0] == 1:
|
|
1247
|
+
return erode_2D(tiff_array, ((eroded_x - 1) / 2))
|
|
1248
|
+
|
|
1151
1249
|
def create_circular_kernel(diameter):
|
|
1152
1250
|
"""Create a 2D circular kernel with a given radius.
|
|
1153
1251
|
Parameters:
|
|
@@ -1242,43 +1340,6 @@ def erode_3D(tiff_array, eroded_x, eroded_y, eroded_z):
|
|
|
1242
1340
|
return final_result
|
|
1243
1341
|
|
|
1244
1342
|
|
|
1245
|
-
def dilate_3D_old(tiff_array, dilated_x, dilated_y, dilated_z):
|
|
1246
|
-
"""(For cubey dilation only). Internal method to dilate an array in 3D.
|
|
1247
|
-
Arguments are an array, and the desired pixel dilation amounts in X, Y, Z."""
|
|
1248
|
-
|
|
1249
|
-
# Create empty arrays to store the dilated results for the XY and XZ planes
|
|
1250
|
-
dilated_xy = np.zeros_like(tiff_array, dtype=np.uint8)
|
|
1251
|
-
dilated_xz = np.zeros_like(tiff_array, dtype=np.uint8)
|
|
1252
|
-
|
|
1253
|
-
# Perform 2D dilation in the XY plane
|
|
1254
|
-
for z in range(tiff_array.shape[0]):
|
|
1255
|
-
kernel_x = int(dilated_x)
|
|
1256
|
-
kernel_y = int(dilated_y)
|
|
1257
|
-
kernel = np.ones((kernel_y, kernel_x), dtype=np.uint8)
|
|
1258
|
-
|
|
1259
|
-
# Convert the slice to the appropriate data type
|
|
1260
|
-
tiff_slice = tiff_array[z].astype(np.uint8)
|
|
1261
|
-
|
|
1262
|
-
dilated_slice = cv2.dilate(tiff_slice, kernel, iterations=1)
|
|
1263
|
-
dilated_xy[z] = dilated_slice
|
|
1264
|
-
|
|
1265
|
-
# Perform 2D dilation in the XZ plane
|
|
1266
|
-
for y in range(tiff_array.shape[1]):
|
|
1267
|
-
kernel_x = int(dilated_x)
|
|
1268
|
-
kernel_z = int(dilated_z)
|
|
1269
|
-
kernel = np.ones((kernel_z, kernel_x), dtype=np.uint8)
|
|
1270
|
-
|
|
1271
|
-
# Convert the slice to the appropriate data type
|
|
1272
|
-
tiff_slice = tiff_array[:, y, :].astype(np.uint8)
|
|
1273
|
-
|
|
1274
|
-
dilated_slice = cv2.dilate(tiff_slice, kernel, iterations=1)
|
|
1275
|
-
dilated_xz[:, y, :] = dilated_slice
|
|
1276
|
-
|
|
1277
|
-
# Overlay the results
|
|
1278
|
-
final_result = dilated_xy | dilated_xz
|
|
1279
|
-
|
|
1280
|
-
return final_result
|
|
1281
|
-
|
|
1282
1343
|
def dilation_length_to_pixels(xy_scaling, z_scaling, micronx, micronz):
|
|
1283
1344
|
"""Internal method to find XY and Z dilation parameters based on voxel micron scaling"""
|
|
1284
1345
|
dilate_xy = 2 * int(round(micronx/xy_scaling))
|
|
@@ -1478,7 +1539,7 @@ def binarize(arrayimage, directory = None):
|
|
|
1478
1539
|
|
|
1479
1540
|
return arrayimage
|
|
1480
1541
|
|
|
1481
|
-
def dilate(arrayimage, amount, xy_scale = 1, z_scale = 1, directory = None, fast_dil = False, recursive = False):
|
|
1542
|
+
def dilate(arrayimage, amount, xy_scale = 1, z_scale = 1, directory = None, fast_dil = False, recursive = False, dilate_xy = None, dilate_z = None):
|
|
1482
1543
|
"""
|
|
1483
1544
|
Can be used to dilate a binary image in 3D. Dilated output will be saved to the active directory if none is specified. Note that dilation is done with single-instance kernels and not iterations, and therefore
|
|
1484
1545
|
objects will lose their shape somewhat and become cube-ish if the 'amount' param is ever significantly larger than the objects in quesiton.
|
|
@@ -1498,20 +1559,16 @@ def dilate(arrayimage, amount, xy_scale = 1, z_scale = 1, directory = None, fast
|
|
|
1498
1559
|
else:
|
|
1499
1560
|
image = None
|
|
1500
1561
|
|
|
1501
|
-
|
|
1562
|
+
if not dilate_xy:
|
|
1563
|
+
dilate_xy, dilate_z = dilation_length_to_pixels(xy_scale, z_scale, amount, amount)
|
|
1502
1564
|
|
|
1503
1565
|
if len(np.unique(arrayimage)) > 2: #binarize
|
|
1504
1566
|
arrayimage = binarize(arrayimage)
|
|
1505
1567
|
|
|
1506
|
-
if
|
|
1507
|
-
arrayimage = (dilate_3D(arrayimage, dilate_xy, dilate_xy, dilate_z))
|
|
1508
|
-
if np.max(arrayimage) == 1:
|
|
1509
|
-
arrayimage = arrayimage * 255
|
|
1510
|
-
elif not recursive:
|
|
1511
|
-
arrayimage = (dilate_3D_old(arrayimage, dilate_xy, dilate_xy, dilate_z)) * 255
|
|
1568
|
+
if fast_dil:
|
|
1569
|
+
arrayimage = (dilate_3D(arrayimage, dilate_xy, dilate_xy, dilate_z))
|
|
1512
1570
|
else:
|
|
1513
|
-
arrayimage = (
|
|
1514
|
-
|
|
1571
|
+
arrayimage = (dilate_3D_dt(arrayimage, amount, xy_scale, z_scale))
|
|
1515
1572
|
|
|
1516
1573
|
|
|
1517
1574
|
if type(image) == str:
|
|
@@ -1525,15 +1582,16 @@ def dilate(arrayimage, amount, xy_scale = 1, z_scale = 1, directory = None, fast
|
|
|
1525
1582
|
|
|
1526
1583
|
return arrayimage
|
|
1527
1584
|
|
|
1528
|
-
def erode(arrayimage, amount, xy_scale = 1, z_scale = 1):
|
|
1585
|
+
def erode(arrayimage, amount, xy_scale = 1, z_scale = 1, mode = 0):
|
|
1529
1586
|
if len(np.unique(arrayimage)) > 2: #binarize
|
|
1530
1587
|
arrayimage = binarize(arrayimage)
|
|
1531
1588
|
erode_xy, erode_z = dilation_length_to_pixels(xy_scale, z_scale, amount, amount)
|
|
1532
1589
|
|
|
1533
|
-
if
|
|
1534
|
-
arrayimage =
|
|
1590
|
+
if mode == 0:
|
|
1591
|
+
arrayimage = (erode_3D(arrayimage, erode_xy, erode_xy, erode_z)) * 255
|
|
1592
|
+
else:
|
|
1593
|
+
arrayimage = erode_3D_dt(arrayimage, amount, xy_scaling=xy_scale, z_scaling=z_scale)
|
|
1535
1594
|
|
|
1536
|
-
arrayimage = (erode_3D(arrayimage, erode_xy, erode_xy, erode_z)) * 255
|
|
1537
1595
|
if np.max(arrayimage) == 1:
|
|
1538
1596
|
arrayimage = arrayimage * 255
|
|
1539
1597
|
|
|
@@ -1688,7 +1746,7 @@ def fix_branches(array, G, communities, fix_val = None):
|
|
|
1688
1746
|
|
|
1689
1747
|
|
|
1690
1748
|
|
|
1691
|
-
def label_vertices(array, peaks = 0, branch_removal = 0, comp_dil = 0, max_vol = 0, down_factor = 0, directory = None, return_skele = False, order = 0):
|
|
1749
|
+
def label_vertices(array, peaks = 0, branch_removal = 0, comp_dil = 0, max_vol = 0, down_factor = 0, directory = None, return_skele = False, order = 0, fastdil = True):
|
|
1692
1750
|
"""
|
|
1693
1751
|
Can be used to label vertices (where multiple branches connect) a binary image. Labelled output will be saved to the active directory if none is specified. Note this works better on already thin filaments and may over-divide larger trunkish objects.
|
|
1694
1752
|
Note that this can be used in tandem with an edge segmentation to create an image containing 'pseudo-nodes', meaning we can make a network out of just a single edge file.
|
|
@@ -1754,19 +1812,19 @@ def label_vertices(array, peaks = 0, branch_removal = 0, comp_dil = 0, max_vol =
|
|
|
1754
1812
|
if peaks > 0:
|
|
1755
1813
|
image_copy = filter_size_by_peaks(image_copy, peaks)
|
|
1756
1814
|
if comp_dil > 0:
|
|
1757
|
-
image_copy = dilate(image_copy, comp_dil)
|
|
1815
|
+
image_copy = dilate(image_copy, comp_dil, fast_dil = fastdil)
|
|
1758
1816
|
|
|
1759
1817
|
labeled_image, num_labels = label_objects(image_copy)
|
|
1760
1818
|
elif max_vol > 0:
|
|
1761
1819
|
image_copy = filter_size_by_vol(image_copy, max_vol)
|
|
1762
1820
|
if comp_dil > 0:
|
|
1763
|
-
image_copy = dilate(image_copy, comp_dil)
|
|
1821
|
+
image_copy = dilate(image_copy, comp_dil, fast_dil = fastdil)
|
|
1764
1822
|
|
|
1765
1823
|
labeled_image, num_labels = label_objects(image_copy)
|
|
1766
1824
|
else:
|
|
1767
1825
|
|
|
1768
1826
|
if comp_dil > 0:
|
|
1769
|
-
image_copy = dilate(image_copy, comp_dil)
|
|
1827
|
+
image_copy = dilate(image_copy, comp_dil, fast_dil = fastdil)
|
|
1770
1828
|
labeled_image, num_labels = label_objects(image_copy)
|
|
1771
1829
|
|
|
1772
1830
|
#if down_factor > 0:
|
|
@@ -3005,7 +3063,7 @@ class Network_3D:
|
|
|
3005
3063
|
print(f"Assembling Network_3D object from files stored in directory: {directory}")
|
|
3006
3064
|
self.load_nodes(directory, node_path)
|
|
3007
3065
|
self.load_edges(directory, edge_path)
|
|
3008
|
-
self.load_search_region(directory, search_region_path)
|
|
3066
|
+
#self.load_search_region(directory, search_region_path)
|
|
3009
3067
|
self.load_network(directory, network_path)
|
|
3010
3068
|
self.load_node_centroids(directory, node_centroids_path)
|
|
3011
3069
|
self.load_node_identities(directory, node_identities_path)
|
|
@@ -3063,7 +3121,7 @@ class Network_3D:
|
|
|
3063
3121
|
|
|
3064
3122
|
self._edge_centroids = edge_centroids
|
|
3065
3123
|
|
|
3066
|
-
def calculate_search_region(self, search_region_size, GPU = True, fast_dil =
|
|
3124
|
+
def calculate_search_region(self, search_region_size, GPU = True, fast_dil = True, GPU_downsample = None):
|
|
3067
3125
|
|
|
3068
3126
|
"""
|
|
3069
3127
|
Method to obtain the search region that will be used to assign connectivity between nodes. May be skipped if nodes do not want to search and only want to look for their
|
|
@@ -3082,7 +3140,7 @@ class Network_3D:
|
|
|
3082
3140
|
|
|
3083
3141
|
if search_region_size != 0:
|
|
3084
3142
|
|
|
3085
|
-
self._search_region = smart_dilate.smart_dilate(self._nodes, dilate_xy, dilate_z, GPU = GPU, fast_dil = fast_dil, predownsample = GPU_downsample) #Call the smart dilate function which essentially is a fast way to enlarge nodes into a 'search region' while keeping their unique IDs.
|
|
3143
|
+
self._search_region = smart_dilate.smart_dilate(self._nodes, dilate_xy, dilate_z, GPU = GPU, fast_dil = fast_dil, predownsample = GPU_downsample, use_dt_dil_amount = search_region_size) #Call the smart dilate function which essentially is a fast way to enlarge nodes into a 'search region' while keeping their unique IDs.
|
|
3086
3144
|
|
|
3087
3145
|
else:
|
|
3088
3146
|
|
|
@@ -3116,14 +3174,15 @@ class Network_3D:
|
|
|
3116
3174
|
if skeletonized:
|
|
3117
3175
|
binary_edges = skeletonize(binary_edges)
|
|
3118
3176
|
|
|
3119
|
-
|
|
3177
|
+
|
|
3178
|
+
|
|
3179
|
+
if search is not None and hasattr(self, '_nodes') and self._nodes is not None and self._search_region is None:
|
|
3120
3180
|
search_region = binarize(self._nodes)
|
|
3121
3181
|
dilate_xy, dilate_z = dilation_length_to_pixels(self._xy_scale, self._z_scale, search, search)
|
|
3122
|
-
if
|
|
3182
|
+
if fast_dil:
|
|
3123
3183
|
search_region = dilate_3D(search_region, dilate_xy, dilate_xy, dilate_z)
|
|
3124
3184
|
else:
|
|
3125
|
-
search_region =
|
|
3126
|
-
|
|
3185
|
+
search_region = dilate_3D_dt(search_region, diledge, self._xy_scale, self._z_scale)
|
|
3127
3186
|
else:
|
|
3128
3187
|
search_region = binarize(self._search_region)
|
|
3129
3188
|
|
|
@@ -3133,19 +3192,20 @@ class Network_3D:
|
|
|
3133
3192
|
del binary_edges
|
|
3134
3193
|
|
|
3135
3194
|
if remove_edgetrunk > 0:
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
outer_edges = remove_trunk(outer_edges)
|
|
3195
|
+
print(f"Snipping trunks...")
|
|
3196
|
+
outer_edges = remove_trunk(outer_edges, remove_edgetrunk)
|
|
3139
3197
|
|
|
3140
3198
|
if diledge is not None:
|
|
3141
3199
|
dilate_xy, dilate_z = dilation_length_to_pixels(self._xy_scale, self._z_scale, diledge, diledge)
|
|
3142
|
-
|
|
3200
|
+
|
|
3201
|
+
if dilate_xy <= 3 and dilate_z <= 3:
|
|
3202
|
+
outer_edges = dilate_3D_old(outer_edges, dilate_xy, dilate_xy, dilate_z)
|
|
3203
|
+
elif fast_dil:
|
|
3143
3204
|
outer_edges = dilate_3D(outer_edges, dilate_xy, dilate_xy, dilate_z)
|
|
3144
3205
|
else:
|
|
3145
|
-
outer_edges =
|
|
3146
|
-
|
|
3206
|
+
outer_edges = dilate_3D_dt(outer_edges, diledge, self._xy_scale, self._z_scale)
|
|
3147
3207
|
else:
|
|
3148
|
-
outer_edges = dilate_3D_old(outer_edges
|
|
3208
|
+
outer_edges = dilate_3D_old(outer_edges)
|
|
3149
3209
|
|
|
3150
3210
|
labelled_edges, num_edge = label_objects(outer_edges)
|
|
3151
3211
|
|
|
@@ -3277,7 +3337,7 @@ class Network_3D:
|
|
|
3277
3337
|
self._network_lists = network_analysis.read_excel_to_lists(df)
|
|
3278
3338
|
self._network, net_weights = network_analysis.weighted_network(df)
|
|
3279
3339
|
|
|
3280
|
-
def calculate_all(self, nodes, edges, xy_scale = 1, z_scale = 1, down_factor = None, search = None, diledge = None, inners = True, hash_inners = True, remove_trunk = 0, ignore_search_region = False, other_nodes = None, label_nodes = True, directory = None, GPU = True, fast_dil =
|
|
3340
|
+
def calculate_all(self, nodes, edges, xy_scale = 1, z_scale = 1, down_factor = None, search = None, diledge = None, inners = True, hash_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):
|
|
3281
3341
|
"""
|
|
3282
3342
|
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.
|
|
3283
3343
|
:param nodes: (Mandatory; String or ndarray). Filepath to segmented nodes mask or a numpy array containing the same.
|
|
@@ -3299,7 +3359,7 @@ class Network_3D:
|
|
|
3299
3359
|
:param label_nodes: (Optional - Val = True; boolean). If True, all discrete objects in the node param (and all those contained in the optional other_nodes param) will be assigned a label. If files a prelabelled, set this to False to avoid labelling.
|
|
3300
3360
|
:param directory: (Optional - Val = None; string). Path to a directory to save to hard mem all Network_3D properties. If not set, these values will be saved to the active directory.
|
|
3301
3361
|
:param GPU: (Optional - Val = True; boolean). Will use GPU if avaialble for calculating the search_region step (including necessary downsampling for GPU RAM). Set to False to use CPU with no downsample. Note this only affects the search_region step.
|
|
3302
|
-
:param fast_dil: (Optional - Val = False, boolean) - A boolean that when True will utilize faster
|
|
3362
|
+
:param fast_dil: (Optional - Val = False, boolean) - A boolean that when True will utilize faster psuedo3d kernel dilation but when false will use slower dt-based dilation.
|
|
3303
3363
|
:param skeletonize: (Optional - Val = False, boolean) - A boolean of whether to skeletonize the edges when using them.
|
|
3304
3364
|
"""
|
|
3305
3365
|
|
|
@@ -4021,48 +4081,10 @@ class Network_3D:
|
|
|
4021
4081
|
return isolated nodes, isolated edges, and isolated network in that order). IF gen_images == False (Will return just the network).
|
|
4022
4082
|
"""
|
|
4023
4083
|
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
if not hasattr(self, '_edges') or self._edges is None:
|
|
4027
|
-
|
|
4028
|
-
connected_component, isonodes, _ = community_extractor.isolate_connected_component(self._nodes, self._network, key=key, directory = directory)
|
|
4029
|
-
|
|
4030
|
-
nodea = []
|
|
4031
|
-
nodeb = []
|
|
4032
|
-
edgec = []
|
|
4033
|
-
nodesa = self._network_lists[0]
|
|
4034
|
-
nodesb = self._network_lists[1]
|
|
4035
|
-
edgesc = self._network_lists[2]
|
|
4036
|
-
for i in range(len(nodesa)):
|
|
4037
|
-
if (nodesa[i], nodesb[i]) in connected_component:
|
|
4038
|
-
nodea.append(nodesa[i])
|
|
4039
|
-
nodeb.append(nodesb[i])
|
|
4040
|
-
edgec.append(edgesc[i])
|
|
4041
|
-
network_lists = [nodea, nodeb, edgec]
|
|
4042
|
-
network, weights = network_analysis.weighted_network(network_lists)
|
|
4084
|
+
#Removed depricated gen_images functions
|
|
4043
4085
|
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
else:
|
|
4047
|
-
if full_edges is not None:
|
|
4048
|
-
connected_component, isonodes, isoedges, searchers = community_extractor.isolate_connected_component(self._nodes, self._network, key=key, edge_file = self._edges, search_region = self.search_region, netlists = self._network_lists, directory = directory)
|
|
4049
|
-
|
|
4050
|
-
else:
|
|
4051
|
-
connected_component, isonodes, isoedges, searchers = community_extractor.isolate_connected_component(self._nodes, self._network, key=key, edge_file = self._edges, netlists = self._network_lists, directory = directory)
|
|
4052
|
-
|
|
4053
|
-
df = create_and_save_dataframe(connected_component)
|
|
4054
|
-
network_lists = network_analysis.read_excel_to_lists(df)
|
|
4055
|
-
network, net_weights = network_analysis.weighted_network(df)
|
|
4056
|
-
|
|
4057
|
-
if full_edges is not None:
|
|
4058
|
-
full_edges = tifffile.imread(full_edges)
|
|
4059
|
-
community_extractor.isolate_full_edges(searchers, full_edges, directory = directory)
|
|
4060
|
-
|
|
4061
|
-
return isonodes, isoedges, network
|
|
4062
|
-
|
|
4063
|
-
else:
|
|
4064
|
-
G = community_extractor._isolate_connected(self._network, key = key)
|
|
4065
|
-
return G
|
|
4086
|
+
G = community_extractor._isolate_connected(self._network, key = key)
|
|
4087
|
+
return G
|
|
4066
4088
|
|
|
4067
4089
|
|
|
4068
4090
|
def isolate_mothers(self, directory = None, down_factor = 1, louvain = True, ret_nodes = False, called = False):
|
|
@@ -4320,7 +4342,7 @@ class Network_3D:
|
|
|
4320
4342
|
return stats
|
|
4321
4343
|
|
|
4322
4344
|
|
|
4323
|
-
def neighborhood_identities(self, root, directory = None, mode = 0, search = 0):
|
|
4345
|
+
def neighborhood_identities(self, root, directory = None, mode = 0, search = 0, fastdil = False):
|
|
4324
4346
|
|
|
4325
4347
|
|
|
4326
4348
|
|
|
@@ -4358,7 +4380,7 @@ class Network_3D:
|
|
|
4358
4380
|
|
|
4359
4381
|
|
|
4360
4382
|
elif mode == 1: #Search neighborhoods morphologically, obtain densities
|
|
4361
|
-
neighborhood_dict, total_dict, densities = morphology.search_neighbor_ids(self._nodes, targets, node_identities, neighborhood_dict, total_dict, search, self._xy_scale, self._z_scale, root)
|
|
4383
|
+
neighborhood_dict, total_dict, densities = morphology.search_neighbor_ids(self._nodes, targets, node_identities, neighborhood_dict, total_dict, search, self._xy_scale, self._z_scale, root, fastdil = fastdil)
|
|
4362
4384
|
title1 = f'Volumetric Neighborhood Distribution of Nodes in image that are {search} from nodes: {root}'
|
|
4363
4385
|
title2 = f'Density Distribution of Nodes in image that are {search} from Nodes {root} as a proportion of total node volume of that ID'
|
|
4364
4386
|
|
|
@@ -4405,19 +4427,23 @@ class Network_3D:
|
|
|
4405
4427
|
|
|
4406
4428
|
|
|
4407
4429
|
|
|
4408
|
-
def interactions(self, search = 0, cores = 0, resize = None, save = False, skele = False):
|
|
4430
|
+
def interactions(self, search = 0, cores = 0, resize = None, save = False, skele = False, fastdil = False):
|
|
4431
|
+
|
|
4432
|
+
return morphology.quantify_edge_node(self._nodes, self._edges, search = search, xy_scale = self._xy_scale, z_scale = self._z_scale, cores = cores, resize = resize, save = save, skele = skele, fastdil = fastdil)
|
|
4409
4433
|
|
|
4410
|
-
return morphology.quantify_edge_node(self._nodes, self._edges, search = search, xy_scale = self._xy_scale, z_scale = self._z_scale, cores = cores, resize = resize, save = save, skele = skele)
|
|
4411
4434
|
|
|
4412
4435
|
|
|
4436
|
+
def morph_proximity(self, search = 0, targets = None, fastdil = False):
|
|
4413
4437
|
|
|
4414
|
-
|
|
4438
|
+
if type(search) == list:
|
|
4439
|
+
search_x, search_z = search #Suppose we just want to directly pass these params
|
|
4440
|
+
else:
|
|
4441
|
+
search_x, search_z = dilation_length_to_pixels(self._xy_scale, self._z_scale, search, search)
|
|
4415
4442
|
|
|
4416
|
-
search_x, search_z = dilation_length_to_pixels(self._xy_scale, self._z_scale, search, search)
|
|
4417
4443
|
|
|
4418
4444
|
num_nodes = np.max(self._nodes)
|
|
4419
4445
|
|
|
4420
|
-
my_dict = proximity.create_node_dictionary(self._nodes, num_nodes, search_x, search_z, targets = targets)
|
|
4446
|
+
my_dict = proximity.create_node_dictionary(self._nodes, num_nodes, search_x, search_z, targets = targets, fastdil = fastdil, xy_scale = self._xy_scale, z_scale = self._z_scale, search = search)
|
|
4421
4447
|
|
|
4422
4448
|
my_dict = proximity.find_shared_value_pairs(my_dict)
|
|
4423
4449
|
|
|
@@ -4490,6 +4516,8 @@ class Network_3D:
|
|
|
4490
4516
|
|
|
4491
4517
|
self._network_lists = network_analysis.read_excel_to_lists(network)
|
|
4492
4518
|
|
|
4519
|
+
#self._network is a networkx graph that stores the connections
|
|
4520
|
+
|
|
4493
4521
|
self.remove_edge_weights()
|
|
4494
4522
|
|
|
4495
4523
|
return array
|