nettracer3d 0.6.6__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 +261 -238
- nettracer3d/nettracer_gui.py +321 -185
- nettracer3d/proximity.py +9 -8
- nettracer3d/segmenter.py +12 -18
- nettracer3d/smart_dilate.py +156 -82
- {nettracer3d-0.6.6.dist-info → nettracer3d-0.6.7.dist-info}/METADATA +8 -4
- nettracer3d-0.6.7.dist-info/RECORD +20 -0
- nettracer3d-0.6.6.dist-info/RECORD +0 -20
- {nettracer3d-0.6.6.dist-info → nettracer3d-0.6.7.dist-info}/WHEEL +0 -0
- {nettracer3d-0.6.6.dist-info → nettracer3d-0.6.7.dist-info}/entry_points.txt +0 -0
- {nettracer3d-0.6.6.dist-info → nettracer3d-0.6.7.dist-info}/licenses/LICENSE +0 -0
- {nettracer3d-0.6.6.dist-info → nettracer3d-0.6.7.dist-info}/top_level.txt +0 -0
nettracer3d/proximity.py
CHANGED
|
@@ -64,13 +64,13 @@ def reslice_3d_array(args):
|
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
def _get_node_node_dict(label_array, label, dilate_xy, dilate_z):
|
|
67
|
+
def _get_node_node_dict(label_array, label, dilate_xy, dilate_z, fastdil = False, xy_scale = 1, z_scale = 1, search = 0):
|
|
68
68
|
"""Internal method used for the secondary algorithm to find which nodes interact
|
|
69
69
|
with which other nodes based on proximity."""
|
|
70
70
|
|
|
71
71
|
# Create a boolean mask where elements with the specified label are True
|
|
72
72
|
binary_array = label_array == label
|
|
73
|
-
binary_array = nettracer.
|
|
73
|
+
binary_array = nettracer.dilate(binary_array, search, xy_scale, z_scale, fast_dil = fastdil, dilate_xy = dilate_xy, dilate_z = dilate_z) #Dilate the label to see where the dilated label overlaps
|
|
74
74
|
label_array = label_array * binary_array # Filter the labels by the node in question
|
|
75
75
|
label_array = label_array.flatten() # Convert 3d array to 1d array
|
|
76
76
|
label_array = nettracer.remove_zeros(label_array) # Remove zeros
|
|
@@ -82,7 +82,7 @@ def _get_node_node_dict(label_array, label, dilate_xy, dilate_z):
|
|
|
82
82
|
def process_label(args):
|
|
83
83
|
"""Modified to use pre-computed bounding boxes instead of argwhere"""
|
|
84
84
|
nodes, label, dilate_xy, dilate_z, array_shape, bounding_boxes = args
|
|
85
|
-
print(f"Processing node {label}")
|
|
85
|
+
#print(f"Processing node {label}")
|
|
86
86
|
|
|
87
87
|
# Get the pre-computed bounding box for this label
|
|
88
88
|
slice_obj = bounding_boxes[label-1] # -1 because label numbers start at 1
|
|
@@ -97,10 +97,11 @@ def process_label(args):
|
|
|
97
97
|
return label, sub_nodes
|
|
98
98
|
|
|
99
99
|
|
|
100
|
-
def create_node_dictionary(nodes, num_nodes, dilate_xy, dilate_z, targets=None):
|
|
101
|
-
"""
|
|
100
|
+
def create_node_dictionary(nodes, num_nodes, dilate_xy, dilate_z, targets=None, fastdil = False, xy_scale = 1, z_scale = 1, search = 0):
|
|
101
|
+
"""pre-compute all bounding boxes using find_objects"""
|
|
102
102
|
node_dict = {}
|
|
103
103
|
array_shape = nodes.shape
|
|
104
|
+
|
|
104
105
|
|
|
105
106
|
# Get all bounding boxes at once
|
|
106
107
|
bounding_boxes = ndimage.find_objects(nodes)
|
|
@@ -118,17 +119,17 @@ def create_node_dictionary(nodes, num_nodes, dilate_xy, dilate_z, targets=None):
|
|
|
118
119
|
|
|
119
120
|
# Process results in parallel
|
|
120
121
|
for label, sub_nodes in results:
|
|
121
|
-
executor.submit(create_dict_entry, node_dict, label, sub_nodes, dilate_xy, dilate_z)
|
|
122
|
+
executor.submit(create_dict_entry, node_dict, label, sub_nodes, dilate_xy, dilate_z, fastdil = fastdil, xy_scale = xy_scale, z_scale = z_scale, search = search)
|
|
122
123
|
|
|
123
124
|
return node_dict
|
|
124
125
|
|
|
125
|
-
def create_dict_entry(node_dict, label, sub_nodes, dilate_xy, dilate_z):
|
|
126
|
+
def create_dict_entry(node_dict, label, sub_nodes, dilate_xy, dilate_z, fastdil = False, xy_scale = 1, z_scale = 1, search = 0):
|
|
126
127
|
"""Internal method used for the secondary algorithm to pass around args in parallel."""
|
|
127
128
|
|
|
128
129
|
if label is None:
|
|
129
130
|
pass
|
|
130
131
|
else:
|
|
131
|
-
node_dict[label] = _get_node_node_dict(sub_nodes, label, dilate_xy, dilate_z)
|
|
132
|
+
node_dict[label] = _get_node_node_dict(sub_nodes, label, dilate_xy, dilate_z, fastdil = fastdil, xy_scale = xy_scale, z_scale = z_scale, search = search)
|
|
132
133
|
|
|
133
134
|
def find_shared_value_pairs(input_dict):
|
|
134
135
|
"""Internal method used for the secondary algorithm to look through discrete
|
nettracer3d/segmenter.py
CHANGED
|
@@ -1,18 +1,5 @@
|
|
|
1
1
|
from sklearn.ensemble import RandomForestClassifier
|
|
2
2
|
import numpy as np
|
|
3
|
-
try:
|
|
4
|
-
import torch
|
|
5
|
-
except:
|
|
6
|
-
pass
|
|
7
|
-
try:
|
|
8
|
-
import cupy as cp
|
|
9
|
-
import cupyx.scipy.ndimage as cpx
|
|
10
|
-
except:
|
|
11
|
-
pass
|
|
12
|
-
try:
|
|
13
|
-
from cuml.ensemble import RandomForestClassifier as cuRandomForestClassifier
|
|
14
|
-
except:
|
|
15
|
-
pass
|
|
16
3
|
import concurrent.futures
|
|
17
4
|
from concurrent.futures import ThreadPoolExecutor
|
|
18
5
|
import threading
|
|
@@ -218,13 +205,20 @@ class InteractiveSegmenter:
|
|
|
218
205
|
|
|
219
206
|
# Gaussian and DoG using scipy
|
|
220
207
|
#print("Obtaining gaussians")
|
|
221
|
-
for sigma in
|
|
208
|
+
for sigma in self.alphas:
|
|
222
209
|
smooth = ndimage.gaussian_filter(image_3d, sigma)
|
|
223
210
|
features.append(smooth)
|
|
211
|
+
|
|
212
|
+
# Difference of Gaussians
|
|
213
|
+
for (s1, s2) in self.dogs:
|
|
214
|
+
g1 = ndimage.gaussian_filter(image_3d, s1)
|
|
215
|
+
g2 = ndimage.gaussian_filter(image_3d, s2)
|
|
216
|
+
dog = g1 - g2
|
|
217
|
+
features.append(dog)
|
|
224
218
|
|
|
225
219
|
#print("Computing local statistics")
|
|
226
220
|
# Local statistics using scipy's convolve
|
|
227
|
-
window_size =
|
|
221
|
+
window_size = self.windows
|
|
228
222
|
kernel = np.ones((window_size, window_size, window_size)) / (window_size**3)
|
|
229
223
|
|
|
230
224
|
# Local mean
|
|
@@ -299,7 +293,7 @@ class InteractiveSegmenter:
|
|
|
299
293
|
for (s1, s2) in self.dogs:
|
|
300
294
|
|
|
301
295
|
future = executor.submit(compute_dog_local, image_3d, s1, s2)
|
|
302
|
-
futures.append('dog', s1, future)
|
|
296
|
+
futures.append(('dog', s1, future))
|
|
303
297
|
|
|
304
298
|
# Local statistics computation
|
|
305
299
|
def compute_local_mean():
|
|
@@ -960,13 +954,13 @@ class InteractiveSegmenter:
|
|
|
960
954
|
|
|
961
955
|
# Gaussian smoothing at different scales
|
|
962
956
|
#print("Obtaining gaussians")
|
|
963
|
-
for sigma in
|
|
957
|
+
for sigma in self.alphas:
|
|
964
958
|
smooth = ndimage.gaussian_filter(image_3d, sigma)
|
|
965
959
|
features.append(smooth)
|
|
966
960
|
|
|
967
961
|
#print("Obtaining dif of gaussians")
|
|
968
962
|
# Difference of Gaussians
|
|
969
|
-
for (s1, s2) in
|
|
963
|
+
for (s1, s2) in self.dogs:
|
|
970
964
|
g1 = ndimage.gaussian_filter(image_3d, s1)
|
|
971
965
|
g2 = ndimage.gaussian_filter(image_3d, s2)
|
|
972
966
|
dog = g1 - g2
|
nettracer3d/smart_dilate.py
CHANGED
|
@@ -2,6 +2,7 @@ import tifffile
|
|
|
2
2
|
import numpy as np
|
|
3
3
|
from scipy.ndimage import binary_dilation, distance_transform_edt
|
|
4
4
|
from scipy.ndimage import gaussian_filter
|
|
5
|
+
from scipy import ndimage
|
|
5
6
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
6
7
|
import cv2
|
|
7
8
|
import os
|
|
@@ -145,45 +146,99 @@ def dilate_3D(tiff_array, dilated_x, dilated_y, dilated_z):
|
|
|
145
146
|
return final_result
|
|
146
147
|
|
|
147
148
|
|
|
148
|
-
def dilate_3D_old(tiff_array, dilated_x, dilated_y, dilated_z):
|
|
149
|
-
"""
|
|
150
|
-
|
|
149
|
+
def dilate_3D_old(tiff_array, dilated_x=3, dilated_y=3, dilated_z=3):
|
|
150
|
+
"""
|
|
151
|
+
Dilate a 3D array using scipy.ndimage.binary_dilation with a 3x3x3 cubic kernel.
|
|
152
|
+
|
|
153
|
+
Arguments:
|
|
154
|
+
tiff_array -- Input 3D binary array
|
|
155
|
+
dilated_x -- Fixed at 3 for X dimension
|
|
156
|
+
dilated_y -- Fixed at 3 for Y dimension
|
|
157
|
+
dilated_z -- Fixed at 3 for Z dimension
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
Dilated 3D array
|
|
161
|
+
"""
|
|
162
|
+
import numpy as np
|
|
163
|
+
from scipy import ndimage
|
|
164
|
+
|
|
165
|
+
# Handle special case for 2D arrays
|
|
166
|
+
if tiff_array.shape[0] == 1:
|
|
167
|
+
# Call 2D dilation function if needed
|
|
168
|
+
return dilate_2D(tiff_array, 1) # For a 3x3 kernel, radius is 1
|
|
169
|
+
|
|
170
|
+
# Create a simple 3x3x3 cubic kernel (all ones)
|
|
171
|
+
kernel = np.ones((3, 3, 3), dtype=bool)
|
|
172
|
+
|
|
173
|
+
# Perform binary dilation
|
|
174
|
+
dilated_array = ndimage.binary_dilation(tiff_array.astype(bool), structure=kernel)
|
|
175
|
+
|
|
176
|
+
return dilated_array.astype(np.uint8)
|
|
151
177
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
178
|
+
def dilate_3D_dt(array, search_distance, xy_scaling=1.0, z_scaling=1.0, GPU = False):
|
|
179
|
+
"""
|
|
180
|
+
Dilate a 3D array using distance transform method. Dt dilation produces perfect results but only works in euclidean geometry and lags in big arrays.
|
|
181
|
+
|
|
182
|
+
Parameters:
|
|
183
|
+
array -- Input 3D binary array
|
|
184
|
+
search_distance -- Distance within which to dilate
|
|
185
|
+
xy_scaling -- Scaling factor for x and y dimensions (default: 1.0)
|
|
186
|
+
z_scaling -- Scaling factor for z dimension (default: 1.0)
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Dilated 3D array
|
|
190
|
+
"""
|
|
191
|
+
if array.shape[0] == 1:
|
|
192
|
+
|
|
193
|
+
return nettracer.dilate_2D(array, search_distance, scaling = xy_scaling)
|
|
194
|
+
|
|
195
|
+
# Determine which dimension needs resampling
|
|
196
|
+
if (z_scaling > xy_scaling):
|
|
197
|
+
# Z dimension needs to be stretched
|
|
198
|
+
zoom_factor = [z_scaling/xy_scaling, 1, 1] # Scale factor for [z, y, x]
|
|
199
|
+
rev_factor = [xy_scaling/z_scaling, 1, 1]
|
|
200
|
+
cardinal = xy_scaling
|
|
201
|
+
elif (xy_scaling > z_scaling):
|
|
202
|
+
# XY dimensions need to be stretched
|
|
203
|
+
zoom_factor = [1, xy_scaling/z_scaling, xy_scaling/z_scaling] # Scale factor for [z, y, x]
|
|
204
|
+
rev_factor = [1, z_scaling/xy_scaling, z_scaling/xy_scaling] # Scale factor for [z, y, x]
|
|
205
|
+
cardinal = z_scaling
|
|
206
|
+
else:
|
|
207
|
+
# Already uniform scaling, no need to resample
|
|
208
|
+
zoom_factor = None
|
|
209
|
+
rev_factor = None
|
|
210
|
+
cardinal = xy_scaling
|
|
155
211
|
|
|
156
|
-
# Perform 2D dilation in the XY plane
|
|
157
|
-
for z in range(tiff_array.shape[0]):
|
|
158
|
-
kernel_x = int(dilated_x)
|
|
159
|
-
kernel_y = int(dilated_y)
|
|
160
|
-
kernel = np.ones((kernel_y, kernel_x), dtype=np.uint8)
|
|
161
212
|
|
|
213
|
+
# Resample the mask if needed
|
|
214
|
+
if zoom_factor:
|
|
215
|
+
array = ndimage.zoom(array, zoom_factor, order=0) # Use order=0 for binary masks
|
|
162
216
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
217
|
+
# Invert the array (find background)
|
|
218
|
+
inv = array < 1
|
|
219
|
+
|
|
220
|
+
if GPU:
|
|
221
|
+
try:
|
|
222
|
+
print("Attempting on GPU...")
|
|
223
|
+
inv, indices = compute_distance_transform_GPU(inv, return_dists = True)
|
|
224
|
+
except:
|
|
225
|
+
print("Failed, attempting on CPU...")
|
|
226
|
+
#Who would have seen this coming?:
|
|
227
|
+
inv, indices = compute_distance_transform(inv, return_dists = True)
|
|
228
|
+
else:
|
|
229
|
+
inv, indices = compute_distance_transform(inv, return_dists = True)
|
|
168
230
|
|
|
169
|
-
# Perform 2D dilation in the XZ plane
|
|
170
|
-
for y in range(tiff_array.shape[1]):
|
|
171
|
-
kernel_x = int(dilated_x)
|
|
172
|
-
kernel_z = int(dilated_z)
|
|
173
|
-
kernel = np.ones((kernel_z, kernel_x), dtype=np.uint8)
|
|
174
231
|
|
|
232
|
+
inv = inv * cardinal
|
|
233
|
+
|
|
234
|
+
# Threshold the distance transform to get dilated result
|
|
235
|
+
inv = inv <= search_distance
|
|
175
236
|
|
|
176
|
-
# Convert the slice to the appropriate data type
|
|
177
|
-
tiff_slice = tiff_array[:, y, :].astype(np.uint8)
|
|
178
237
|
|
|
179
|
-
|
|
180
|
-
dilated_xz[:, y, :] = dilated_slice
|
|
238
|
+
return inv.astype(np.uint8), indices, array, rev_factor
|
|
181
239
|
|
|
182
|
-
# Overlay the results (you can use logical OR operation or another method)
|
|
183
|
-
final_result = dilated_xy | dilated_xz
|
|
184
240
|
|
|
185
241
|
|
|
186
|
-
return final_result
|
|
187
242
|
|
|
188
243
|
def binarize(image):
|
|
189
244
|
"""Convert an array from numerical values to boolean mask"""
|
|
@@ -209,71 +264,73 @@ def process_chunk(start_idx, end_idx, nodes, ring_mask, nearest_label_indices):
|
|
|
209
264
|
|
|
210
265
|
return dilated_nodes_with_labels_chunk
|
|
211
266
|
|
|
212
|
-
def smart_dilate(nodes, dilate_xy, dilate_z, directory = None, GPU = True, fast_dil =
|
|
267
|
+
def smart_dilate(nodes, dilate_xy, dilate_z, directory = None, GPU = True, fast_dil = True, predownsample = None, use_dt_dil_amount = None, xy_scale = 1, z_scale = 1):
|
|
213
268
|
|
|
214
269
|
original_shape = nodes.shape
|
|
215
270
|
|
|
216
|
-
# Step 1: Binarize the labeled array
|
|
217
|
-
binary_nodes = binarize(nodes)
|
|
218
271
|
|
|
219
|
-
#
|
|
220
|
-
if
|
|
272
|
+
#Dilate the binarized array
|
|
273
|
+
if fast_dil:
|
|
274
|
+
# Step : Binarize the labeled array
|
|
275
|
+
binary_nodes = binarize(nodes)
|
|
221
276
|
dilated_binary_nodes = dilate_3D(binary_nodes, dilate_xy, dilate_xy, dilate_z)
|
|
222
277
|
else:
|
|
223
|
-
dilated_binary_nodes =
|
|
224
|
-
|
|
278
|
+
dilated_binary_nodes, nearest_label_indices, nodes, rev_factor = dilate_3D_dt(nodes, use_dt_dil_amount, GPU = GPU, xy_scaling = xy_scale, z_scaling = z_scale)
|
|
279
|
+
binary_nodes = binarize(nodes)
|
|
225
280
|
|
|
226
281
|
# Step 3: Isolate the ring (binary dilated mask minus original binary mask)
|
|
227
282
|
ring_mask = dilated_binary_nodes & invert_array(binary_nodes)
|
|
228
283
|
|
|
284
|
+
del binary_nodes
|
|
285
|
+
|
|
229
286
|
print("Preforming distance transform for smart search... this step may take some time if computed on CPU...")
|
|
230
287
|
|
|
231
|
-
|
|
288
|
+
if fast_dil:
|
|
232
289
|
|
|
233
|
-
|
|
234
|
-
print("GPU detected. Using CuPy for distance transform.")
|
|
290
|
+
try:
|
|
235
291
|
|
|
236
|
-
|
|
292
|
+
if GPU == True and cp.cuda.runtime.getDeviceCount() > 0:
|
|
293
|
+
print("GPU detected. Using CuPy for distance transform.")
|
|
237
294
|
|
|
238
|
-
|
|
295
|
+
try:
|
|
239
296
|
|
|
240
|
-
|
|
241
|
-
nearest_label_indices = compute_distance_transform_GPU(invert_array(nodes))
|
|
297
|
+
if predownsample is None:
|
|
242
298
|
|
|
243
|
-
|
|
244
|
-
|
|
299
|
+
# Step 4: Find the nearest label for each voxel in the ring
|
|
300
|
+
nearest_label_indices = compute_distance_transform_GPU(invert_array(nodes))
|
|
245
301
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
down_factor = catch_memory(e) #Obtain downsample amount based on memory missing
|
|
249
|
-
else:
|
|
250
|
-
down_factor = (predownsample)**3
|
|
302
|
+
else:
|
|
303
|
+
gotoexcept = 1/0
|
|
251
304
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
305
|
+
except (cp.cuda.memory.OutOfMemoryError, ZeroDivisionError) as e:
|
|
306
|
+
if predownsample is None:
|
|
307
|
+
down_factor = catch_memory(e) #Obtain downsample amount based on memory missing
|
|
308
|
+
else:
|
|
309
|
+
down_factor = (predownsample)**3
|
|
310
|
+
|
|
311
|
+
while True:
|
|
312
|
+
downsample_needed = down_factor**(1./3.)
|
|
313
|
+
small_nodes = nettracer.downsample(nodes, downsample_needed) #Apply downsample
|
|
314
|
+
try:
|
|
315
|
+
nearest_label_indices = compute_distance_transform_GPU(invert_array(small_nodes)) #Retry dt on downsample
|
|
316
|
+
print(f"Using {down_factor} downsample ({downsample_needed} in each dim - Largest possible with this GPU unless user specified downsample)")
|
|
317
|
+
break
|
|
318
|
+
except cp.cuda.memory.OutOfMemoryError:
|
|
319
|
+
down_factor += 1
|
|
320
|
+
binary_nodes = binarize(small_nodes) #Recompute variables for downsample
|
|
321
|
+
dilated_mask = dilated_binary_nodes #Need this for later to stamp out the correct output
|
|
264
322
|
dilated_binary_nodes = dilate_3D(binary_nodes, 2 + round_to_odd(dilate_xy/downsample_needed), 2 + round_to_odd(dilate_xy/downsample_needed), 2 + round_to_odd(dilate_z/downsample_needed)) #Mod dilation to recompute variables for downsample while also over dilatiing
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
nearest_label_indices = compute_distance_transform(invert_array(nodes))
|
|
323
|
+
|
|
324
|
+
ring_mask = dilated_binary_nodes & invert_array(binary_nodes)
|
|
325
|
+
nodes = small_nodes
|
|
326
|
+
del small_nodes
|
|
327
|
+
else:
|
|
328
|
+
goto_except = 1/0
|
|
329
|
+
except Exception as e:
|
|
330
|
+
print("GPU dt failed or did not detect GPU (cupy must be installed with a CUDA toolkit setup...). Computing CPU distance transform instead.")
|
|
331
|
+
if GPU:
|
|
332
|
+
print(f"Error message: {str(e)}")
|
|
333
|
+
nearest_label_indices = compute_distance_transform(invert_array(nodes))
|
|
277
334
|
|
|
278
335
|
|
|
279
336
|
# Step 5: Process in parallel chunks using ThreadPoolExecutor
|
|
@@ -284,12 +341,19 @@ def smart_dilate(nodes, dilate_xy, dilate_z, directory = None, GPU = True, fast_
|
|
|
284
341
|
args_list = [(i * chunk_size, (i + 1) * chunk_size if i != num_cores - 1 else nodes.shape[0], nodes, ring_mask, nearest_label_indices) for i in range(num_cores)]
|
|
285
342
|
results = list(executor.map(lambda args: process_chunk(*args), args_list))
|
|
286
343
|
|
|
344
|
+
del ring_mask
|
|
345
|
+
del nodes
|
|
346
|
+
del nearest_label_indices
|
|
347
|
+
|
|
287
348
|
# Combine results from chunks
|
|
288
349
|
dilated_nodes_with_labels = np.concatenate(results, axis=0)
|
|
289
350
|
|
|
290
|
-
|
|
351
|
+
|
|
352
|
+
if (dilated_nodes_with_labels.shape[1] < original_shape[1]) and fast_dil: #If downsample was used, upsample output
|
|
291
353
|
dilated_nodes_with_labels = nettracer.upsample_with_padding(dilated_nodes_with_labels, downsample_needed, original_shape)
|
|
292
354
|
dilated_nodes_with_labels = dilated_nodes_with_labels * dilated_mask
|
|
355
|
+
elif (dilated_nodes_with_labels.shape[1] != original_shape[1]) and not fast_dil:
|
|
356
|
+
dilated_nodes_with_labels = ndimage.zoom(dilated_nodes_with_labels, rev_factor, order=0)
|
|
293
357
|
|
|
294
358
|
if directory is not None:
|
|
295
359
|
try:
|
|
@@ -364,7 +428,7 @@ def smart_label(binary_array, label_array, directory = None, GPU = True, predown
|
|
|
364
428
|
binary_core = binarize(small_array)
|
|
365
429
|
label_array = small_array
|
|
366
430
|
binary_array = nettracer.downsample(binary_array, downsample_needed)
|
|
367
|
-
binary_array = nettracer.
|
|
431
|
+
binary_array = nettracer.dilate_3D_old(binary_array)
|
|
368
432
|
ring_mask = binary_array & invert_array(binary_core)
|
|
369
433
|
|
|
370
434
|
else:
|
|
@@ -408,7 +472,7 @@ def smart_label(binary_array, label_array, directory = None, GPU = True, predown
|
|
|
408
472
|
|
|
409
473
|
return dilated_nodes_with_labels
|
|
410
474
|
|
|
411
|
-
def compute_distance_transform_GPU(nodes):
|
|
475
|
+
def compute_distance_transform_GPU(nodes, return_dists = False):
|
|
412
476
|
is_pseudo_3d = nodes.shape[0] == 1
|
|
413
477
|
if is_pseudo_3d:
|
|
414
478
|
nodes = np.squeeze(nodes) # Convert to 2D for processing
|
|
@@ -417,7 +481,7 @@ def compute_distance_transform_GPU(nodes):
|
|
|
417
481
|
nodes_cp = cp.asarray(nodes)
|
|
418
482
|
|
|
419
483
|
# Compute the distance transform on the GPU
|
|
420
|
-
|
|
484
|
+
dists, nearest_label_indices = cpx.distance_transform_edt(nodes_cp, return_indices=True)
|
|
421
485
|
|
|
422
486
|
# Convert results back to numpy arrays
|
|
423
487
|
nearest_label_indices_np = cp.asnumpy(nearest_label_indices)
|
|
@@ -430,18 +494,22 @@ def compute_distance_transform_GPU(nodes):
|
|
|
430
494
|
# indices_4d[0] stays 0 for all Z coordinates
|
|
431
495
|
nearest_label_indices_np = indices_4d
|
|
432
496
|
|
|
433
|
-
|
|
497
|
+
if not return_dists:
|
|
434
498
|
|
|
499
|
+
return nearest_label_indices_np
|
|
500
|
+
|
|
501
|
+
else:
|
|
502
|
+
dists = cp.asnumpy(dists)
|
|
435
503
|
|
|
436
|
-
|
|
504
|
+
return dists, nearest_label_indices_np
|
|
437
505
|
|
|
438
506
|
|
|
439
|
-
def compute_distance_transform(nodes):
|
|
507
|
+
def compute_distance_transform(nodes, return_dists = False):
|
|
440
508
|
is_pseudo_3d = nodes.shape[0] == 1
|
|
441
509
|
if is_pseudo_3d:
|
|
442
510
|
nodes = np.squeeze(nodes) # Convert to 2D for processing
|
|
443
511
|
|
|
444
|
-
|
|
512
|
+
dists, nearest_label_indices = distance_transform_edt(nodes, return_indices=True)
|
|
445
513
|
|
|
446
514
|
if is_pseudo_3d:
|
|
447
515
|
# For 2D input, we get (2, H, W) but need (3, 1, H, W)
|
|
@@ -451,7 +519,13 @@ def compute_distance_transform(nodes):
|
|
|
451
519
|
# indices_4d[0] stays 0 for all Z coordinates
|
|
452
520
|
nearest_label_indices = indices_4d
|
|
453
521
|
|
|
454
|
-
|
|
522
|
+
if not return_dists:
|
|
523
|
+
|
|
524
|
+
return nearest_label_indices
|
|
525
|
+
|
|
526
|
+
else:
|
|
527
|
+
|
|
528
|
+
return dists, nearest_label_indices
|
|
455
529
|
|
|
456
530
|
|
|
457
531
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nettracer3d
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.7
|
|
4
4
|
Summary: Scripts for intializing and analyzing networks from segmentations of three dimensional images.
|
|
5
5
|
Author-email: Liam McLaughlin <mclaughlinliam99@gmail.com>
|
|
6
6
|
Project-URL: User_Tutorial, https://www.youtube.com/watch?v=cRatn5VTWDY
|
|
@@ -46,8 +46,12 @@ NetTracer3D is free to use/fork for academic/nonprofit use so long as citation i
|
|
|
46
46
|
|
|
47
47
|
NetTracer3D was developed by Liam McLaughlin while working under Dr. Sanjay Jain at Washington University School of Medicine.
|
|
48
48
|
|
|
49
|
-
-- Version 0.6.
|
|
49
|
+
-- Version 0.6.7 updates --
|
|
50
50
|
|
|
51
|
-
1. Updated
|
|
51
|
+
1. Updated all methods to use dilation to allow the user to select between perfect distance transform based dilation (which can be slower but allows for perfect searching - and is designed to account for scaling differences), or the current pseudo-3d kernel method.
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
1.5. The dt dilator accounts for scaling by stretching (upsampling) images to equivalent scales before dilating with the distance transform. It will not attempt to downsample. This admittedly will ask for greater memory and some more processing. To give the user the option to use the dt dilator without dealing with this, I added two new options to the resize method. You can now have it upsample your image until its equivalently scaled, or, if you don't need the fidelity, downsample your image until it's equivalently scaled. When the scaling is equivalent, the dt dilator will always just use the regular distance transform without attempting to resize the array.
|
|
54
|
+
|
|
55
|
+
2. Fixed radius finding method to also account for scaling correctly. Previous method scaled wrong. New method predictably accounts for differing scaling in xy vs z dims as well.
|
|
56
|
+
|
|
57
|
+
3. Bug fixes.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
nettracer3d/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
nettracer3d/community_extractor.py,sha256=5v9SCCLX3P1RX0fjPVKH5NHMFkMolZ5BTe0bR_a67xg,24479
|
|
3
|
+
nettracer3d/modularity.py,sha256=V1f3s_vGd8EuVz27mzq6ycIGr0BWIpH7c7NU4QjgAHU,30247
|
|
4
|
+
nettracer3d/morphology.py,sha256=P7hH9qpDBs0JtMSd95NmrvqoyD8BVq3AjAYv-MWoS_Y,19498
|
|
5
|
+
nettracer3d/nettracer.py,sha256=HDU7z9UOpc2rK1tp-gV7vum27NBNVe8CmfZEeJ7lDBc,209469
|
|
6
|
+
nettracer3d/nettracer_gui.py,sha256=ba_Kx84iu6qfdYKLjhwrS7cXvQQL0_YxrxM9PdIp6kk,401549
|
|
7
|
+
nettracer3d/network_analysis.py,sha256=q1q7lxtA3lebxitfC_jfiT9cnpYXJw4q0Oy2_-Aj8qE,48068
|
|
8
|
+
nettracer3d/network_draw.py,sha256=F7fw6Pcf4qWOhdKwLmhwqWdschbDlHzwCVolQC9imeU,14117
|
|
9
|
+
nettracer3d/node_draw.py,sha256=k3sCTfUCJs3aH1C1q1gTNxDz9EAQbBd1hsUIJajxRx8,9823
|
|
10
|
+
nettracer3d/proximity.py,sha256=mHgU24GZy5GGrR6RETB7QACfOURxgiyUze2dxMfs_3o,10917
|
|
11
|
+
nettracer3d/run.py,sha256=xYeaAc8FCx8MuzTGyL3NR3mK7WZzffAYAH23bNRZYO4,127
|
|
12
|
+
nettracer3d/segmenter.py,sha256=NcNeLSfg3ox-CfnUajT1E2iDqDkv5H6zSJOK_L5N4YI,85020
|
|
13
|
+
nettracer3d/simple_network.py,sha256=fP1gkDdtQcHruEZpUdasKdZeVacoLOxKhR3bY0L1CAQ,15426
|
|
14
|
+
nettracer3d/smart_dilate.py,sha256=ISZR6v52zf-MwhGx-JTfTOP8uo7pNGt-GJj7ydeuMAo,25587
|
|
15
|
+
nettracer3d-0.6.7.dist-info/licenses/LICENSE,sha256=gM207DhJjWrxLuEWXl0Qz5ISbtWDmADfjHp3yC2XISs,888
|
|
16
|
+
nettracer3d-0.6.7.dist-info/METADATA,sha256=jAjlCQhz-wFDiHbqF-nbVpEKu95n77H8CZNLP2cRnGI,4172
|
|
17
|
+
nettracer3d-0.6.7.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
18
|
+
nettracer3d-0.6.7.dist-info/entry_points.txt,sha256=Nx1rr_0QhJXDBHAQg2vcqCzLMKBzSHfwy3xwGkueVyc,53
|
|
19
|
+
nettracer3d-0.6.7.dist-info/top_level.txt,sha256=zsYy9rZwirfCEOubolhee4TyzqBAL5gSUeFMzhFTX8c,12
|
|
20
|
+
nettracer3d-0.6.7.dist-info/RECORD,,
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
nettracer3d/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
nettracer3d/community_extractor.py,sha256=Zq8ZM595CTzeR6zLEZ4I6KvhkNfCPUReWvAKxTlaVfk,33495
|
|
3
|
-
nettracer3d/modularity.py,sha256=V1f3s_vGd8EuVz27mzq6ycIGr0BWIpH7c7NU4QjgAHU,30247
|
|
4
|
-
nettracer3d/morphology.py,sha256=yncUj04Noj_mcdJze4qMfYw-21AbebwiIcu1bDWGgCM,17778
|
|
5
|
-
nettracer3d/nettracer.py,sha256=iq2EybaXzC9pdkNMmLQ_EqfFqHWMK-jxYqpb8Su61xQ,210230
|
|
6
|
-
nettracer3d/nettracer_gui.py,sha256=o2DiNbEDrf5CK_CdIZNjArmx-eKbwHk25rzBwVeRE5A,394393
|
|
7
|
-
nettracer3d/network_analysis.py,sha256=q1q7lxtA3lebxitfC_jfiT9cnpYXJw4q0Oy2_-Aj8qE,48068
|
|
8
|
-
nettracer3d/network_draw.py,sha256=F7fw6Pcf4qWOhdKwLmhwqWdschbDlHzwCVolQC9imeU,14117
|
|
9
|
-
nettracer3d/node_draw.py,sha256=k3sCTfUCJs3aH1C1q1gTNxDz9EAQbBd1hsUIJajxRx8,9823
|
|
10
|
-
nettracer3d/proximity.py,sha256=FnIiI_AzfXd22HwCIFIyQRZxKYJ8YscIDdPnIv-wsO4,10560
|
|
11
|
-
nettracer3d/run.py,sha256=xYeaAc8FCx8MuzTGyL3NR3mK7WZzffAYAH23bNRZYO4,127
|
|
12
|
-
nettracer3d/segmenter.py,sha256=MC4-Bkz1JKcbkISwJaoCwhNmys9E2iXp1gmCn049JjU,85023
|
|
13
|
-
nettracer3d/simple_network.py,sha256=fP1gkDdtQcHruEZpUdasKdZeVacoLOxKhR3bY0L1CAQ,15426
|
|
14
|
-
nettracer3d/smart_dilate.py,sha256=vnBj2soDGVBioKaNQi-bcyAtg0nuWcNGmlrzUNFFYQE,23191
|
|
15
|
-
nettracer3d-0.6.6.dist-info/licenses/LICENSE,sha256=gM207DhJjWrxLuEWXl0Qz5ISbtWDmADfjHp3yC2XISs,888
|
|
16
|
-
nettracer3d-0.6.6.dist-info/METADATA,sha256=gro7B_GK6lL1oL-nd64pO69ynXwbZKBvShNBTKJVYu0,3559
|
|
17
|
-
nettracer3d-0.6.6.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
18
|
-
nettracer3d-0.6.6.dist-info/entry_points.txt,sha256=Nx1rr_0QhJXDBHAQg2vcqCzLMKBzSHfwy3xwGkueVyc,53
|
|
19
|
-
nettracer3d-0.6.6.dist-info/top_level.txt,sha256=zsYy9rZwirfCEOubolhee4TyzqBAL5gSUeFMzhFTX8c,12
|
|
20
|
-
nettracer3d-0.6.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|