# Surflo: unposed RGB views to a 3D surface mesh, and what it costs to run

> Surflo is the official inference and evaluation release for a flow-matching model that turns a variable number of unposed RGB views into oriented surface points and a mesh. It installs from a source checkout with compiled CUDA extensions, not from PyPI, and the licence is non-commercial.

**Anttwo/Surflo** — Official implementation of Surflo: Consistent 3D Surface Flow Model with Global State.

- Repository: https://github.com/Anttwo/Surflo
- Website: https://anttwo.github.io/surflo/
- Stars: 451 · Forks: 15
- Language: Python
- License: NOASSERTION
- Published: 2026-09-17 · Updated: 2026-09-17 · Language: en
- Canonical page: https://hysenlabs.com/projects/anttwo-surflo

## The reconstruction setting Surflo is built for

Most multi-view reconstruction pipelines assume you know where the cameras were. Surflo does not. The README describes the input as a variable number N of unposed RGB views, and says N can be 2, 10, 30 or even 80. The output is oriented surface points decoded into a mesh, not a radiance field and not a set of Gaussians.

The intended user is a researcher or evaluation engineer working on surface reconstruction, who has a handful of photographs of one scene and no camera calibration. The README positions the model against two families: pointmap-feed-forward models, which it claims to exceed in quality, and optimization-based methods such as Gaussian Wrapping, which it says need hundreds of views. That second comparison is the interesting one. Surflo's selling point is not just accuracy but the view budget: the same scene from a few dozen images rather than a few hundred.

One constraint shapes everything downstream. The README states the model is trained on landscape images only, meaning width greater than height, and that at inference portrait images are automatically rotated 90 degrees to landscape, with a warning logged for each. This applies to every inference path: CLI, demo and Python API. The consequence is stated plainly in the README: because the world frame is the first camera's frame, if the first view was portrait, the reconstruction and any saved point cloud or mesh come out in that rotated frame. Plan for that in your viewer before you conclude the model failed.

## Global state, flow matching, and why the view count does not change the latent size

The architecture name is the description. Surflo encodes the scene into a fixed-size latent and decodes it via flow matching into an arbitrary number of oriented surface points. That fixed-size latent is the global state: whatever N is, the scene representation has the same shape, which is what lets the same checkpoint handle 2 views and 80 views without a separate configuration.

Flow matching is the decoder side. Rather than regressing points directly, the model transports a sample to the surface distribution, and the number of points produced is a decoding parameter rather than a property of the encoder. The practical effect is that mesh density is something you choose at reconstruction time, not something baked into training.

The pipeline image in the repository shows the stages the demo wraps: load images, encode, reconstruct points, convert to mesh. Each stage is a separate step in the Gradio interface, which matters for debugging. If the point cloud looks right and the mesh does not, the failure is in conversion, not in the model. The README does not document a rollback path or an intermediate checkpoint format for the encode step, so treat the reconstruction as a single pass you rerun rather than a pipeline you resume.

## Installing Surflo from a source checkout

There is no PyPI release. The pyproject.toml comment states this directly: Surflo needs compiled CUDA extensions built against the torch already in your environment, so it is always installed through one of the files in install/. The README offers two routes, conda and uv, and recommends conda because it builds a self-contained environment with its own nvcc and compiler, roughly 55 MB, so nothing on the host has to match. The uv route assumes your system CUDA is 11.8, 12.1 or 12.4.

Start by cloning with submodules. The README uses an SSH URL:

```bash
git clone git@github.com:Anttwo/Surflo.git --recursive
```

The --recursive flag matters. The repository has a submodules/ directory, and the README does not describe what happens if you clone without it.

The conda path, swapping cu118 for cu121 or cu124 to match your family:

```bash
conda env create -f install/environment-cu118.yml
conda activate surflo-cu118
pip install -e ".[demo,texture,train]"
bash install/activate_cuda.sh && conda deactivate && conda activate surflo-cu118
bash install/build_extensions.sh --all
python install/verify_install.py --check-isolation
```

The activate_cuda.sh step writes the CUDA hook for the environment, and the deactivate and reactivate around it is deliberate, not a typo. The README notes you might need to set TORCH_CUDA_ARCH_LIST before building extensions. The uv route is shorter but needs two OS packages for textured mesh export, libegl1-mesa-dev and libgl1-mesa-dev on Debian or Ubuntu, which CUDA does not provide and pip cannot install.

Weights come from Hugging Face, and hf ships with huggingface_hub, already a dependency:

```bash
hf download AntoineGuedon/Surflo-v0 surflo_v0.pt --local-dir checkpoints/
```

On older versions of the package the command is huggingface-cli download. You can also let Python fetch and cache the file:

```python
from huggingface_hub import hf_hub_download
from surflo import Surflo

ckpt = hf_hub_download("AntoineGuedon/Surflo-v0", "surflo_v0.pt")
surflo = Surflo.from_checkpoint(ckpt, device="cuda")
```

For a first real reconstruction, the repository ships a sample. The README points to media/sample/, which holds 16 views of the garden scene from mipnerf360, so you have something to run immediately without hunting for data:

```bash
python examples/gradio_demo.py --ckpt /path/to/surflo_v0.pt
```

The interface takes images, a mode and preset, then reconstructs. The README lists guided mode with presets minimal, short, default, default_highres and long, or plain to deactivate guidance. The flags are --host, --port, --share for a public link, and --dev to load images from a server-side directory, which is the one you want over SSH. There is also examples/quickstart.py in the repository, though the README excerpt does not document its arguments.

## Where Surflo is the wrong tool

