Topological skeleton analysis in 3D

Topological skeleton analysis in 3D#

# import 3d binary blobs
import numpy as np
import napari_toska as nts
from skimage import data, measure
import napari
import networkx as nx
viewer = napari.Viewer(ndisplay=3)
Assistant skips harvesting pyclesperanto as it's not installed.
image = data.binary_blobs(length=64, n_dim=3, rng=0, blob_size_fraction=0.3)
labels = measure.label(image)
skeleton_labeled = nts.generate_labeled_skeletonization(labels)
skeleton_parsed = nts.parse_all_skeletons(skeleton_labeled, neighborhood='n26')
viewer.add_labels(labels, name='blobs')
viewer.add_labels(skeleton_labeled, name='skeleton')
viewer.add_labels(skeleton_parsed, name='parsed skeleton')
<Labels layer 'parsed skeleton' at 0x20f8a8c6a30>
skeleton_parsed_single = skeleton_parsed * (skeleton_labeled == 2)
viewer.add_labels(skeleton_parsed_single, name='parsed_skeleton_single')
<Labels layer 'parsed_skeleton_single' at 0x20f986a7fd0>
# create an adjacency matrix for the skeleton
adjacency_matrix = nts.create_adjacency_matrix(skeleton_parsed_single,
                                            neighborhood='n26')
graph = nts.convert_adjacency_matrix_to_graph(adjacency_matrix)
features_single = nts.analyze_single_skeleton(skeleton_parsed_single, neighborhood='n26')
features_single
n_endpoints n_branch_points n_nodes n_branches n_cycle_basis n_possible_undirected_cycles
0 10 8 18 18 1 1
labeled_branches_single = nts.label_branches(skeleton_parsed_single, skeleton_labeled, neighborhood='n26')
viewer.add_labels(labeled_branches_single, name='labeled_branches')
<Labels layer 'labeled_branches' at 0x20f9890d1f0>
spine_image = nts.create_spine_image(adjacency_matrix=adjacency_matrix, labeled_branches=labeled_branches_single)
viewer.add_labels(spine_image, name='spine_image')
<Labels layer 'spine_image' at 0x20f986a7d30>
nts.calculate_branch_lengths(labeled_branches_single)
label branch_length
0 1 17.899495
1 2 10.242641
2 3 3.414214
3 4 4.560478
4 5 2.828427
5 6 20.120956
6 7 27.388905
7 8 91.698485
8 9 13.242641
9 10 33.116827
10 11 10.974691
11 12 1.732051
12 13 5.656854
13 14 19.999271
14 15 20.071068
15 16 4.242641
16 17 13.242641
17 18 3.414214
# analyze all skeletons
features = nts.analyze_skeletons(
    labeled_skeletons=skeleton_labeled,
    parsed_skeletons=skeleton_parsed,
    neighborhood='n26')
features
skeleton_id n_endpoints n_branch_points n_nodes n_branches n_cycle_basis n_possible_undirected_cycles label
0 1 0 0 0 1 0 0 1
0 2 10 8 18 18 1 1 2