nettracer3d 1.0.7__tar.gz → 1.0.9__tar.gz
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-1.0.7/src/nettracer3d.egg-info → nettracer3d-1.0.9}/PKG-INFO +6 -4
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/README.md +5 -3
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/pyproject.toml +1 -1
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/morphology.py +108 -17
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/nettracer.py +64 -14
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/nettracer_gui.py +480 -180
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/segmenter.py +67 -25
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/segmenter_GPU.py +67 -29
- nettracer3d-1.0.9/src/nettracer3d/stats.py +861 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9/src/nettracer3d.egg-info}/PKG-INFO +6 -4
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d.egg-info/SOURCES.txt +1 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/LICENSE +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/setup.cfg +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/__init__.py +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/cellpose_manager.py +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/community_extractor.py +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/excelotron.py +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/modularity.py +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/neighborhoods.py +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/network_analysis.py +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/network_draw.py +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/node_draw.py +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/painting.py +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/proximity.py +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/run.py +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/simple_network.py +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d/smart_dilate.py +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d.egg-info/dependency_links.txt +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d.egg-info/entry_points.txt +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d.egg-info/requires.txt +0 -0
- {nettracer3d-1.0.7 → nettracer3d-1.0.9}/src/nettracer3d.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nettracer3d
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.9
|
|
4
4
|
Summary: Scripts for intializing and analyzing networks from segmentations of three dimensional images.
|
|
5
5
|
Author-email: Liam McLaughlin <liamm@wustl.edu>
|
|
6
6
|
Project-URL: Documentation, https://nettracer3d.readthedocs.io/en/latest/
|
|
@@ -110,7 +110,9 @@ McLaughlin, L., Zhang, B., Sharma, S. et al. Three dimensional multiscalar neuro
|
|
|
110
110
|
|
|
111
111
|
NetTracer3D was developed by Liam McLaughlin while working under Dr. Sanjay Jain at Washington University School of Medicine.
|
|
112
112
|
|
|
113
|
-
-- Version 1.0.
|
|
113
|
+
-- Version 1.0.9 Updates --
|
|
114
114
|
|
|
115
|
-
*
|
|
116
|
-
* Added
|
|
115
|
+
* Some bug fixes
|
|
116
|
+
* Added ability to load a full sized highlight overlay from the file menu (in case you need it for a picture - on big images the highlight overlay is computed by slice so if you reload the channel its trying to highlight, it will alter the highlight overlay, but loading in the entire highlight overlay directly will stop this behavior until a new highlight is generated).
|
|
117
|
+
* For the 'calculate edge < > node interaction' method - now can compute the length of nearby edges as an alternative option to just the volumes.
|
|
118
|
+
* Added ability to select all nodes/edges participating in the network.
|
|
@@ -65,7 +65,9 @@ McLaughlin, L., Zhang, B., Sharma, S. et al. Three dimensional multiscalar neuro
|
|
|
65
65
|
|
|
66
66
|
NetTracer3D was developed by Liam McLaughlin while working under Dr. Sanjay Jain at Washington University School of Medicine.
|
|
67
67
|
|
|
68
|
-
-- Version 1.0.
|
|
68
|
+
-- Version 1.0.9 Updates --
|
|
69
69
|
|
|
70
|
-
*
|
|
71
|
-
* Added
|
|
70
|
+
* Some bug fixes
|
|
71
|
+
* Added ability to load a full sized highlight overlay from the file menu (in case you need it for a picture - on big images the highlight overlay is computed by slice so if you reload the channel its trying to highlight, it will alter the highlight overlay, but loading in the entire highlight overlay directly will stop this behavior until a new highlight is generated).
|
|
72
|
+
* For the 'calculate edge < > node interaction' method - now can compute the length of nearby edges as an alternative option to just the volumes.
|
|
73
|
+
* Added ability to select all nodes/edges participating in the network.
|
|
@@ -65,7 +65,7 @@ def reslice_3d_array(args):
|
|
|
65
65
|
return resliced_array
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
def _get_node_edge_dict(label_array, edge_array, label, dilate_xy, dilate_z, cores = 0, search = 0, fastdil = False, xy_scale = 1, z_scale = 1):
|
|
68
|
+
def _get_node_edge_dict(label_array, edge_array, label, dilate_xy, dilate_z, cores = 0, search = 0, fastdil = False, length = False, xy_scale = 1, z_scale = 1):
|
|
69
69
|
"""Internal method used for the secondary algorithm to find pixel involvement of nodes around an edge."""
|
|
70
70
|
|
|
71
71
|
# Create a boolean mask where elements with the specified label are True
|
|
@@ -74,24 +74,25 @@ def _get_node_edge_dict(label_array, edge_array, label, dilate_xy, dilate_z, cor
|
|
|
74
74
|
|
|
75
75
|
if cores == 0: #For getting the volume of objects. Cores presumes you want the 'core' included in the interaction.
|
|
76
76
|
edge_array = edge_array * dil_array # Filter the edges by the label in question
|
|
77
|
-
label_array = np.count_nonzero(dil_array)
|
|
78
|
-
edge_array = np.count_nonzero(edge_array) # For getting the interacting skeleton
|
|
79
|
-
|
|
80
77
|
elif cores == 1: #Cores being 1 presumes you do not want to 'core' included in the interaction
|
|
81
78
|
label_array = dil_array - label_array
|
|
82
79
|
edge_array = edge_array * label_array
|
|
83
|
-
label_array = np.count_nonzero(label_array)
|
|
84
|
-
edge_array = np.count_nonzero(edge_array) # For getting the interacting skeleton
|
|
85
|
-
|
|
86
80
|
elif cores == 2: #Presumes you want skeleton within the core but to only 'count' the stuff around the core for volumes... because of imaging artifacts, perhaps
|
|
87
81
|
edge_array = edge_array * dil_array
|
|
88
82
|
label_array = dil_array - label_array
|
|
89
|
-
label_array = np.count_nonzero(label_array)
|
|
90
|
-
edge_array = np.count_nonzero(edge_array) # For getting the interacting skeleton
|
|
91
83
|
|
|
84
|
+
label_count = np.count_nonzero(label_array) * xy_scale * xy_scale * z_scale
|
|
92
85
|
|
|
93
|
-
|
|
94
|
-
|
|
86
|
+
if not length:
|
|
87
|
+
edge_count = np.count_nonzero(edge_array) * xy_scale * xy_scale * z_scale # For getting the interacting skeleton
|
|
88
|
+
else:
|
|
89
|
+
edge_count = calculate_skeleton_lengths(
|
|
90
|
+
edge_array,
|
|
91
|
+
xy_scale=xy_scale,
|
|
92
|
+
z_scale=z_scale
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
args = [edge_count, label_count]
|
|
95
96
|
|
|
96
97
|
return args
|
|
97
98
|
|
|
@@ -115,7 +116,7 @@ def process_label(args):
|
|
|
115
116
|
|
|
116
117
|
|
|
117
118
|
|
|
118
|
-
def create_node_dictionary(nodes, edges, num_nodes, dilate_xy, dilate_z, cores=0, search = 0, fastdil = False, xy_scale = 1, z_scale = 1):
|
|
119
|
+
def create_node_dictionary(nodes, edges, num_nodes, dilate_xy, dilate_z, cores=0, search = 0, fastdil = False, length = False, xy_scale = 1, z_scale = 1):
|
|
119
120
|
"""Modified to pre-compute all bounding boxes using find_objects"""
|
|
120
121
|
node_dict = {}
|
|
121
122
|
array_shape = nodes.shape
|
|
@@ -135,20 +136,20 @@ def create_node_dictionary(nodes, edges, num_nodes, dilate_xy, dilate_z, cores=0
|
|
|
135
136
|
# Process results in parallel
|
|
136
137
|
for label, sub_nodes, sub_edges in results:
|
|
137
138
|
executor.submit(create_dict_entry, node_dict, label, sub_nodes, sub_edges,
|
|
138
|
-
dilate_xy, dilate_z, cores, search, fastdil, xy_scale, z_scale)
|
|
139
|
+
dilate_xy, dilate_z, cores, search, fastdil, length, xy_scale, z_scale)
|
|
139
140
|
|
|
140
141
|
return node_dict
|
|
141
142
|
|
|
142
|
-
def create_dict_entry(node_dict, label, sub_nodes, sub_edges, dilate_xy, dilate_z, cores = 0, search = 0, fastdil = False, xy_scale = 1, z_scale = 1):
|
|
143
|
+
def create_dict_entry(node_dict, label, sub_nodes, sub_edges, dilate_xy, dilate_z, cores = 0, search = 0, fastdil = False, length = False, xy_scale = 1, z_scale = 1):
|
|
143
144
|
"""Internal method used for the secondary algorithm to pass around args in parallel."""
|
|
144
145
|
|
|
145
146
|
if label is None:
|
|
146
147
|
pass
|
|
147
148
|
else:
|
|
148
|
-
node_dict[label] = _get_node_edge_dict(sub_nodes, sub_edges, label, dilate_xy, dilate_z, cores = cores, search = search, fastdil = fastdil, xy_scale = xy_scale, z_scale = z_scale)
|
|
149
|
+
node_dict[label] = _get_node_edge_dict(sub_nodes, sub_edges, label, dilate_xy, dilate_z, cores = cores, search = search, fastdil = fastdil, length = length, xy_scale = xy_scale, z_scale = z_scale)
|
|
149
150
|
|
|
150
151
|
|
|
151
|
-
def quantify_edge_node(nodes, edges, search = 0, xy_scale = 1, z_scale = 1, cores = 0, resize = None, save = True, skele = False, fastdil = False):
|
|
152
|
+
def quantify_edge_node(nodes, edges, search = 0, xy_scale = 1, z_scale = 1, cores = 0, resize = None, save = True, skele = False, length = False, auto = True, fastdil = False):
|
|
152
153
|
|
|
153
154
|
def save_dubval_dict(dict, index_name, val1name, val2name, filename):
|
|
154
155
|
|
|
@@ -168,6 +169,9 @@ def quantify_edge_node(nodes, edges, search = 0, xy_scale = 1, z_scale = 1, core
|
|
|
168
169
|
edges = tifffile.imread(edges)
|
|
169
170
|
|
|
170
171
|
if skele:
|
|
172
|
+
if auto:
|
|
173
|
+
edges = nettracer.skeletonize(edges)
|
|
174
|
+
edges = nettracer.fill_holes_3d(edges)
|
|
171
175
|
edges = nettracer.skeletonize(edges)
|
|
172
176
|
else:
|
|
173
177
|
edges = nettracer.binarize(edges)
|
|
@@ -188,7 +192,7 @@ def quantify_edge_node(nodes, edges, search = 0, xy_scale = 1, z_scale = 1, core
|
|
|
188
192
|
dilate_xy, dilate_z = 0, 0
|
|
189
193
|
|
|
190
194
|
|
|
191
|
-
edge_quants = create_node_dictionary(nodes, edges, num_nodes, dilate_xy, dilate_z, cores = cores, search = search, fastdil = fastdil, xy_scale = xy_scale, z_scale = z_scale) #Find which edges connect which nodes and put them in a dictionary.
|
|
195
|
+
edge_quants = create_node_dictionary(nodes, edges, num_nodes, dilate_xy, dilate_z, cores = cores, search = search, fastdil = fastdil, length = length, xy_scale = xy_scale, z_scale = z_scale) #Find which edges connect which nodes and put them in a dictionary.
|
|
192
196
|
|
|
193
197
|
if save:
|
|
194
198
|
|
|
@@ -199,6 +203,93 @@ def quantify_edge_node(nodes, edges, search = 0, xy_scale = 1, z_scale = 1, core
|
|
|
199
203
|
return edge_quants
|
|
200
204
|
|
|
201
205
|
|
|
206
|
+
# Helper methods for counting the lens of skeletons:
|
|
207
|
+
|
|
208
|
+
def calculate_skeleton_lengths(skeleton_binary, xy_scale=1.0, z_scale=1.0):
|
|
209
|
+
"""
|
|
210
|
+
Calculate total length of all skeletons in a 3D binary image.
|
|
211
|
+
|
|
212
|
+
skeleton_binary: 3D boolean array where True = skeleton voxel
|
|
213
|
+
xy_scale, z_scale: physical units per voxel
|
|
214
|
+
"""
|
|
215
|
+
# Find all skeleton voxels
|
|
216
|
+
skeleton_coords = np.argwhere(skeleton_binary)
|
|
217
|
+
|
|
218
|
+
if len(skeleton_coords) == 0:
|
|
219
|
+
return 0.0
|
|
220
|
+
|
|
221
|
+
# Create a mapping from coordinates to indices for fast lookup
|
|
222
|
+
coord_to_idx = {tuple(coord): idx for idx, coord in enumerate(skeleton_coords)}
|
|
223
|
+
|
|
224
|
+
# Build adjacency graph
|
|
225
|
+
adjacency_list = build_adjacency_graph(skeleton_coords, coord_to_idx, skeleton_binary.shape)
|
|
226
|
+
|
|
227
|
+
# Calculate lengths using scaled distances
|
|
228
|
+
total_length = calculate_graph_length(skeleton_coords, adjacency_list, xy_scale, z_scale)
|
|
229
|
+
|
|
230
|
+
return total_length
|
|
231
|
+
|
|
232
|
+
def build_adjacency_graph(skeleton_coords, coord_to_idx, shape):
|
|
233
|
+
"""Build adjacency list for skeleton voxels using 26-connectivity."""
|
|
234
|
+
adjacency_list = [[] for _ in range(len(skeleton_coords))]
|
|
235
|
+
|
|
236
|
+
# 26-connectivity offsets (all combinations of -1,0,1 except 0,0,0)
|
|
237
|
+
offsets = []
|
|
238
|
+
for dz in [-1, 0, 1]:
|
|
239
|
+
for dy in [-1, 0, 1]:
|
|
240
|
+
for dx in [-1, 0, 1]:
|
|
241
|
+
if not (dx == 0 and dy == 0 and dz == 0):
|
|
242
|
+
offsets.append((dz, dy, dx))
|
|
243
|
+
|
|
244
|
+
for idx, coord in enumerate(skeleton_coords):
|
|
245
|
+
z, y, x = coord
|
|
246
|
+
|
|
247
|
+
# Check all 26 neighbors
|
|
248
|
+
for dz, dy, dx in offsets:
|
|
249
|
+
nz, ny, nx = z + dz, y + dy, x + dx
|
|
250
|
+
|
|
251
|
+
# Check bounds
|
|
252
|
+
if (0 <= nz < shape[0] and
|
|
253
|
+
0 <= ny < shape[1] and
|
|
254
|
+
0 <= nx < shape[2]):
|
|
255
|
+
|
|
256
|
+
neighbor_coord = (nz, ny, nx)
|
|
257
|
+
if neighbor_coord in coord_to_idx:
|
|
258
|
+
neighbor_idx = coord_to_idx[neighbor_coord]
|
|
259
|
+
adjacency_list[idx].append(neighbor_idx)
|
|
260
|
+
|
|
261
|
+
return adjacency_list
|
|
262
|
+
|
|
263
|
+
def calculate_graph_length(skeleton_coords, adjacency_list, xy_scale, z_scale):
|
|
264
|
+
"""Calculate total length by summing distances between adjacent voxels."""
|
|
265
|
+
total_length = 0.0
|
|
266
|
+
processed_edges = set()
|
|
267
|
+
|
|
268
|
+
for idx, neighbors in enumerate(adjacency_list):
|
|
269
|
+
coord = skeleton_coords[idx]
|
|
270
|
+
|
|
271
|
+
for neighbor_idx in neighbors:
|
|
272
|
+
# Avoid double-counting edges
|
|
273
|
+
edge = tuple(sorted([idx, neighbor_idx]))
|
|
274
|
+
if edge in processed_edges:
|
|
275
|
+
continue
|
|
276
|
+
processed_edges.add(edge)
|
|
277
|
+
|
|
278
|
+
neighbor_coord = skeleton_coords[neighbor_idx]
|
|
279
|
+
|
|
280
|
+
# Calculate scaled distance
|
|
281
|
+
dz = (coord[0] - neighbor_coord[0]) * z_scale
|
|
282
|
+
dy = (coord[1] - neighbor_coord[1]) * xy_scale
|
|
283
|
+
dx = (coord[2] - neighbor_coord[2]) * xy_scale
|
|
284
|
+
|
|
285
|
+
distance = np.sqrt(dx*dx + dy*dy + dz*dz)
|
|
286
|
+
total_length += distance
|
|
287
|
+
|
|
288
|
+
return total_length
|
|
289
|
+
|
|
290
|
+
# End helper methods
|
|
291
|
+
|
|
292
|
+
|
|
202
293
|
|
|
203
294
|
def calculate_voxel_volumes(array, xy_scale=1, z_scale=1):
|
|
204
295
|
"""
|
|
@@ -804,7 +804,7 @@ def threshold(arr, proportion, custom_rad = None):
|
|
|
804
804
|
|
|
805
805
|
threshold_index = int(len(sorted_values) * proportion)
|
|
806
806
|
threshold_value = sorted_values[threshold_index]
|
|
807
|
-
print(f"Thresholding as if smallest_radius
|
|
807
|
+
print(f"Thresholding as if smallest_radius was assigned {threshold_value}")
|
|
808
808
|
|
|
809
809
|
|
|
810
810
|
mask = arr > threshold_value
|
|
@@ -3143,6 +3143,14 @@ class Network_3D:
|
|
|
3143
3143
|
Can be called on a Network_3D object to save the nodes property to hard mem as a tif. It will save to the active directory if none is specified.
|
|
3144
3144
|
:param directory: (Optional - Val = None; String). The path to an indended directory to save the nodes to.
|
|
3145
3145
|
"""
|
|
3146
|
+
imagej_metadata = {
|
|
3147
|
+
'spacing': self.z_scale,
|
|
3148
|
+
'slices': self._nodes.shape[0],
|
|
3149
|
+
'channels': 1,
|
|
3150
|
+
'axes': 'ZYX'
|
|
3151
|
+
}
|
|
3152
|
+
resolution_value = 1.0 / self.xy_scale if self.xy_scale != 0 else 1
|
|
3153
|
+
|
|
3146
3154
|
if filename is None:
|
|
3147
3155
|
filename = "labelled_nodes.tif"
|
|
3148
3156
|
elif not filename.endswith(('.tif', '.tiff')):
|
|
@@ -3151,13 +3159,19 @@ class Network_3D:
|
|
|
3151
3159
|
if self._nodes is not None:
|
|
3152
3160
|
if directory is None:
|
|
3153
3161
|
try:
|
|
3154
|
-
|
|
3162
|
+
if len(self._nodes.shape) == 3:
|
|
3163
|
+
tifffile.imwrite(f"{filename}", self._nodes, imagej=True, metadata=imagej_metadata, resolution=(resolution_value, resolution_value))
|
|
3164
|
+
else:
|
|
3165
|
+
tifffile.imwrite(f"{filename}", self._nodes)
|
|
3155
3166
|
print(f"Nodes saved to {filename}")
|
|
3156
3167
|
except Exception as e:
|
|
3157
3168
|
print("Could not save nodes")
|
|
3158
3169
|
if directory is not None:
|
|
3159
3170
|
try:
|
|
3160
|
-
|
|
3171
|
+
if len(self._nodes.shape) == 3:
|
|
3172
|
+
tifffile.imwrite(f"{directory}/{filename}", self._nodes, imagej=True, metadata=imagej_metadata, resolution=(resolution_value, resolution_value))
|
|
3173
|
+
else:
|
|
3174
|
+
tifffile.imwrite(f"{directory}/{filename}")
|
|
3161
3175
|
print(f"Nodes saved to {directory}/{filename}")
|
|
3162
3176
|
except Exception as e:
|
|
3163
3177
|
print(f"Could not save nodes to {directory}")
|
|
@@ -3170,6 +3184,15 @@ class Network_3D:
|
|
|
3170
3184
|
:param directory: (Optional - Val = None; String). The path to an indended directory to save the edges to.
|
|
3171
3185
|
"""
|
|
3172
3186
|
|
|
3187
|
+
imagej_metadata = {
|
|
3188
|
+
'spacing': self.z_scale,
|
|
3189
|
+
'slices': self._edges.shape[0],
|
|
3190
|
+
'channels': 1,
|
|
3191
|
+
'axes': 'ZYX'
|
|
3192
|
+
}
|
|
3193
|
+
|
|
3194
|
+
resolution_value = 1.0 / self.xy_scale if self.xy_scale != 0 else 1
|
|
3195
|
+
|
|
3173
3196
|
if filename is None:
|
|
3174
3197
|
filename = "labelled_edges.tif"
|
|
3175
3198
|
elif not filename.endswith(('.tif', '.tiff')):
|
|
@@ -3177,11 +3200,11 @@ class Network_3D:
|
|
|
3177
3200
|
|
|
3178
3201
|
if self._edges is not None:
|
|
3179
3202
|
if directory is None:
|
|
3180
|
-
tifffile.imwrite(f"{filename}", self._edges)
|
|
3203
|
+
tifffile.imwrite(f"{filename}", self._edges, imagej=True, metadata=imagej_metadata, resolution=(resolution_value, resolution_value))
|
|
3181
3204
|
print(f"Edges saved to {filename}")
|
|
3182
3205
|
|
|
3183
3206
|
if directory is not None:
|
|
3184
|
-
tifffile.imwrite(f"{directory}/{filename}", self._edges)
|
|
3207
|
+
tifffile.imwrite(f"{directory}/{filename}", self._edges, imagej=True, metadata=imagej_metadata, resolution=(resolution_value, resolution_value))
|
|
3185
3208
|
print(f"Edges saved to {directory}/{filename}")
|
|
3186
3209
|
|
|
3187
3210
|
if self._edges is None:
|
|
@@ -3337,6 +3360,13 @@ class Network_3D:
|
|
|
3337
3360
|
|
|
3338
3361
|
def save_network_overlay(self, directory = None, filename = None):
|
|
3339
3362
|
|
|
3363
|
+
imagej_metadata = {
|
|
3364
|
+
'spacing': self.z_scale,
|
|
3365
|
+
'slices': self._network_overlay.shape[0],
|
|
3366
|
+
'channels': 1,
|
|
3367
|
+
'axes': 'ZYX'
|
|
3368
|
+
}
|
|
3369
|
+
resolution_value = 1.0 / self.xy_scale if self.xy_scale != 0 else 1
|
|
3340
3370
|
|
|
3341
3371
|
if filename is None:
|
|
3342
3372
|
filename = "overlay_1.tif"
|
|
@@ -3345,15 +3375,29 @@ class Network_3D:
|
|
|
3345
3375
|
|
|
3346
3376
|
if self._network_overlay is not None:
|
|
3347
3377
|
if directory is None:
|
|
3348
|
-
|
|
3378
|
+
if len(self._network_overlay.shape) == 3:
|
|
3379
|
+
tifffile.imwrite(f"{filename}", self._network_overlay, imagej=True, metadata=imagej_metadata, resolution=(resolution_value, resolution_value))
|
|
3380
|
+
else:
|
|
3381
|
+
tifffile.imwrite(f"{filename}", self._network_overlay)
|
|
3349
3382
|
print(f"Network overlay saved to {filename}")
|
|
3350
3383
|
|
|
3351
3384
|
if directory is not None:
|
|
3352
|
-
|
|
3385
|
+
if len(self._network_overlay.shape) == 3:
|
|
3386
|
+
tifffile.imwrite(f"{directory}/{filename}", self._network_overlay, imagej=True, metadata=imagej_metadata, resolution=(resolution_value, resolution_value))
|
|
3387
|
+
else:
|
|
3388
|
+
tifffile.imwrite(f"{directory}/{filename}", self._network_overlay)
|
|
3353
3389
|
print(f"Network overlay saved to {directory}/{filename}")
|
|
3354
3390
|
|
|
3355
3391
|
def save_id_overlay(self, directory = None, filename = None):
|
|
3356
3392
|
|
|
3393
|
+
imagej_metadata = {
|
|
3394
|
+
'spacing': self.z_scale,
|
|
3395
|
+
'slices': self._id_overlay.shape[0],
|
|
3396
|
+
'channels': 1,
|
|
3397
|
+
'axes': 'ZYX'
|
|
3398
|
+
}
|
|
3399
|
+
resolution_value = 1.0 / self.xy_scale if self.xy_scale != 0 else 1
|
|
3400
|
+
|
|
3357
3401
|
if filename is None:
|
|
3358
3402
|
filename = "overlay_2.tif"
|
|
3359
3403
|
if not filename.endswith(('.tif', '.tiff')):
|
|
@@ -3361,11 +3405,17 @@ class Network_3D:
|
|
|
3361
3405
|
|
|
3362
3406
|
if self._id_overlay is not None:
|
|
3363
3407
|
if directory is None:
|
|
3364
|
-
|
|
3408
|
+
if len(self._id_overlay.shape) == 3:
|
|
3409
|
+
tifffile.imwrite(f"{filename}", self._id_overlay, imagej=True, metadata=imagej_metadata, resolution=(resolution_value, resolution_value))
|
|
3410
|
+
else:
|
|
3411
|
+
tifffile.imwrite(f"{filename}", self._id_overlay, imagej=True)
|
|
3365
3412
|
print(f"Network overlay saved to {filename}")
|
|
3366
3413
|
|
|
3367
3414
|
if directory is not None:
|
|
3368
|
-
|
|
3415
|
+
if len(self._id_overlay.shape) == 3:
|
|
3416
|
+
tifffile.imwrite(f"{directory}/{filename}", self._id_overlay, imagej=True, metadata=imagej_metadata, resolution=(resolution_value, resolution_value))
|
|
3417
|
+
else:
|
|
3418
|
+
tifffile.imwrite(f"{directory}/{filename}", self._id_overlay)
|
|
3369
3419
|
print(f"ID overlay saved to {directory}/{filename}")
|
|
3370
3420
|
|
|
3371
3421
|
|
|
@@ -3488,7 +3538,7 @@ class Network_3D:
|
|
|
3488
3538
|
|
|
3489
3539
|
if file_path is not None:
|
|
3490
3540
|
self._xy_scale, self_z_scale = read_scalings(file_path)
|
|
3491
|
-
print("Succesfully loaded voxel_scalings")
|
|
3541
|
+
print(f"Succesfully loaded voxel_scalings; values overriden to xy_scale: {self.xy_scale}, z_scale: {self.z_scale}")
|
|
3492
3542
|
return
|
|
3493
3543
|
|
|
3494
3544
|
items = directory_info(directory)
|
|
@@ -3497,11 +3547,11 @@ class Network_3D:
|
|
|
3497
3547
|
if item == 'voxel_scalings.txt':
|
|
3498
3548
|
if directory is not None:
|
|
3499
3549
|
self._xy_scale, self._z_scale = read_scalings(f"{directory}/{item}")
|
|
3500
|
-
print("Succesfully loaded voxel_scalings")
|
|
3550
|
+
print(f"Succesfully loaded voxel_scalings; values overriden to xy_scale: {self.xy_scale}, z_scale: {self.z_scale}")
|
|
3501
3551
|
return
|
|
3502
3552
|
else:
|
|
3503
3553
|
self._xy_scale, self._z_scale = read_scalings(item)
|
|
3504
|
-
print("Succesfully loaded
|
|
3554
|
+
print(f"Succesfully loaded voxel_scaling; values overriden to xy_scale: {self.xy_scale}, z_scale: {self.z_scale}s")
|
|
3505
3555
|
return
|
|
3506
3556
|
|
|
3507
3557
|
print("Could not find voxel scalings. They must be in the specified directory and named 'voxel_scalings.txt'")
|
|
@@ -5403,9 +5453,9 @@ class Network_3D:
|
|
|
5403
5453
|
|
|
5404
5454
|
|
|
5405
5455
|
|
|
5406
|
-
def interactions(self, search = 0, cores = 0, resize = None, save = False, skele = False, fastdil = False):
|
|
5456
|
+
def interactions(self, search = 0, cores = 0, resize = None, save = False, skele = False, length = False, auto = True, fastdil = False):
|
|
5407
5457
|
|
|
5408
|
-
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)
|
|
5458
|
+
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, length = length, auto = auto, fastdil = fastdil)
|
|
5409
5459
|
|
|
5410
5460
|
|
|
5411
5461
|
|