Skip to content

Init modes

Registration is two stages: a coarse initializer finds the right basin, then the Levenberg–Marquardt core polishes the pose within it. register(..., init=...) (and splatreg align --init ...) selects the initializer: the single most important knob in the library. The trade is speed ↔ robustness:

init= what when cost
"fast" (default) FPFH descriptors + GPU-batched 3-point RANSAC seed → LM polish objects / full-overlap captures ~17 ms
"robust" Open3D FPFH+RANSAC seed (scale-correct, auto-voxelled) → overlap-aware refine real metre-scale scans ~100 ms+
"learned" pretrained GeoTransformer seed → the same overlap-aware refine best accuracy on real scans (91.5% official 3DMatch) ~104 ms
"bufferx" pretrained BUFFER-X zero-shot seed (ICCV 2025) → the same overlap-aware refine cross-sensor / cross-scale scans, no per-dataset training GPU (falls back to "robust" on CPU)
"global" blind super-Fibonacci SO(3) sweep + batched trimmed ICP unknown, possibly huge rotation; no features needed ~0.8–1.4 s
"mac" MAC maximal-clique consensus over the FPFH correspondences (Zhang et al. CVPR 2023) → weighted SVD per clique → overlap-aware refine outlier-heavy / multi-consensus correspondence sets ~0.03–0.3 s (CPU, scales with clique count)
"features" complete partial-overlap registrar (FPFH → clique-filtered RANSAC → overlap-aware point-to-plane refine + basin-sweep fallback) the two captures see different parts of the object seconds (deep sweep)

You can also pass an explicit 4×4 tensor as init (e.g. a pose prior from odometry), or None, which resolves to "fast".

splatreg pipeline: two 3DGS splats, six coarse-init seeds (fast/robust/learned/bufferx/mac/global), a multi-residual Levenberg-Marquardt core (ICP + Gaussian-SDF, SE(3)/Sim(3)), outputs of the transform plus recovered scale and pose covariance, feeding the merge / align / track / pose-graph consumers
Where the init seed sits in the pipeline: any one of the six coarse-init seeds feeds the same multi-residual Levenberg–Marquardt refine (ICP + Gaussian-SDF, SE(3)/Sim(3)), which emits the transform, recovered scale, and pose covariance.

Picking one

  • Merging two captures of an object / small scene → keep the default "fast". It handles full rotations and is two orders of magnitude faster than the blind sweep.
  • Real indoor/outdoor scans (metre scale, sensor noise)"robust", or "learned" for the best accuracy (GeoTransformer-class recall; needs its weights available).
  • Captures from different sensors / at different scales, and you don't want to train a per-dataset model"bufferx" (zero-shot; falls back to "robust" when its weights are absent). Add seed_gate=True to "learned" to reject/reseed a low-confidence learned seed.
  • You have no idea how the splats are oriented and they look feature-poor (smooth, near-symmetric geometry where descriptors are non-discriminative) → "global".
  • Partial overlap (one capture saw the left side, the other the right) → "features".
  • Outlier-heavy or multi-consensus correspondences (repetitive structure, symmetric decoys, a contaminated learned matcher) → "mac" (below).

init="mac": maximal-clique hypothesis generation

"mac" reimplements MAC (3D Registration with Maximal Cliques, Zhang, Sun, Wang & Guo, CVPR 2023) in pure torch + networkx, replacing RANSAC minimal samples as the hypothesis generator:

  1. a rigidity compatibility graph over the correspondences (edge iff | ‖p_i−p_j‖ − ‖q_i−q_j‖ | < γ), edge weights re-scored by the second-order SC² measure (w₂ = s ⊙ (S·S), an edge is only as strong as the compatible neighbourhood the two correspondences share, which zeroes chance-compatible outlier pairs);
  2. all maximal cliques of that graph (Bron–Kerbosch with pivoting), each one a consensus hypothesis, including secondary consensus sets a greedy prefilter or a lucky-draw RANSAC never isolates. Worst-case blowup is capped: ≤ 1000 correspondences, per-node degree cap (top-48 edges by SC² weight), clique-count cap + wall-clock budget on the lazy enumeration, and node-guided selection down to ≤ 64 hypotheses;
  3. a weighted SVD (Kabsch, SC² weights) per clique, scored by inlier count over all correspondences; the winner is refit on its full consensus set, then polished by the same overlap-aware ICP the "robust"/"learned" registrars use.

