ffgs 0.1.0.dev0__tar.gz → 0.1.0.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/PKG-INFO +29 -11
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/README.md +26 -10
- ffgs-0.1.0.dev1/docs/models/c3g.md +89 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/docs/models/index.md +3 -2
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/pyproject.toml +8 -4
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/__init__.py +1 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/processing.py +1 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/LICENSE +650 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/__init__.py +51 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/gmae.py +89 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/modeling.py +103 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/processing.py +95 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/vggt/__init__.py +0 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/vggt/aggregator.py +355 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/vggt/heads/__init__.py +0 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/vggt/heads/dpt_head.py +599 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/vggt/heads/head_act.py +130 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/vggt/heads/utils.py +127 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/vggt/layers/__init__.py +23 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/vggt/layers/attention.py +86 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/vggt/layers/block.py +150 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/vggt/layers/drop_path.py +39 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/vggt/layers/layer_scale.py +26 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/vggt/layers/mlp.py +43 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/vggt/layers/patch_embed.py +100 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/vggt/layers/rope.py +209 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/vggt/layers/swiglu_ffn.py +74 -0
- ffgs-0.1.0.dev1/src/ffgs/models/c3g/vggt/layers/vision_transformer.py +448 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/tokengs/__init__.py +1 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/tokengs/processing.py +5 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/pipeline.py +9 -9
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/processor.py +19 -6
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/registry.py +3 -0
- ffgs-0.1.0.dev1/src/ffgs/zoo/c3g/default.json +21 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/_dummy.py +15 -0
- ffgs-0.1.0.dev1/tests/fixtures/c3g_checkpoint.json +8220 -0
- ffgs-0.1.0.dev1/tests/fixtures/c3g_reference.safetensors +0 -0
- ffgs-0.1.0.dev1/tests/fixtures/make_c3g_reference.py +286 -0
- ffgs-0.1.0.dev1/tests/test_c3g.py +263 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/test_pipeline.py +17 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/uv.lock +12 -1
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/.gitignore +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/LICENSE +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/docs/models/anysplat.md +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/docs/models/tokengs.md +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/examples/infer_nerfstudio_scene.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/examples/infer_pose_free.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/__init__.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/dynamic.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/geometry.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/image.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/__init__.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/LICENSE +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/encoder.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/evaluation.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/gaussian_adapter.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/gs_head.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/modeling.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/__init__.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/heads/__init__.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/heads/camera_head.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/heads/dpt_head.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/heads/head_act.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/heads/utils.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/layers/__init__.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/layers/attention.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/layers/block.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/layers/drop_path.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/layers/layer_scale.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/layers/mlp.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/layers/patch_embed.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/layers/rope.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/layers/swiglu_ffn.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/layers/vision_transformer.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/models/__init__.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/models/aggregator.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/utils/__init__.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/utils/geometry.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/utils/pose_enc.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/anysplat/vggt/utils/rotation.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/tokengs/activations.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/tokengs/attention.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/tokengs/enc_dec.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/models/tokengs/modeling.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/render.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/types.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/zoo/__init__.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/zoo/anysplat/default.json +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/zoo/tokengs/dl3dv-2v.json +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/zoo/tokengs/dl3dv-4v.json +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/zoo/tokengs/dl3dv-6v.json +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/zoo/tokengs/dl3dv-base.json +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/zoo/tokengs/dl3dv-latent-2v-lpips.json +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/zoo/tokengs/dl3dv-latent-2v-ssim.json +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/zoo/tokengs/dl3dv-latent-4v-lpips.json +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/zoo/tokengs/dl3dv-latent-4v-ssim.json +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/zoo/tokengs/dl3dv-latent-6v-lpips.json +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/src/ffgs/zoo/tokengs/dl3dv-latent-6v-ssim.json +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/_empty.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/_numeric.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/conftest.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/fixtures/anysplat_checkpoint.json +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/fixtures/anysplat_reference.safetensors +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/fixtures/make_anysplat_reference.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/fixtures/make_tokengs_reference.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/fixtures/reference.safetensors +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/fixtures/tokengs_checkpoints.json +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/fixtures/tokengs_reference.safetensors +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/test_anysplat.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/test_boundary.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/test_data_path.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/test_examples.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/test_geometry.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/test_image.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/test_pose_free.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/test_render.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/test_third_party.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/test_tokengs.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/test_types.py +0 -0
- {ffgs-0.1.0.dev0 → ffgs-0.1.0.dev1}/tests/test_zoo.py +0 -0
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ffgs
|
|
3
|
-
Version: 0.1.0.
|
|
3
|
+
Version: 0.1.0.dev1
|
|
4
4
|
Summary: Standard inference for feed-forward 3D Gaussian Splatting models
|
|
5
5
|
Project-URL: Repository, https://github.com/libgs/ffgs
|
|
6
6
|
Author: libgs contributors
|
|
7
7
|
License-Expression: Apache-2.0 AND MIT AND CC-BY-NC-4.0 AND CC-BY-NC-SA-4.0
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
License-File: src/ffgs/models/anysplat/LICENSE
|
|
10
|
+
License-File: src/ffgs/models/c3g/LICENSE
|
|
10
11
|
Classifier: Programming Language :: Python :: 3
|
|
11
12
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
12
13
|
Requires-Python: >=3.10
|
|
14
|
+
Requires-Dist: einops>=0.8.2
|
|
13
15
|
Requires-Dist: huggingface-hub>=0.23
|
|
14
16
|
Requires-Dist: numpy
|
|
15
17
|
Requires-Dist: pillow
|
|
@@ -62,12 +64,13 @@ python examples/infer_nerfstudio_scene.py <scene> --model <family>/<variant> \
|
|
|
62
64
|
--images-dir <scene>/images_4 --out out
|
|
63
65
|
```
|
|
64
66
|
|
|
65
|
-
Models that
|
|
66
|
-
frame then: the Gaussians and the
|
|
67
|
-
|
|
67
|
+
Models that do not need poses (`camera_input` "optional" or "none") also run on
|
|
68
|
+
images alone. There is no world frame then: the Gaussians, and the input cameras
|
|
69
|
+
for models that predict them, are in the model's frame, and renders take cameras
|
|
70
|
+
in that frame (see [Conventions](#conventions)):
|
|
68
71
|
|
|
69
72
|
```python
|
|
70
|
-
pipe = GSPipeline.from_pretrained("<family>/<variant>", device="cuda") #
|
|
73
|
+
pipe = GSPipeline.from_pretrained("<family>/<variant>", device="cuda") # e.g. anysplat/default
|
|
71
74
|
out = pipe(Views(images)) # no intrinsics, no c2w: out.frame is None
|
|
72
75
|
out.gaussians.save_ply("scene.ply") # in the model's frame
|
|
73
76
|
renders = pipe.render(out, out.cameras)["images"] # the inputs, at the predicted cameras
|
|
@@ -96,7 +99,7 @@ Check the license of a model before use: some are for non-commercial use only.
|
|
|
96
99
|
| | |
|
|
97
100
|
|---|---|
|
|
98
101
|
| images | float in [0, 1] (uint8 accepted), `[V, 3, H, W]` or `[B, V, 3, H, W]`, any resolution |
|
|
99
|
-
| c2w | camera-to-world, OpenCV axes (x right, y down, z forward); optional
|
|
102
|
+
| c2w | camera-to-world, OpenCV axes (x right, y down, z forward); optional unless the model requires them (`camera_input="required"`) |
|
|
100
103
|
| intrinsics | pixel K for the image size given, or normalised K with `normalized_intrinsics=True`; optional like c2w |
|
|
101
104
|
| output | `GSOutput`: `Gaussians` (means, scales, wxyz quats, opacities, SH or RGB colours) in the model's frame, that frame (or `None`), and the input cameras the model predicted (or `None`) |
|
|
102
105
|
|
|
@@ -220,16 +223,22 @@ pipe = GSPipeline.from_pretrained("owner/family-repo", subfolder="variant",
|
|
|
220
223
|
## Adding a model
|
|
221
224
|
|
|
222
225
|
A model is a model class with `PyTorchModelHubMixin`, a `Processor` subclass
|
|
223
|
-
(`preprocess`, returning the model input and its `ModelFrame`, or `None`
|
|
224
|
-
model predicts its own cameras; `postprocess`, to model-frame `Gaussians`;
|
|
226
|
+
(`preprocess`, returning the model input and its `ModelFrame`, or `None` without
|
|
227
|
+
input poses or when the model predicts its own cameras; `postprocess`, to model-frame `Gaussians`;
|
|
225
228
|
`render_planes`, near / far in world units, or in model units when the frame is
|
|
226
229
|
`None`; for models that predict cameras, `predicted_cameras`, from the model output
|
|
227
230
|
to model-frame `Cameras`, and `output_frame`, whose default fits the frame to the
|
|
228
231
|
given c2w as above; optionally `background` and `render_settings`, the model's
|
|
229
232
|
rasteriser settings such as `radius_clip`, `rasterize_mode` or `clamp`, which
|
|
230
|
-
`pipe.render` uses unless the caller passes them
|
|
231
|
-
|
|
232
|
-
`
|
|
233
|
+
`pipe.render` uses unless the caller passes them; `camera_input`, whether the
|
|
234
|
+
model is conditioned on the input cameras: `"required"` (the default, always),
|
|
235
|
+
`"optional"` (when given) or `"none"` (never);
|
|
236
|
+
for models trained on poses of a fixed scale, a `camera_spread` in the config, the
|
|
237
|
+
mean distance of the input cameras to the first one in the training data) and a
|
|
238
|
+
`ModelSpec(model_type, model_cls, processor_cls, display_name=...)`. Applications
|
|
239
|
+
read `camera_input`, `camera_spread` and `display_name` to handle a new
|
|
240
|
+
model without changes. `from_pretrained` resolves the `model_type` in
|
|
241
|
+
`config.json` in this order:
|
|
233
242
|
|
|
234
243
|
1. **In your code** — `ffgs.register_model(spec)` before loading (built-in models
|
|
235
244
|
are registered the same way, on first use).
|
|
@@ -303,6 +312,13 @@ torch first (see `.github/workflows/ci.yml`). `tests/fixtures/reference.safetens
|
|
|
303
312
|
holds frozen reference outputs of the default data path; change it only together
|
|
304
313
|
with a deliberate change to that path.
|
|
305
314
|
|
|
315
|
+
To release, set `version` in `pyproject.toml` (then `uv lock`), commit it as
|
|
316
|
+
`chore: release X.Y.Z`, merge it into main and push the tag `vX.Y.Z` on that commit.
|
|
317
|
+
`.github/workflows/release.yml` then runs CI, publishes to PyPI and creates the
|
|
318
|
+
[GitHub release](https://github.com/libgs/ffgs/releases), whose notes are generated
|
|
319
|
+
by [git-cliff](https://git-cliff.org/) from the conventional commits since the
|
|
320
|
+
previous tag (`cliff.toml`; preview with `uvx git-cliff --unreleased`).
|
|
321
|
+
|
|
306
322
|
## License
|
|
307
323
|
|
|
308
324
|
**The ffgs package as distributed is not entirely Apache-2.0: it includes code for
|
|
@@ -314,6 +330,8 @@ non-commercial use only.** Its license expression is
|
|
|
314
330
|
- vendored model code under `src/ffgs/models/<family>/` keeps its upstream license.
|
|
315
331
|
`src/ffgs/models/anysplat/` mixes MIT, Apache-2.0 and non-commercial
|
|
316
332
|
(CC-BY-NC-4.0, CC-BY-NC-SA-4.0) files; its `LICENSE` lists the license per file;
|
|
333
|
+
`src/ffgs/models/c3g/` mixes MIT, Apache-2.0 and VGGT files kept with the
|
|
334
|
+
CC-BY-NC-4.0 text; its `LICENSE` describes which is which;
|
|
317
335
|
- model weights are not part of the package and carry their own licenses.
|
|
318
336
|
|
|
319
337
|
The code and weight licenses of each model are listed in
|
|
@@ -40,12 +40,13 @@ python examples/infer_nerfstudio_scene.py <scene> --model <family>/<variant> \
|
|
|
40
40
|
--images-dir <scene>/images_4 --out out
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
Models that
|
|
44
|
-
frame then: the Gaussians and the
|
|
45
|
-
|
|
43
|
+
Models that do not need poses (`camera_input` "optional" or "none") also run on
|
|
44
|
+
images alone. There is no world frame then: the Gaussians, and the input cameras
|
|
45
|
+
for models that predict them, are in the model's frame, and renders take cameras
|
|
46
|
+
in that frame (see [Conventions](#conventions)):
|
|
46
47
|
|
|
47
48
|
```python
|
|
48
|
-
pipe = GSPipeline.from_pretrained("<family>/<variant>", device="cuda") #
|
|
49
|
+
pipe = GSPipeline.from_pretrained("<family>/<variant>", device="cuda") # e.g. anysplat/default
|
|
49
50
|
out = pipe(Views(images)) # no intrinsics, no c2w: out.frame is None
|
|
50
51
|
out.gaussians.save_ply("scene.ply") # in the model's frame
|
|
51
52
|
renders = pipe.render(out, out.cameras)["images"] # the inputs, at the predicted cameras
|
|
@@ -74,7 +75,7 @@ Check the license of a model before use: some are for non-commercial use only.
|
|
|
74
75
|
| | |
|
|
75
76
|
|---|---|
|
|
76
77
|
| images | float in [0, 1] (uint8 accepted), `[V, 3, H, W]` or `[B, V, 3, H, W]`, any resolution |
|
|
77
|
-
| c2w | camera-to-world, OpenCV axes (x right, y down, z forward); optional
|
|
78
|
+
| c2w | camera-to-world, OpenCV axes (x right, y down, z forward); optional unless the model requires them (`camera_input="required"`) |
|
|
78
79
|
| intrinsics | pixel K for the image size given, or normalised K with `normalized_intrinsics=True`; optional like c2w |
|
|
79
80
|
| output | `GSOutput`: `Gaussians` (means, scales, wxyz quats, opacities, SH or RGB colours) in the model's frame, that frame (or `None`), and the input cameras the model predicted (or `None`) |
|
|
80
81
|
|
|
@@ -198,16 +199,22 @@ pipe = GSPipeline.from_pretrained("owner/family-repo", subfolder="variant",
|
|
|
198
199
|
## Adding a model
|
|
199
200
|
|
|
200
201
|
A model is a model class with `PyTorchModelHubMixin`, a `Processor` subclass
|
|
201
|
-
(`preprocess`, returning the model input and its `ModelFrame`, or `None`
|
|
202
|
-
model predicts its own cameras; `postprocess`, to model-frame `Gaussians`;
|
|
202
|
+
(`preprocess`, returning the model input and its `ModelFrame`, or `None` without
|
|
203
|
+
input poses or when the model predicts its own cameras; `postprocess`, to model-frame `Gaussians`;
|
|
203
204
|
`render_planes`, near / far in world units, or in model units when the frame is
|
|
204
205
|
`None`; for models that predict cameras, `predicted_cameras`, from the model output
|
|
205
206
|
to model-frame `Cameras`, and `output_frame`, whose default fits the frame to the
|
|
206
207
|
given c2w as above; optionally `background` and `render_settings`, the model's
|
|
207
208
|
rasteriser settings such as `radius_clip`, `rasterize_mode` or `clamp`, which
|
|
208
|
-
`pipe.render` uses unless the caller passes them
|
|
209
|
-
|
|
210
|
-
`
|
|
209
|
+
`pipe.render` uses unless the caller passes them; `camera_input`, whether the
|
|
210
|
+
model is conditioned on the input cameras: `"required"` (the default, always),
|
|
211
|
+
`"optional"` (when given) or `"none"` (never);
|
|
212
|
+
for models trained on poses of a fixed scale, a `camera_spread` in the config, the
|
|
213
|
+
mean distance of the input cameras to the first one in the training data) and a
|
|
214
|
+
`ModelSpec(model_type, model_cls, processor_cls, display_name=...)`. Applications
|
|
215
|
+
read `camera_input`, `camera_spread` and `display_name` to handle a new
|
|
216
|
+
model without changes. `from_pretrained` resolves the `model_type` in
|
|
217
|
+
`config.json` in this order:
|
|
211
218
|
|
|
212
219
|
1. **In your code** — `ffgs.register_model(spec)` before loading (built-in models
|
|
213
220
|
are registered the same way, on first use).
|
|
@@ -281,6 +288,13 @@ torch first (see `.github/workflows/ci.yml`). `tests/fixtures/reference.safetens
|
|
|
281
288
|
holds frozen reference outputs of the default data path; change it only together
|
|
282
289
|
with a deliberate change to that path.
|
|
283
290
|
|
|
291
|
+
To release, set `version` in `pyproject.toml` (then `uv lock`), commit it as
|
|
292
|
+
`chore: release X.Y.Z`, merge it into main and push the tag `vX.Y.Z` on that commit.
|
|
293
|
+
`.github/workflows/release.yml` then runs CI, publishes to PyPI and creates the
|
|
294
|
+
[GitHub release](https://github.com/libgs/ffgs/releases), whose notes are generated
|
|
295
|
+
by [git-cliff](https://git-cliff.org/) from the conventional commits since the
|
|
296
|
+
previous tag (`cliff.toml`; preview with `uvx git-cliff --unreleased`).
|
|
297
|
+
|
|
284
298
|
## License
|
|
285
299
|
|
|
286
300
|
**The ffgs package as distributed is not entirely Apache-2.0: it includes code for
|
|
@@ -292,6 +306,8 @@ non-commercial use only.** Its license expression is
|
|
|
292
306
|
- vendored model code under `src/ffgs/models/<family>/` keeps its upstream license.
|
|
293
307
|
`src/ffgs/models/anysplat/` mixes MIT, Apache-2.0 and non-commercial
|
|
294
308
|
(CC-BY-NC-4.0, CC-BY-NC-SA-4.0) files; its `LICENSE` lists the license per file;
|
|
309
|
+
`src/ffgs/models/c3g/` mixes MIT, Apache-2.0 and VGGT files kept with the
|
|
310
|
+
CC-BY-NC-4.0 text; its `LICENSE` describes which is which;
|
|
295
311
|
- model weights are not part of the package and carry their own licenses.
|
|
296
312
|
|
|
297
313
|
The code and weight licenses of each model are listed in
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# C3G
|
|
2
|
+
|
|
3
|
+
C3G ([paper](https://arxiv.org/abs/2512.04021),
|
|
4
|
+
[code](https://github.com/cvlab-kaist/C3G)) predicts a fixed budget of 2048
|
|
5
|
+
Gaussians from unposed images: learnable query tokens decode the features of a
|
|
6
|
+
VGGT aggregator. It predicts no cameras, and poses never enter the model: given
|
|
7
|
+
c2w only fix the output frame (see [Conventions](../../README.md#conventions)).
|
|
8
|
+
|
|
9
|
+
## Licenses
|
|
10
|
+
|
|
11
|
+
The code is vendored from the official code (`src/ffgs/models/c3g/`, Gaussian
|
|
12
|
+
inference only), each file under its own license: MIT (C3G), Apache-2.0 (DINOv2
|
|
13
|
+
layers) and the VGGT files with upstream's root-license pointer, kept with the
|
|
14
|
+
CC BY-NC 4.0 text. **The C3G model directory as a whole is for non-commercial
|
|
15
|
+
use only**; see its [`LICENSE`](../../src/ffgs/models/c3g/LICENSE). The authors
|
|
16
|
+
declare no license for the weights; they are fine-tuned from VGGT-1B, whose
|
|
17
|
+
weights are CC BY-NC 4.0 (non-commercial). The zoo downloads them from the
|
|
18
|
+
authors' repository; ffgs does not redistribute them.
|
|
19
|
+
|
|
20
|
+
## Zoo entries
|
|
21
|
+
|
|
22
|
+
| zoo name | model | input views | input size | weights |
|
|
23
|
+
|---|---|---|---|---|
|
|
24
|
+
| `c3g/default` | the released multiview Gaussian decoder: 2048 Gaussians, SH degree 0 | 2 or more | 224×224 | [honggyuAn/C3G](https://huggingface.co/honggyuAn/C3G) (`gaussian_decoder_multiview.ckpt`) |
|
|
25
|
+
|
|
26
|
+
The checkpoint is the authors' Lightning checkpoint (4.6 GB, with the frozen
|
|
27
|
+
DPT head it was trained with); the zoo pins its revision and SHA-256 and loads
|
|
28
|
+
the encoder strictly. The 2-view checkpoint and the feature decoders (LSeg,
|
|
29
|
+
DINOv2, DINOv3) are not in the zoo.
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
Without poses there is no world frame and no cameras: the Gaussians are in the
|
|
34
|
+
model's frame (the first camera at the origin, looking down +z), and renders take
|
|
35
|
+
cameras in that frame:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from ffgs import GSPipeline, Views
|
|
39
|
+
|
|
40
|
+
pipe = GSPipeline.from_pretrained("c3g/default", device="cuda")
|
|
41
|
+
out = pipe(Views(images)) # out.frame and out.cameras are None
|
|
42
|
+
out.gaussians.save_ply("scene.ply") # in the model's frame
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
With poses the output is placed in their frame, as upstream normalises the scene:
|
|
46
|
+
the first camera as the anchor and the distance between the first and last
|
|
47
|
+
cameras as the unit (it must lie in [1e-3, 1e10]):
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
out = pipe(Views(images, intrinsics, c2w)) # c2w do not change the prediction
|
|
51
|
+
out.to_world().gaussians.save_ply("scene.ply") # in the frame of c2w
|
|
52
|
+
renders = pipe.render(out, target_cameras)["images"] # cameras in that frame
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The processor resizes with PIL Lanczos and centre-crops to 224×224, as upstream
|
|
56
|
+
does. It renders as upstream: black background, no radius clip, no colour clamp.
|
|
57
|
+
Upstream's renderer is scale-invariant (it rescales near / far and the scene);
|
|
58
|
+
ffgs renders with gsplat at the nominal near / far 0.1 / 100, in the units of the
|
|
59
|
+
given c2w (of the model frame without poses).
|
|
60
|
+
|
|
61
|
+
## Results on RealEstate10K
|
|
62
|
+
|
|
63
|
+
`c3g/default` and the official code (cvlab-kaist/C3G 40b2cc9: the encoder and its
|
|
64
|
+
CUDA renderer, two context views, `test.align_pose=false`) on the same weights,
|
|
65
|
+
the first three scenes of the authors' RE10K evaluation index, each rendered at
|
|
66
|
+
its target cameras (normalised as upstream: first context camera, endpoint
|
|
67
|
+
baseline). This checks that ffgs runs the model as upstream does; it does not
|
|
68
|
+
reproduce the paper (no pose optimisation, three scenes).
|
|
69
|
+
|
|
70
|
+
| scene | official vs ffgs renders, PSNR | official vs GT, PSNR | ffgs vs GT, PSNR |
|
|
71
|
+
|---|---|---|---|
|
|
72
|
+
| `000c3ab189999a83` | 38.6 / 37.8 / 37.6 | 16.35 / 16.39 / 16.31 | 16.37 / 16.37 / 16.29 |
|
|
73
|
+
| `000db54a47bd43fe` | 37.0 / 35.5 / 35.2 | 21.45 / 20.76 / 20.24 | 21.49 / 20.83 / 20.31 |
|
|
74
|
+
| `0017ce4c6a39d122` | 40.0 / 40.2 / 40.0 | 19.20 / 16.82 / 16.61 | 19.15 / 16.81 / 16.60 |
|
|
75
|
+
|
|
76
|
+
- **Inputs**: the preprocessed context and target images are identical; the
|
|
77
|
+
normalised target cameras agree to 1e-7.
|
|
78
|
+
- **Prediction**: on the CPU, the official encoder and ffgs give bit-identical
|
|
79
|
+
aggregator tokens and Gaussian parameters. On CUDA neither is reproducible:
|
|
80
|
+
run twice on the same input, the official model itself moves about 175 of the
|
|
81
|
+
2048 Gaussians by more than 1e-2, as many as official vs ffgs. The cause is the
|
|
82
|
+
head's attention: in fp32, `scaled_dot_product_attention` runs PyTorch's
|
|
83
|
+
memory-efficient kernel (`fmha_cutlassF_f32_aligned_64x128_rf_sm80`, measured on
|
|
84
|
+
an RTX PRO 6000 Blackwell with torch 2.7.1), whose output varies from run to run,
|
|
85
|
+
also under `torch.use_deterministic_algorithms(True)`; the second decoder layer
|
|
86
|
+
amplifies it. Every other op of the head is bit-reproducible, and so is the whole
|
|
87
|
+
head with the math backend. ffgs keeps upstream's default backend.
|
|
88
|
+
- **Rendering**: the remaining render difference comes from that and from the
|
|
89
|
+
renderers (upstream's scale-invariant CUDA rasteriser vs gsplat).
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
The models ffgs ships. Each has a page with its source, licenses, zoo entries,
|
|
4
4
|
usage notes and results against the official code.
|
|
5
5
|
|
|
6
|
-
| model | zoo names | poses | input views | input size | license (code / weights) | page |
|
|
6
|
+
| model | zoo names | poses (`camera_input`) | input views | input size | license (code / weights) | page |
|
|
7
7
|
|---|---|---|---|---|---|---|
|
|
8
8
|
| TokenGS | `tokengs/*` | required | 2 / 4 / 6 | 256×256, 256×448 | Apache-2.0 / non-commercial | [tokengs.md](tokengs.md) |
|
|
9
|
-
| AnySplat | `anysplat/default` |
|
|
9
|
+
| AnySplat | `anysplat/default` | none (given poses only place the output) | any | 448×448 | non-commercial / non-commercial | [anysplat.md](anysplat.md) |
|
|
10
|
+
| C3G | `c3g/default` | none (given poses only place the output) | 2 or more | 224×224 | non-commercial / non-commercial (undeclared, VGGT-derived) | [c3g.md](c3g.md) |
|
|
10
11
|
|
|
11
12
|
`ffgs.zoo.list_models()` lists every zoo name; each zoo entry also records its
|
|
12
13
|
source, pinned revision and license.
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ffgs"
|
|
3
|
-
version = "0.1.0.
|
|
3
|
+
version = "0.1.0.dev1"
|
|
4
4
|
description = "Standard inference for feed-forward 3D Gaussian Splatting models"
|
|
5
5
|
readme = "README.md"
|
|
6
|
-
#
|
|
7
|
-
# its LICENSE.
|
|
6
|
+
# AnySplat and C3G include non-commercial components: see each model's LICENSE.
|
|
8
7
|
license = "Apache-2.0 AND MIT AND CC-BY-NC-4.0 AND CC-BY-NC-SA-4.0"
|
|
9
|
-
license-files = [
|
|
8
|
+
license-files = [
|
|
9
|
+
"LICENSE",
|
|
10
|
+
"src/ffgs/models/anysplat/LICENSE",
|
|
11
|
+
"src/ffgs/models/c3g/LICENSE",
|
|
12
|
+
]
|
|
10
13
|
authors = [{ name = "libgs contributors" }]
|
|
11
14
|
requires-python = ">=3.10"
|
|
12
15
|
classifiers = [
|
|
@@ -14,6 +17,7 @@ classifiers = [
|
|
|
14
17
|
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
15
18
|
]
|
|
16
19
|
dependencies = [
|
|
20
|
+
"einops>=0.8.2",
|
|
17
21
|
"huggingface_hub>=0.23",
|
|
18
22
|
"numpy",
|
|
19
23
|
"pillow",
|
|
@@ -77,6 +77,7 @@ class AnySplatProcessorConfig(ImageFitConfig):
|
|
|
77
77
|
|
|
78
78
|
class AnySplatProcessor(Processor):
|
|
79
79
|
config_cls = AnySplatProcessorConfig
|
|
80
|
+
camera_input = "none"
|
|
80
81
|
|
|
81
82
|
def preprocess(self, views: Views, device: torch.device) -> Prepared:
|
|
82
83
|
images = self.fit_views(views).images
|