neu_eval.assign

Matching one side’s labels to the other’s, and the completeness metrics that follow.

Pure functions of a Contingency, in two families:

  • Matching: which segment is which body, one-to-one, as Plaza & Funke’s connectivity metrics require (otherwise a segmentation can claim one body twice). Greedy by default; the optimal assignment is opt-in, being the only non-linear cost in the package.

  • Completeness: how many segments account for a share of the volume. fragmentation estimates how many merges a proofreader would need; covering is the size-weighted share of each body captured by its best segment.

fragmentation is positive when side b has more labels (over-segmentation); that is the only asymmetry, and c.T reverses it.

Attributes

Classes

Completeness

How many segments account for a given share of each side.

Match

A one-to-one assignment between the two sides.

Functions

best_match(c, *[, method, min_iou, by]) → Match

A one-to-one assignment between the two sides of c.

completeness(c[, at]) → Completeness

The smallest set of labels on each side covering at of that side's voxels.

covering(c) → float

Size-weighted mean, over side a's labels, of the best single b overlap.

fragmentation(c) → int

n_b - n_a over the scored region: extra segments, as an edit-distance estimate.

iou(c) → numpy.ndarray

Intersection over union for each pair in c, parallel to its arrays.

summary(c, *[, at]) → dict[str, float | int]

Matching and completeness as one flat row, to merge with voxel.summary().

Module Contents

class neu_eval.assign.Completeness

How many segments account for a given share of each side.

fragmentation is n_b_kept - n_a_kept, the extra segments a proofreader would merge to recover the kept reference bodies. An estimate, not a true edit distance: it assumes b over-segments a.

as_dict() → dict[str, float | int]
a_ids: numpy.ndarray
at: float
b_ids: numpy.ndarray
property fragmentation: int
n_a_kept: int
n_b_kept: int
class neu_eval.assign.Match

A one-to-one assignment between the two sides.

a_to_b and b_to_a are inverse mappings over matched labels; any other label is unmatched. method is recorded because greedy and optimal results are not comparable.

unmatched_a(c: neu_eval.overlap.Contingency) → list[int]
unmatched_b(c: neu_eval.overlap.Contingency) → list[int]
a_to_b: dict[int, int]
b_to_a: dict[int, int]
method: str = 'greedy'
min_iou: float = 0.0
property n_matched: int
neu_eval.assign.best_match(c: neu_eval.overlap.Contingency, *, method: str = 'greedy', min_iou: float = 0.0, by: str = 'iou') → Match

A one-to-one assignment between the two sides of c.

method="greedy" repeatedly takes the best remaining pair, O(pairs log pairs), for volume scale. method="hungarian" maximises the total instead; it densifies the table, so it is capped at HUNGARIAN_MAX_LABELS per side.

by is "iou" (default) or "overlap" (raw voxels); they differ when a large segment overlaps a small body. min_iou drops pairs below it: 0 admits any co-occurring pair, and 0.5 is the usual choice for calling a body “found” (above 0.5 a label can have only one admissible partner).

neu_eval.assign.completeness(c: neu_eval.overlap.Contingency, at: float = 0.9) → Completeness

The smallest set of labels on each side covering at of that side’s voxels.

“How many segments reconstruct 90% of this volume” is a question a proofreading plan can act on, where a raw label count is dominated by specks. The sides are thresholded independently, not paired.

neu_eval.assign.covering(c: neu_eval.overlap.Contingency) → float

Size-weighted mean, over side a’s labels, of the best single b overlap.

The IoU of each a body’s best b segment, weighted by body size; 1.0 when every body is captured exactly. Unlike VOI it is a bounded fraction, but it ignores the rest of the best segment, so it misses a merge that leaves the body itself intact.

neu_eval.assign.fragmentation(c: neu_eval.overlap.Contingency) → int

n_b - n_a over the scored region: extra segments, as an edit-distance estimate.

neu_eval.assign.iou(c: neu_eval.overlap.Contingency) → numpy.ndarray

Intersection over union for each pair in c, parallel to its arrays.

|a ∩ b| / (|a| + |b| - |a ∩ b|), with each label’s total taken over the scored region, so a body clipped by the crop is not penalised for the part outside it.

neu_eval.assign.summary(c: neu_eval.overlap.Contingency, *, at: tuple[float, ...] = (0.5, 0.9)) → dict[str, float | int]

Matching and completeness as one flat row, to merge with voxel.summary().

neu_eval.assign.HUNGARIAN_MAX_LABELS = 5000