Skip to content

About

No description, website, or topics provided.

Resources

Contributing

Stars

21 stars

Watchers

1 watching

Forks

Latest commit

 

History

3 Commits

Folders and files

Repository files navigation

TriGS

Temporal Rigid-Body Motion for Scalable 4D Gaussian Splatting

Python License: AGPL v3

Open-source implementation of the TRiGS paper — coupled SE(3) motion, hierarchical Bézier residuals, and local anchor-centered deformation for dynamic 3D Gaussian Splatting.


TRiGS: Temporal Rigid-Body Motion for Scalable 4D Gaussian Splatting. Suwoong Yeom*, Joonsik Nam*, Seunggyu Choi*, Lucas Yunkyu Lee, Sangmin Kim, Jaesik Park, Joonsoo Kim, Kugjin Yun, Kyeongbo Kong†, Sukju Kang†. (Sogang University · Pusan National University · POSTECH · Seoul National University · ETRI) * equal contribution     † corresponding authors

Project Page · Paper (arXiv)

This repository is an independent, open-source re-implementation. All algorithmic credit belongs to the original authors — please cite their paper (see Citation) if you use this code or the ideas it realizes. We thank the authors for making their work public and for the clear supplementary-material derivations that made this implementation tractable.


Overview

TRiGS reconstructs dynamic 3D Gaussian scenes by assigning each primitive a continuous, geometrically-consistent rigid-body trajectory. Unlike prior 4DGS methods that rely on piecewise-linear velocity and short temporal windows (and therefore fragment motion into many short-lived primitives), TRiGS keeps a fixed canonical set whose identity persists across the whole sequence.

Key ideas (from the paper):

  1. Coupled SE(3) motion. Per-primitive translation and rotation are optimized jointly as a single Lie-algebra coefficient ζᵢ(t) = (ωᵢ, νᵢ) ∈ 𝔰𝔢(3) and mapped to SE(3) via the closed-form exponential — removing the rotation/translation ambiguity that plagues independent parameterizations.

  2. Hierarchical Bézier residuals. ζᵢ(t) = ζᵢᵇᵃˢᵉ + ζᵢʳᵉˢ(τ) where ζᵢʳᵉˢ is a quadratic Bézier curve in a normalized local time τ ∈ [0, 1] defined over the effective visibility window Wᵢ = [μₜ,ᵢ − 2sₜ,ᵢ, μₜ,ᵢ + 2sₜ,ᵢ]. This captures smooth nonlinear motion without over-parameterizing weakly-supervised time regions.

  3. Local anchor-centered deformation with gauge fixing. Each primitive carries a learnable anchor aᵢ that acts as its pivot; we canonicalize it as aᵢ,⊥ (orthogonal to the rotation axis) and keep only the axis-aligned translation νᵢ,∥, eliminating the optimization ambiguity between anchor and translation.

  4. Motion-guided relocation. Instead of densifying, the paper recycles low-opacity primitives by cloning active ones weighted by a four-term score (opacity · motion-magnitude × visibility · accumulated ∇₂D · accumulated ∂L/∂μₜ). The canonical count stays fixed.

For a section-by-section mapping between the paper and this codebase see docs/PAPER_MAPPING.md.


Repository Structure

TriGS/
├── src/trigs/
│   ├── math/          # SE(3) exp/log, quaternion utilities
│   │   ├── se3.py
│   │   └── quaternion.py
│   ├── core/          # Trainer, losses, camera, renderer, transforms
│   │   ├── trainer.py       # TrigsTrainer + TrigsTrainingConfig
│   │   ├── losses.py        # reconstruction / bezier_motion / rigid_coherence
│   │   ├── camera.py
│   │   ├── renderer.py
│   │   └── transforms.py
│   ├── strategy/      # Motion-guided relocation strategy (gsplat Strategy subclass)
│   │   └── motion_guided_relocation.py
│   ├── io/            # COLMAP/SelfCap IO, normalization, trajectories, dataset, PLY
│   ├── init/          # Canonical-set builders for all input modes
│   │   ├── from_plys.py     # PLY-sequence initialization helper
│   │   ├── from_colmap.py   # ROMA/COLMAP NPZ → canonical params
│   │   ├── from_selfcap.py  # SelfCap pcd sequence → canonical params
│   │   └── roma_combine.py  # Per-frame NPYs → combined NPZ
│   └── cli/
│       ├── train_from_plys.py    # trigs-train-plys
│       ├── train_from_colmap.py  # trigs-train-colmap
│       └── train_from_selfcap.py # trigs-train-selfcap
├── scripts/
│   ├── preprocess_selfcap.py
│   ├── run_plys.sh
│   └── run_colmap.sh
├── tests/             # SE(3) math + trainer smoke tests
├── docs/
│   └── PAPER_MAPPING.md   # Paper ↔ code cross-reference
├── LICENSE                # AGPL-3.0
├── README.md
└── pyproject.toml

