Spaces:
nemo10101
/
Runtime error

aRy4n commited on
Commit
6c52580
·
verified ·
1 Parent(s): 9b15315

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +145 -8
app.py CHANGED
@@ -1,9 +1,7 @@
1
  #!/usr/bin/env python3
2
  """gQIR Gradio demo
3
-
4
  Single-frame mode follows infer_sd2GAN_stage2.py (color path only).
5
  Burst mode follows infer_burst_realistic.py for 77->11 aggregation and reconstruction.
6
-
7
  Local run cmd:
8
  python gradio_app.py
9
  --single-config configs/inference/eval_sd2GAN.yaml \
@@ -49,7 +47,13 @@ try:
49
  except Exception:
50
  h5py = None
51
 
 
 
 
 
 
52
 
 
53
  DEFAULT_SINGLE_CONFIG_COLOR = "configs/inference/eval_3bit_color.yaml"
54
  DEFAULT_SINGLE_CONFIG_MONO = "configs/inference/eval_3bit_mono.yaml"
55
  DEFAULT_BURST_CONFIG_COLOR = "configs/inference/eval_burst_mosaic.yaml"
@@ -60,6 +64,23 @@ BURST_WINDOW = 77
60
  PIPELINE_COLOR = "Color"
61
  PIPELINE_MONO = "Monochrome"
62
  PIPELINE_OPTIONS = [PIPELINE_COLOR, PIPELINE_MONO]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
  SINGLE_MODE_GT = "GT image (simulate 3-bit SPAD)"
65
  SINGLE_MODE_REAL = "Real SPAD frame"
@@ -77,6 +98,9 @@ RUNTIME_SINGLE_CONFIGS: dict[str, Path] = {}
77
  RUNTIME_BURST_CONFIGS: dict[str, Path] = {}
78
  RUNTIME_DEVICE: str = "cuda"
79
  RUNTIME_BURST_OUT_SIZES: dict[str, int] = {}
 
 
 
80
  _TEMP_VIDEO_DIRS: list[str] = []
81
 
82
 
@@ -129,12 +153,114 @@ def parse_args() -> argparse.Namespace:
129
  default="cuda" if torch.cuda.is_available() else "cpu",
130
  help="Inference device, e.g. cuda, cuda:0, cpu",
131
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  parser.add_argument("--port", type=int, default=7860)
133
  parser.add_argument("--local", action="store_true", help="Bind to 127.0.0.1 instead of 0.0.0.0")
134
  parser.add_argument("--share", action="store_true")
135
  return parser.parse_args()
136
 
137
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  def _ensure_rgb_image(arr: np.ndarray) -> np.ndarray:
139
  arr = np.asarray(arr)
140
  if arr.ndim == 2:
@@ -378,9 +504,10 @@ def differentiable_warp(x: torch.Tensor, flow: torch.Tensor) -> torch.Tensor:
378
 
379
 
380
  class SingleColorPipeline:
381
- def __init__(self, config_path: Path, device: str):
382
  self.config_path = config_path
383
  self.device = device
 
384
  self.max_size = DEFAULT_MAX_SIZE
385
  self.model: Optional[SD2Enhancer] = None
386
 
@@ -388,6 +515,7 @@ class SingleColorPipeline:
388
  if self.model is not None:
389
  return
390
  cfg = OmegaConf.load(str(self.config_path))
 
391
  if cfg.base_model_type != "sd2":
392
  raise ValueError(f"Unsupported base_model_type for single pipeline: {cfg.base_model_type}")
393
  self.model = SD2Enhancer(
@@ -462,9 +590,10 @@ class SingleColorPipeline:
462
 
463
 
464
  class BurstColorPipeline:
465
- def __init__(self, config_path: Path, device: str):
466
  self.config_path = config_path
467
  self.device = device
 
468
  self.cfg = None
469
  self.out_size = 512
470
  self.weight_dtype = torch.bfloat16 if str(device).startswith("cuda") else torch.float32
@@ -482,6 +611,7 @@ class BurstColorPipeline:
482
  return
483
 
484
  cfg = OmegaConf.load(str(self.config_path))
 
485
  self.cfg = cfg
486
  self.out_size = int(cfg.dataset.val.params.out_size)
487
 
@@ -504,7 +634,7 @@ class BurstColorPipeline:
504
  dropout = False
505
 
506
  raft_model = RAFT(RAFTArgs())
507
- raft_path = "pretrained_ckpts/models/raft-things.pth"
508
  raft_dict = torch.load(str(raft_path), map_location="cpu")
509
  corrected = {}
510
  for k, v in raft_dict.items():
@@ -687,6 +817,7 @@ def _get_single_pipeline(pipeline_type: str) -> SingleColorPipeline:
687
  _SINGLE_PIPELINES[pipeline_type] = SingleColorPipeline(
688
  RUNTIME_SINGLE_CONFIGS[pipeline_type],
689
  RUNTIME_DEVICE,
 
690
  )
691
  _SINGLE_PIPELINES[pipeline_type].load()
692
  return _SINGLE_PIPELINES[pipeline_type]
@@ -702,6 +833,7 @@ def _get_burst_pipeline(pipeline_type: str) -> BurstColorPipeline:
702
  _BURST_PIPELINES[pipeline_type] = BurstColorPipeline(
703
  RUNTIME_BURST_CONFIGS[pipeline_type],
704
  RUNTIME_DEVICE,
 
705
  )
706
  _BURST_PIPELINES[pipeline_type].load()
707
  return _BURST_PIPELINES[pipeline_type]
@@ -1201,18 +1333,15 @@ def build_demo() -> gr.Blocks:
1201
  <a href="https://arxiv.org/abs/2602.20417">ArXiv</a> |
1202
  <a href="https://github.com/Aryan-Garg/gQIR">GitHub</a>
1203
  </p>
1204
-
1205
  ### What You Can Run
1206
  - **Single Frame (Stage-2):** Reconstruct one frame from either a clean GT image (internally simulated to SPAD) or a real SPAD frame.
1207
  - **Burst (Stage-3):** Reconstruct from a fixed **77-frame** window using either GT videos/cubes or real photon cubes.
1208
  - **Pipelines:** Toggle between **Color** and **Monochrome** reconstruction in both tabs.
1209
-
1210
  ### Supported Inputs
1211
  - **Single GT:** Standard image uploads.
1212
  - **Single Real:** Real SPAD frame image uploads.
1213
  - **Burst GT:** Public-friendly videos (`.mp4`, `.mov`, `.wmv`, `.avi`, `.mkv`, `.webm`) plus research cube formats (`.npy`, `.npz`, `.pt`, `.h5`) or image folders.
1214
  - **Burst Real:** Photon cubes (`.npy`, `.npz`, `.pt`, `.h5`) or image folders.
1215
-
1216
  ### Quick Usage
1217
  1. Pick pipeline and input mode.
1218
  2. Load input and select burst start index (for Stage-3).
@@ -1341,12 +1470,16 @@ def build_demo() -> gr.Blocks:
1341
 
1342
  def main() -> None:
1343
  global RUNTIME_SINGLE_CONFIGS, RUNTIME_BURST_CONFIGS, RUNTIME_DEVICE, RUNTIME_BURST_OUT_SIZES
 
1344
 
1345
  args = parse_args()
1346
  single_color_cfg = Path(args.single_config if args.single_config else args.single_config_color).resolve()
1347
  burst_color_cfg = Path(args.burst_config if args.burst_config else args.burst_config_color).resolve()
1348
  single_mono_cfg = Path(args.single_config_mono).resolve()
1349
  burst_mono_cfg = Path(args.burst_config_mono).resolve()
 
 
 
1350
 
1351
  RUNTIME_SINGLE_CONFIGS = {
1352
  PIPELINE_COLOR: single_color_cfg,
@@ -1356,6 +1489,10 @@ def main() -> None:
1356
  PIPELINE_COLOR: burst_color_cfg,
1357
  PIPELINE_MONO: burst_mono_cfg,
1358
  }
 
 
 
 
1359
  RUNTIME_BURST_OUT_SIZES = {}
1360
  for key, cfg_path in RUNTIME_BURST_CONFIGS.items():
1361
  burst_cfg = OmegaConf.load(str(cfg_path))
 
1
  #!/usr/bin/env python3
2
  """gQIR Gradio demo
 
