nettracer3d 0.6.3__tar.gz → 0.6.5__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.
- {nettracer3d-0.6.3/src/nettracer3d.egg-info → nettracer3d-0.6.5}/PKG-INFO +8 -5
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/README.md +6 -4
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/pyproject.toml +4 -2
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/src/nettracer3d/morphology.py +207 -5
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/src/nettracer3d/nettracer.py +82 -12
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/src/nettracer3d/nettracer_gui.py +330 -92
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/src/nettracer3d/network_analysis.py +6 -6
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/src/nettracer3d/smart_dilate.py +2 -31
- {nettracer3d-0.6.3 → nettracer3d-0.6.5/src/nettracer3d.egg-info}/PKG-INFO +8 -5
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/src/nettracer3d.egg-info/requires.txt +1 -0
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/LICENSE +0 -0
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/setup.cfg +0 -0
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/src/nettracer3d/__init__.py +0 -0
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/src/nettracer3d/community_extractor.py +0 -0
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/src/nettracer3d/modularity.py +0 -0
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/src/nettracer3d/network_draw.py +0 -0
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/src/nettracer3d/node_draw.py +0 -0
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/src/nettracer3d/proximity.py +0 -0
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/src/nettracer3d/run.py +0 -0
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/src/nettracer3d/segmenter.py +0 -0
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/src/nettracer3d/simple_network.py +0 -0
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/src/nettracer3d.egg-info/SOURCES.txt +0 -0
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/src/nettracer3d.egg-info/dependency_links.txt +0 -0
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/src/nettracer3d.egg-info/entry_points.txt +0 -0
- {nettracer3d-0.6.3 → nettracer3d-0.6.5}/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: 0.6.
|
|
3
|
+
Version: 0.6.5
|
|
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
|
|
@@ -27,6 +27,7 @@ Requires-Dist: qtrangeslider==0.1.5
|
|
|
27
27
|
Requires-Dist: PyQt6==6.8.0
|
|
28
28
|
Requires-Dist: scikit-learn==1.6.1
|
|
29
29
|
Requires-Dist: nibabel==5.2.0
|
|
30
|
+
Requires-Dist: setuptools>=65.0.0
|
|
30
31
|
Provides-Extra: cuda11
|
|
31
32
|
Requires-Dist: cupy-cuda11x; extra == "cuda11"
|
|
32
33
|
Provides-Extra: cuda12
|
|
@@ -45,10 +46,12 @@ NetTracer3D is free to use/fork for academic/nonprofit use so long as citation i
|
|
|
45
46
|
|
|
46
47
|
NetTracer3D was developed by Liam McLaughlin while working under Dr. Sanjay Jain at Washington University School of Medicine.
|
|
47
48
|
|
|
48
|
-
-- Version 0.6.
|
|
49
|
+
-- Version 0.6.5 updates --
|
|
49
50
|
|
|
50
|
-
1.
|
|
51
|
+
1. Added new method for obtaining radii of labeled objects (analyze -> stats -> calculate radii)
|
|
51
52
|
|
|
52
|
-
2. Updated
|
|
53
|
+
2. Updated functionality of split nontouching labels function to handle situations where said label is touching other labeled objects in space.
|
|
53
54
|
|
|
54
|
-
3.
|
|
55
|
+
3. Image -> Select Objects will now navigate to the first selected object in the array for user, allowing it to be used to also find whatever labeled object.
|
|
56
|
+
|
|
57
|
+
4. Minor bug fixes/improvements.
|
|
@@ -8,10 +8,12 @@ NetTracer3D is free to use/fork for academic/nonprofit use so long as citation i
|
|
|
8
8
|
|
|
9
9
|
NetTracer3D was developed by Liam McLaughlin while working under Dr. Sanjay Jain at Washington University School of Medicine.
|
|
10
10
|
|
|
11
|
-
-- Version 0.6.
|
|
11
|
+
-- Version 0.6.5 updates --
|
|
12
12
|
|
|
13
|
-
1.
|
|
13
|
+
1. Added new method for obtaining radii of labeled objects (analyze -> stats -> calculate radii)
|
|
14
14
|
|
|
15
|
-
2. Updated
|
|
15
|
+
2. Updated functionality of split nontouching labels function to handle situations where said label is touching other labeled objects in space.
|
|
16
16
|
|
|
17
|
-
3.
|
|
17
|
+
3. Image -> Select Objects will now navigate to the first selected object in the array for user, allowing it to be used to also find whatever labeled object.
|
|
18
|
+
|
|
19
|
+
4. Minor bug fixes/improvements.
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "nettracer3d"
|
|
3
|
-
version = "0.6.
|
|
3
|
+
version = "0.6.5"
|
|
4
4
|
authors = [
|
|
5
5
|
{ name="Liam McLaughlin", email="mclaughlinliam99@gmail.com" },
|
|
6
6
|
]
|
|
7
7
|
description = "Scripts for intializing and analyzing networks from segmentations of three dimensional images."
|
|
8
|
+
|
|
8
9
|
dependencies = [
|
|
9
10
|
"numpy == 1.26.4",
|
|
10
11
|
"scipy == 1.14.1",
|
|
@@ -21,7 +22,8 @@ dependencies = [
|
|
|
21
22
|
"qtrangeslider == 0.1.5",
|
|
22
23
|
"PyQt6 == 6.8.0",
|
|
23
24
|
"scikit-learn == 1.6.1",
|
|
24
|
-
"nibabel == 5.2.0"
|
|
25
|
+
"nibabel == 5.2.0",
|
|
26
|
+
"setuptools >= 65.0.0"
|
|
25
27
|
]
|
|
26
28
|
|
|
27
29
|
readme = "README.md"
|
|
@@ -6,8 +6,17 @@ import multiprocessing as mp
|
|
|
6
6
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
7
7
|
import tifffile
|
|
8
8
|
from functools import partial
|
|
9
|
-
import
|
|
9
|
+
import concurrent.futures
|
|
10
|
+
from functools import partial
|
|
10
11
|
from scipy import ndimage
|
|
12
|
+
import pandas as pd
|
|
13
|
+
# Import CuPy conditionally for GPU support
|
|
14
|
+
try:
|
|
15
|
+
import cupy as cp
|
|
16
|
+
import cupyx.scipy.ndimage as cpx
|
|
17
|
+
HAS_CUPY = True
|
|
18
|
+
except ImportError:
|
|
19
|
+
HAS_CUPY = False
|
|
11
20
|
|
|
12
21
|
def get_reslice_indices(slice_obj, dilate_xy, dilate_z, array_shape):
|
|
13
22
|
"""Convert slice object to padded indices accounting for dilation and boundaries"""
|
|
@@ -279,9 +288,6 @@ def search_neighbor_ids(nodes, targets, id_dict, neighborhood_dict, totals, sear
|
|
|
279
288
|
|
|
280
289
|
|
|
281
290
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
291
|
def get_search_space_dilate(target, centroids, id_dict, search, scaling = 1):
|
|
286
292
|
|
|
287
293
|
ymax = np.max(centroids[:, 0])
|
|
@@ -308,4 +314,200 @@ def get_search_space_dilate(target, centroids, id_dict, search, scaling = 1):
|
|
|
308
314
|
|
|
309
315
|
|
|
310
316
|
|
|
311
|
-
return array
|
|
317
|
+
return array
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
# Methods pertaining to getting radii:
|
|
321
|
+
|
|
322
|
+
def process_object_cpu(label, objects, labeled_array):
|
|
323
|
+
"""
|
|
324
|
+
Process a single labeled object to estimate its radius (CPU version).
|
|
325
|
+
This function is designed to be called in parallel.
|
|
326
|
+
|
|
327
|
+
Parameters:
|
|
328
|
+
-----------
|
|
329
|
+
label : int
|
|
330
|
+
The label ID to process
|
|
331
|
+
objects : list
|
|
332
|
+
List of slice objects from ndimage.find_objects
|
|
333
|
+
labeled_array : numpy.ndarray
|
|
334
|
+
The full 3D labeled array
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
--------
|
|
338
|
+
tuple: (label, radius, mask_volume, dimensions)
|
|
339
|
+
"""
|
|
340
|
+
# Get the slice object (bounding box) for this label
|
|
341
|
+
# Index is label-1 because find_objects returns 0-indexed results
|
|
342
|
+
obj_slice = objects[label-1]
|
|
343
|
+
|
|
344
|
+
if obj_slice is None:
|
|
345
|
+
return label, 0, 0, np.array([0, 0, 0])
|
|
346
|
+
|
|
347
|
+
# Extract subarray containing just this object (plus padding)
|
|
348
|
+
# Create padded slices to ensure there's background around the object
|
|
349
|
+
padded_slices = []
|
|
350
|
+
for dim_idx, dim_slice in enumerate(obj_slice):
|
|
351
|
+
start = max(0, dim_slice.start - 1)
|
|
352
|
+
stop = min(labeled_array.shape[dim_idx], dim_slice.stop + 1)
|
|
353
|
+
padded_slices.append(slice(start, stop))
|
|
354
|
+
|
|
355
|
+
# Extract the subarray
|
|
356
|
+
subarray = labeled_array[tuple(padded_slices)]
|
|
357
|
+
|
|
358
|
+
# Create binary mask for this object within the subarray
|
|
359
|
+
mask = (subarray == label)
|
|
360
|
+
|
|
361
|
+
# Compute distance transform on the smaller mask
|
|
362
|
+
dist_transform = compute_distance_transform_distance(mask)
|
|
363
|
+
|
|
364
|
+
# Filter out small values near the edge to focus on more central regions
|
|
365
|
+
radius = np.max(dist_transform)
|
|
366
|
+
|
|
367
|
+
# Calculate basic shape metrics
|
|
368
|
+
volume = np.sum(mask)
|
|
369
|
+
|
|
370
|
+
# Calculate bounding box dimensions
|
|
371
|
+
x_len = obj_slice[0].stop - obj_slice[0].start
|
|
372
|
+
y_len = obj_slice[1].stop - obj_slice[1].start
|
|
373
|
+
z_len = obj_slice[2].stop - obj_slice[2].start
|
|
374
|
+
dimensions = np.array([x_len, y_len, z_len])
|
|
375
|
+
|
|
376
|
+
return label, radius, volume, dimensions
|
|
377
|
+
|
|
378
|
+
def estimate_object_radii_cpu(labeled_array, n_jobs=None):
|
|
379
|
+
"""
|
|
380
|
+
Estimate the radii of labeled objects in a 3D numpy array using distance transform.
|
|
381
|
+
CPU parallel implementation.
|
|
382
|
+
|
|
383
|
+
Parameters:
|
|
384
|
+
-----------
|
|
385
|
+
labeled_array : numpy.ndarray
|
|
386
|
+
3D array where each object has a unique integer label (0 is background)
|
|
387
|
+
n_jobs : int or None
|
|
388
|
+
Number of parallel jobs. If None, uses all available cores.
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
--------
|
|
392
|
+
dict: Dictionary mapping object labels to estimated radii
|
|
393
|
+
dict: (optional) Dictionary of shape statistics for each label
|
|
394
|
+
"""
|
|
395
|
+
# Find bounding box for each labeled object
|
|
396
|
+
objects = ndimage.find_objects(labeled_array)
|
|
397
|
+
|
|
398
|
+
unique_labels = np.unique(labeled_array)
|
|
399
|
+
unique_labels = unique_labels[unique_labels != 0] # Remove background
|
|
400
|
+
|
|
401
|
+
# Create a partial function for parallel processing
|
|
402
|
+
process_func = partial(process_object_cpu, objects=objects, labeled_array=labeled_array)
|
|
403
|
+
|
|
404
|
+
# Process objects in parallel
|
|
405
|
+
results = []
|
|
406
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=n_jobs) as executor:
|
|
407
|
+
# Submit all jobs
|
|
408
|
+
future_to_label = {executor.submit(process_func, label): label for label in unique_labels}
|
|
409
|
+
|
|
410
|
+
# Collect results as they complete
|
|
411
|
+
for future in concurrent.futures.as_completed(future_to_label):
|
|
412
|
+
results.append(future.result())
|
|
413
|
+
|
|
414
|
+
# Organize results
|
|
415
|
+
radii = {}
|
|
416
|
+
|
|
417
|
+
for label, radius, volume, dimensions in results:
|
|
418
|
+
radii[label] = radius
|
|
419
|
+
|
|
420
|
+
return radii
|
|
421
|
+
|
|
422
|
+
def estimate_object_radii_gpu(labeled_array):
|
|
423
|
+
"""
|
|
424
|
+
Estimate the radii of labeled objects in a 3D numpy array using distance transform.
|
|
425
|
+
GPU implementation using CuPy.
|
|
426
|
+
|
|
427
|
+
Parameters:
|
|
428
|
+
-----------
|
|
429
|
+
labeled_array : numpy.ndarray
|
|
430
|
+
3D array where each object has a unique integer label (0 is background)
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
--------
|
|
434
|
+
dict: Dictionary mapping object labels to estimated radii
|
|
435
|
+
dict: (optional) Dictionary of shape statistics for each label
|
|
436
|
+
"""
|
|
437
|
+
|
|
438
|
+
try:
|
|
439
|
+
if not HAS_CUPY:
|
|
440
|
+
raise ImportError("CuPy is required for GPU acceleration")
|
|
441
|
+
|
|
442
|
+
# Find bounding box for each labeled object (on CPU)
|
|
443
|
+
objects = ndimage.find_objects(labeled_array)
|
|
444
|
+
|
|
445
|
+
# Transfer entire labeled array to GPU once
|
|
446
|
+
labeled_array_gpu = cp.asarray(labeled_array)
|
|
447
|
+
|
|
448
|
+
unique_labels = cp.unique(labeled_array_gpu)
|
|
449
|
+
unique_labels = cp.asnumpy(unique_labels)
|
|
450
|
+
unique_labels = unique_labels[unique_labels != 0] # Remove background
|
|
451
|
+
|
|
452
|
+
radii = {}
|
|
453
|
+
|
|
454
|
+
for label in unique_labels:
|
|
455
|
+
# Get the slice object (bounding box) for this label
|
|
456
|
+
obj_slice = objects[label-1]
|
|
457
|
+
|
|
458
|
+
if obj_slice is None:
|
|
459
|
+
continue
|
|
460
|
+
|
|
461
|
+
# Extract subarray from GPU array
|
|
462
|
+
padded_slices = []
|
|
463
|
+
for dim_idx, dim_slice in enumerate(obj_slice):
|
|
464
|
+
start = max(0, dim_slice.start - 1)
|
|
465
|
+
stop = min(labeled_array.shape[dim_idx], dim_slice.stop + 1)
|
|
466
|
+
padded_slices.append(slice(start, stop))
|
|
467
|
+
|
|
468
|
+
# Create binary mask for this object (directly on GPU)
|
|
469
|
+
mask_gpu = (labeled_array_gpu[tuple(padded_slices)] == label)
|
|
470
|
+
|
|
471
|
+
# Compute distance transform on GPU
|
|
472
|
+
dist_transform_gpu = compute_distance_transform_distance_GPU(mask_gpu)
|
|
473
|
+
|
|
474
|
+
radius = float(cp.max(dist_transform_gpu).get())
|
|
475
|
+
|
|
476
|
+
# Store the radius
|
|
477
|
+
radii[label] = radius
|
|
478
|
+
|
|
479
|
+
# Clean up GPU memory
|
|
480
|
+
del labeled_array_gpu
|
|
481
|
+
|
|
482
|
+
return radii
|
|
483
|
+
|
|
484
|
+
except Exception as e:
|
|
485
|
+
print(f"GPU calculation failed, trying CPU instead -> {e}")
|
|
486
|
+
return estimate_object_radii_cpu(labeled_array)
|
|
487
|
+
|
|
488
|
+
def compute_distance_transform_distance_GPU(nodes):
|
|
489
|
+
|
|
490
|
+
is_pseudo_3d = nodes.shape[0] == 1
|
|
491
|
+
if is_pseudo_3d:
|
|
492
|
+
nodes = cp.squeeze(nodes) # Convert to 2D for processing
|
|
493
|
+
|
|
494
|
+
# Compute the distance transform on the GPU
|
|
495
|
+
distance = cpx.distance_transform_edt(nodes)
|
|
496
|
+
|
|
497
|
+
if is_pseudo_3d:
|
|
498
|
+
cp.expand_dims(distance, axis = 0)
|
|
499
|
+
|
|
500
|
+
return distance
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
def compute_distance_transform_distance(nodes):
|
|
504
|
+
|
|
505
|
+
is_pseudo_3d = nodes.shape[0] == 1
|
|
506
|
+
if is_pseudo_3d:
|
|
507
|
+
nodes = np.squeeze(nodes) # Convert to 2D for processing
|
|
508
|
+
|
|
509
|
+
# Fallback to CPU if there's an issue with GPU computation
|
|
510
|
+
distance = ndimage.distance_transform_edt(nodes)
|
|
511
|
+
if is_pseudo_3d:
|
|
512
|
+
np.expand_dims(distance, axis = 0)
|
|
513
|
+
return distance
|
|
@@ -547,6 +547,43 @@ 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):
|
|
551
|
+
"""
|
|
552
|
+
Estimate the radii of labeled objects in a 3D numpy array.
|
|
553
|
+
Dispatches to appropriate implementation based on parameters.
|
|
554
|
+
|
|
555
|
+
Parameters:
|
|
556
|
+
-----------
|
|
557
|
+
labeled_array : numpy.ndarray
|
|
558
|
+
3D array where each object has a unique integer label (0 is background)
|
|
559
|
+
gpu : bool
|
|
560
|
+
Whether to use GPU acceleration via CuPy (if available)
|
|
561
|
+
n_jobs : int or None
|
|
562
|
+
Number of parallel jobs for CPU version. If None, uses all available cores.
|
|
563
|
+
|
|
564
|
+
Returns:
|
|
565
|
+
--------
|
|
566
|
+
dict: Dictionary mapping object labels to estimated radii
|
|
567
|
+
dict: (optional) Dictionary of shape statistics for each label
|
|
568
|
+
"""
|
|
569
|
+
# Check if GPU is requested but not available
|
|
570
|
+
try:
|
|
571
|
+
import cupy as cp
|
|
572
|
+
import cupyx.scipy.ndimage as cpx
|
|
573
|
+
HAS_CUPY = True
|
|
574
|
+
except ImportError:
|
|
575
|
+
HAS_CUPY = False
|
|
576
|
+
|
|
577
|
+
if gpu and not HAS_CUPY:
|
|
578
|
+
print("Warning: GPU acceleration requested but CuPy not available. Falling back to CPU.")
|
|
579
|
+
gpu = False
|
|
580
|
+
|
|
581
|
+
if gpu:
|
|
582
|
+
return morphology.estimate_object_radii_gpu(labeled_array)
|
|
583
|
+
else:
|
|
584
|
+
return morphology.estimate_object_radii_cpu(labeled_array, n_jobs)
|
|
585
|
+
|
|
586
|
+
|
|
550
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):
|
|
551
588
|
"""Internal method to break open a skeleton at its branchpoints and label the remaining components, for an 8bit binary array"""
|
|
552
589
|
|
|
@@ -751,6 +788,8 @@ def fill_holes_3d(array):
|
|
|
751
788
|
|
|
752
789
|
return holes_mask
|
|
753
790
|
|
|
791
|
+
print("Filling Holes...")
|
|
792
|
+
|
|
754
793
|
array = binarize(array)
|
|
755
794
|
inv_array = invert_array(array)
|
|
756
795
|
|
|
@@ -3270,7 +3309,10 @@ class Network_3D:
|
|
|
3270
3309
|
self._xy_scale = xy_scale
|
|
3271
3310
|
self._z_scale = z_scale
|
|
3272
3311
|
|
|
3273
|
-
|
|
3312
|
+
try:
|
|
3313
|
+
self.save_scaling(directory)
|
|
3314
|
+
except:
|
|
3315
|
+
pass
|
|
3274
3316
|
|
|
3275
3317
|
if search is None and ignore_search_region == False:
|
|
3276
3318
|
search = 0
|
|
@@ -3286,29 +3328,51 @@ class Network_3D:
|
|
|
3286
3328
|
if other_nodes is not None:
|
|
3287
3329
|
self.merge_nodes(other_nodes, label_nodes)
|
|
3288
3330
|
|
|
3289
|
-
|
|
3290
|
-
|
|
3331
|
+
try:
|
|
3332
|
+
self.save_nodes(directory)
|
|
3333
|
+
except:
|
|
3334
|
+
pass
|
|
3335
|
+
try:
|
|
3336
|
+
self.save_node_identities(directory)
|
|
3337
|
+
except:
|
|
3338
|
+
pass
|
|
3291
3339
|
|
|
3292
3340
|
if not ignore_search_region:
|
|
3293
3341
|
self.calculate_search_region(search, GPU = GPU, fast_dil = fast_dil, GPU_downsample = GPU_downsample)
|
|
3294
|
-
self._nodes = None
|
|
3342
|
+
#self._nodes = None # I originally put this here to micromanage RAM a little bit (it writes it to disk so I wanted to purge it from mem briefly but now idt thats necessary and I'd rather give it flexibility when lacking write permissions)
|
|
3295
3343
|
search = None
|
|
3296
|
-
|
|
3344
|
+
try:
|
|
3345
|
+
self.save_search_region(directory)
|
|
3346
|
+
except:
|
|
3347
|
+
pass
|
|
3297
3348
|
|
|
3298
3349
|
self.calculate_edges(edges, diledge = diledge, inners = inners, hash_inner_edges = hash_inners, search = search, remove_edgetrunk = remove_trunk, GPU = GPU, fast_dil = fast_dil, skeletonized = skeletonize)
|
|
3299
3350
|
del edges
|
|
3300
|
-
|
|
3351
|
+
try:
|
|
3352
|
+
self.save_edges(directory)
|
|
3353
|
+
except:
|
|
3354
|
+
pass
|
|
3301
3355
|
|
|
3302
3356
|
self.calculate_network(search = search, ignore_search_region = ignore_search_region)
|
|
3303
|
-
|
|
3357
|
+
|
|
3358
|
+
try:
|
|
3359
|
+
self.save_network(directory)
|
|
3360
|
+
except:
|
|
3361
|
+
pass
|
|
3304
3362
|
|
|
3305
3363
|
if self._nodes is None:
|
|
3306
3364
|
self.load_nodes(directory)
|
|
3307
3365
|
|
|
3308
3366
|
self.calculate_node_centroids(down_factor)
|
|
3309
|
-
|
|
3367
|
+
try:
|
|
3368
|
+
self.save_node_centroids(directory)
|
|
3369
|
+
except:
|
|
3370
|
+
pass
|
|
3310
3371
|
self.calculate_edge_centroids(down_factor)
|
|
3311
|
-
|
|
3372
|
+
try:
|
|
3373
|
+
self.save_edge_centroids(directory)
|
|
3374
|
+
except:
|
|
3375
|
+
pass
|
|
3312
3376
|
|
|
3313
3377
|
|
|
3314
3378
|
def draw_network(self, directory = None, down_factor = None, GPU = False):
|
|
@@ -3557,15 +3621,21 @@ class Network_3D:
|
|
|
3557
3621
|
list1 = self._network_lists[0] #Get network lists to change
|
|
3558
3622
|
list2 = self._network_lists[1]
|
|
3559
3623
|
list3 = self._network_lists[2]
|
|
3624
|
+
return1 = []
|
|
3625
|
+
return2 = []
|
|
3626
|
+
return3 = []
|
|
3560
3627
|
|
|
3561
3628
|
for i in range(len(list1)):
|
|
3562
3629
|
list1[i] = self.communities[list1[i]] #Set node at network list spot to its community instead
|
|
3563
3630
|
list2[i] = self.communities[list2[i]]
|
|
3564
|
-
if list1[i]
|
|
3565
|
-
|
|
3631
|
+
if list1[i] != list2[i]: #Avoid self - self connections
|
|
3632
|
+
return1.append(list1[i])
|
|
3633
|
+
return2.append(list2[i])
|
|
3634
|
+
return3.append(list3[i])
|
|
3635
|
+
|
|
3566
3636
|
|
|
3567
3637
|
|
|
3568
|
-
self.network_lists = [
|
|
3638
|
+
self.network_lists = [return1, return2, return3]
|
|
3569
3639
|
|
|
3570
3640
|
if self._nodes is not None:
|
|
3571
3641
|
self._nodes = update_array(self._nodes, inverted, targets = targets) #Set the array to match the new network
|