neu_lib.skeleton

A skeleton as vertices and edges, in nm, zyx: Skeleton.

An edge list, never decomposed into branch paths, so any topology works: cycles, self-loops, thousands of disconnected components. It draws directly as line segments, in one buffer per body:

positions = to_xyz(skeleton.segments())
pygfx.Line(pygfx.Geometry(positions=positions), pygfx.LineSegmentMaterial(...))

Precomputed skeletons (vertices, edges, radii) and DVID’s SWC tables (nodes and parents) both map onto it directly.

Attributes

Classes

Skeleton

Vertices in nm (zyx), the edges between them, and optional per-vertex radii.

Module Contents

class neu_lib.skeleton.Skeleton

Vertices in nm (zyx), the edges between them, and optional per-vertex radii.

crop(inside: InsideFn, *, keep_inside: bool = True, tolerance_nm: float = 8.0) → Skeleton

Keep the part of the skeleton on one side of a region boundary.

inside takes an (N, 3) zyx nm array and returns an (N,) bool array (see neu_lib.predicates). An edge with one endpoint kept is bisected to within tolerance_nm and ends at a new vertex on the boundary, so branches stop at the region surface. Every kept vertex is retained, including ones left with no edges.

Limitation: an edge whose endpoints are on the same side is assumed not to cross, so a region thinner than the edge spacing can be missed.

exclude(inside: InsideFn, **kwargs) → Skeleton

The complement of crop() — drop what is inside the region.

classmethod from_precomputed(vertices_xyz_nm: Any, edges: Any, radii_nm: Any = None, name: str | None = None) → Skeleton

From xyz vertices, as the precomputed format stores them and neu_vol.readback.read_body_skeleton returns them, flipped to zyx.

classmethod from_swc(table: Any, *, frame: neu_lib.frame.Frame, node_col: str = 'node', parent_col: str = 'parent', coord_cols: Sequence[str] = ('x', 'y', 'z'), radius_col: str | None = 'radius', name: str | None = None) → Skeleton

From a node/parent table, DVID’s skeleton form.

coord_cols are read in the order given and then reversed, so the SWC default ("x", "y", "z") becomes zyx. Coordinates are voxel indices, converted to nm with frame. A parent id absent from the table (SWC’s -1 root, or a dangling reference) adds no edge; the result need not be a tree.

segments() → numpy.ndarray

(2M, 3) zyx nm, the two endpoints of each edge in turn: pass through to_xyz() to draw with pygfx.LineSegmentMaterial.

split_at(inside: InsideFn, *, tolerance_nm: float = 8.0) → tuple[Skeleton, numpy.ndarray, numpy.ndarray]

Insert a vertex where each edge crosses a region boundary, keeping both sides.

Returns (skeleton, inside, boundary): the skeleton with each crossing edge split in two, plus one bool per vertex for “in the region” and “is an inserted boundary vertex”. Original vertices keep their indices; boundary vertices are appended, on the inside, within tolerance_nm of the surface (the bisection crop() uses).

The edge order is guaranteed, so per-edge data can follow the split: uncrossed edges in their original order, then one (inside endpoint, boundary) edge per crossing edge, then one (boundary, outside endpoint) edge per crossing edge. Same limitation as crop().

translate(offset_zyx_nm: Sequence[float]) → Skeleton
property bbox: neu_lib.grid.BBox
edges: numpy.ndarray
name: str | None = None
radii_nm: numpy.ndarray | None = None
vertices_zyx_nm: numpy.ndarray
neu_lib.skeleton.InsideFn