3
  Single-frame mode follows infer_sd2GAN_stage2.py (color path only).
4
  Burst mode follows infer_burst_realistic.py for 77->11 aggregation and reconstruction.
 
5
  Local run cmd:
6
  python gradio_app.py
7
  --single-config configs/inference/eval_sd2GAN.yaml \
 
47
  except Exception:
48
  h5py = None
49
 
50
+ try:
51
+ from huggingface_hub import hf_hub_download
52
+ except Exception:
53
+ hf_hub_download = None
54
+
55
 
56
+ APP_ROOT = Path(__file__).resolve().parent
57
  DEFAULT_SINGLE_CONFIG_COLOR = "configs/inference/eval_3bit_color.yaml"
58
  DEFAULT_SINGLE_CONFIG_MONO = "configs/inference/eval_3bit_mono.yaml"
59
  DEFAULT_BURST_CONFIG_COLOR = "configs/inference/eval_burst_mosaic.yaml"
 
64
  PIPELINE_COLOR = "Color"
65
  PIPELINE_MONO = "Monochrome"
66
  PIPELINE_OPTIONS = [PIPELINE_COLOR, PIPELINE_MONO]
67
+ HF_DEFAULT_REPO_ID = "aRy4n/gQIR"
68
+ HF_MODEL_FILES = {
69
+ PIPELINE_COLOR: {
70
+ "single_qvae": "0105000.pt",
71
+ "single_lora": "state_dict.pth",
72
+ "burst_qvae": "0105000.pt",
73
+ "burst_lora": "state_dict.pth",
74
+ "burst_fusion": "fusion_vit_0050000.pt",
75
+ },
76
+ PIPELINE_MONO: {
77
+ "single_qvae": "mono/0150000.pt",
78
+ "single_lora": "mono/state_dict.pth",
79
+ "burst_qvae": "mono/0150000.pt",
80
+ "burst_lora": "mono/state_dict.pth",
81
+ "burst_fusion": "mono/fusion_vit_0020000.pt",
82
+ },
83
+ }
84
 