Sim(3): MAC's rigidity constraint is SE(3)-only, so the scale is estimated first (median of correspondence pairwise-distance ratios), the source de-scaled, SE(3) MAC run, and a residual scale refit on the consensus inliers.

Measured on synthetic contaminated correspondence sets (CPU, tests/test_mac.py): at 30/60/90 % random outliers MAC matches the fast-init RANSAC engine (rot err ≤ 0.2°); on a 90 %-contaminated set with a structured decoy cluster (reflection-consistent, it out-degrees the true inliers) the greedy-prefilter+RANSAC engine fails at ~78° while MAC stays < 0.2°; an all-outlier set returns an honest info["success"]=False identity. 500 correspondences run in ~0.1 s on a 2-thread CPU (budget-tested < 5 s).

Inside init="learned", seed_selector="mac" (learned_feature_align(..., seed_selector="mac")) runs MAC over GeoTransformer's learned correspondences instead of the model's own LGR estimator: the exact combination the MAC paper reports lifting GeoTransformer's 3DLoMatch registration recall ~71 % → ~78 %.

3DLoMatch verdict: measured, a wash; lgr stays the default

Measured on the full official splits (GPU, single shared forward, native 0.025 voxel, same residual-gated refine, only the hypothesis stage differs): 3DLoMatch 72.1 % mean / 74.6 % pooled (MAC) vs 72.5 % / 74.4 % (LGR); 3DMatch 91.7 % / 93.8 % vs 91.5 % / 93.5 %. Every delta is within ±4 pairs, not the paper's +6–7 pp, at ~+50 % runtime. MAC genuinely engaged on 100 % of pairs (median ~600–800 consensus inliers): at native voxel the learned correspondences are already consensus-dominated, so the multi-consensus regime MAC wins (the synthetic decoy above) does not occur, and the guarded refine absorbs seed-level differences. Details in RESULTS.md §5k. Needs networkx: pip install "splatreg[mac]".

init="bufferx": zero-shot seed (BUFFER-X, ICCV 2025)

"bufferx" swaps GeoTransformer for BUFFER-XTowards Zero-Shot Point Cloud Registration in Diverse Scenes (ICCV 2025, MIT-SPARK/BUFFER-X) — as the coarse seed, then runs the same overlap-aware refine (+ Sim(3) scale) as "learned"/"robust". The point is generality: BUFFER-X is a single zero-shot model that registers across sensors and scales with no per-dataset training, which is why it is splatreg's learned seed of choice — a splat registrar should not need a per-scene/per-sensor trained model to align two captures.

BUFFER-X is optional and lazily loaded (its CUDA neighbour/subsampling extensions + Hugging Face checkpoints are not shipped). When absent — always the case on a CPU box — "bufferx" transparently falls back to the classical "robust" seed with a logged note. Setup (clone + build + weights) is in splatreg/third_party_models/README-BUFFERX.md.

Validated on real 3DMatch

Both seeds are pushed through the identical splatreg refine, so the comparison isolates the seed rather than the pipeline. On the complete official gt.log pair set (3DMatch, 8/8 scenes, n=1619; a pair counts as recalled at RRE < 15° and RTE < 0.3 m) the BUFFER-X seed reaches 0.962 (median RRE 1.46°) against 0.630 (2.12°) for the classical robust FPFH seed. On the official 3DLoMatch pair set (n=1781) it holds 0.777 (2.77°) against 0.122 (103.4°) — 6.4× the recall where the classical seed's median error is effectively random. An earlier GT-derived low-overlap run (overlap 0.10–0.30, n=400) shows the same pattern (0.752 vs 0.092, an 8× lift). BUFFER-X wins every scene on both splits.

BUFFER-X zero-shot seed vs classical FPFH seed: registration recall on 3DMatch and the low-overlap regime
3DMatch bars are the complete official gt.log pair set (8/8 scenes, n=1619); the low-overlap bars are the official 3DLoMatch gt.log set (n=1781). Both seeds share the identical lighter feature_align refine, so these isolate the seed rather than report full-pipeline absolute numbers.

Registration, happening

