SfM Backends
Structure-from-Motion (SfM) is the first stage of the pipeline. It takes your images and estimates where each camera was positioned in 3D space. These camera poses are then used to train the Gaussian Splat model. Two SfM backends run today:Backend comparison
prebuilt_colmap is selected automatically when your zip contains a
sparse/ model — it is not something you request via sfm.backend.
The pipeline also registers none_arkit, a pose passthrough, but no API
input gives it the model it copies, so selecting it fails the scene
(refunded; NoneArkitBackend.run in sfm_backends/none_arkit.py).The API also accepts legacy short-names for backward compatibility:
glomap and colmap both resolve to colmap_sift_glomap, and none
resolves to none_arkit. Prefer the canonical names in new code; the
colmap alias emits a deprecation warning and may be removed.colmap_sift_glomap (default)
GPU SIFT feature extraction (via COLMAP) combined with GLOMAP’s modern global mapper, which solves all camera poses simultaneously rather than incrementally.
- Significantly faster than incremental COLMAP
- GPU SIFT for feature extraction
- Global optimization is more robust for video walkthroughs
- Default for all presets
sfm.backend, this is what runs.
Options
Set these insfm.options. SfM reads only this object: the top-level sfm.matcher is ignored, and sfm.max_image_size sets only the training resolution (SfmRequest.from_api_payload in sfm_backends/configs.py).
The backend also accepts tuning keys for COLMAP and GLOMAP (
camera_model, max_num_features, triangulation thresholds and others, in ColmapSiftGlomapOptions); they are unsupported and may change. An unknown key or bad value is refused when /process dispatches the job: 400 invalid_input (Invalid sfm options: ...), and the charge is refunded (translatePydantic422 in api/src/lib/scenes.ts).
Camera poses you already have
There are two ways to supply poses. Only a COLMAP model skips SfM.Path A: ARKit poses via API (recommended for iOS)
Pass camera poses directly in thePOST /scenes/{id}/process request body.
The API stores them server-side. No zip required — upload your video as
normal. GLOMAP still solves the camera poses from the frames (_stage_sfm in
pipeline/training/impl.py); the ARKit poses set which way is up and, for a
video, which frames are cut: up to 150, at the pose timestamps
(_extract_arkit_timestamp_frames).
Minimum 5 poses, maximum 1000. You don’t set
sfm.backend: the API sends
the poses with the job, and the pipeline runs GLOMAP as for any upload.
The scene’s orientation comes from these poses. The pipeline reads gravity
off the world’s +Y axis, so record them in an ARKit session with
worldAlignment set to .gravity (the default) or .gravityAndHeading, not
.camera. It needs at least 30 frames or photos paired with a pose and a
median tilt residual (each pair’s up against the fitted up) of 3° or less;
otherwise it estimates up from the cameras, which lays portrait captures on
their side (MIN_PAIRS, MAX_MEDIAN_TILT_DEG in
pipeline/training/arkit_gravity.py). Photos are used as stored: EXIF
orientation is ignored when arkit_poses is set, so each image’s pixels must
be in the camera axes its transform describes, as ARKit’s capturedImage is.
If you’re building an iOS app, the Splat3D Swift SDK handles
all of this for you — ARKit capture, pose encoding, upload, and polling in one call.
Path B: COLMAP zip upload
If you’ve already run COLMAP (or Metashape, RealityCapture, or your own SfM code that exports COLMAP binaries), bundle your images and sparse model into a zip. The pipeline detects thesparse/ model, skips SfM entirely, and starts
training immediately. This is the fastest path to a splat and gives you full
control over the reconstruction.
Zip structure:
my-scene/images/ + my-scene/sparse/0/) —
zip the folder or its contents, whichever your tool produces. If several models
are present, sparse/0 is used; among several sparse/0 folders, the one with
images/ beside its sparse/.
Two tool outputs also work zipped as they are:
- Inria 3DGS
convert.py— the undistortedsparse/0andimages/are used;input/anddistorted/are ignored. - nerfstudio
ns-process-data— the model incolmap/sparse/0is paired with the photos in the top-levelimages/;images_2/and the other downscaled copies are ignored.
sfm.backend — detection is automatic. Uploading a
zip with no sparse/ directory runs the normal GLOMAP path.
Requirements
Your model is validated before training starts, so a mismatch fails fast with a message instead of burning GPU minutes:
Photos in the zip that the model never registered are ignored.
Getting COLMAP binary output:
When to use pre-supplied poses
Matching strategies
Feature matching only happens when SfM actually runs, so a COLMAP model zip (prebuilt_colmap) ignores these settings. Otherwise you can control the matching strategy in sfm.options: