def remove_ticks(skeleton, threshold): """ Simple merging of individual TESAR cubes results in lots of little ticks due to the edge effect. We can remove them by thresholding the path length from a given branch to the "main body" of the neurite. We successively remove paths from shortest to longest until no branches below threshold remain. If TEASAR parameters were chosen such that they allowed for spines to be traced, this is also an opportunity to correct for that. This algorithm is O(N^2) in the number of terminal nodes. Parameters: threshold: The maximum length in nanometers that may be culled. Returns: tick free skeleton """ if skeleton.empty() or threshold == 0: return skeleton skels = [] for component in skeleton.components(): skels.append(_remove_ticks(component, threshold)) return Skeleton.simple_merge(skels).consolidate( remove_disconnected_vertices=False)
def remove_loops(skeleton): if skeleton.empty(): return skeleton skels = [] for component in skeleton.components(): skels.append(_remove_loops(component)) return Skeleton.simple_merge(skels).consolidate(remove_disconnected_vertices=False)
def remove_dust(skeleton, dust_threshold): """Dust threshold in physical cable length.""" if skeleton.empty() or dust_threshold == 0: return skeleton skels = [] for skel in skeleton.components(): if skel.cable_length() > dust_threshold: skels.append(skel) return Skeleton.simple_merge(skels)
def test_simple_merge(): skel1 = Skeleton( [ (0, 0, 0), (1, 0, 0), (2, 0, 0), ], edges=[ (0, 1), (1, 2), ], segid=1, ) skel2 = Skeleton( [ (0, 0, 1), (1, 0, 2), (2, 0, 3), ], edges=[ (0, 1), (1, 2), ], segid=1, ) result = Skeleton.simple_merge([skel1, skel2]) expected = Skeleton( [ (0, 0, 0), (1, 0, 0), (2, 0, 0), (0, 0, 1), (1, 0, 2), (2, 0, 3), ], edges=[(0, 1), (1, 2), (3, 4), (4, 5)], segid=1, ) assert result == expected skel1.extra_attributes = [{ "id": "wow", "data_type": "uint8", "components": 1, }] skel1.wow = np.array([1, 2, 3], dtype=np.uint8) skel2.extra_attributes = [{ "id": "wow", "data_type": "uint8", "components": 1, }] skel2.wow = np.array([4, 5, 6], dtype=np.uint8) result = Skeleton.simple_merge([skel1, skel2]) expected.wow = np.array([1, 2, 3, 4, 5, 6], dtype=np.uint8) assert result == expected skel2.extra_attributes[0]['data_type'] = np.uint8 try: Skeleton.simple_merge([skel1, skel2]) assert False except SkeletonAttributeMixingError: pass skel2.extra_attributes[0]['data_type'] = 'uint8' skel2.extra_attributes.append({ "id": "amaze", "data_type": "float32", "components": 2, }) try: Skeleton.simple_merge([skel1, skel2]) assert False except SkeletonAttributeMixingError: pass
def join_close_components(skeletons, radius=None): """ Given a set of skeletons which may contain multiple connected components, attempt to connect each component to the nearest other component via the nearest two vertices. Repeat until no components remain or no points closer than `radius` are available. radius: float in same units as skeletons Returns: Skeleton """ if radius is not None and radius <= 0: raise ValueError("radius must be greater than zero: " + str(radius)) if isinstance(skeletons, Skeleton): skeletons = [skeletons] skels = [] for skeleton in skeletons: skels += skeleton.components() skels = [skl.consolidate() for skl in skels if not skl.empty()] if len(skels) == 1: return skels[0] elif len(skels) == 0: return Skeleton() while len(skels) > 1: N = len(skels) radii_matrix = np.zeros((N, N), dtype=np.float32) + np.inf index_matrix = np.zeros((N, N, 2), dtype=np.uint32) + -1 for i in range(len(skels)): for j in range(len(skels)): if i == j: continue elif radii_matrix[i, j] != np.inf: continue s1, s2 = skels[i], skels[j] dist_matrix = scipy.spatial.distance.cdist( s1.vertices, s2.vertices) radii_matrix[i, j] = np.min(dist_matrix) radii_matrix[j, i] = radii_matrix[i, j] index_matrix[i, j] = np.unravel_index(np.argmin(dist_matrix), dist_matrix.shape) index_matrix[j, i] = index_matrix[i, j] if np.all(radii_matrix) == np.inf: break min_radius = np.min(radii_matrix) if radius is not None and min_radius > radius: break i, j = np.unravel_index(np.argmin(radii_matrix), radii_matrix.shape) s1, s2 = skels[i], skels[j] fused = Skeleton.simple_merge([s1, s2]) fused.edges = np.concatenate([ fused.edges, [[ index_matrix[i, j, 0], index_matrix[i, j, 1] + s1.vertices.shape[0] ]] ]) skels[i] = None skels[j] = None skels = [_ for _ in skels if _ is not None] + [fused] return Skeleton.simple_merge(skels).consolidate()