Installation

This project uses uv for dependency management.

git clone https://github.com/OpsiClear-4DGS/TriGSVanilla.git
cd TriGS
uv sync --preview-features extra-build-dependencies

The --preview-features extra-build-dependencies flag is required: gsplat and pytorch3d are built from source and don't declare their build-time deps (torch, setuptools, numpy) in their own pyproject.toml. TriGS injects them via [tool.uv.extra-build-dependencies], which is a uv preview feature on current releases. Once uv stabilizes it, plain uv sync will work.

To avoid typing the flag every time:

# bash / zsh
export UV_PREVIEW_FEATURES=extra-build-dependencies

# PowerShell (persistent)
setx UV_PREVIEW_FEATURES extra-build-dependencies

Windows: long-path fix

On Windows, building pytorch3d may fail with:

error: could not create '<...very long path...>': The filename or extension is too long

Cause. pytorch3d's setup.py uses absolute source paths, and setuptools stacks them onto the build-output path. Combined with Windows' 260-char MAX_PATH limit and uv's default cache under %LOCALAPPDATA%\uv\cache (~80 chars), the output path exceeds the limit.

Fix — relocate uv's cache to a short path:

setx UV_CACHE_DIR "C:\uc"

Open a new PowerShell (so the env var takes effect), delete any stale .venv, and re-run:

Remove-Item -Recurse -Force .venv
uv sync --preview-features extra-build-dependencies

Alternative (requires admin + reboot): enable Windows long-path support system-wide:

New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" `
  -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force

Usage

TriGS supports three input modes through a single trainer. All produce the same canonical ParameterDict (means, quats, scales, opacities, SH, temporal window params, anchors, ζ-base + Bézier controls) that drives training.

1. PLY-sequence mode

Input: a directory of dense per-frame Gaussian PLYs. Every keyframe_stride-th PLY becomes part of the canonical set; the trainer renders GT from the PLYs themselves.

uv run trigs-train-plys \
    --input-dir /path/to/dense_frames \
    --output-dir /path/to/output \
    --config.keyframe-stride 3 \
    --config.fps 30 \
    --config.n-steps 30000

Or via the script wrapper:

bash scripts/run_plys.sh /path/to/dense_frames /path/to/output 3 30000 0

Outputs: trigs_ckpt.pt, frames_ply/frame_000000.ply, …, and optionally renders/cam_XXXX/render_YYYYYY.png.

2. Multi-view COLMAP + ROMA mode

