CLI reference¶
One command, sfmkit, with one subcommand per stage, plus run and new. Every
stage takes the same two options, and each stage reads what the one before it
wrote, so they can be run one at a time, re-run, or resumed.
| Option | Default | |
|---|---|---|
--config |
required | the YAML experiment file; see Configuration |
--out |
<project>/runs/<config name> |
where the run's directories go |
There is no root to set: a config sits in <project>/configs/, so the project
it belongs to is where it is, and the photographs and the runs are its
neighbours. The same config therefore works on the host and inside a container
without either knowing where the other put the project. --out is the way to
write a run somewhere else.
sfmkit new¶
A project laid out: data/scene/, configs/ and a config that parses.
| Option | Default | |
|---|---|---|
--photos DIR |
photographs to copy in, and to list under sfm.images |
|
--in DIR |
projects |
where projects live |
Without --photos the layout is written empty and the config says what to put
where. The config it writes configures neither a query nor a COLMAP, which is
what a first project usually has: see what run does with that below.
sfmkit run¶
Every stage in order, stopping at the first one that fails.
| Option | |
|---|---|
--from STAGE |
start there instead of at the beginning |
--only A,B |
those stages only, in pipeline order |
--skip-done |
skip any stage whose manifest already exists |
--trials N |
seeds for localize (default 20) |
sfmkit run --help lists the stages in the order it does them — the order
lives in run.STAGES, and the help is generated from it, so the two cannot
drift apart.
sfmkit run --config projects/valencia/configs/cpu.yaml --from reconstruct
sfmkit run --config projects/valencia/configs/cpu.yaml --only changes,figures
A stage that needs CUDA and cannot have it (dense) is refused before anything
runs, rather than an hour in.
A stage the config says nothing about is walked past, the way dense skips
itself when it is off: no localize.query means nothing to localise or compare
against, and no colmap.matches or colmap.precomputed means nothing to be
scored by. Naming one in --only runs it anyway, so a mistyped key is reported
by the stage rather than hidden here.
The stages¶
calibrate¶
The camera's intrinsics for the run: from chessboard photographs, from a saved
3×3 matrix, or from the scene photographs' EXIF. Writes calibrate/K.txt.
match¶
SuperPoint keypoints and LightGlue matches for every configured pair. Writes
one .npz per pair, match/ImgA__ImgB.npz.
Features are detected on the photograph the right way up, and the keypoints are mapped back to the stored orientation afterwards: SuperPoint is not rotation invariant, and a phone that records "rotate me 90°" in EXIF would otherwise produce photographs that only match each other.
The weights (53 MB) download on first use; see Install.
verify¶
A fundamental matrix per pair by RANSAC, keeping the inliers. Writes the same
pair files with the verified subset, verify/ImgA__ImgB.npz. Pairs with fewer
than min_inliers survivors are dropped entirely.
reconstruct¶
Tracks, an initial pair, then incremental registration: PnP, triangulation,
filtering and a bundle adjustment after every camera, then one global
refinement. Writes reconstruct/reconstruction.npz and the state from just
before that final refinement, so before-and-after is two real reconstructions
rather than one and a guess.
It publishes its progress step by step when $SFMKIT_BROKER names a Redis
server — Live messages.
localize¶
Places the query photograph — the old one — against the finished model, and
leaves it out of the model itself so that a shaky camera cannot bend the map.
Its focal length is unknown, so a whole projection matrix is estimated by
RANSAC-DLT and decomposed, then refined in pixels under a Huber loss. Writes
localize/query_pose.npz.
| Option | |
|---|---|
--trials N |
run N seeds and report the pose as a distribution (default 20) |
colmap¶
The model the run is scored against: COLMAP run on the same photographs, or a
saved model copied in (colmap.precomputed). The query is registered in a
second pass with its principal point free. Writes COLMAP's text model into
colmap/.
dense¶
COLMAP's dense stereo over that model: undistort, PatchMatch, fusion. Needs an
NVIDIA GPU. Writes dense/fused.ply.
evaluate¶
Compares the two reconstructions after aligning them: rotation error per
camera, position error, the scale between them, and the query's own error
reported apart. Writes evaluate/evaluation.json.
changes¶
Warps the old photograph onto a modern one through a homography from the
verified matches, matches their tones, and marks what differs. Writes the
overlay, the difference and the change mask into changes/.
| Option | Default | |
|---|---|---|
--against IMAGE |
the reference | the modern photograph to compare with |
--threshold T |
0.38 |
dissimilarity threshold, 0 to 1 |
figures¶
The plots for a finished run: the two reconstructions side by side, the camera
positions against COLMAP's, matches, epipolar lines, residuals, track lengths,
and the dense cloud when there is one. Writes figures/.
Exit codes and errors¶
0 on success, 1 on failure. Failures are reported as a line, not a
traceback: a missing file says which file, an unusable config says which key,
and missing feature weights print the URLs and the directory to put them in.