neu_morpho.measure.paths¶
Distances ALONG a skeleton from a reference compartment, for points attached to it.
For each synapse, a signed path length in nm: positive outside the region, negative inside it (depth from the nearest boundary crossing). Four pure steps:
bridge_gaps()reconnects pieces across breaks belowmax_gap_nm. Bridges are cable nobody traced, so they are flagged and every result says how much it crossed.neu_lib.Skeleton.split_at()adds a vertex where each edge crosses the boundary.attach_points()snaps each synapse to the nearest point on an edge (vertices can be hundreds of nm apart) and reports the snap distance.path_distances(): one multi-source shortest-path pass from every boundary vertex.
Nothing is dropped: an unreachable synapse is unreachable, a body that never enters
the region no_compartment. Normalisation is left to aggregation; distances are absolute.
Attributes¶
Functions¶
|
The nearest point on any edge, for each point. |
|
Join the skeleton's pieces wherever two come within |
|
The bridged, boundary-split skeleton and which of its cable is the compartment. |
|
|
|
|
|
|
|
Shortest path length from the nearest of |
|
Symmetric |
|
Per-synapse and per-body tables over a set of bodies. |
|
Signed path length from the reference compartment, for each point on one body. |
|
|
Module Contents¶
- neu_morpho.measure.paths.attach_points(skeleton: neu_lib.Skeleton, points_zyx_nm: Any) dict[str, numpy.ndarray]¶
The nearest point on any edge, for each point.
Returns
{"edge", "t", "position", "snap_nm"}: the edge index, the fraction along it fromedges[:, 0]toedges[:, 1], the attached position (zyx nm) and the distance to it. Exact: every edge is tested, in bounded chunks. Isolated vertices are not targets.
- neu_morpho.measure.paths.bridge_gaps(skeleton: neu_lib.Skeleton, max_gap_nm: float) tuple[neu_lib.Skeleton, numpy.ndarray]¶
Join the skeleton’s pieces wherever two come within
max_gap_nmof each other.Returns
(skeleton, is_bridge): the original vertices and edges, unchanged and in order, with bridge edges appended, and one bool per edge marking the bridges.A minimum spanning tree over pieces, using the nearest vertex pair between each two and keeping links no longer than
max_gap_nm: the result of a greedy closest-pair merge, with vertex indices stable and bridges marked. Never adds a cycle;max_gap_nm = 0bridges nothing. Cost grows with vertex density times the gap (fine up to ~a micron).
- neu_morpho.measure.paths.compartment_graph(skeleton: neu_lib.Skeleton, inside: neu_lib.InsideFn, *, max_gap_nm: float, tolerance_nm: float = 8.0, region_pieces: str = 'largest', merge_nm: float = 0.0) dict[str, Any]¶
The bridged, boundary-split skeleton and which of its cable is the compartment.
The graph
synapse_distances()measures on, exposed so a renderer can draw exactly the cable that was treated as the compartment. Returnsskeleton(bridged and split), per-edgeis_bridge/in_region/in_compartment, per-vertexinside/boundary/in_compartment, thesources(boundary vertices of the compartment), edgelengths, and the piece counts. Seesynapse_distances()forregion_piecesandmerge_nm.
- neu_morpho.measure.paths.components(skeleton: neu_lib.Skeleton) tuple[int, numpy.ndarray]¶
(n_components, label_per_vertex). An isolated vertex is its own component.
- neu_morpho.measure.paths.dvid_roi_region(source: Any, rois: Sequence[str] | str, *, voxel_size_nm: Sequence[float], origin_nm: Sequence[float] = (0.0, 0.0, 0.0))¶
(inside, roi_volume)for the union of named DVID ROIs.sourceis advid://URL or spec for any instance on the node.voxel_size_nm/origin_nmare the node’s scale-0 grid — the frame its synapse coordinates are in (neu_draw.sources.volume_frame(volume, 0)for the exported segmentation).
- neu_morpho.measure.paths.near_points_region(points_zyx_nm: Any, radius_nm: float) neu_lib.InsideFn¶
inside()for “withinradius_nmof any of these points”.For a compartment the cell itself marks where no ROI separates it, such as an axon terminal marked by the cell’s own presynaptic sites when dendrite and terminal share a neuropil.
radius_nmmust reach the skeleton from the membrane; gaps between sites become separate pieces, whichmerge_nmrejoins.
- neu_morpho.measure.paths.path_distances(skeleton: neu_lib.Skeleton, sources: Sequence[int], is_bridge: numpy.ndarray | None = None) dict[str, numpy.ndarray]¶
Shortest path length from the nearest of
sources, for every vertex.Returns
{"distance_nm", "n_bridges", "bridge_nm", "source"}per vertex: the bridge edges on that shortest path and their length, and which source vertex it starts from. Unreachable vertices getinf, zero bridges and source-1.
- neu_morpho.measure.paths.skeleton_graph(vertices: numpy.ndarray, edges: numpy.ndarray)¶
Symmetric
csr_matrixadjacency weighted by edge length.Two scipy behaviours are silent here and both are handled: an explicit zero weight is no edge (floored at
_MIN_EDGE_NM), and duplicate entries are summed on conversion (duplicates are removed first, so a doubly-listed edge is not twice as long). Self-loops are dropped.
- neu_morpho.measure.paths.synapse_distance_table(skeletons: Mapping[int, neu_lib.Skeleton], synapses: Any, inside: neu_lib.InsideFn | Mapping[int, neu_lib.InsideFn], *, frame: Any, max_gap_nm: float, body_column: str = 'post_body', prefix: str = 'post_', tolerance_nm: float = 8.0, region_pieces: str = 'largest', merge_nm: float = 0.0, dataset: str | None = None)¶
Per-synapse and per-body tables over a set of bodies.
synapsesis any table with{prefix}z/y/xcolumns andbody_column— e.g. neu-mark’sconnectionsfiltered to the partners wanted, wherepost_*is the postsynaptic site on the body being measured. Its coordinates are inframe’s voxels (scale 0 for DVID). Every input column is carried through, sopre_bodyor a partner cell type stays on each row.insideis one region for every body (an ROI), or a mapping{body: inside}for a compartment each cell marks itself (near_points_region()over its own outputs). A body the mapping lacks getsstatus == "no_compartment".Returns
(rows, bodies).rowsaddsdistance_nm(signed; negative inside the compartment — seesynapse_distances()onregion_pieces),inside,in_roi,snap_nm,n_bridges,bridge_nm,on_bridge,status,radius_nm,attached_z/y/x_nm,entry_z/y/x_nm(the boundary crossing measured from) and themax_gap_nm/region_pieces/merge_nmit was measured with. A body in the table with no skeleton getsstatus == "no_skeleton"rather than vanishing.bodiesholdssynapse_distances()’ summary per body.
- neu_morpho.measure.paths.synapse_distances(skeleton: neu_lib.Skeleton, points_zyx_nm: Any, inside: neu_lib.InsideFn, *, max_gap_nm: float, tolerance_nm: float = 8.0, region_pieces: str = 'largest', merge_nm: float = 0.0) tuple[dict[str, numpy.ndarray], dict[str, Any]]¶
Signed path length from the reference compartment, for each point on one body.
The compartment is a connected piece of in-region cable, not the region itself.
region_pieces="largest"(default) takes the piece with the most cable;"all"takes every piece."all"lets a coarse ROI’s brush with neighbouring arbor (a dendrite dipping briefly into the region) become the reference, giving short distances with nothing raised."largest"instead under-counts a terminal that weaves across the boundary, adding the weave’s length to every distance.merge_nm(with"largest") repeatedly absorbs every piece within that much cable of the compartment, so a weaving terminal is one piece again while a far dip is not. It is a parameter to sweep, not a constant.Returns
(per_point, summary).per_pointholds arrays, one entry per point:distance_nm(negative inside the compartment; NaN unlessstatus == "ok"),inside(in the compartment),in_roi(in the region at all),snap_nm,n_bridges,bridge_nm,on_bridge(attached to a bridge edge itself),status,position(attached point, zyx nm),radius_nm(interpolated; NaN without radii) andentry(the boundary crossing measured from, zyx nm).summarydescribes the body’s graph: pieces before and after bridging, bridges and their length, total, in-region and compartment cable, piece and crossing counts, andn_entries_used(more than one: the region is entered at several places).
- neu_morpho.measure.paths.volume_roi_region(volume: str, labels: Sequence[int | str] | int | str, *, level: int)¶
(inside, piece)for labels of a precomputed ROI volume, read whole atlevel.Labels may be ids or names; names resolve through the volume’s own
segment_propertiesand are never guessed. Pick a coarse level — the whole level is read into memory.
- neu_morpho.measure.paths.NO_COMPARTMENT = 'no_compartment'¶
- neu_morpho.measure.paths.NO_SKELETON = 'no_skeleton'¶
- neu_morpho.measure.paths.OK = 'ok'¶
- neu_morpho.measure.paths.REGION_PIECES = ('largest', 'all')¶
- neu_morpho.measure.paths.STATUSES¶
- neu_morpho.measure.paths.UNREACHABLE = 'unreachable'¶