The licence is the first hard boundary. The pyproject.toml comment says the Gaussian-Splatting License applies, non-commercial, research and evaluation use only, and points to THIRD_PARTY_NOTICES.md for why it applies to Surflo as a whole. The package metadata also uses license = { file = "LICENSE.md" } rather than an SPDX identifier, which is why the repository reports NOASSERTION. If you are shipping a product, this is not the dependency you are looking for, and no amount of engineering work changes that.

The second boundary is the install. Compiled CUDA extensions tied to your torch build mean there is no wheel, no Docker image mentioned in the README, and no CPU path described anywhere. A team that wants to add surface reconstruction to a Python service with a pip install surflo line has the wrong shape of project in front of them.

The third is the portrait rotation. Automatic rotation to landscape is not a preprocessing suggestion you can disable per the README; the README states it applies to every inference path, and that training preprocessing is left untouched. If your captures are portrait and your downstream consumer expects the camera frame you provided, you will be reconciling coordinate frames yourself.

Finally, the README does not document a training recipe in the excerpt, only the training extra in the install line and a training/ directory in the repository layout. If your goal is to retrain on your own data, the release is presenting itself as inference and evaluation, which is what the pyproject description says.

## How Surflo differs from Gaussian-splatting and optimization pipelines

The obvious alternative for multi-view reconstruction is a 3D Gaussian Splatting pipeline, and the comparison is not close in kind. A Gaussian-splatting reconstruction optimizes per-scene parameters against the input views, so the artifact is a set of Gaussians fitted to that one scene, and the cost scales with the number of views you feed it. Surflo amortizes that: one checkpoint, one forward pass, a fixed-size latent, and points decoded from it. The README's own framing is that Surflo runs an order of magnitude faster than optimization-based methods like Gaussian Wrapping that require hundreds of views.

The trade is that Gaussian splatting gives you a renderable radiance field and Surflo gives you oriented surface points and a mesh. If your downstream task is novel-view synthesis, splatting is the direct answer and Surflo is not. If your downstream task is a watertight-ish surface, a splat is an intermediate you would have to convert anyway.

Against pointmap-feed-forward models the difference is the global state. A pointmap predictor emits per-view geometry that has to be fused, and fusion is where inconsistency between views shows up. Surflo's README claims it exceeds pointmap-feed-forward models in quality, which is a claim about that fusion step being unnecessary rather than about any single view. The README does not give numbers, and the arXiv paper is where you would check the comparison rather than the repository.

## Maintenance, dependencies and what the caps tell you

The last push to the repository was on 2026-08-25, which is recent, and the repository is not archived. There are no releases retrieved, so version 0.1.0 in pyproject.toml is the only version number visible.

The dependency list is unusually opinionated and worth reading before you commit. Every dependency carries an upper bound, and the pyproject comment explains why: two of the bounds were added after an unconstrained release broke a fresh install, specifically torch-geometric 2.8 removing the torch_cluster path that knn needs, and trimesh 5 being untested. The stated convention is next major for 1.x and above projects and next minor for 0.x ones. The comment also notes that both numpy 1.26.x and 2.x are verified, including that numpy's RNG streams are identical between them, which matters because those streams draw the ground-truth evaluation cloud.

That is a maintenance posture, not a maintenance guarantee. The caps mean a fresh install should keep working without intervention, but they also mean you will not get a newer torch-geometric or trimesh without editing pyproject.toml yourself. Python is pinned to ==3.10.*, so a project on 3.11 or 3.12 cannot use this environment as specified.

On licence, the repository ships LICENSE.md and THIRD_PARTY_NOTICES.md, and the pyproject comment states the Gaussian-Splatting License terms apply to Surflo as a whole because of a third-party component. Read both files and get your own legal read; the practical point for an engineer is that the non-commercial restriction is a property of the release, not a configuration you can switch off.

## Conclusion

Adopt Surflo if you are doing research or evaluation on multi-view surface reconstruction, you have a CUDA GPU, and the non-commercial terms in LICENSE.md and THIRD_PARTY_NOTICES.md are acceptable to you. Do not adopt it if you need a pip-installable library, a commercial licence, or a CPU-only path: the README states there is no pip install surflo and that CUDA extensions must be compiled against your torch. Before anything else, run python install/verify_install.py --check-isolation and confirm that the CUDA family you have on the machine (11.8, 12.1 or 12.4) matches one of the files in install/.

## FAQ

### What is Surflo?

Surflo is the official implementation of a consistent 3D surface flow model with global state. It encodes a variable number of unposed RGB views into a fixed-size latent and decodes it via flow matching into oriented surface points, which are converted to a mesh.

### Can I install Surflo with pip install surflo?

No. The README states there is no pip install surflo, because Surflo needs compiled CUDA extensions built against the torch already in your environment. It is installed from a source checkout using one of the files in install/, with conda or uv.

### Which CUDA versions does Surflo support?

The README provides ready-made installation files for CUDA 11.8, 12.1 and 12.4 in the install/ directory. The conda route is self-contained and brings its own nvcc, while the uv route requires your system CUDA to match one of those three families.

### What happens if I feed Surflo portrait images?

The README states the model is trained on landscape images only, so portrait images are automatically rotated 90 degrees to landscape at inference, with a warning logged for each. Since the world frame is the first camera's frame, a portrait first view means the reconstruction and any saved point cloud or mesh come out in that rotated frame.

### How many input views does Surflo need?

The README says the input view count N is variable and can be 2, 10, 30 or even 80. The repository ships 16 views of the garden scene in media/sample/, which the README suggests using to try the demo immediately.

## Sources

- [Anttwo/Surflo on GitHub](https://github.com/Anttwo/Surflo)
- [Issues](https://github.com/Anttwo/Surflo/issues)
- [Project website](https://anttwo.github.io/surflo/)
- [README](https://github.com/Anttwo/Surflo/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/anttwo-surflo
