segment_rigid_motions — 3D motion_segment op

• Data kinds: points × points → labels

• Call: import fullseye as fs; fs.ledger.segment_rigid_motions(pts0, pts1, thresh, max_bodies: 'int' = 5, min_inliers=None, n_iter: 'int' = 100, k_sample: 'int' = 6, seed: 'int' = 0) -> 'dict' (to call the implementation directly, import motion_seg3d; motion_seg3d.segment_rigid_motions(pts0, pts1, thresh, max_bodies: 'int' = 5, min_inliers=None, n_iter: 'int' = 100, k_sample: 'int' = 6, seed: 'int' = 0) -> 'dict'; from the registry, ops3d.get("segment_rigid_motions"))

• Return value through the ledger: fullseye.ledger.segment_rigid_motions(...) returns only the declared out type labels (the underlying function also returns auxiliary values). When you need what was dropped, use fullseye.ledger.segment_rigid_motions.raw(...) or call motion_seg3d.segment_rigid_motions directly.

• Underlying return: {"labels", "motions"} → labels

Usage

Split two point sets into the rigid bodies whose motion agrees (multi-body segmentation by iterated RANSAC).

First takes the observed flow with estimate_flow (each pts0 point -> its nearest neighbour in pts1) and fixes target = pts0 + flow as the correspondence. Then, on the set of remaining points:

1. builds a rigid hypothesis with Kabsch, seeded by k_sample spatially close points (seed + its nearest neighbours),

2. counts the inliers satisfying flow consistency || (R·s + t) - target(s) || < thresh,

3. over n_iter trials picks the best hypothesis that passes the significance gate (median inlier residual below a fixed fraction of thresh = residuals concentrated near 0, as for a true rigid body; chance fits of uncorrelated data/noise fill the tolerance sphere with residual ≈ 0.6*thresh and are therefore rejected), and refits Kabsch on its inliers (refine),

4. if the inlier count is at least min_inliers and the hypothesis stays significant after refine, confirms it as 1 rigid body and removes it. Otherwise, creates no body and finishes with the remainder left at -1 (does not fabricate a false rigid body).

This is repeated until max_bodies bodies are found or the remaining points fall below the threshold. Remaining points that belong to no rigid body get labels = -1 (outlier / unmatched; no pretending). Neighbourhood seed sampling is a locality prior for grabbing spatially contiguous objects as a whole; it uses only relative ranks, not a threshold, so it is scale invariant.

Args:

pts0: (N, 3) point cloud at time 0.

pts1: (M, 3) point cloud at time 1 (need not match N).

thresh: inlier distance threshold for flow consistency. Give it relative to the coordinate scale

(the module holds no absolute epsilon, so the caller adapts it to the coordinate system).

max_bodies: upper limit on the number of rigid bodies to extract (>= 1).

min_inliers: minimum inlier count to accept as 1 rigid body. If None, max(3, round(0.1*N)).

A lower bound so that a clump of outliers does not become a false rigid body.

n_iter: number of RANSAC trials per 1 rigid body.

k_sample: number of seed sample points used to build 1 hypothesis (seed + nearest neighbours, >= 3).

seed: RANSAC random seed (reproducibility).

Returns:

dict: {"labels": (N,) int (rigid-body id of each point, 0..K-1 / outlier=-1), "motions": [(R, t), ...] (rigid transforms in id order, K of them)}.

Raises:

ValueError: bad shape / non-finite / pts1 empty / thresh <= 0 / max_bodies < 1 /

k_sample < 3.

References (sample data, literature)

• Sample-data catalog (download URLs / licences) — 2-D uses skimage.data (BSD/public domain) plus synthetic images; 3-D lists download URLs for real data sources (Stanford, PDS, …).

• Operator provenance and references — the sources of the research/methods this op family came from.

Runnable examples (verified samples that actually call this op)

• motion_seg — py -3.11 examples_3d/motion_seg.py

Ops the type connects to (they accept labels as input)

fuse_to_voxel · vol_region_props

Same category (motion_segment)

estimate_flow · fit_rigid


*Provenance: motion_seg3d.py — 3D operator registry. This per-op note is generated by tools/opdocs.py md (do not hand-edit).*

© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.