85
  SINGLE_MODE_GT = "GT image (simulate 3-bit SPAD)"
86
  SINGLE_MODE_REAL = "Real SPAD frame"
 
98
  RUNTIME_BURST_CONFIGS: dict[str, Path] = {}
99
  RUNTIME_DEVICE: str = "cuda"
100
  RUNTIME_BURST_OUT_SIZES: dict[str, int] = {}
101
+ RUNTIME_HF_REPO_ID: str = HF_DEFAULT_REPO_ID
102
+ RUNTIME_HF_CACHE_DIR: Optional[str] = None
103
+ RUNTIME_HF_TOKEN: Optional[str] = None
104
  _TEMP_VIDEO_DIRS: list[str] = []
105
 
106
 
 
153
  default="cuda" if torch.cuda.is_available() else "cpu",
154
  help="Inference device, e.g. cuda, cuda:0, cpu",
155
  )
156
+ parser.add_argument(
157
+ "--hf-repo-id",
158
+ type=str,
159
+ default=HF_DEFAULT_REPO_ID,
160
+ help="Hugging Face repo containing gQIR checkpoints used when config paths are not local files.",
161
+ )
162
+ parser.add_argument(
163
+ "--hf-cache-dir",
164
+ type=str,
165
+ default=None,
166
+ help="Optional Hugging Face cache directory for checkpoint downloads.",
167
+ )
168
+ parser.add_argument(
169
+ "--hf-token",
170
+ type=str,
171
+ default=None,
172
+ help="Optional HF token. If omitted, app reads HF_TOKEN or HUGGINGFACE_HUB_TOKEN env vars.",
173
+ )
174
  parser.add_argument("--port", type=int, default=7860)
175
  parser.add_argument("--local", action="store_true", help="Bind to 127.0.0.1 instead of 0.0.0.0")
176
  parser.add_argument("--share", action="store_true")
177
  return parser.parse_args()
178
 
179
 
