Tohru127 commited on
Commit
6cd3957
·
verified ·
1 Parent(s): d970abc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +54 -67
app.py CHANGED
@@ -1,69 +1,56 @@
1
  import os
2
- import shutil
3
- import subprocess
4
- import tempfile
5
- import time
6
- from pathlib import Path
7
- from typing import List, Tuple
8
 
9
- import gradio as gr
10
- import numpy as np
11
- import open3d as o3d
12
- from PIL import Image
13
-
14
- APP_DIR = Path(__file__).parent.resolve()
15
- OUT_DIR = APP_DIR / "outputs"
16
- RUNS_DIR = APP_DIR / "runs"
17
- OUT_DIR.mkdir(exist_ok=True)
18
- RUNS_DIR.mkdir(exist_ok=True)
19
-
20
-
21
- def _run(cmd: List[str], cwd: Path, logfile: Path) -> Tuple[int, str]:
22
- """Run a shell command, tee output to logfile, return (code, text)."""
23
- proc = subprocess.Popen(cmd, cwd=str(cwd), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
24
- lines = []
25
- with logfile.open("a") as f:
26
- for line in iter(proc.stdout.readline, ""):
27
- f.write(line)
28
- lines.append(line)
29
- proc.wait()
30
- out = "".join(lines)
31
- return proc.returncode, out
32
-
33
-
34
- def _save_images_to(dirpath: Path, images: List[Image.Image], max_size: int) -> None:
35
- dirpath.mkdir(parents=True, exist_ok=True)
36
- for i, im in enumerate(images):
37
- im = im.convert("RGB")
38
- im.thumbnail((max_size, max_size))
39
- im.save(dirpath / f"im_{i:03d}.jpg", quality=92)
40
-
41
-
42
- def _colmap_pipeline(img_dir: Path, work_dir: Path, num_threads: int = 4) -> Path:
43
- """Run COLMAP SfM+MVS. Returns path to fused point cloud (PLY)."""
44
- os.environ.setdefault("OMP_NUM_THREADS", str(num_threads))
45
- db = work_dir / "database.db"
46
- sparse = work_dir / "sparse"
47
- dense = work_dir / "dense"
48
- logs = work_dir / "logs.txt"
49
-
50
- sparse.mkdir(exist_ok=True)
51
- dense.mkdir(exist_ok=True)
52
-
53
- # 1) Feature extraction
54
- code, _ = _run([
55
- "colmap", "feature_extractor",
56
- "--database_path", str(db),
57
- "--image_path", str(img_dir),
58
- "--ImageReader.single_camera", "1",
59
- "--SiftExtraction.max_image_size", "2400",
60
- "--SiftExtraction.num_threads", str(num_threads),
61
- ], cwd=work_dir, logfile=logs)
62
- if code != 0:
63
- raise RuntimeError("COLMAP feature_extractor failed. See logs.txt")
64
-
65
- # 2) Exhaustive matching
66
- code, _ = _run([
67
- "colmap", "exhaustive_matcher",
68
- "--database_path", str(db),
69
- demo.launch()
 
1
  import os
 
 
 
 
 
 
2
 
3
+ def reconstruct(files: List[gr.File], max_img_size: int, threads: int) -> Tuple[str, str, str]:
4
+ """Main entry for Gradio. Returns (obj_path, obj_download, ply_download)."""
5
+ ts = time.strftime("%Y%m%d_%H%M%S")
6
+ run_dir = RUNS_DIR / f"run_{ts}"
7
+ img_dir = run_dir / "images"
8
+ run_dir.mkdir(parents=True, exist_ok=True)
9
+
10
+ paths = [Path(f.name) for f in files]
11
+ _save_images_to(img_dir, paths, int(max_img_size))
12
+
13
+ fused = _colmap_pipeline(img_dir, run_dir, num_threads=int(threads))
14
+ obj = _poisson_mesh_from_ply(fused)
15
+
16
+ logs = run_dir / "logs.txt"
17
+ if logs.exists():
18
+ shutil.copy2(logs, OUT_DIR / f"logs_{ts}.txt")
19
+ fused_out = OUT_DIR / f"fused_{ts}.ply"
20
+ shutil.copy2(fused, fused_out)
21
+
22
+ return str(obj), str(obj), str(fused_out)
23
+
24
+
25
+ def ui():
26
+ with gr.Blocks(title="Sparse2City3D: Urban massing from few photos") as demo:
27
+ gr.Markdown(
28
+ """
29
+ # Sparse2City3D
30
+ Upload 5–30 photos of a street/building. The app runs **COLMAP** to reconstruct a dense point cloud and converts it into a coarse **OBJ** mesh for urban‑planning massing.
31
+ """
32
+ )
33
+ with gr.Row():
34
+ with gr.Column(scale=2):
35
+ files = gr.Files(label="Upload images", file_types=["image"], file_count="multiple")
36
+ max_size = gr.Slider(800, 3200, value=2000, step=100, label="Max image size (px)")
37
+ threads = gr.Slider(1, 8, value=4, step=1, label="# CPU threads")
38
+ run_btn = gr.Button("Reconstruct 3D", variant="primary")
39
+ with gr.Column(scale=3):
40
+ model3d = gr.Model3D(label="3D preview (OBJ)")
41
+ dl_mesh = gr.File(label="Download mesh (OBJ)")
42
+ dl_ply = gr.File(label="Download point cloud (PLY)")
43
+
44
+ def _wrap(files, max_size, threads):
45
+ if not files:
46
+ raise gr.Error("Please upload at least 5 images.")
47
+ obj, obj_dl, ply_dl = reconstruct(files, int(max_size), int(threads))
48
+ return obj, obj_dl, ply_dl
49
+
50
+ run_btn.click(_wrap, inputs=[files, max_size, threads], outputs=[model3d, dl_mesh, dl_ply])
51
+ return demo
52
+
53
+
54
+ if __name__ == "__main__":
55
+ demo = ui()
56
+ demo.launch()