|
@@ -79,16 +79,22 @@ def run_colmap(job: Path, log: Log) -> None:
|
|
|
sparse.mkdir(exist_ok=True)
|
|
sparse.mkdir(exist_ok=True)
|
|
|
out.mkdir(exist_ok=True)
|
|
out.mkdir(exist_ok=True)
|
|
|
C = config.COLMAP_BIN
|
|
C = config.COLMAP_BIN
|
|
|
|
|
+ # COLMAP links Qt and aborts on QApplication init in a headless container
|
|
|
|
|
+ # (no display). QT_QPA_PLATFORM=offscreen makes every CLI subcommand run
|
|
|
|
|
+ # without a display. SIFT extract/match are forced to CPU because the apt
|
|
|
|
|
+ # COLMAP build's GPU SIFT needs an OpenGL/EGL context we don't have headless.
|
|
|
|
|
+ cenv = dict(os.environ, QT_QPA_PLATFORM="offscreen")
|
|
|
_run([C, "feature_extractor", "--database_path", str(db), "--image_path", str(images),
|
|
_run([C, "feature_extractor", "--database_path", str(db), "--image_path", str(images),
|
|
|
- "--ImageReader.single_camera", "1"], cwd=job, log=log)
|
|
|
|
|
- _run([C, "exhaustive_matcher", "--database_path", str(db)], cwd=job, log=log)
|
|
|
|
|
|
|
+ "--ImageReader.single_camera", "1", "--SiftExtraction.use_gpu", "0"], cwd=job, log=log, env=cenv)
|
|
|
|
|
+ _run([C, "exhaustive_matcher", "--database_path", str(db),
|
|
|
|
|
+ "--SiftMatching.use_gpu", "0"], cwd=job, log=log, env=cenv)
|
|
|
_run([C, "mapper", "--database_path", str(db), "--image_path", str(images),
|
|
_run([C, "mapper", "--database_path", str(db), "--image_path", str(images),
|
|
|
- "--output_path", str(sparse)], cwd=job, log=log)
|
|
|
|
|
|
|
+ "--output_path", str(sparse)], cwd=job, log=log, env=cenv)
|
|
|
model = sparse / "0"
|
|
model = sparse / "0"
|
|
|
if not model.exists():
|
|
if not model.exists():
|
|
|
raise RuntimeError("COLMAP mapper produced no model — not enough overlapping views?")
|
|
raise RuntimeError("COLMAP mapper produced no model — not enough overlapping views?")
|
|
|
_run([C, "model_converter", "--input_path", str(model),
|
|
_run([C, "model_converter", "--input_path", str(model),
|
|
|
- "--output_path", str(out), "--output_type", "TXT"], cwd=job, log=log)
|
|
|
|
|
|
|
+ "--output_path", str(out), "--output_type", "TXT"], cwd=job, log=log, env=cenv)
|
|
|
if not (out / "cameras.txt").exists():
|
|
if not (out / "cameras.txt").exists():
|
|
|
raise RuntimeError("COLMAP did not write colmap/cameras.txt")
|
|
raise RuntimeError("COLMAP did not write colmap/cameras.txt")
|
|
|
|
|
|
|
@@ -100,11 +106,21 @@ def _reconstruct_cmd(job: Path) -> list[str]:
|
|
|
"--mode", "Iphone",
|
|
"--mode", "Iphone",
|
|
|
"--path", str(job),
|
|
"--path", str(job),
|
|
|
"--output_path", str(job / "out"),
|
|
"--output_path", str(job / "out"),
|
|
|
- "--ss_ckpt", str(ck / "sparse_structure.pt"),
|
|
|
|
|
- "--shape_ckpt", str(ck / "shape_slat.pt"),
|
|
|
|
|
- "--tex_ckpt", str(ck / "texture_slat.pt"),
|
|
|
|
|
|
|
+ # GenRecon derives each stage's training config as <parent-of-ckpt-dir>/config.json
|
|
|
|
|
+ # (setup_utils.load_train_config), so each checkpoint lives in its own
|
|
|
|
|
+ # run dir: <ck>/<stage>/checkpoints/<name>.pt + <ck>/<stage>/config.json
|
|
|
|
|
+ # (the matching configs/gen/*/genrecon*.json). Weights are symlinked so
|
|
|
|
|
+ # the flat 13.7GB download is not duplicated.
|
|
|
|
|
+ "--ss_ckpt", str(ck / "ss" / "checkpoints" / "sparse_structure.pt"),
|
|
|
|
|
+ "--shape_ckpt", str(ck / "shape" / "checkpoints" / "shape_slat.pt"),
|
|
|
|
|
+ "--tex_ckpt", str(ck / "tex" / "checkpoints" / "texture_slat.pt"),
|
|
|
"--pipeline_config", config.PIPELINE_CONFIG,
|
|
"--pipeline_config", config.PIPELINE_CONFIG,
|
|
|
"--num_imgs_per_scene", str(config.NUM_IMGS_PER_SCENE),
|
|
"--num_imgs_per_scene", str(config.NUM_IMGS_PER_SCENE),
|
|
|
|
|
+ "--chunk_size_factor", str(config.CHUNK_SIZE_FACTOR),
|
|
|
|
|
+ "--stat_std_ratio", str(config.STAT_STD_RATIO),
|
|
|
|
|
+ "--radius_nb_points", str(config.RADIUS_NB_POINTS),
|
|
|
|
|
+ "--radius_m", str(config.RADIUS_M),
|
|
|
|
|
+ "--proj_batch_voxels", str(config.PROJ_BATCH_VOXELS),
|
|
|
])
|
|
])
|
|
|
|
|
|
|
|
|
|
|