180
+ def _resolve_existing_file(path_value: Optional[str]) -> Optional[str]:
181
+ if not path_value:
182
+ return None
183
+ raw = Path(str(path_value)).expanduser()
184
+ candidates = [raw]
185
+ if not raw.is_absolute():
186
+ candidates.append(APP_ROOT / raw)
187
+ for p in candidates:
188
+ if p.is_file():
189
+ return str(p.resolve())
190
+ return None
191
+
192
+
193
+ def _download_hf_checkpoint(filename: str) -> str:
194
+ if hf_hub_download is None:
195
+ raise RuntimeError(
196
+ "huggingface_hub is required to download checkpoints. Install it or provide local model paths."
197
+ )
198
+
199
+ kwargs: dict[str, Any] = {
200
+ "repo_id": RUNTIME_HF_REPO_ID,
201
+ "filename": filename,
202
+ }
203
+ if RUNTIME_HF_CACHE_DIR:
204
+ kwargs["cache_dir"] = RUNTIME_HF_CACHE_DIR
205
+ if RUNTIME_HF_TOKEN:
206
+ kwargs["token"] = RUNTIME_HF_TOKEN
207
+
208
+ try:
209
+ downloaded = hf_hub_download(**kwargs)
210
+ except Exception as exc:
211
+ raise RuntimeError(
212
+ f"Failed to download '{filename}' from '{RUNTIME_HF_REPO_ID}'. "
213
+ "Check repo, token permissions, and network availability."
214
+ ) from exc
215
+ return str(Path(downloaded).resolve())
216
+
217
+
218
+ def _resolve_checkpoint_path(config_value: Optional[str], pipeline_type: str, file_key: str) -> str:
219
+ if pipeline_type not in HF_MODEL_FILES:
220
+ raise ValueError(f"Unknown pipeline type for checkpoint resolution: {pipeline_type}")
221
+ if file_key not in HF_MODEL_FILES[pipeline_type]:
222
+ raise ValueError(f"Unknown checkpoint key '{file_key}' for pipeline type '{pipeline_type}'")
223
+
224
+ existing = _resolve_existing_file(config_value)
225
+ if existing is not None:
226
+ return existing
227
+
228
+ hf_file = HF_MODEL_FILES[pipeline_type][file_key]
229
+ print(f"[gQIR] Missing local checkpoint; downloading {RUNTIME_HF_REPO_ID}/{hf_file}")
230
+ return _download_hf_checkpoint(hf_file)
231
+
232
+
233
+ def _prepare_single_cfg_paths(cfg: Any, pipeline_type: str) -> Any:
234
+ cfg = OmegaConf.create(OmegaConf.to_container(cfg, resolve=False))
235
+ if "model" not in cfg or "vae_cfg" not in cfg.model:
236
+ raise ValueError("Single-frame config missing model.vae_cfg")
237
+
238
+ qvae_path_cfg = None
239
+ if "qvae_path" in cfg.model.vae_cfg:
240
+ qvae_path_cfg = cfg.model.vae_cfg.qvae_path
241
+ if not qvae_path_cfg and "qvae_path" in cfg:
242
+ qvae_path_cfg = cfg.qvae_path
243
+
244
+ cfg.weight_path = _resolve_checkpoint_path(cfg.get("weight_path"), pipeline_type, "single_lora")
245
+ resolved_qvae = _resolve_checkpoint_path(qvae_path_cfg, pipeline_type, "single_qvae")
246
+ cfg.model.vae_cfg.qvae_path = resolved_qvae
247
+ if "qvae_path" in cfg:
248
+ cfg.qvae_path = resolved_qvae
249
+ return cfg
250
+
251
+
252
+ def _prepare_burst_cfg_paths(cfg: Any, pipeline_type: str) -> Any:
253
+ cfg = OmegaConf.create(OmegaConf.to_container(cfg, resolve=False))
254
+ cfg.qvae_path = _resolve_checkpoint_path(cfg.get("qvae_path"), pipeline_type, "burst_qvae")
255
+ cfg.unet_weight_path = _resolve_checkpoint_path(cfg.get("unet_weight_path"), pipeline_type, "burst_lora")
256
+ cfg.fusion_vit_weight_path = _resolve_checkpoint_path(
257
+ cfg.get("fusion_vit_weight_path"), pipeline_type, "burst_fusion"
258
+ )
259
+ if "model" in cfg and "vae_cfg" in cfg.model:
260
+ cfg.model.vae_cfg.qvae_path = cfg.qvae_path
261
+ return cfg
262
+
263
+
264
  def _ensure_rgb_image(arr: np.ndarray) -> np.ndarray:
265
  arr = np.asarray(arr)
266
  if arr.ndim == 2:
 
504
 
505
 
506
  class SingleColorPipeline:
507
+ def __init__(self, config_path: Path, device: str, pipeline_type: str):
508
  self.config_path = config_path
509
  self.device = device
510
+ self.pipeline_type = pipeline_type
511
  self.max_size = DEFAULT_MAX_SIZE
512
  self.model: Optional[SD2Enhancer] = None
513
 
 
515
  if self.model is not None:
516
  return
517
  cfg = OmegaConf.load(str(self.config_path))
518
+ cfg = _prepare_single_cfg_paths(cfg, self.pipeline_type)
519
  if cfg.base_model_type != "sd2":
520
  raise ValueError(f"Unsupported base_model_type for single pipeline: {cfg.base_model_type}")
