API reference¶
The import surface is flat: everything below is available from splatreg directly (or the
named submodule). Optional-dependency features (localize_camera needs splatreg[render])
degrade to None at import time rather than breaking the package.
Registration¶
splatreg.api.register ¶
register(
target,
source,
*,
residuals=None,
init=None,
transform="se3",
backend="builtin",
max_iters=None,
quality="full",
refine=None,
refine_kwargs=None
)
Register source onto target over a list of residuals, returning the 4x4 transform.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
the reference (a :class:`~splatreg.core.types.Gaussians`, usually).
|
|
required |
source
|
what is aligned to ``target`` — a ``Gaussians`` (splat-to-splat) or a ``Frame``
|
(tracking); handed straight to each residual. |
required |
residuals
|
sequence of :class:`~splatreg.residuals.base.Residual`, or ``None`` (default) to
|
build an ICP-dominant default set |
None
|
init
|
initial 4x4 transform, or ``None`` for identity, or one of the strings:
|
All string forms are guarded — fall back to identity with a logged note if the module
is unavailable. For |
None
|
transform
|
``"se3"`` (dof 6) or ``"sim3"`` (dof 7; the scale DoF is solved, autodiffed).
|
|
'se3'
|
backend
|
the solver engine. ``"builtin"`` (DEFAULT, fastest) is splatreg's closed-form-Jacobian
|
Levenberg-Marquardt core. |
'builtin'
|
max_iters
|
maximum LM iterations. ``None`` (default) takes the value from ``quality``
|
( |
None
|
quality
|
the quality / machine-adaptivity policy (see :func:`splatreg.quality.resolve_quality`):
|
|
'full'
|
refine
|
optional OPT-IN second refinement stage run AFTER the geometric solve. The only value
|
today is |
None
|
refine_kwargs
|
optional dict of keyword overrides for the refine stage (see
|
:func: |
None
|
Returns:
| Type | Description |
|---|---|
class:`~splatreg.core.types.RegisterResult` whose ``T`` is the full transform (the similarity
|
|
``[[s*R, t], [0, 1]]`` for ``transform="sim3"``), ``scale`` the recovered ``s`` (``1.0`` for
|
|
SE(3)), and ``info`` carries ``cost`` / ``n_iters`` / ``rmse`` (filled by
|
|
func:`~splatreg.solvers.lm.run_lm`) plus the resolved ``quality`` label.
|
|
splatreg.api.merge ¶
merge(
gaussians_list,
ref=0,
*,
residuals=None,
transform="sim3",
init="global",
dedupe=True,
dedupe_method="voxel",
voxel=None,
knn_radius=None,
max_iters=None,
quality="full",
refine=None,
refine_kwargs=None
)
Register every splat onto gaussians_list[ref], fuse, and return one Gaussians.
This is the v0.1 headline — a merge that is not a naive cat. For each non-reference splat
it :func:register\ s onto the reference (init="global" so large offsets recover; default
ICP-dominant residuals; transform="sim3" so scale differences are absorbed), bakes the
recovered Sim(3)/SE(3) into that splat's means / quats / scales, concatenates everything, then
dedupes the overlap: a voxel-grid pass (:func:splatreg.fuse.voxel_dedupe) keeps the
highest-opacity Gaussian per occupied voxel, so the double-density seam collapses to single
density. The reference passes through unregistered. The result drops straight into
:func:splatreg.io.save_ply.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gaussians_list
|
the splats to merge.
|
|
required |
ref
|
index of the reference splat (all others register onto it). Default 0.
|
|
0
|
residuals
|
residual list per :func:`register` (``None`` -> the ICP-dominant default set).
|
|
None
|
transform
|
``"sim3"`` (default; recovers scale) or ``"se3"`` for each pairwise registration.
|
|
'sim3'
|
init
|
initial transform / ``"global"`` (default coarse basin-finder) / a 4x4, per
|
:func: |
'global'
|
dedupe
|
when ``True`` (default) run the overlap dedupe after concatenation; when ``False`` the
|
merge is a registered concatenation (still aligned, but double-density seam). |
True
|
dedupe_method
|
``"voxel"`` (default) for the voxel-grid pass (:func:`splatreg.fuse.voxel_dedupe`)
|
or |
'voxel'
|
voxel
|
voxel-pass edge in splat units; ``None`` auto-derives it from the anchor spacing (see
|
:func: |
None
|
knn_radius
|
KNN-pass suppression radius in splat units; ``None`` auto-derives it (half the
|
anchor spacing, :func: |
None
|
max_iters
|
LM iterations per pairwise registration. ``None`` (default) takes the value from
|
|
None
|
quality
|
quality / machine-adaptivity policy applied to every pairwise registration — ``"full"``
|
(DEFAULT), |
'full'
|
refine
|
optional opt-in per-pair refinement stage, forwarded to :func:`register`. The merge
|
seam is exactly where |
None
|
refine_kwargs
|
keyword overrides for the refine stage, forwarded to :func:`register`.
|
|
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Gaussians |
the fused splat. Raises ``ValueError`` on an empty list or out-of-range ``ref``.
|
|
splatreg.api.Tracker ¶
Stateful pose tracker: fixed target + residuals, warm-started across frames.
Hold the reference splat and the residual list once, then call :meth:track per frame; each
call seeds the LM from the previously estimated pose so small inter-frame motion converges in a
few iterations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
the reference (a :class:`~splatreg.core.types.Gaussians`, usually).
|
|
required |
residuals
|
sequence of :class:`~splatreg.residuals.base.Residual`.
|
|
required |
transform
|
``"se3"`` or ``"sim3"``.
|
|
'se3'
|
max_iters
|
LM iterations per :meth:`track` call. ``None`` takes the value from ``quality``.
|
|
None
|
init
|
optional initial 4x4 pose (identity when ``None``).
|
|
None
|
quality
|
quality / machine-adaptivity policy (see
|
:func: |
'full'
|
Multi-splat bundle registration¶
splatreg.bundle.bundle_register ¶
bundle_register(
splats,
ref=0,
pairs="auto",
*,
transform="se3",
init="global",
register_kwargs=None,
max_iters=30,
damping=1e-06,
convergence_tol=1e-09,
robust="huber",
robust_scale="auto",
reject_threshold=0.1,
fuse=False,
return_info=False,
dedupe=True
)
Jointly register N splats into one loop-consistent frame via a pose-graph solve.
Builds a relative-pose constraint T_ij for every edge in pairs (each from
:func:splatreg.register), then optimises all N absolute poses T_i so every edge
T_i @ T_ij ≈ T_j holds simultaneously — Gauss-Newton in the SE(3)/Sim(3) tangent, with the
reference pose pinned to identity to fix the gauge. Unlike the sequential merge (which chains the
edges and accumulates drift around a loop) the joint optimum spreads the closure error over the
whole graph, so the max pairwise inconsistency drops.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
splats
|
the ``N`` splats to register together.
|
|
required |
ref
|
index of the reference splat, held at identity (the global frame). Default 0.
|
|
0
|
pairs
|
``"auto"`` (chain ``0-1-…-(N-1)`` + loop-closure ``(N-1, 0)``) or an explicit
|
|
'auto'
|
transform
|
``"se3"`` (6-DoF edges/poses) or ``"sim3"`` (7-DoF, scale included).
|
|
'se3'
|
init
|
Union[str, Tensor, None]
|
to |
'global'
|
max_iters
|
int
|
(a small Levenberg damping keeps the normal equations SPD; the solve stops when the pose
update norm drops below |
30
|
robust
|
per-edge robust kernel applied in the joint solve (IRLS). ``"huber"`` (default) or
|
|
'huber'
|
robust_scale
|
the kernel scale ``c`` (the residual norm at which an edge starts to be
|
down-weighted). |
'auto'
|
reject_threshold
|
an edge whose final IRLS weight is below this is reported in
|
|
0.1
|
fuse
|
also return one merged :class:`~splatreg.core.types.Gaussians` — every splat baked into
|
its recovered absolute pose, concatenated, and ( |
False
|
return_info
|
also return a :class:`BundleResult` with the consistency diagnostics.
|
|
False
|
dedupe
|
voxel-dedupe the fused splat when ``fuse=True`` (default ``True``).
|
|
True
|
Returns:
| Type | Description |
|---|---|
``list[Tensor]`` of the ``N`` absolute 4x4 poses, by default. With ``fuse=True`` a
|
|
``(poses, fused)`` tuple; with ``return_info=True`` a trailing :class:`BundleResult` is appended.
|
|
splatreg.bundle.pairwise_consistency ¶
Max and mean per-edge tangent inconsistency of an absolute-pose set against the measurements.
rel maps (i, j) -> T_ij. Returns (max||e_ij||, mean||e_ij||) — the metric the joint
solve drives down and the headline "loop closes" number. The tangent norm mixes rotation
(radians) and translation (splat units); for the synthetic loop both are small so it is a fair
single scalar, but callers comparing regimes should look at the components.
Object pose¶
splatreg.object_pose.estimate_object_pose ¶
estimate_object_pose(
model,
observation,
*,
init="fast",
transform="se3",
residuals=None,
backend="builtin",
max_iters=None,
quality="full"
)
Estimate the 6-DoF pose T_SO of a known model splat from an observation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
the canonical object splat (the "CAD model" of the splat world), in its own object frame.
|
|
required |
observation
|
the observed instance — a :class:`~splatreg.core.types.Gaussians` (an observed
|
splat / crop) or a :class: |
required |
init
|
coarse-init mode, per :func:`splatreg.register`. Default ``"fast"`` (FPFH + GPU-batched
|
RANSAC) finds the rotation basin for an arbitrarily-rotated object; pass a 4x4 to warm-start
from a prior pose, or |
'fast'
|
transform
|
``"se3"`` (default, rigid 6-DoF — a real object does not change size) or ``"sim3"``
|
when the observation is at an unknown metric scale. |
'se3'
|
residuals
|
forwarded to :func:`splatreg.register`.
|
|
None
|
backend
|
forwarded to :func:`splatreg.register`.
|
|
None
|
max_iters
|
forwarded to :func:`splatreg.register`.
|
|
None
|
quality
|
forwarded to :func:`splatreg.register`.
|
|
None
|
Returns:
| Type | Description |
|---|---|
class:`ObjectPose` whose ``T_SO`` maps model points into the observation frame.
|
|
Notes
Internally this is register(target=observation, source=model) — the observation is the
target so the recovered transform moves the model onto the observation, i.e. exactly the object
pose T_SO the caller wants. The honest occlusion / partial-view limit of register carries
over: a heavily cropped observation can leave the pose ambiguous, surfaced as info['ambiguous'].
splatreg.object_pose.ObjectPoseEstimator ¶
Stateful 6-DoF object-pose tracker for a fixed known model (the FoundationPose regime).
Build once with the canonical model splat, then call :meth:estimate per frame. The first
frame pays the global/feature init to find the pose basin; every subsequent frame warm-starts
from the previous pose via the fast :func:splatreg.track path (skip-global-init + truncated-SDF
closed-form LM), so a tracked object updates in a few LM iterations rather than re-searching SO(3).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
the canonical object splat (held fixed for the estimator's life).
|
|
required |
transform
|
``"se3"`` (default) or ``"sim3"``.
|
|
'se3'
|
init
|
the FIRST-frame coarse init mode (per :func:`estimate_object_pose`); later frames ignore it.
|
|
'fast'
|
track_iters
|
LM iterations per warm-started frame (default from :func:`splatreg.track`).
|
|
4
|
quality
|
quality / machine-adaptivity policy for the first-frame :func:`register`.
|
|
'full'
|
splatreg.object_pose.add_metric ¶
Hinterstoisser ADD: mean model-point distance between the predicted and GT pose.
ADD = mean_i || T_pred · x_i − T_gt · x_i || over the model points x_i. The standard
non-symmetric pose-error metric (asymmetric objects). Returned in the model's units (metres).
splatreg.object_pose.adds_metric ¶
ADD-S: symmetric (closest-point) variant of ADD for symmetric objects.
ADD-S = mean_i min_j || T_pred · x_i − T_gt · x_j || — each transformed-by-prediction point is
matched to its nearest GT-transformed model point, so a rotation about a symmetry axis (which
maps the model onto itself) is not penalised. This is the metric YCB-Video / FoundationPose use
for symmetric objects. Both sides are deterministically strided to max_pts to bound the
pairwise distance. Returned in model units (metres).
splatreg.object_pose.add_auc ¶
Area-under-curve of the ADD/ADD-S accuracy-threshold recall (the YCB-Video AUC).
Sweeps a distance threshold d from 0 to max_threshold (default 0.1 m = 10 cm, the
YCB-Video convention), at each d measures the fraction of poses whose error ≤ d, and
returns the normalised area under that curve in [0, 1] (×100 gives the reported AUC). A higher
AUC means more poses are accurate at a tighter threshold.
Camera localization¶
splatreg.camera_loc.localize_camera ¶
localize_camera(
splat,
frame,
init_T_WC,
*,
iters=150,
lr=0.01,
refold_every=40,
mask_to_rendered=True,
huber_k=None,
sh_degree=None,
coarse_kwargs=None
)
Localize a query camera in a splat by differentiable-render pose optimization.
Refines init_T_WC so the gsplat render of splat from that camera matches frame.rgb,
optimizing the camera→world pose through gsplat's differentiable rasteriser (exact render
gradient). Returns a :class:~splatreg.core.types.RegisterResult whose T is the refined
T_WC and whose info carries the loss history.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
splat
|
the world-fixed Gaussian splat (must carry ``colors``).
|
|
required |
frame
|
the query observation — needs ``rgb`` and ``K`` (optional ``mask``).
|
|
required |
init_T_WC
|
``(4, 4)`` initial camera→world prior, OR the string ``"coarse"`` to first run the
|
prior-free :func: |
required |
iters
|
Adam steps on the pose tangent.
|
|
150
|
lr
|
Adam learning rate on the 6-vector right-perturbation tangent.
|
|
0.01
|
refold_every
|
every this many steps, fold the accumulated tangent into ``T_WC`` and reset it to
|
zero (keeps |
40
|
mask_to_rendered
|
when ``True``, the photometric loss is restricted to pixels the splat actually
|
renders (rendered depth > 0), so empty background does not dominate the loss. |
True
|
huber_k
|
optional Huber threshold on the per-pixel RGB residual (robust to occluders / outliers);
|
|
None
|
sh_degree
|
SH degree if ``splat.colors`` are SH coefficients; ``None`` treats them as RGB.
|
|
None
|
coarse_kwargs
|
forwarded to :func:`coarse_localize_camera` when ``init_T_WC == "coarse"``
|
(e.g. |
None
|
Returns:
| Type | Description |
|---|---|
class:`~splatreg.core.types.RegisterResult` with the refined ``T_WC``; ``info['mode'] ==
|
|
'camera_loc'``, ``info['loss']`` (final), ``info['loss_history']``.
|
|
splatreg.camera_loc.coarse_localize_camera ¶
coarse_localize_camera(
splat,
frame,
*,
candidates=None,
n_az=12,
n_el=5,
radius=None,
grid=24,
dilate=2,
return_score=False
)
Coarse, prior-free camera-pose seed by a project-and-compare viewpoint sweep (CPU, no gsplat).
:func:localize_camera refines a pose only within the narrow basin of direct image alignment, so
it needs a decent prior. This provides that prior when none exists (a wide-baseline relocalise):
it scores a sphere of candidate camera poses by how well the splat's projected occupancy
overlaps the query frame's foreground silhouette (from frame.mask, or a luminance threshold of
frame.rgb), and returns the best-scoring pose. Pure pinhole projection — it runs on CPU and
needs no rasteriser — so it is a coarse seed, deliberately cheap, not a final pose. Feed its
result as init_T_WC to :func:localize_camera for the fine refine.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
splat
|
the world-fixed splat (only ``means`` are used here).
|
|
required |
frame
|
query observation; needs ``K`` and a foreground cue (``mask`` preferred, else ``rgb``).
|
|
required |
candidates
|
explicit list of ``(4, 4)`` ``T_WC`` to score; ``None`` auto-builds a look-at sphere
|
( |
None
|
n_az
|
int
|
⇒ ~2.5× the splat's bounding radius, a typical object-framing distance). |
12
|
grid
|
occupancy-bitmap resolution the IoU score is computed at (coarse on purpose).
|
|
24
|
dilate
|
binary-dilation radius (in grid cells) applied to both the projected and the query
|
occupancy before scoring — closes the holes a sparse point splat leaves so the IoU compares
connected silhouettes (higher and far more viewpoint-discriminative). |
2
|
return_score
|
also return the best IoU score (diagnostic).
|
|
False
|
Returns:
| Type | Description |
|---|---|
The best ``(4, 4)`` ``T_WC`` (camera→world); with ``return_score=True`` a ``(T_WC, score)`` tuple.
|
|
Core types¶
splatreg.core.types.Gaussians
dataclass
¶
A 3D Gaussian Splat, gsplat-compatible. All tensors share a device.
Convention: quats are wxyz; scales are linear unless log_scales is True;
colors are either (N,3) RGB or (N,K,3) SH coefficients.
splatreg.core.types.Frame
dataclass
¶
A single observation (for the camera/object tracking modes).
For splat-to-splat registration the 'source' is another Gaussians, not a Frame —
residuals receive whichever is relevant via the source argument.
splatreg.core.types.RegisterResult
dataclass
¶
Output of register / Tracker.track.
T is the 4x4 transform aligning source to target (rotation*scale | translation
for Sim(3); plain SE(3) when transform='se3'). info carries diagnostics
(per-iter cost, rmse, overlap, n_iters, timings, residual breakdown).
PLY + gsplat I/O¶
splatreg.io.load_ply ¶
Load a standard 3D Gaussian Splatting .ply into a :class:Gaussians.
Recognises the canonical INRIA/gsplat layout (x y z, f_dc_0..2, f_rest_*,
opacity, scale_0..2, rot_0..3). Stored values are raw — the returned
Gaussians has log_scales=True, raw (pre-sigmoid) opacities, wxyz quats,
and colors as SH coefficients shaped (N, K, 3) (or RGB (N, 3) if only DC is present,
i.e. K == 1, kept 2-D for convenience).
Args:
path: Path to the .ply file.
device: Target device for the tensors (default: CPU).
dtype: Floating dtype for the tensors (default: float32).
Returns:
Gaussians: with log_scales=True and SH colours.
Raises:
FileNotFoundError: if path does not exist.
ValueError: if the required 3DGS properties are missing.
splatreg.io.save_ply ¶
Write a :class:Gaussians to a standard 3D Gaussian Splatting binary .ply.
The output is the canonical INRIA/gsplat layout consumable by SuperSplat, the antimatter15
viewer, gsplat, nerfstudio, etc. Parameters are stored raw: scale_* are log-scales
(the input is log-transformed if log_scales is False), opacity is the stored logit,
and the colour is written as SH (f_dc + f_rest). RGB colours are encoded to a DC-only
SH; an (N, K, 3) SH input is written with full f_rest.
Args:
gaussians: The splat to serialise.
path: Destination .ply path (parent dirs are created).
splatreg.io.from_gsplat ¶
Wrap gsplat-style rasteriser tensors as a :class:Gaussians (no copy of valid inputs).
This is the entry point for "I already have my splat as gsplat tensors." It performs only light normalisation (shape/contiguity), leaving values untouched.
Args:
means: (N, 3) centres.
quats: (N, 4) rotations, wxyz (gsplat's convention).
scales: (N, 3). Linear by default; pass log_scales=True if these are log-scales.
opacities: (N,) or (N, 1). Whatever activation state your gsplat call expects —
splatreg treats this as opaque and passes it back unchanged in :func:to_gsplat.
colors: optional (N, 3) RGB or (N, K, 3) SH coefficients.
log_scales: set True if scales are already log-transformed.
Returns: Gaussians.
splatreg.io.to_gsplat ¶
Unpack a :class:Gaussians into the keyword bundle gsplat's rasteriser consumes.
Scales are returned linear (exp applied if the Gaussians holds log-scales), since
gsplat.rasterization expects linear scales. opacities and colors are passed
through unchanged. The result is intended for gsplat.rasterization(..., **to_gsplat(g))::
from gsplat import rasterization
out = rasterization(viewmats=..., Ks=..., width=W, height=H, **to_gsplat(g))
Args: g: The splat to unpack.
Returns:
dict: {"means", "quats", "scales", "opacities", "colors"} (colors omitted if
g.colors is None).
Gaussian-SDF field¶
splatreg.geometry.gaussian_sdf.gaussian_sdf ¶
gaussian_sdf(
gaussians,
points,
*,
sigma,
normals=None,
trunc_sigmas=None,
use_opacity=False,
knn=50,
chunk_size=2048,
index=None
)
Sample the Gaussian-derived signed-distance proxy and its gradient at points.
See the module docstring for the proxy definition and assumptions.
Args:
gaussians: the splat providing the anchors (means, opacities). Only
means (and opacities when use_opacity) are read.
points: (N, 3) query positions, in the splat's own frame.
sigma: Gaussian kernel bandwidth (influence radius), same units as means.
Required — there is no universal default. Must be > 0.
normals: optional (M, 3) per-anchor normals. If None they are estimated
once via :func:estimate_anchor_normals (k-NN PCA, outward-oriented).
trunc_sigmas: if set, only the anchors within trunc_sigmas * sigma of each query
(via a per-query top-k gather) contribute; None uses every anchor. A speed
knob for very large splats; does not change the proxy otherwise.
use_opacity: multiply each kernel weight by its anchor opacity.
knn: neighbourhood size passed to the normal estimator (ignored if normals given).
chunk_size: rows of points processed per block, bounding the (chunk, M) weight
matrix's memory.
index: optional prebuilt :class:splatreg.spatial_index.SpatialIndex over the target
anchors. When supplied with trunc_sigmas the per-query k-nearest gather is
served by the voxel-hash grid (near-O(N) candidate lookup) instead of the full
(chunk, M) distance matrix — the EXACT same truncated proxy, only the neighbour
search is pruned, so a scene-scale target field stays cheap. Ignored when
trunc_sigmas is None (the full-field path reads every anchor by definition).
Returns:
(sdf, grad) where sdf is (N,) signed distances (> 0 outside) and
grad is (N, 3) unit surface normals n~(p) (the spatial gradient of the
proxy). Both live on points' device and dtype.
Raises:
ValueError: empty splat, mismatched normals, malformed points, or sigma<=0.
splatreg.geometry.gaussian_sdf.gaussian_sdf_grad ¶
gaussian_sdf_grad(
gaussians,
points,
*,
sigma,
normals=None,
use_opacity=False,
knn=50,
chunk_size=2048,
trunc_sigmas=None
)
Signed distance AND its EXACT spatial gradient ∇_p d, computed in closed form.
Same proxy as :func:gaussian_sdf, but the second return is the TRUE field gradient ∇_p d
analytically — so the SE(3) SDF-residual path needs neither an autograd graph nor a second
forward (this is the fast path; :func:gaussian_sdf + autodiff is the truncated fallback).
Derivation (u = p - q~, a_i = p - q_i, c_i = q_i - q~)::
∂q~/∂p = (1/σ²) Cov_w, Cov_w = (1/W) Σ_i w_i c_i c_iᵀ (weighted anchor covariance)
∇_p d = n~ - (1/σ²) Cov_w n~ - (1/(σ²‖S_n‖)) Σ_i w_i (n_i·x) a_i, x = u - d n~
EXACT (~1e-8 vs central-difference numerical) wherever the field has anchor support — the
entire regime where the SDF is meaningful and where the registration residual operates
(residual audit tests/test_jacobians.py 8/8). At degenerate far queries (the weight-sum
clamp activates, the field itself is undefined) it falls back to the bounded surface normal
n~ rather than the blown-up un-clamped expression. Returns (sdf (N,), grad (N, 3))
where grad is ∇_p d — NOT the surface normal n~.
Truncation (the tracking fast path)
trunc_sigmas (default None = every anchor) restricts each query to its k nearest
anchors via a per-query top-k gather, then zeros the weight of any beyond trunc_sigmas*sigma
— exactly the support :func:gaussian_sdf uses. Because all the closed-form sums above
(q~, S_n, Cov_w n~, the normal-derivative term) are computed over the SAME truncated
anchor set, the gradient stays the EXACT analytic field gradient of the truncated proxy with
NO autodiff — so warm-start tracking with a tight sigma costs N*k not N*M. k is
sized from knn (clamped to the cloud), the same convention as :func:gaussian_sdf.
Fusion / dedupe¶
splatreg.fuse.voxel_dedupe ¶
Collapse near-coincident Gaussians to one-per-voxel, keeping the highest-opacity survivor.
Args:
g: the (typically concatenated) splat to dedupe.
voxel: grid edge length in the splat's units; None derives it via
:func:auto_voxel_size (a small multiple of the median Gaussian scale).
Returns:
Gaussians: a subset of g with at most one Gaussian per occupied voxel. Fields,
colors (if present), log_scales, device and dtype are preserved. An empty or
single-Gaussian input is returned unchanged.
splatreg.fuse.knn_dedupe ¶
Cross-splat radius dedupe: keep the highest-opacity survivor within every radius ball.
The translation-invariant complement to :func:voxel_dedupe — it removes the residual overlap a
voxel grid leaves at cell boundaries (see this section's note). Use it (e.g. via
merge(..., dedupe_method="knn")) when the voxel pass under-dedupes a registered seam.
Args:
g: the (typically concatenated) splat to dedupe.
radius: suppression ball radius in the splat's units; None derives it via
:func:auto_knn_radius (half the median anchor spacing).
use_index: when True route the neighbour search through the voxel-hash
:class:splatreg.spatial_index.SpatialIndex (near-O(N) on scene-scale splats) instead of
the default O(N^2) chunked cdist scan. The survivor set is IDENTICAL either way — only
the neighbour search is pruned. Default False (the original brute-force path).
Returns:
Gaussians: a subset of g with no two survivors closer than radius to a
higher-priority neighbour. Fields, colors, log_scales, device and dtype are
preserved. An empty / single-Gaussian input is returned unchanged.
splatreg.fuse.auto_voxel_size ¶
Derive a dedupe voxel edge from the splat's anchor SPACING (median nearest-neighbour
distance) -- _VOXEL_SPACING_MULT x it.
NOTE: call this on a single, duplicate-free splat (e.g. the merge reference). Running it on an already-concatenated splat would read the overlap duplicates themselves as the spacing and under-dedupe. Falls back to the median scale, then a bbox fraction, for degenerate inputs.
splatreg.fuse.auto_knn_radius ¶
Derive a cross-splat KNN-dedupe radius from the anchor SPACING -- _KNN_RADIUS_MULT x it.
Same spacing basis (and the same "call on the clean reference, not the merged splat" caveat) as
:func:auto_voxel_size; only the multiplier differs. A radius of half the anchor spacing
suppresses a duplicate that landed anywhere within half a lattice step of a kept anchor —
including the sub-voxel-boundary pairs a grid snap leaves behind — while never reaching a genuine
one-spacing-away neighbour. Falls back to the median scale, then a bbox fraction, when degenerate.
Spatial index¶
splatreg.spatial_index.build_index ¶
Build a :class:SpatialIndex over a splat's means (or a raw (M, 3) tensor).
Convenience constructor: accepts a :class:~splatreg.core.types.Gaussians (indexes its
means) or a point tensor directly. cell None auto-sizes to the median anchor spacing.
splatreg.spatial_index.SpatialIndex ¶
A voxel-hash grid over a point set supporting radius / knn / region queries.
Build once from a (M, 3) point tensor (or a :class:~splatreg.core.types.Gaussians via
:func:build_index), then query repeatedly. All queries return results IDENTICAL to a
brute-force scan — the grid only prunes which anchors are distance-tested, never the answer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
points
|
``(M, 3)`` positions to index (the anchor means).
|
|
required |
cell
|
voxel edge in the points' units. ``None`` auto-derives it from the median anchor
|
spacing, so a radius up to one cell needs only the 27-cell neighbourhood. Must be > 0. |
None
|
radius ¶
All anchors within distance r of each query, in flat (query, anchor) pair form.
Returns (pair_query_idx, pair_anchor_idx) — two (P,) long tensors so that
points[pair_anchor_idx[j]] is within r of queries[pair_query_idx[j]]. This flat
form composes directly with the chunked SDF / dedupe code (no ragged padding). The result is
EXACT: every in-radius anchor is returned and no out-of-radius anchor is, identical to a
brute-force cdist <= r — the grid only limits which anchors are distance-tested.
radius_batch ¶
Loop-free batch radius query — identical result to :meth:radius, no Python per-query loop.
Enumerates every query's candidate anchors in one vectorised pass (:meth:_candidate_pairs),
then does a single batched exact distance test over all (query, candidate) pairs at once. The
returned flat (pair_query_idx, pair_anchor_idx) pair set is identical (as a set) to
:meth:radius / a brute-force cdist <= r — only the which-anchors-tested pruning is
shared. Wins over the looped path when there are many queries on a small/moderate cloud (the
Python loop overhead dominates there).
knn_batch ¶
Loop-free batch knn — same (Q, k) (idx, dist) result as :meth:knn, no per-query loop.
Picks one ring wide enough to contain the k-th neighbour for every query (grown until each
query has ≥ k candidates and the ring margin clears the k-th distance), enumerates all
candidates vectorised, then does a single padded scatter + batched top-k. Falls back to
growing the shared ring; for very non-uniform densities the ring may be larger than the
per-query :meth:knn would use, but the result is identical (exact top-k over a superset).
knn ¶
The k nearest anchors to each query.
Returns (idx, dist) of shape (Q, k) (idx into points, dist Euclidean),
sorted nearest-first per query — EXACTLY the brute-force cdist-topk result. Expands the
searched cell ring until at least k candidates are found AND the ring radius safely
exceeds the k-th distance (so no closer anchor in an unsearched outer cell is missed), then
does the exact top-k over the candidates. k is clamped to the anchor count.
region ¶
Indices of every anchor inside the axis-aligned box [lo, hi] (inclusive).
lo / hi are length-3 (tensor or sequence). Gathers the box's covered cells, then
does the exact per-axis bound test on those candidates — the EXACT set a brute-force
((points >= lo) & (points <= hi)).all(-1) would return.
Quality policy¶
splatreg.quality.resolve_quality ¶
Turn a user quality request into a concrete, memory-fitted :class:QualityConfig.
Two stages: (1) pick the ACCURACY knobs (n_points / knn / max_iters) from the
requested policy, then (2) ALWAYS fit the quality-neutral CHUNK knobs (jac_row_chunk /
sdf_chunk_size) to the available memory via :func:_fit_chunks so the Sim(3) autodiff peak
can never OOM — for every mode, full included. Chunking is numerically lossless, so stage 2
changes footprint, not the result.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
quality
|
Union[str, float, QualityConfig, None]
|
|
required |
device
|
the device the run will execute on (read for memory in ``"auto"`` and in the chunk
|
fit). Defaults to CUDA-current when available else CPU. |
None
|
target_anchors
|
the target splat's Gaussian count, if known — sharpens both the ``"auto"``
|
|
None
|
source_anchors
|
the source splat's Gaussian count, if known — sharpens the chunk fit for
|
|
None
|
Returns:
| Name | Type | Description |
|---|---|---|
QualityConfig |
the resolved, memory-fitted sizing for this run.
|
|
splatreg.quality.QualityConfig
dataclass
¶
QualityConfig(
n_points=_FULL_N_POINTS,
jac_row_chunk=_FULL_JAC_ROW_CHUNK,
sdf_chunk_size=_FULL_SDF_CHUNK,
knn=_FULL_KNN,
max_iters=_FULL_MAX_ITERS,
refine_iters=_FULL_REFINE_ITERS,
label="full",
)
Resolved per-run sizing knobs (the output of :func:resolve_quality).
Attributes:
| Name | Type | Description |
|---|---|---|
n_points |
source-anchor sample size for the SDF/ICP residuals, or ``None`` for *all* anchors
|
(full quality). An explicit |
jac_row_chunk |
row-chunk for the Sim(3) autodiff Jacobian (``jacrev(chunk_size=...)``). Bounds
|
peak autodiff memory with no effect on the result. |
sdf_chunk_size |
per-query row block for :func:`splatreg.geometry.gaussian_sdf.gaussian_sdf`.
|
|
knn |
neighbourhood size for anchor-normal estimation.
|
|
max_iters |
default LM iteration count (an explicit ``max_iters=`` to ``register`` wins).
|
|
refine_iters |
default LM iteration count for the opt-in photometric refinement stage
|
( |
label |
human-readable provenance (e.g. ``"full"``, ``"auto:cuda 6.0GiB-free -> 0.50"``).
|
|
Extension points¶
splatreg.residuals.base.Residual ¶
Bases: ABC
residual
abstractmethod
¶
Return r with shape (..., dim).
target is the reference splat; source is what is being aligned to it — a
Gaussians (splat-to-splat registration) or a Frame (camera/object tracking),
depending on the residual. Use self.requires() to declare which.
jacobian ¶
Optional analytic Jacobian, shape (..., dim, dof) wrt the se(3)/sim(3) tangent.
Return None to let splatreg autodiff it (functorch jacrev/vmap).
splatreg.solvers.base.Solver ¶
Bases: ABC
solve
abstractmethod
¶
Solve the (damped) normal equations (JᵀWJ + λD) δ = −JᵀW r and return the step.
splatreg.testing.assert_residual_jacobian ¶
assert_residual_jacobian(
residual,
T,
target,
source,
*,
atol=0.0001,
eps=1e-06,
max_mismatch=0.02
)
Assert residual.jacobian matches the numerical Jacobian to atol for at least
(1 - max_mismatch) of rows (a small NN-switch boundary fraction is tolerated for
correspondence residuals). Returns the max per-row error. Raises AssertionError.