Releases: Archerkattri/splatreg
v1.3.2 — Phase 4: verified examples + Sim(3) autodiff fix
What's new
Bug fixes
- Sim(3) autodiff Jacobian graph accumulation (
solvers/lm.py): detachTbefore passing it into thejacrevclosure. Without this, autograd accumulated a growing computation graph across LM iterations, causing quadratic memory use and eventual OOM on large inputs.
Tests
tests/test_solver.py: new regression testtest_sim3_autodiff_jacobian_detaches_Tgates the fix against future regressions.
Examples
examples/merge_demo.pyrestructured: the synthetic mode now uses a full-objectAandB = Sim(3)(A)instead of mismatched partial crops. This matches the realistic "two complete reconstructions in different frames" scenario and produces meaningful metrics (rot 0.6°, 68.7x Chamfer improvement, overlap 0.199 → 1.000).
Validation
pytest: 152/152 passedexamples/validate_recovery.pyon GPU: 36/36 cells, 100% success rate, 172s, 0.15 GiB peak
v1.3.1 — SDF residual weight fix (load-bearing)
Bug-fix release from an independent whole-repo review.
Fixed
- The Gaussian-SDF residual pre-multiplied its weight in both residual() and jacobian() while the solver also applies sqrt(weight): the objective scaled as weight cubed, so the default register stack ran SDF at an effective 0.027 instead of the documented 0.3. The solver now owns the weight, applied exactly once, matching every other residual. Regression test asserts linear weight scaling in the assembled normal equations.
- merge() and bundle fusion now normalize mixed log/linear scale conventions before concatenation (mixing a PLY-loaded splat with a from_gsplat splat no longer corrupts fused scales).
- knn() on an empty spatial index returns empty instead of crashing, matching radius().
Honesty note: the official 3DMatch (91.5 percent) and 3DLoMatch (72.5 percent) results are unaffected (the learned pipeline does not use the SDF residual). The synthetic-recovery and splat-tool numbers in RESULTS section 5c were measured under the old weighting; they remain within all validation gates after the fix and will be re-measured in the next benchmark pass.
pip install -U splatreg
splatreg 1.3.0: MAC seed, the honest verdict, and the production sweep
splatreg 1.3.0
The production-sweep release: the MAC maximal-clique seed with its honest measured verdict, the align-without-merge workflow, and a full code/docs audit. Every claim below traces to a recorded run or a test (RESULTS.md).
init="mac": maximal-clique correspondence seed (Zhang et al., CVPR 2023)
Pure torch + networkx reimplementation (pip install "splatreg[mac]"): SC2-weighted rigidity compatibility graph, Bron-Kerbosch maximal cliques as consensus hypotheses, weighted SVD per clique, plus a Sim(3) extension via the median pairwise-distance-ratio scale. Measured (tests/test_mac.py):
- matches the fast-init RANSAC engine at 30/60/90% random outliers (rot err <= 0.2 deg);
- decisively wins the structured-decoy regime: on a 90%-contaminated set with a reflection-consistent decoy cluster the greedy-prefilter+RANSAC engine fails at ~78 deg while MAC stays < 0.2 deg;
- all-outlier sets return an honest
success=Falseidentity, never a silent wrong pose; - 500 correspondences run in ~0.1 s on a 2-thread CPU.
The official-split verdict, stated honestly
seed_selector="mac" runs MAC over GeoTransformer's learned correspondences on the full official 3DMatch/3DLoMatch splits (same forward, same native 0.025 voxel, same residual-gated refine; only the hypothesis stage differs). Result: a wash, not a lift. 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 within plus or minus 4 pairs, at ~+50% runtime. At native voxel the learned correspondences are already consensus-dominated (median 600-800 MAC inliers), so seed_selector="lgr" stays the default and "mac" remains the tool for genuinely contaminated correspondence sets (RESULTS.md section 5k).
Carried forward from 1.2.0 (same-day release)
- SH Wigner rotation: higher-order SH bands (
f_rest) now rotate with the splat inapply_transform/merge/ thealignCLI (Ivanic-Ruedenberg real-basis Wigner-D, 3DGS sign convention); rotated-coefficient evaluation matches an independent basis evaluator to ~2.4e-15 in float64. - Public
apply_transform()and the single-forward align-without-merge workflow: register two scans, bake the recovered SE(3)/Sim(3) into the source, keep both scans as separate registered PLYs. - Photometric exposure compensation (default ON) and the coarse-to-fine render ladder: a x1.3 tint that absorbed into the Sim(3) scale (0.10% -> 3.99% scale error) recovers to 0.47% with compensation; the 32-64-96 ladder lands 2.55 deg where a cold single rung stalls at 5.61 deg.
- Pose covariance: builtin-LM solves expose
info["information"]/info["covariance"]for pose-graph weighting (Nonewhen singular, never faked).
Production sweep
- Dead code removed, ruff clean across package/tests/benchmarks; CLI and the Colab notebook now use the public
apply_transform. - README restructured as a storefront: capability matrix vs the alternative splat tools, results table with per-row provenance.
- Docs refreshed: https://archerkattri.github.io/splatreg/ (MAC verdict, SH rotation, covariance, align-without-merge).
CHANGELOG.mdadded.
Gate
143 tests passing (CPU, CUDA_VISIBLE_DEVICES=""), validate_recovery.py --fast 6/6 cells within gate, mkdocs build --strict clean.
Install: pip install splatreg==1.3.0 · Docs: https://archerkattri.github.io/splatreg/ · Cite: DOI 10.5281/zenodo.20618389
v1.2.0 — SH Wigner rotation · exposure compensation · render ladder · pose covariance
splatreg 1.2.0 — the correctness release.
Spherical harmonics now rotate correctly. Real-basis Wigner-D rotation (any degree; Ivanic–Ruedenberg recurrence with the 1998 erratum corrections, conjugated into the 3DGS f_rest sign convention) is baked into apply_transform, merge, and the align CLI. Verified renderer-free to 2.4e-15: evaluating rotated coefficients at d equals evaluating the originals at R⁻¹d, plus closed-form l=1, composition, and PLY round-trip tests. To our knowledge splatreg is the only splat registrar that rotates view-dependent color correctly. Also exported standalone: splatreg.rotate_sh / sh_rotation_matrix.
Photometric refine upgrades
- Per-pair exposure compensation (closed-form gain/bias, clamped, alternated with the pose LM, default ON): a ×1.3 tint that silently corrupted the recovered Sim(3) scale (0.10% → 3.99% error) is absorbed back to 0.47%, harmless on clean pairs.
- Coarse-to-fine render ladder (
refine_kwargs["ladder"]): at equal budget, 5.61° (single rung) → 2.55°.
New API
RegisterResult.info["information"]/["covariance"]— JᵀWJ and σ̂²(JᵀWJ)⁻¹ at convergence (6×6 SE(3) / 7×7 Sim(3));Nonewhen singular, never faked. Unlocks pose-graph / submap fusion use.apply_transform()— align scans without merging: bake the recovered transform into the source and save each scan as its own PLY (mirrorssplatreg align).validate_recovery.py --fast(6 cells, ~41 s CPU).
Test suite: 105 → 126 passed. Docs updated throughout.
pip install -U splatreg
v1.1.0 — CLI · photometric refinement · docs site
splatreg 1.1.0 — the CLI release, plus photometric refinement.
New
- CLI:
splatreg align a.ply b.ply -o aligned.ply/splatreg merge ... -o fused.ply/splatreg info x.ply— register and fuse standard 3DGS PLYs from the terminal (recorded run: 154 mm Chamfer → 0.05 mm). - Photometric refinement (
register(..., refine="photometric")): a splat-to-splat photometric stage after the geometric solve — decisive where geometry under-constrains the pose (rotation-symmetric / texture-only cases: 5°/7 mm → 0.36°/0.5 mm with gsplat in ~1.1 s), neutral where geometry already converges. Opt-in; works with a customrender_fnwithout gsplat. Three-case benchmark in the docs. - Docs site: https://archerkattri.github.io/splatreg/ — quickstart, CLI guide, init modes, PLY interop (incl. the honest SH-rotation scoping), API reference, benchmarks.
- Colab quickstart notebook (runs CPU-only end-to-end) + CITATION.cff.
Fixed
- DC-only PLY colour round-trip:
load_plynow returns RGB for DC-only files (2-D colors == RGB everywhere), fixing a load→save double-encode. Regression-locked.
105 tests, BSD-3-Clause. pip install -U splatreg
v1.0.2 — object-pose · camera-loc · bundle · spatial-index
splatreg 1.0.2 brings the full v0.2/v0.3 feature set to PyPI (was registration-only):
- 6-DoF object-pose (estimate_object_pose) — ADD/ADD-S/AUC; YCB-CAD ADD-S AUC 0.995
- Camera localization in a splat (localize_camera) — differentiable-render pose refine
- Multi-splat bundle registration (bundle_register) — pose-graph + robust outlier rejection
- Scene-scale spatial index (SpatialIndex) — voxel-hash knn/radius/region
69 tests, BSD-3-Clause. pip install -U splatreg
splatreg 1.0.1
Production release. Composable SE(3)/Sim(3) Gaussian-splat registration (inverse of gsplat) — matches GeoTransformer on official 3DMatch, real-splat merge, pluggable backends. pip install splatreg