The bars are the aggregate; here is one real low-overlap pair watched end to end. The source fragment starts unaligned, the classical FPFH+RANSAC seed slews it into the wrong basin, then the BUFFER-X seed + splatreg refine rotates it onto the target and locks on.

Three-phase animation of a real low-overlap 3DMatch pair: unaligned, wrong classical FPFH+RANSAC basin (RRE 151.5 degrees), then BUFFER-X seed + splatreg refine locking onto the target (RRE 2.0 degrees)
Real 3DMatch pair 7-scenes-redkitchen 35→46, GT overlap 0.10. Both transforms are the actual robust_feature_align (classical, RRE 151.5° ✗) and bufferx_feature_align (BUFFER-X, RRE 2.0° ✓) library outputs — the animation interpolates between the real estimates, nothing is hand-posed. At 10% overlap the source only shares a corner with the target, so a correct lock overlaps just that corner. Regenerate with examples/make_lowoverlap_gif.py.

Positioning: a zero-shot seed by choice

Per-dataset-trained backbones now lead 3DMatch: PSReg and DiffusionPCR report 95 %+ registration recall, above the ~91.5 % GeoTransformer seed splatreg wraps. splatreg does not chase that number — it keeps a zero-shot learned option (BUFFER-X) instead. The value is a generalist seed plus splatreg's provable SH rotation, honest pose covariance, Sim(3) scale, and overlap-aware refine on top, not the last recall point on one benchmark; a higher-recall correspondence model can be dropped in as the seed the day it ships a permissive zero-shot checkpoint.

seed_gate=True: Decision-PCR-style seed confidence (opt-in)

register(init="learned", seed_gate=True) (default off) adds a lightweight, training-free stand-in for Decision PCR's learned confidence head (arXiv 2507.14965). Before LM refinement it scores the candidate learned seed with two cheap signals over mutual-NN correspondences — the inlier ratio (fraction landing within tolerance after the seed transform) and an SC² spatial consistency term (the same rigidity-graph machinery init="mac" uses) — and rejects and reseeds a low-confidence hypothesis from the classical "robust" path (keeping whichever scores higher) instead of blindly refining a bad seed. The scores land in result.info["seed_gate"]. On synthetic known-transform pairs the gate never rejects a correct seed and rejects a planted decoy (tests/test_bufferx_seedgate.py); full retraining of the classification head is out of scope.

Partial overlap: the honest contract

init="features", "robust", "learned", "bufferx", and "mac" are complete registrars, not just seeds. The default residual set assumes full overlap (its ICP would drag a good partial-overlap pose off-target), so with the default residuals these modes return their own registration directly and skip the LM. Pass an explicit overlap-safe residuals=[...] if you want the LM to run on top of the feature init.

They also return honest diagnostics:

result = register(target, source, init="features")
result.info["ambiguous"]    # True when the overlap does NOT constrain the pose
result.info["confidence"]   # 0..1 grade
result.info["feature"]      # the full per-stage diagnostic dict

Ambiguity is flagged, not hidden

When a crop removes the rotation-disambiguating geometry, the pose is genuinely unrecoverable, even the true pose doesn't seat cleanly. splatreg returns its best feasible guess flagged ambiguous=True rather than a silently wrong pose (verified: 0 silent-wrong across the robustness sweep). At overlap ≤ 40% expect flags; merge and Tracker are designed for high-overlap captures.

Fallback chain

All string inits are guarded: "learned" and "bufferx" fall back to "robust", "fast" falls back to "global", "mac" raises a clear ImportError with the install hint when networkx is missing (an explicit opt-in extra, like the solver backends) and returns an honestly-flagged identity when the correspondences carry no consensus, and everything else falls back to identity (with a logged note) when an optional dependency or pretrained weight is unavailable. Your call never hard-fails because a model file is missing.

Quality policy

Orthogonal to init, quality= bounds the work the refinement does:

  • "full" (default), nothing capped, every source anchor.
  • "balanced" / "low", bounded sample counts + tighter autodiff chunks.
  • a float 0..1, interpolates the caps.
  • "auto", detect free GPU/CPU memory and pick the largest sizes that fit.

The Sim(3) autodiff Jacobian is always row-chunked, so peak memory stays bounded with no quality loss. See resolve_quality.