521
  self.model = SD2Enhancer(
 
590
 
591
 
592
  class BurstColorPipeline:
593
+ def __init__(self, config_path: Path, device: str, pipeline_type: str):
594
  self.config_path = config_path
595
  self.device = device
596
+ self.pipeline_type = pipeline_type
597
  self.cfg = None
598
  self.out_size = 512
599
  self.weight_dtype = torch.bfloat16 if str(device).startswith("cuda") else torch.float32
 
611
  return
612
 
613
  cfg = OmegaConf.load(str(self.config_path))
614
+ cfg = _prepare_burst_cfg_paths(cfg, self.pipeline_type)
615
  self.cfg = cfg
616
  self.out_size = int(cfg.dataset.val.params.out_size)
617
 
 
634
  dropout = False
635
 
636
  raft_model = RAFT(RAFTArgs())
637
+ raft_path = APP_ROOT / "pretrained_ckpts" / "models" / "raft-things.pth"
638
  raft_dict = torch.load(str(raft_path), map_location="cpu")
639
  corrected = {}
640
  for k, v in raft_dict.items():
 
817
  _SINGLE_PIPELINES[pipeline_type] = SingleColorPipeline(
818
  RUNTIME_SINGLE_CONFIGS[pipeline_type],
819
  RUNTIME_DEVICE,
820
+ pipeline_type,
821
  )
822
  _SINGLE_PIPELINES[pipeline_type].load()
823
  return _SINGLE_PIPELINES[pipeline_type]
 
833
  _BURST_PIPELINES[pipeline_type] = BurstColorPipeline(
834
  RUNTIME_BURST_CONFIGS[pipeline_type],
835
  RUNTIME_DEVICE,
836
+ pipeline_type,
837
  )
838
  _BURST_PIPELINES[pipeline_type].load()
839
  return _BURST_PIPELINES[pipeline_type]
 
1333
  <a href="https://arxiv.org/abs/2602.20417">ArXiv</a> |
1334
  <a href="https://github.com/Aryan-Garg/gQIR">GitHub</a>
1335
  </p>
 
1336
  ### What You Can Run
1337
  - **Single Frame (Stage-2):** Reconstruct one frame from either a clean GT image (internally simulated to SPAD) or a real SPAD frame.
1338
  - **Burst (Stage-3):** Reconstruct from a fixed **77-frame** window using either GT videos/cubes or real photon cubes.
1339
  - **Pipelines:** Toggle between **Color** and **Monochrome** reconstruction in both tabs.
 
1340
  ### Supported Inputs
1341
  - **Single GT:** Standard image uploads.
1342
  - **Single Real:** Real SPAD frame image uploads.
1343
  - **Burst GT:** Public-friendly videos (`.mp4`, `.mov`, `.wmv`, `.avi`, `.mkv`, `.webm`) plus research cube formats (`.npy`, `.npz`, `.pt`, `.h5`) or image folders.
1344
  - **Burst Real:** Photon cubes (`.npy`, `.npz`, `.pt`, `.h5`) or image folders.
 
1345
  ### Quick Usage
1346
  1. Pick pipeline and input mode.
1347
  2. Load input and select burst start index (for Stage-3).
 
1470
 
1471
  def main() -> None:
1472
  global RUNTIME_SINGLE_CONFIGS, RUNTIME_BURST_CONFIGS, RUNTIME_DEVICE, RUNTIME_BURST_OUT_SIZES
1473
+ global RUNTIME_HF_REPO_ID, RUNTIME_HF_CACHE_DIR, RUNTIME_HF_TOKEN
1474
 
1475
  args = parse_args()
1476
  single_color_cfg = Path(args.single_config if args.single_config else args.single_config_color).resolve()
1477
  burst_color_cfg = Path(args.burst_config if args.burst_config else args.burst_config_color).resolve()
1478
  single_mono_cfg = Path(args.single_config_mono).resolve()
1479
  burst_mono_cfg = Path(args.burst_config_mono).resolve()
1480
+ hf_token = args.hf_token
1481
+ if not hf_token:
1482
+ hf_token = os.environ.get("HF_TOKEN") or os.environ.get("HUGGINGFACE_HUB_TOKEN")
1483
 
1484
  RUNTIME_SINGLE_CONFIGS = {
1485
  PIPELINE_COLOR: single_color_cfg,
 
1489
  PIPELINE_COLOR: burst_color_cfg,
1490
  PIPELINE_MONO: burst_mono_cfg,
1491
  }
1492
+ RUNTIME_DEVICE = args.device
1493
+ RUNTIME_HF_REPO_ID = str(args.hf_repo_id or HF_DEFAULT_REPO_ID).strip()
1494
+ RUNTIME_HF_CACHE_DIR = str(Path(args.hf_cache_dir).expanduser()) if args.hf_cache_dir else None
1495
+ RUNTIME_HF_TOKEN = hf_token
1496
  RUNTIME_BURST_OUT_SIZES = {}
1497
  for key, cfg_path in RUNTIME_BURST_CONFIGS.items():
1498
  burst_cfg = OmegaConf.load(str(cfg_path))