Input: COLMAP sparse reconstruction (<data_dir>/sparse/0/ + <data_dir>/images/cam##/) plus a directory of per-frame ROMA-triangulated point NPY files (points3d_frameXXXXXX.npy, colors_frameXXXXXX.npy).

Under the hood the pipeline (1) runs trigs.init.roma_combine to build an NPZ of keyframe points with kNN-estimated velocities, (2) converts it to a TriGS canonical set via trigs.init.from_colmap.build_canonical_from_npz (initializing scales from a per-keyframe kNN distance, and running the same radius-ball anchor + (r × v) ω initialization as the PLY path), and (3) calls TrigsTrainer.train_from_params with GT images loaded via FreeTimeParser/FreeTimeDataset.

uv run trigs-train-colmap \
    --data-dir /path/to/colmap_data \
    --roma-input-dir /path/to/per_frame_npys \
    --output-dir /path/to/output \
    --start-frame 0 --end-frame 61 --keyframe-step 5

Or:

bash scripts/run_colmap.sh \
    /path/to/per_frame_npys /path/to/colmap_data /path/to/output \
    0 61 5 0

3. SelfCap (4DV) mode

Input: a SelfCap <scene>-release directory with videos/, optimized/, and per-frame pcds/. Dataset/project page: Long Volumetric Video (SelfCap Dataset).

First preprocess a frame window (extract jpgs + copy matching point clouds + write preprocess_meta.json):

uv run python scripts/preprocess_selfcap.py \
    --scene-dir /path/to/corgi-release \
    --output-dir /path/to/corgi_prepped \
    --start-frame 0 --end-frame 1000 \
    --factor 1 --fps 60 --workers 24

Then train TriGS on the preprocessed directory:

uv run trigs-train-selfcap \
    --data-dir /path/to/corgi_prepped \
    --output-dir /path/to/output \
    --dense-subdir dense_pcds \
    --sparse-subdir pcds \
    --config.keyframe-stride 3 \
    --config.n-steps 30000

Notes:

  • preprocess_selfcap.py auto-detects pcd absolute-frame origin from the minimum filename in pcds/; override with --pcd-origin when needed.
  • By default, preprocess_selfcap.py rectifies images and rewrites optimized/intri.yml to a pinhole model baked to cropped image space. Pass --no-rectify-pinhole to keep original distorted images/calibration.
  • Pass --no-sync to trigs-train-selfcap to disable optimized/sync.json per-camera timing correction.

Key Concepts

The canonical set is fixed

Unlike methods that densify/prune during training, TriGS keeps the canonical primitive count constant. Capacity that ends up unused (σ(αᵢ) < τ_α) is recycled by motion-guided relocation — cloning active primitives into inactive slots, weighted by four normalized cues:

score_i = p^(α) + p^(dν) + p^(2D) + p^(t)

where the four terms are opacity, motion × visibility, accumulated 2D projected-means gradient, and accumulated ∂L/∂μₜ gradient. Implemented as a gsplat Strategy subclass so it plugs into any gsplat training loop.

Time is in physical units (seconds)

All temporal quantities (μₜ, σₜ, ν, ω, Bézier controls, loss weights) are stored in seconds — not normalized [0, 1]. This makes paper hyperparameters (λ_motion = 1e-4, λ_rigid = 1, λ_c = 50, …) transfer directly across sequences of different length and FPS.

Two-phase training

  1. Motion-only phase (first unfreeze_temporal_step iters): opacities, μₜ, log σₜ are frozen. Forces the model to find a motion decomposition that fits all frames before it can cheat by shrinking temporal windows.
  2. Full training phase: temporal params unfreeze; Adam moments are reset for a clean restart.

Initialization is shared between the two input modes

Both the PLY and COLMAP paths end up running the same three-step init on their per-keyframe Gaussians:

  1. Centered finite-difference velocity from ±1 frame neighbors.
  2. Radius-ball anchor centroids (anchor ≠ mean, so rotation actually displaces the primitive).
  3. ω = (r × v) / ‖r‖² with residual ν = v − ω × r — so the translation component only carries what rotation can't explain.

Development

# Install with dev dependencies
uv sync --preview-features extra-build-dependencies --group dev

# Run tests (SE(3) math + trainer smoke tests)
uv run pytest

# Type check / lint
uv run mypy src/trigs/ --ignore-missing-imports
uv run ruff check src/trigs/

Citation

If you use this code or build on the TRiGS method, please cite the original paper:

@article{yeom2026trigs,
  title   = {TRiGS: Temporal Rigid-Body Motion for Scalable 4D Gaussian Splatting},
  author  = {Yeom, Suwoong and Nam, Joonsik and Choi, Seunggyu and
             Lee, Lucas Yunkyu and Kim, Sangmin and Park, Jaesik and
             Kim, Joonsoo and Yun, Kugjin and Kong, Kyeongbo and Kang, Sukju},
  journal = {arXiv preprint},
  year    = {2026},
  url     = {https://wwwjjn.github.io/TRiGS-project_page/}
}

Please also cite the projects this implementation builds on:

  • 3D Gaussian Splatting (Kerbl et al., SIGGRAPH 2023)
  • FreeTimeGS (Wang et al., CVPR 2025) — the translation-only baseline TriGS generalizes, and the dataset-loading lineage of this code.
  • LongVolcap / SelfCap Dataset (Xu et al., SIGGRAPH Asia 2024): https://zju3dv.github.io/longvolcap/
  • gsplat (nerfstudio-project) — CUDA rasterization backend.

License

AGPL-3.0. See LICENSE.

This project is an independent open-source re-implementation of the TRiGS paper. It is not affiliated with, nor endorsed by, the paper's authors or institutions.

About

No description, website, or topics provided.

Resources

Contributing

Stars

21 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages