Source code for napari_toska._network_analysis

import numpy as np
import networkx as nx


[docs] def create_all_adjancency_matrices( labeled_skeletons: "napari.types.LabelsData", parsed_skeletons: "napari.types.LabelsData", neighborhood: str ): """ Calculate all adjacency matrices for a given skeleton image. Parameters: ----------- labeled_skeletons: napari.types.LabelsData A skeleton image with each skeleton carrying a unique label. parsed_skeletons: napari.types.LabelsData A skeleton image where each pixel is labelled according to the point type which can be either a terminal point (1), a branching point (3), or a chain point (2). neighborhood: str The neighborhood connectivity of the skeleton. Can be "n4", "n6", "n8", "n18", or "n26". Returns: -------- adjacency_matrices: list A list of adjacency matrices for each skeleton in the image. """ skeleton_ids = np.unique(labeled_skeletons)[1:] adjacency_matrices = [0] * len(skeleton_ids) skeleton_counter = 0 for skeleton_id in skeleton_ids: # create a sub-skeleton image with only one skeleton with values # 0 (background) and 1 (skeleton end point) etc. sub_skeleton = parsed_skeletons * (labeled_skeletons == skeleton_id) adjacency_matrices[skeleton_counter] = create_adjacency_matrix(sub_skeleton, neighborhood) skeleton_counter += 1 return adjacency_matrices
[docs] def create_adjacency_matrix(parsed_skeletons: "napari.types.LabelsData", neighborhood: str = "n8") -> np.ndarray: """ Create an adjacency matrix for a given skeleton image. Parameters: ----------- parsed_skeletons: napari.types.LabelsData A skeleton image where each pixel is labelled according to the point type which can be either a terminal point (1), a branching point (3), or a chain point (2). neighborhood: str The neighborhood connectivity of the skeleton. Can be "n4", "n6", "n8", "n18", or "n26". Returns: -------- M: np.ndarray An adjacency matrix for the skeleton. """ from scipy import ndimage from ._backend_toska_functions import _generate_adjacency_matrix from ._utils import get_neighborhood structure = get_neighborhood(neighborhood) # Retrieve branch points branch_points = parsed_skeletons == 3 branch_points, _ = ndimage.label( branch_points, structure=structure) # Retrieve end points end_points = np.asarray(np.where(parsed_skeletons == 1)).T # Retrieve branches image branches, _ = ndimage.label(parsed_skeletons == 2, structure=structure) # generate adjacency matrix M = _generate_adjacency_matrix(end_points, branch_points, branches, structure) return M
[docs] def convert_adjacency_matrix_to_graph( adj_mat: np.ndarray, weights: np.ndarray = None ) -> nx.Graph: """ Convert an adjacency matrix to a networkx graph. Parameters ---------- adj_mat : numpy.ndarray An adjacency matrix where each row represents a branching- or endpoint and each column represents a branch. A value of 1 indicates that the branching point or endpoint is connected to the branch. weights : numpy.ndarray, optional An array of weights for each branch, by default 1 (unweighted) Returns ------- G : networkx.Graph A networkx graph where each node represents a branching- or endpoint and each edge represents a branch. """ if weights is None: weights = np.ones((adj_mat.shape[1])) nodes = adj_mat weighted_edges = [] for i in range(nodes.shape[1]): edge = list(np.where(nodes[:, i])[0]) if len(edge) == 2: edge.append(weights[i]) weighted_edges.append(tuple(edge)) else: continue G = nx.Graph() G.add_weighted_edges_from(weighted_edges) return G
[docs] def create_spine_image( adjacency_matrix: np.ndarray, labeled_branches: "napari.types.LabelsData" ) -> "napari.types.LabelsData": """ Create an image where the pixels of the spine are labeled with the branch ID of the branch they belong to. Parameters ---------- labeled_branches : napari.types.LabelsData A labeled image with labelled skeleton branches. img_spine_ids : list A list of branch labels that represent a path in a skeleton graph. Returns ------- img_spine : napari.types.LabelsData A labeled image with the same shape as `labeled_branches` where the pixels of the spine are labeled with the branch ID of the branch they belong to. """ from ._backend_toska_functions import ( skeleton_spine_search, find_spine_edges, map_spine_edges_to_skeleton ) # for skeletons consisting only of a single point if (labeled_branches > 0).sum() == 1: return labeled_branches G = convert_adjacency_matrix_to_graph(adjacency_matrix) spine_path_nodes, spine_path_length = skeleton_spine_search( adjacency_matrix, G) spine_edges = find_spine_edges(spine_path_nodes) if len(spine_edges) == 1: branch_labels = [1] else: branch_labels = np.arange(1, np.amax(labeled_branches)) spine_edges = map_spine_edges_to_skeleton( spine_edges, adjancency_matrix=adjacency_matrix, branch_labels=branch_labels) img_spine = np.zeros_like(labeled_branches) for i in spine_edges: img_spine[labeled_branches == i] = i return img_spine