Surflo: Consistent 3D Surface Flow Model with Global State
1
8 commits
4 linked in READMEs
updated Aug 25, 2026
Surflo turns a handful of unposed RGB views into a detailed 3D surface. It encodes a variable number of input views (2 to ~80) into a fixed-size latent and decodes that latent by flow matching into an arbitrary number of oriented surface points, which are then turned into a clean mesh.
| Surflo | v0 Release |
|---|---|
| Task | Feed-forward surface reconstruction from unposed multi-view images |
| Input | 2–80 RGB images of one static scene, no poses, no intrinsics |
| Output | Oriented 3D point cloud (position + normal) at arbitrary resolution, plus an optional mesh |
| Total parameters | 1.39 B |
| Trainable parameters | 128.8 M |
| Frozen backbone | 1.257 B (VGGT-1B) |
| Precision | fp32 weights; inference runs under bf16 autocast for the backbone |
The size of the latent does not depend on the number of input views, which is what lets one encode serve arbitrarily many decoded points.
Surflo needs the code repository — it is a
source install with compiled CUDA extensions, not a pip install package. See
the repository README for installation.
from surflo import Surflo, load_preset, save_ply, save_mesh
surflo = Surflo.from_checkpoint("checkpoint_200.pt", device="cuda")
# Encode any number of unposed images into the fixed-size global state.
scene = surflo.encode("/path/to/images", n_images=16)
print(scene.global_state.shape) # (128, 512) latent tokens
# Decode oriented surface points.
result = scene.reconstruct(
mode="guided", # or "plain"
config_block=load_preset("guided", "default"),
expert_cfg=load_preset("expert"),
num_query_points=100_000,
)
save_ply(result, "point_cloud.ply")
# Optional mesh.
mesh = scene.extract_mesh(result, mesh_cfg=load_preset("mesh"))
scene.color_mesh(mesh, result)
save_mesh(mesh, "mesh.ply")
Or from the command line:
python scripts/infer.py mode=guided guided=default \
ckpt=/path/to/checkpoint_200.pt \
source.image_folder=/path/to/images \
num_query_points=100000 \
output_dir=outputs/surflo
| Mode | What it does | Cost (H100, 16 views, 100 k points) |
|---|---|---|
plain | Pure flow-matching ODE. Deterministic, no rendering. | ~8 s, ~8.5 GiB |
guided=minimal | Adds rendering guidance, shortest schedule | ~16 s, ~14 GiB |
guided=default | Recommended quality/time trade-off | ~47 s, ~14 GiB |
guided=long | Longest schedule; does not improve on default | ~95 s, ~14 GiB |
N ∈ [2, 16] drawn per iteration
(curriculum), 4×H100, bf16 AMP, AdamW with fvcore schedulers, EMA
(β = 0.9999). The reported checkpoint is epoch 200.requires_grad = False)
throughout; only surface_net (compressor + decoder + learned tokens) is
trained.Training the variable-view schedule is what makes the model robust across a wide range of input view counts at inference.
⚠️ This checkpoint cannot be released under MIT. Part of its parameters are the VGGT-1B backbone, distributed by Meta under CC BY-NC 4.0, and those weights are stored verbatim in the checkpoint. Redistributing them under a permissive license is not available to us. The checkpoint is therefore released under CC BY-NC 4.0 — non-commercial use only.
The Surflo code is released under the Gaussian-Splatting License (Inria/MPII), which likewise restricts use to non-commercial research and evaluation, because Surflo contains modified 3D Gaussian Splatting code.
If you want a permissively licensed artifact, publish the trained head
alone: surface_net is 128.8 M parameters (~9 % of the checkpoint) and is
entirely our own work, so it can be MIT. Users would then obtain VGGT-1B
themselves from the Hub. Note two
caveats: the system still depends on CC BY-NC weights at run time, so an MIT
tag on the head alone does not make commercial use possible; and the current
loader calls load_state_dict(..., strict=True), so it would need to accept a
partial state dict before a head-only checkpoint could be loaded.
@article{guedon2026surflo,
title = {Surflo: Consistent 3D Surface Flow Model with Global State},
author = {Gu{\'e}don, Antoine and Nakamura, Shu and Dufour, Nicolas
and Lei, Jiahui and Nishino, Ko and Kanazawa, Angjoo},
journal = {arXiv preprint arXiv:2606.13644},
year = {2026}
}
Surflo builds on VGGT (Meta),
3D Gaussian Splatting
(Inria/MPII), and Depth-Anything-3
(ByteDance) for the optional monodepth expert. See
THIRD_PARTY_NOTICES.md
in the code repository for the full inventory.
Surflo: Consistent 3D Surface Flow Model with Global State
1
8 commits
4 linked in READMEs
updated Aug 25, 2026
Surflo turns a handful of unposed RGB views into a detailed 3D surface. It encodes a variable number of input views (2 to ~80) into a fixed-size latent and decodes that latent by flow matching into an arbitrary number of oriented surface points, which are then turned into a clean mesh.
| Surflo | v0 Release |
|---|---|
| Task | Feed-forward surface reconstruction from unposed multi-view images |
| Input | 2–80 RGB images of one static scene, no poses, no intrinsics |
| Output | Oriented 3D point cloud (position + normal) at arbitrary resolution, plus an optional mesh |
| Total parameters | 1.39 B |
| Trainable parameters | 128.8 M |
| Frozen backbone | 1.257 B (VGGT-1B) |
| Precision | fp32 weights; inference runs under bf16 autocast for the backbone |
The size of the latent does not depend on the number of input views, which is what lets one encode serve arbitrarily many decoded points.
Surflo needs the code repository — it is a
source install with compiled CUDA extensions, not a pip install package. See
the repository README for installation.
from surflo import Surflo, load_preset, save_ply, save_mesh
surflo = Surflo.from_checkpoint("checkpoint_200.pt", device="cuda")
# Encode any number of unposed images into the fixed-size global state.
scene = surflo.encode("/path/to/images", n_images=16)
print(scene.global_state.shape) # (128, 512) latent tokens
# Decode oriented surface points.
result = scene.reconstruct(
mode="guided", # or "plain"
config_block=load_preset("guided", "default"),
expert_cfg=load_preset("expert"),
num_query_points=100_000,
)
save_ply(result, "point_cloud.ply")
# Optional mesh.
mesh = scene.extract_mesh(result, mesh_cfg=load_preset("mesh"))
scene.color_mesh(mesh, result)
save_mesh(mesh, "mesh.ply")
Or from the command line:
python scripts/infer.py mode=guided guided=default \
ckpt=/path/to/checkpoint_200.pt \
source.image_folder=/path/to/images \
num_query_points=100000 \
output_dir=outputs/surflo
| Mode | What it does | Cost (H100, 16 views, 100 k points) |
|---|---|---|
plain | Pure flow-matching ODE. Deterministic, no rendering. | ~8 s, ~8.5 GiB |
guided=minimal | Adds rendering guidance, shortest schedule | ~16 s, ~14 GiB |
guided=default | Recommended quality/time trade-off | ~47 s, ~14 GiB |
guided=long | Longest schedule; does not improve on default | ~95 s, ~14 GiB |
N ∈ [2, 16] drawn per iteration
(curriculum), 4×H100, bf16 AMP, AdamW with fvcore schedulers, EMA
(β = 0.9999). The reported checkpoint is epoch 200.requires_grad = False)
throughout; only surface_net (compressor + decoder + learned tokens) is
trained.Training the variable-view schedule is what makes the model robust across a wide range of input view counts at inference.
⚠️ This checkpoint cannot be released under MIT. Part of its parameters are the VGGT-1B backbone, distributed by Meta under CC BY-NC 4.0, and those weights are stored verbatim in the checkpoint. Redistributing them under a permissive license is not available to us. The checkpoint is therefore released under CC BY-NC 4.0 — non-commercial use only.
The Surflo code is released under the Gaussian-Splatting License (Inria/MPII), which likewise restricts use to non-commercial research and evaluation, because Surflo contains modified 3D Gaussian Splatting code.
If you want a permissively licensed artifact, publish the trained head
alone: surface_net is 128.8 M parameters (~9 % of the checkpoint) and is
entirely our own work, so it can be MIT. Users would then obtain VGGT-1B
themselves from the Hub. Note two
caveats: the system still depends on CC BY-NC weights at run time, so an MIT
tag on the head alone does not make commercial use possible; and the current
loader calls load_state_dict(..., strict=True), so it would need to accept a
partial state dict before a head-only checkpoint could be loaded.
@article{guedon2026surflo,
title = {Surflo: Consistent 3D Surface Flow Model with Global State},
author = {Gu{\'e}don, Antoine and Nakamura, Shu and Dufour, Nicolas
and Lei, Jiahui and Nishino, Ko and Kanazawa, Angjoo},
journal = {arXiv preprint arXiv:2606.13644},
year = {2026}
}
Surflo builds on VGGT (Meta),
3D Gaussian Splatting
(Inria/MPII), and Depth-Anything-3
(ByteDance) for the optional monodepth expert. See
THIRD_PARTY_NOTICES.md
in the code repository for the full inventory.