Spaces:
Running on Zero
Running on Zero
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,1326 +1,387 @@
|
|
| 1 |
-
import
|
| 2 |
-
|
| 3 |
-
APP_BUILD_ID = "relit-live-zerogpu-te-shim-2026-06-29-1908"
|
| 4 |
-
import gradio as gr
|
| 5 |
import os
|
|
|
|
| 6 |
import subprocess
|
| 7 |
import sys
|
| 8 |
-
import
|
| 9 |
-
import shutil
|
| 10 |
-
import signal
|
| 11 |
-
import threading
|
| 12 |
from pathlib import Path
|
| 13 |
-
from
|
| 14 |
-
from datetime import datetime
|
| 15 |
-
import socket
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
# -------------------------- Core Configuration --------------------------
|
| 19 |
-
BASE_UPLOAD_DIR = "./datasets/gradio_data/upload_data"
|
| 20 |
-
BASE_RESULT_DIR = "./datasets/gradio_data/results"
|
| 21 |
-
DEMO_IMAGES_DIR = "./datasets/gradio_data/assets/images_demo"
|
| 22 |
-
BASH_SCRIPT_PATH = "./tools/full_inference_modules_gradio.sh"
|
| 23 |
-
|
| 24 |
-
SUPPORTED_IMAGE_FORMATS = [".jpg", ".jpeg", ".png", ".bmp", ".tiff", ".hdr"]
|
| 25 |
-
SUPPORTED_VIDEO_FORMATS = [".mp4", ".mov", ".avi", ".mkv"]
|
| 26 |
-
SUPPORTED_HDR_FORMATS = [".hdr", ".exr", ".jpg", ".png"]
|
| 27 |
-
|
| 28 |
-
ASSETS_ENV_DIR = "./datasets/gradio_data/assets/envs_demo"
|
| 29 |
-
BUILTIN_ENV_OPTIONS = []
|
| 30 |
-
if os.path.exists(ASSETS_ENV_DIR):
|
| 31 |
-
for ext in SUPPORTED_HDR_FORMATS:
|
| 32 |
-
BUILTIN_ENV_OPTIONS.extend([f.stem for f in Path(ASSETS_ENV_DIR).glob(f"*{ext}")])
|
| 33 |
-
BUILTIN_ENV_OPTIONS = sorted(set(BUILTIN_ENV_OPTIONS))
|
| 34 |
-
DEFAULT_ENV = "Pink_Sunrise" if "Pink_Sunrise" in BUILTIN_ENV_OPTIONS else (BUILTIN_ENV_OPTIONS[0] if BUILTIN_ENV_OPTIONS else None)
|
| 35 |
-
|
| 36 |
-
MODULE1_RESULT_TYPES = {
|
| 37 |
-
"base_color": {"name": "Base Color", "dir": "Base Color", "glob_pattern": "frame_*"},
|
| 38 |
-
"normal": {"name": "Normal Map", "dir": "normal", "glob_pattern": "frame_*"},
|
| 39 |
-
"roughness": {"name": "Roughness Map", "dir": "Roughness", "glob_pattern": "frame_*"},
|
| 40 |
-
}
|
| 41 |
-
|
| 42 |
-
INPUT_PREVIEW_HEIGHT = 240
|
| 43 |
-
MODULE1_VIS_HEIGHT = 180
|
| 44 |
-
MODULE2_VIS_HEIGHT = 200
|
| 45 |
-
MODULE3_VIS_HEIGHT = 400
|
| 46 |
-
DEMO_IMAGE_HEIGHT = 200
|
| 47 |
-
|
| 48 |
-
RUNNING_PROCESSES = {}
|
| 49 |
-
RUNNING_PROCESSES_LOCK = threading.RLock()
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
def patch_bash_script_for_hf_spaces():
|
| 53 |
-
"""Disable hard-coded conda activation lines that do not exist in HF Spaces."""
|
| 54 |
-
script = Path(BASH_SCRIPT_PATH)
|
| 55 |
-
if not script.exists():
|
| 56 |
-
return
|
| 57 |
-
try:
|
| 58 |
-
text = script.read_text(encoding="utf-8")
|
| 59 |
-
except Exception as exc:
|
| 60 |
-
print(f"Warning: could not read {script}: {exc}")
|
| 61 |
-
return
|
| 62 |
-
|
| 63 |
-
original = text
|
| 64 |
-
patched_lines = []
|
| 65 |
-
changed = False
|
| 66 |
-
for line in text.splitlines():
|
| 67 |
-
stripped = line.strip()
|
| 68 |
-
if "/home/user/miniconda3/bin/activate" in stripped or stripped.startswith("conda activate"):
|
| 69 |
-
patched_lines.append("# Disabled by app.py for Hugging Face Spaces: " + line)
|
| 70 |
-
patched_lines.append("true")
|
| 71 |
-
changed = True
|
| 72 |
-
else:
|
| 73 |
-
patched_lines.append(line)
|
| 74 |
-
|
| 75 |
-
if changed:
|
| 76 |
-
try:
|
| 77 |
-
script.write_text("\n".join(patched_lines) + "\n", encoding="utf-8")
|
| 78 |
-
print(f"Patched {script}: disabled hard-coded conda activation.")
|
| 79 |
-
except Exception as exc:
|
| 80 |
-
print(f"Warning: could not patch {script}: {exc}")
|
| 81 |
-
RUNTIME_SETUP_LOCK = threading.RLock()
|
| 82 |
-
RUNTIME_SETUP_DONE = False
|
| 83 |
-
RUNTIME_SETUP_ATTEMPTED = False
|
| 84 |
-
RUNTIME_SETUP_LOG = ""
|
| 85 |
-
|
| 86 |
-
RELIT_CHECKPOINT_REPO = os.environ.get("RELIT_CHECKPOINT_REPO", "weiqingXiao/Relit-LiVE")
|
| 87 |
-
WAN_REPO = os.environ.get("RELIT_WAN_REPO", "Wan-AI/Wan2.1-T2V-1.3B")
|
| 88 |
-
COSMOS_REPO = os.environ.get("RELIT_COSMOS_REPO", "https://github.com/nv-tlabs/cosmos-transfer1-diffusion-renderer.git")
|
| 89 |
-
|
| 90 |
-
DOWNLOAD_RELIT_CHECKPOINTS = os.environ.get("RELIT_DOWNLOAD_CHECKPOINTS", "1") != "0"
|
| 91 |
-
DOWNLOAD_WAN = os.environ.get("RELIT_DOWNLOAD_WAN", "1") != "0"
|
| 92 |
-
CLONE_COSMOS = os.environ.get("RELIT_CLONE_COSMOS", "1") != "0"
|
| 93 |
-
STARTUP_RUNTIME_SETUP = os.environ.get("RELIT_STARTUP_SETUP", "1") != "0"
|
| 94 |
-
INSTALL_COSMOS_DEPS = os.environ.get("RELIT_INSTALL_COSMOS_DEPS", "1") != "0"
|
| 95 |
-
INSTALL_TRANSFORMER_ENGINE = os.environ.get("RELIT_INSTALL_TRANSFORMER_ENGINE", "0") == "1"
|
| 96 |
-
INSTALL_NVDIFFRAST = os.environ.get("RELIT_INSTALL_NVDIFFRAST", "0") == "1"
|
| 97 |
-
DOWNLOAD_COSMOS_CHECKPOINTS = os.environ.get("RELIT_DOWNLOAD_COSMOS_CHECKPOINTS", "1") != "0"
|
| 98 |
-
STRICT_COSMOS_DEPS = os.environ.get("RELIT_STRICT_COSMOS_DEPS", "0") == "1"
|
| 99 |
-
USE_TRANSFORMER_ENGINE_SHIM = os.environ.get("RELIT_USE_TRANSFORMER_ENGINE_SHIM", "1") != "0"
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
# -------------------------- Utility Functions --------------------------
|
| 103 |
-
def get_server_ip():
|
| 104 |
-
try:
|
| 105 |
-
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
| 106 |
-
s.connect(("8.8.8.8", 80))
|
| 107 |
-
local_ip = s.getsockname()[0]
|
| 108 |
-
s.close()
|
| 109 |
-
return local_ip
|
| 110 |
-
except Exception:
|
| 111 |
-
return "Unknown IP"
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
def generate_test_id(flag):
|
| 115 |
-
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
| 116 |
-
random_str = uuid.uuid4().hex[:8]
|
| 117 |
-
return f"{flag}_{timestamp}_{random_str}"
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
def clear_test_dir(test_id):
|
| 121 |
-
upload_dir = os.path.join(BASE_UPLOAD_DIR, test_id)
|
| 122 |
-
result_dir = os.path.join(BASE_RESULT_DIR, test_id)
|
| 123 |
-
if os.path.exists(upload_dir):
|
| 124 |
-
shutil.rmtree(upload_dir)
|
| 125 |
-
if os.path.exists(result_dir):
|
| 126 |
-
shutil.rmtree(result_dir)
|
| 127 |
-
os.makedirs(upload_dir, exist_ok=True)
|
| 128 |
-
os.makedirs(result_dir, exist_ok=True)
|
| 129 |
-
|
| 130 |
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
if img.mode in ("RGBA", "P"):
|
| 135 |
-
img = img.convert("RGB")
|
| 136 |
-
img.save(save_path, "JPEG", quality=95)
|
| 137 |
-
return True
|
| 138 |
-
except Exception as e:
|
| 139 |
-
print(f"Image format conversion failed: {e}")
|
| 140 |
-
return False
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
def save_uploaded_file(file, test_id, is_env=False):
|
| 144 |
-
if file is None:
|
| 145 |
-
return None, None, None
|
| 146 |
-
|
| 147 |
-
upload_dir = os.path.join(BASE_UPLOAD_DIR, test_id)
|
| 148 |
-
os.makedirs(upload_dir, exist_ok=True)
|
| 149 |
-
|
| 150 |
-
original_path = file.name if hasattr(file, "name") else file
|
| 151 |
-
file_suffix = Path(original_path).suffix.lower()
|
| 152 |
-
|
| 153 |
-
if is_env:
|
| 154 |
-
env_filename = f"env_{uuid.uuid4().hex[:4]}"
|
| 155 |
-
save_path = os.path.join(upload_dir, f"{env_filename}{file_suffix}")
|
| 156 |
-
shutil.copy(original_path, save_path)
|
| 157 |
-
return file_suffix, save_path, env_filename
|
| 158 |
-
|
| 159 |
-
base_filename = test_id
|
| 160 |
-
if file_suffix in [".jpg", ".jpeg", ".png"]:
|
| 161 |
-
save_path = os.path.join(upload_dir, f"{base_filename}.jpg")
|
| 162 |
-
if not convert_to_jpg(original_path, save_path):
|
| 163 |
-
shutil.copy(original_path, save_path)
|
| 164 |
-
return ".jpg", save_path, None
|
| 165 |
-
|
| 166 |
-
save_path = os.path.join(upload_dir, f"{base_filename}{file_suffix}")
|
| 167 |
-
shutil.copy(original_path, save_path)
|
| 168 |
-
return file_suffix, save_path, None
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
def clear_module_results(test_id, module_num, test_env=""):
|
| 172 |
-
try:
|
| 173 |
-
if module_num == 1:
|
| 174 |
-
for path in [
|
| 175 |
-
os.path.join(BASE_RESULT_DIR, test_id, "frames"),
|
| 176 |
-
os.path.join(BASE_RESULT_DIR, test_id, "frames_delighting"),
|
| 177 |
-
os.path.join(BASE_RESULT_DIR, test_id, "rego"),
|
| 178 |
-
]:
|
| 179 |
-
if os.path.exists(path):
|
| 180 |
-
shutil.rmtree(path)
|
| 181 |
-
elif module_num == 2:
|
| 182 |
-
path = os.path.join(BASE_RESULT_DIR, test_id, "envs", test_env)
|
| 183 |
-
if os.path.exists(path):
|
| 184 |
-
shutil.rmtree(path)
|
| 185 |
-
return True
|
| 186 |
-
except Exception as e:
|
| 187 |
-
print(f"Failed to clear Module {module_num} results: {e}")
|
| 188 |
-
return False
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
def find_first_file(root_dir, glob_pattern, formats):
|
| 192 |
-
if not os.path.exists(root_dir):
|
| 193 |
-
return None
|
| 194 |
-
matches = []
|
| 195 |
-
for fmt in formats:
|
| 196 |
-
matches.extend(Path(root_dir).rglob(f"{glob_pattern}{fmt}"))
|
| 197 |
-
return str(sorted(matches)[0]) if matches else None
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
def find_first_keyword_file(root_dir, keywords):
|
| 201 |
-
if not os.path.exists(root_dir):
|
| 202 |
-
return None
|
| 203 |
-
matches = []
|
| 204 |
-
for fmt in SUPPORTED_IMAGE_FORMATS:
|
| 205 |
-
matches.extend(Path(root_dir).rglob(f"*{fmt}"))
|
| 206 |
-
for path in sorted(matches):
|
| 207 |
-
path_lower = str(path).lower()
|
| 208 |
-
if any(keyword in path_lower for keyword in keywords):
|
| 209 |
-
return str(path)
|
| 210 |
-
return None
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
def get_result_files(test_id, params):
|
| 214 |
-
input_ext = ".jpg" if params["test_type"] == 1 else ".mp4"
|
| 215 |
-
results = {
|
| 216 |
-
"input_file": os.path.join(BASE_UPLOAD_DIR, test_id, f"{test_id}{input_ext}"),
|
| 217 |
-
"input_file_type": "image" if params["test_type"] == 1 else "video",
|
| 218 |
-
"module1": {"base_color": None, "normal": None, "roughness": None, "status": "Not Executed", "exists": False, "missing_types": []},
|
| 219 |
-
"module2": {"ldr_video": None, "env_dir_video": None, "status": "Not Executed", "exists": False},
|
| 220 |
-
"module3": {"final": None, "status": "Not Executed", "exists": False, "file_type": None},
|
| 221 |
-
"test_id": test_id,
|
| 222 |
-
"test_env": params["test_env"],
|
| 223 |
-
}
|
| 224 |
-
|
| 225 |
-
module1_root_candidates = [
|
| 226 |
-
os.path.join(BASE_RESULT_DIR, test_id, "rego", f"{test_id}.0"),
|
| 227 |
-
os.path.join(BASE_RESULT_DIR, test_id, "frames_delighting", "gbuffer_frames", test_id),
|
| 228 |
-
os.path.join(BASE_RESULT_DIR, test_id, "frames_delighting", "gbuffer_frames"),
|
| 229 |
-
os.path.join(BASE_RESULT_DIR, test_id, "frames"),
|
| 230 |
-
]
|
| 231 |
-
existing_module1_roots = [path for path in module1_root_candidates if os.path.exists(path)]
|
| 232 |
-
|
| 233 |
-
if params["infer_1"] == 1 or existing_module1_roots:
|
| 234 |
-
if existing_module1_roots:
|
| 235 |
-
for result_type, config in MODULE1_RESULT_TYPES.items():
|
| 236 |
-
found_file = None
|
| 237 |
-
for root_dir in existing_module1_roots:
|
| 238 |
-
exact_dir = os.path.join(root_dir, config["dir"])
|
| 239 |
-
found_file = find_first_file(exact_dir, config["glob_pattern"], SUPPORTED_IMAGE_FORMATS)
|
| 240 |
-
if found_file:
|
| 241 |
-
break
|
| 242 |
-
|
| 243 |
-
if not found_file:
|
| 244 |
-
keywords = {
|
| 245 |
-
"base_color": ["base", "albedo", "color"],
|
| 246 |
-
"normal": ["normal"],
|
| 247 |
-
"roughness": ["rough", "roughness"],
|
| 248 |
-
}[result_type]
|
| 249 |
-
for root_dir in existing_module1_roots:
|
| 250 |
-
found_file = find_first_keyword_file(root_dir, keywords)
|
| 251 |
-
if found_file:
|
| 252 |
-
break
|
| 253 |
-
|
| 254 |
-
if found_file:
|
| 255 |
-
results["module1"][result_type] = found_file
|
| 256 |
-
else:
|
| 257 |
-
results["module1"]["missing_types"].append(config["name"])
|
| 258 |
-
|
| 259 |
-
has_result = any(results["module1"][k] for k in ["base_color", "normal", "roughness"])
|
| 260 |
-
results["module1"]["exists"] = has_result
|
| 261 |
-
if not results["module1"]["missing_types"]:
|
| 262 |
-
results["module1"]["status"] = "Execution Successful (All 3 results generated)"
|
| 263 |
-
elif has_result:
|
| 264 |
-
missing = ", ".join(results["module1"]["missing_types"])
|
| 265 |
-
results["module1"]["status"] = f"Execution Successful (Partial results missing: {missing})"
|
| 266 |
-
else:
|
| 267 |
-
results["module1"]["status"] = "Execution Failed: No results generated"
|
| 268 |
-
else:
|
| 269 |
-
checked = "\n".join(f"- {path}" for path in module1_root_candidates)
|
| 270 |
-
results["module1"]["status"] = f"Execution Failed: Module 1 output directory not found. Checked:\n{checked}"
|
| 271 |
-
|
| 272 |
-
env_dir = os.path.join(BASE_RESULT_DIR, test_id, "envs", params["test_env"])
|
| 273 |
-
if params["infer_2"] == 1 or os.path.exists(env_dir):
|
| 274 |
-
if os.path.exists(env_dir):
|
| 275 |
-
for fmt in SUPPORTED_VIDEO_FORMATS:
|
| 276 |
-
candidate = os.path.join(env_dir, f"ldr_video_fix_first_frame{fmt}")
|
| 277 |
-
if os.path.exists(candidate):
|
| 278 |
-
results["module2"]["ldr_video"] = candidate
|
| 279 |
-
break
|
| 280 |
-
for fmt in SUPPORTED_VIDEO_FORMATS:
|
| 281 |
-
candidate = os.path.join(env_dir, f"env_dir_video_fix_first_frame{fmt}")
|
| 282 |
-
if os.path.exists(candidate):
|
| 283 |
-
results["module2"]["env_dir_video"] = candidate
|
| 284 |
-
break
|
| 285 |
-
results["module2"]["exists"] = bool(results["module2"]["ldr_video"] or results["module2"]["env_dir_video"])
|
| 286 |
-
if results["module2"]["ldr_video"]:
|
| 287 |
-
results["module2"]["status"] = "Execution Successful (LDR video generated)"
|
| 288 |
-
elif results["module2"]["env_dir_video"]:
|
| 289 |
-
results["module2"]["status"] = "Execution Successful (Only environment direction video generated)"
|
| 290 |
-
else:
|
| 291 |
-
results["module2"]["status"] = "Execution Failed: No video files generated"
|
| 292 |
-
else:
|
| 293 |
-
results["module2"]["status"] = "Execution Failed: Directory not found"
|
| 294 |
-
|
| 295 |
-
final_files = list(Path(BASE_RESULT_DIR, test_id).glob(f"{test_id}.{params['test_env']}.*"))
|
| 296 |
-
if params["infer_3"] == 1 or final_files:
|
| 297 |
-
if final_files:
|
| 298 |
-
final_file = str(sorted(final_files)[0])
|
| 299 |
-
suffix = Path(final_file).suffix.lower()
|
| 300 |
-
results["module3"]["final"] = final_file
|
| 301 |
-
results["module3"]["exists"] = True
|
| 302 |
-
if suffix in SUPPORTED_IMAGE_FORMATS:
|
| 303 |
-
results["module3"]["file_type"] = "image"
|
| 304 |
-
elif suffix in SUPPORTED_VIDEO_FORMATS:
|
| 305 |
-
results["module3"]["file_type"] = "video"
|
| 306 |
-
results["module3"]["status"] = "Execution Successful"
|
| 307 |
-
else:
|
| 308 |
-
results["module3"]["status"] = "Execution Failed: No image/video results generated"
|
| 309 |
|
| 310 |
-
|
| 311 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 312 |
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
|
|
|
| 316 |
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 320 |
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
if module_num == 2:
|
| 324 |
-
return min(600, max(90, 90 + frames * 4))
|
| 325 |
-
if module_num == 3:
|
| 326 |
-
return min(1200, max(120, int(120 + frames * steps * 0.7)))
|
| 327 |
-
return 300
|
| 328 |
|
| 329 |
|
| 330 |
-
def
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
return any(item.is_file() for item in root.rglob("*"))
|
| 336 |
-
for pattern in patterns:
|
| 337 |
-
if any(root.glob(pattern)):
|
| 338 |
-
return True
|
| 339 |
-
return False
|
| 340 |
|
| 341 |
|
| 342 |
-
def
|
| 343 |
-
print(f"Runtime setup command: {' '.join(command)}")
|
| 344 |
env = os.environ.copy()
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
|
|
|
|
|
|
| 350 |
env=env,
|
| 351 |
text=True,
|
| 352 |
stdout=subprocess.PIPE,
|
| 353 |
-
stderr=subprocess.
|
|
|
|
| 354 |
)
|
| 355 |
-
output = completed.stdout or ""
|
| 356 |
-
if completed.returncode != 0:
|
| 357 |
-
raise RuntimeError(f"Command failed: {' '.join(command)}\n{output}")
|
| 358 |
-
return output.strip()
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
def pip_install(args, cwd=None):
|
| 362 |
-
return run_setup_command([sys.executable, "-m", "pip", "install", *args], cwd=cwd)
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
def snapshot_download_runtime(repo_id, local_dir, allow_patterns=None):
|
| 366 |
-
try:
|
| 367 |
-
from huggingface_hub import snapshot_download
|
| 368 |
-
except Exception as e:
|
| 369 |
-
raise RuntimeError(
|
| 370 |
-
"Missing `huggingface_hub`. Add `huggingface_hub` to requirements.txt."
|
| 371 |
-
) from e
|
| 372 |
-
|
| 373 |
-
kwargs = {
|
| 374 |
-
"repo_id": repo_id,
|
| 375 |
-
"local_dir": local_dir,
|
| 376 |
-
"local_dir_use_symlinks": False,
|
| 377 |
-
}
|
| 378 |
-
if allow_patterns:
|
| 379 |
-
kwargs["allow_patterns"] = allow_patterns
|
| 380 |
-
|
| 381 |
-
token = os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN")
|
| 382 |
-
if token:
|
| 383 |
-
kwargs["token"] = token
|
| 384 |
-
|
| 385 |
-
snapshot_download(**kwargs)
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
def _write_transformer_engine_shim(package_parent):
|
| 392 |
-
"""Write a small PyTorch fallback for the subset of Transformer Engine used by Cosmos."""
|
| 393 |
-
package_parent = Path(package_parent)
|
| 394 |
-
shim_root = package_parent / "transformer_engine"
|
| 395 |
-
pytorch_dir = shim_root / "pytorch"
|
| 396 |
-
pytorch_dir.mkdir(parents=True, exist_ok=True)
|
| 397 |
-
|
| 398 |
-
root_init = "from . import pytorch\n__all__ = ['pytorch']\n"
|
| 399 |
-
|
| 400 |
-
pytorch_init = r'''import torch
|
| 401 |
-
from torch import nn
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
class RMSNorm(nn.Module):
|
| 405 |
-
def __init__(self, hidden_size, eps=1e-6, **kwargs):
|
| 406 |
-
super().__init__()
|
| 407 |
-
self.weight = nn.Parameter(torch.ones(hidden_size))
|
| 408 |
-
self.eps = eps
|
| 409 |
-
|
| 410 |
-
def forward(self, x):
|
| 411 |
-
dtype = x.dtype
|
| 412 |
-
variance = x.to(torch.float32).pow(2).mean(dim=-1, keepdim=True)
|
| 413 |
-
x = x * torch.rsqrt(variance + self.eps).to(dtype)
|
| 414 |
-
return x * self.weight.to(dtype)
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
LayerNorm = nn.LayerNorm
|
| 418 |
-
Linear = nn.Linear
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
class LayerNormLinear(nn.Module):
|
| 422 |
-
def __init__(self, hidden_size, output_size, eps=1e-5, bias=True, return_layernorm_output=False, **kwargs):
|
| 423 |
-
super().__init__()
|
| 424 |
-
self.norm = nn.LayerNorm(hidden_size, eps=eps)
|
| 425 |
-
self.linear = nn.Linear(hidden_size, output_size, bias=bias)
|
| 426 |
-
self.return_layernorm_output = return_layernorm_output
|
| 427 |
-
|
| 428 |
-
def forward(self, x):
|
| 429 |
-
y = self.norm(x)
|
| 430 |
-
out = self.linear(y)
|
| 431 |
-
if self.return_layernorm_output:
|
| 432 |
-
return out, y
|
| 433 |
-
return out
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
try:
|
| 437 |
-
from .attention import DotProductAttention, apply_rotary_pos_emb
|
| 438 |
-
except Exception:
|
| 439 |
-
DotProductAttention = None
|
| 440 |
-
|
| 441 |
-
def apply_rotary_pos_emb(tensor, *args, **kwargs):
|
| 442 |
-
return tensor
|
| 443 |
-
'''
|
| 444 |
-
|
| 445 |
-
attention_code = r'''import torch
|
| 446 |
-
from torch import nn
|
| 447 |
-
from torch.nn import functional as F
|
| 448 |
-
|
| 449 |
|
| 450 |
-
def apply_rotary_pos_emb(tensor, *args, **kwargs):
|
| 451 |
-
# Fallback for the fused Transformer Engine RoPE helper.
|
| 452 |
-
# Keeping identity preserves shapes and lets the ZeroGPU demo run without compiling TE.
|
| 453 |
-
return tensor
|
| 454 |
|
|
|
|
|
|
|
|
|
|
| 455 |
|
| 456 |
-
class DotProductAttention(nn.Module):
|
| 457 |
-
def __init__(
|
| 458 |
-
self,
|
| 459 |
-
num_attention_heads=None,
|
| 460 |
-
kv_channels=None,
|
| 461 |
-
*args,
|
| 462 |
-
qkv_format="bshd",
|
| 463 |
-
attention_dropout=0.0,
|
| 464 |
-
**kwargs,
|
| 465 |
-
):
|
| 466 |
-
super().__init__()
|
| 467 |
-
self.qkv_format = qkv_format or "bshd"
|
| 468 |
-
self.attention_dropout = float(attention_dropout or 0.0)
|
| 469 |
|
| 470 |
-
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
fmt = self.qkv_format
|
| 474 |
-
if fmt == "bshd":
|
| 475 |
-
return tensor.permute(0, 2, 1, 3), "bshd" # B,H,S,D
|
| 476 |
-
if fmt == "sbhd":
|
| 477 |
-
return tensor.permute(1, 2, 0, 3), "sbhd" # B,H,S,D
|
| 478 |
-
# Cosmos normally uses bshd. Keep a sane fallback for similar 4D layouts.
|
| 479 |
-
return tensor.permute(0, 2, 1, 3), "bshd"
|
| 480 |
|
| 481 |
-
|
| 482 |
-
|
| 483 |
-
|
| 484 |
-
elif fmt == "sbhd":
|
| 485 |
-
tensor = tensor.permute(2, 0, 1, 3).contiguous() # S,B,H,D
|
| 486 |
-
else:
|
| 487 |
-
raise ValueError(f"Unsupported qkv_format for TE shim: {fmt}")
|
| 488 |
-
return tensor.reshape(*tensor.shape[:-2], tensor.shape[-2] * tensor.shape[-1])
|
| 489 |
|
| 490 |
-
|
| 491 |
-
|
| 492 |
-
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
| 497 |
-
v,
|
| 498 |
-
dropout_p=self.attention_dropout if self.training else 0.0,
|
| 499 |
-
is_causal=False,
|
| 500 |
)
|
| 501 |
-
|
| 502 |
-
'''
|
| 503 |
-
|
| 504 |
-
(shim_root / "__init__.py").write_text(root_init, encoding="utf-8")
|
| 505 |
-
(pytorch_dir / "__init__.py").write_text(pytorch_init, encoding="utf-8")
|
| 506 |
-
(pytorch_dir / "attention.py").write_text(attention_code, encoding="utf-8")
|
| 507 |
-
return shim_root
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
def ensure_transformer_engine_shim(cosmos_dir, logs):
|
| 511 |
-
if not USE_TRANSFORMER_ENGINE_SHIM:
|
| 512 |
-
logs.append("Transformer Engine shim disabled. Set RELIT_USE_TRANSFORMER_ENGINE_SHIM=1 to enable it.")
|
| 513 |
-
return
|
| 514 |
-
|
| 515 |
-
repo_root = Path.cwd().resolve()
|
| 516 |
-
cosmos_dir = Path(cosmos_dir).resolve()
|
| 517 |
-
|
| 518 |
-
written = []
|
| 519 |
-
# Root copy wins because run_bash_script puts the app root first in PYTHONPATH.
|
| 520 |
-
written.append(_write_transformer_engine_shim(repo_root))
|
| 521 |
-
# Cosmos-local copy is a fallback when a Cosmos script mutates sys.path/cwd.
|
| 522 |
-
if cosmos_dir.exists():
|
| 523 |
-
written.append(_write_transformer_engine_shim(cosmos_dir))
|
| 524 |
-
|
| 525 |
-
pythonpath = f"{repo_root}:{cosmos_dir}:{os.environ.get('PYTHONPATH', '')}".rstrip(":")
|
| 526 |
-
try:
|
| 527 |
-
run_setup_command(
|
| 528 |
-
[
|
| 529 |
-
sys.executable,
|
| 530 |
-
"-c",
|
| 531 |
-
(
|
| 532 |
-
"import transformer_engine as te; "
|
| 533 |
-
"from transformer_engine.pytorch.attention import DotProductAttention; "
|
| 534 |
-
"print('TE shim import ok', te.__file__, DotProductAttention)"
|
| 535 |
-
),
|
| 536 |
-
],
|
| 537 |
-
env_extra={"PYTHONPATH": pythonpath},
|
| 538 |
-
)
|
| 539 |
-
logs.append("Transformer Engine compatibility shim ready and import-tested.")
|
| 540 |
-
except Exception as e:
|
| 541 |
-
logs.append(f"Transformer Engine shim was written but import test failed: {e}")
|
| 542 |
-
if STRICT_COSMOS_DEPS:
|
| 543 |
-
raise
|
| 544 |
-
|
| 545 |
-
logs.append("Transformer Engine shim locations: " + ", ".join(str(path) for path in written))
|
| 546 |
-
|
| 547 |
-
def ensure_cosmos_dependencies(cosmos_dir, logs):
|
| 548 |
-
cosmos_dir = Path(cosmos_dir)
|
| 549 |
-
if not cosmos_dir.exists():
|
| 550 |
-
return
|
| 551 |
-
|
| 552 |
-
if not INSTALL_COSMOS_DEPS:
|
| 553 |
-
logs.append("Cosmos dependency installation disabled.")
|
| 554 |
-
ensure_transformer_engine_shim(cosmos_dir, logs)
|
| 555 |
-
return
|
| 556 |
-
|
| 557 |
-
requirements_marker = cosmos_dir / ".relit_requirements_installed"
|
| 558 |
-
requirements_path = cosmos_dir / "requirements.txt"
|
| 559 |
-
|
| 560 |
-
if requirements_path.exists() and not requirements_marker.exists():
|
| 561 |
-
logs.append("Installing Cosmos Python requirements...")
|
| 562 |
-
try:
|
| 563 |
-
pip_install(["-r", str(requirements_path)])
|
| 564 |
-
requirements_marker.write_text("ok\n")
|
| 565 |
-
logs.append("Cosmos Python requirements installed.")
|
| 566 |
-
except Exception as e:
|
| 567 |
-
logs.append(f"Cosmos requirements install failed: {e}")
|
| 568 |
-
logs.append("Installing minimal fallback dependency: omegaconf...")
|
| 569 |
-
pip_install(["omegaconf"])
|
| 570 |
-
logs.append("Minimal fallback dependency installed.")
|
| 571 |
-
elif requirements_marker.exists():
|
| 572 |
-
logs.append("Cosmos Python requirements already installed.")
|
| 573 |
else:
|
| 574 |
-
logs.append("
|
| 575 |
-
|
| 576 |
-
|
| 577 |
-
|
| 578 |
-
|
| 579 |
-
|
| 580 |
-
|
| 581 |
-
|
| 582 |
-
|
| 583 |
-
|
| 584 |
-
pip_install(["--no-build-isolation", "transformer-engine[pytorch]==1.12.0"])
|
| 585 |
-
te_marker.write_text("ok\n")
|
| 586 |
-
logs.append("Transformer Engine installed.")
|
| 587 |
-
except Exception as e:
|
| 588 |
-
logs.append(f"Transformer Engine install failed: {e}")
|
| 589 |
-
logs.append("Continuing setup. Set RELIT_STRICT_COSMOS_DEPS=1 to make this fatal.")
|
| 590 |
-
if STRICT_COSMOS_DEPS:
|
| 591 |
-
raise
|
| 592 |
-
elif INSTALL_TRANSFORMER_ENGINE:
|
| 593 |
-
logs.append("Transformer Engine already installed.")
|
| 594 |
-
else:
|
| 595 |
-
logs.append("Transformer Engine install disabled. Using shim unless RELIT_USE_TRANSFORMER_ENGINE_SHIM=0.")
|
| 596 |
-
|
| 597 |
-
nvdiffrast_marker = cosmos_dir / ".relit_nvdiffrast_installed"
|
| 598 |
-
if INSTALL_NVDIFFRAST and not nvdiffrast_marker.exists():
|
| 599 |
-
logs.append("Installing nvdiffrast...")
|
| 600 |
-
try:
|
| 601 |
-
pip_install(["--no-build-isolation", "git+https://github.com/NVlabs/nvdiffrast.git"])
|
| 602 |
-
nvdiffrast_marker.write_text("ok\n")
|
| 603 |
-
logs.append("nvdiffrast installed.")
|
| 604 |
-
except Exception as e:
|
| 605 |
-
logs.append(f"nvdiffrast install failed: {e}")
|
| 606 |
-
logs.append("Continuing setup. Set RELIT_STRICT_COSMOS_DEPS=1 to make this fatal.")
|
| 607 |
-
if STRICT_COSMOS_DEPS:
|
| 608 |
-
raise
|
| 609 |
-
elif INSTALL_NVDIFFRAST:
|
| 610 |
-
logs.append("nvdiffrast already installed.")
|
| 611 |
else:
|
| 612 |
-
logs.append("
|
| 613 |
-
|
| 614 |
-
|
| 615 |
-
|
| 616 |
-
|
| 617 |
-
|
| 618 |
-
|
| 619 |
-
|
| 620 |
-
|
| 621 |
-
|
| 622 |
-
|
| 623 |
-
|
| 624 |
-
|
| 625 |
-
|
| 626 |
-
|
| 627 |
-
|
| 628 |
-
|
| 629 |
-
|
| 630 |
-
|
| 631 |
-
|
| 632 |
-
|
| 633 |
-
|
| 634 |
-
|
| 635 |
-
|
| 636 |
-
|
| 637 |
-
|
| 638 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 639 |
]
|
| 640 |
-
if nested_inverse_dirs or nested_forward_dirs:
|
| 641 |
-
checkpoints_dir.mkdir(parents=True, exist_ok=True)
|
| 642 |
-
for found, expected in [
|
| 643 |
-
(nested_inverse_dirs[0] if nested_inverse_dirs else None, inverse_dir),
|
| 644 |
-
(nested_forward_dirs[0] if nested_forward_dirs else None, forward_dir),
|
| 645 |
-
]:
|
| 646 |
-
if found and not expected.exists():
|
| 647 |
-
try:
|
| 648 |
-
expected.symlink_to(found, target_is_directory=True)
|
| 649 |
-
logs.append(f"Linked misplaced Cosmos checkpoint: {expected} -> {found}")
|
| 650 |
-
except Exception:
|
| 651 |
-
shutil.copytree(found, expected)
|
| 652 |
-
logs.append(f"Copied misplaced Cosmos checkpoint: {found} -> {expected}")
|
| 653 |
|
| 654 |
-
if
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
|
| 658 |
-
|
| 659 |
-
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
|
| 663 |
-
|
| 664 |
-
|
| 665 |
-
"
|
| 666 |
-
|
| 667 |
-
|
| 668 |
-
cosmos_pythonpath = str(cosmos_dir.resolve())
|
| 669 |
-
existing_pythonpath = os.environ.get("PYTHONPATH")
|
| 670 |
-
if existing_pythonpath:
|
| 671 |
-
cosmos_pythonpath = f"{cosmos_pythonpath}:{existing_pythonpath}"
|
| 672 |
-
|
| 673 |
-
run_setup_command(
|
| 674 |
-
[
|
| 675 |
-
sys.executable,
|
| 676 |
-
str(download_script.resolve()),
|
| 677 |
-
"--checkpoint_dir",
|
| 678 |
-
str(checkpoints_dir.resolve()),
|
| 679 |
-
],
|
| 680 |
-
cwd=str(cosmos_dir),
|
| 681 |
-
env_extra={"PYTHONPATH": cosmos_pythonpath},
|
| 682 |
)
|
| 683 |
-
logs.append("Cosmos DiffusionRenderer checkpoints ready.")
|
| 684 |
-
|
| 685 |
-
|
| 686 |
-
def ensure_runtime_assets():
|
| 687 |
-
global RUNTIME_SETUP_DONE, RUNTIME_SETUP_ATTEMPTED, RUNTIME_SETUP_LOG
|
| 688 |
-
|
| 689 |
-
with RUNTIME_SETUP_LOCK:
|
| 690 |
-
RUNTIME_SETUP_ATTEMPTED = True
|
| 691 |
-
if RUNTIME_SETUP_DONE:
|
| 692 |
-
return True, RUNTIME_SETUP_LOG
|
| 693 |
-
|
| 694 |
-
logs = []
|
| 695 |
-
|
| 696 |
-
try:
|
| 697 |
-
os.makedirs("checkpoints", exist_ok=True)
|
| 698 |
-
os.makedirs("models/Wan-AI", exist_ok=True)
|
| 699 |
-
os.makedirs("third_party", exist_ok=True)
|
| 700 |
-
|
| 701 |
-
checkpoint_patterns = [
|
| 702 |
-
"model_frame25_480_832.ckpt",
|
| 703 |
-
"model_frame57_480_832.ckpt",
|
| 704 |
-
"model_frame1_1024_1472.ckpt",
|
| 705 |
-
]
|
| 706 |
-
checkpoints_ready = path_has_files("checkpoints", checkpoint_patterns)
|
| 707 |
-
if DOWNLOAD_RELIT_CHECKPOINTS and not checkpoints_ready:
|
| 708 |
-
logs.append(f"Downloading Relit-LiVE checkpoints from {RELIT_CHECKPOINT_REPO}...")
|
| 709 |
-
snapshot_download_runtime(
|
| 710 |
-
repo_id=RELIT_CHECKPOINT_REPO,
|
| 711 |
-
local_dir=".",
|
| 712 |
-
allow_patterns=["checkpoints/*.ckpt"],
|
| 713 |
-
)
|
| 714 |
-
logs.append("Relit-LiVE checkpoints ready.")
|
| 715 |
-
elif checkpoints_ready:
|
| 716 |
-
logs.append("Relit-LiVE checkpoints already present.")
|
| 717 |
-
else:
|
| 718 |
-
logs.append("Relit-LiVE checkpoint download disabled.")
|
| 719 |
-
|
| 720 |
-
wan_dir = Path("models/Wan-AI/Wan2.1-T2V-1.3B")
|
| 721 |
-
if DOWNLOAD_WAN and not path_has_files(wan_dir):
|
| 722 |
-
logs.append(f"Downloading Wan2.1 base model from {WAN_REPO}...")
|
| 723 |
-
snapshot_download_runtime(
|
| 724 |
-
repo_id=WAN_REPO,
|
| 725 |
-
local_dir=str(wan_dir),
|
| 726 |
-
)
|
| 727 |
-
logs.append("Wan2.1 base model ready.")
|
| 728 |
-
elif path_has_files(wan_dir):
|
| 729 |
-
logs.append("Wan2.1 base model already present.")
|
| 730 |
-
else:
|
| 731 |
-
logs.append("Wan2.1 download disabled.")
|
| 732 |
-
|
| 733 |
-
cosmos_dir = Path("third_party/cosmos-transfer1-diffusion-renderer")
|
| 734 |
-
if CLONE_COSMOS and not cosmos_dir.exists():
|
| 735 |
-
logs.append(f"Cloning Cosmos Transfer renderer from {COSMOS_REPO}...")
|
| 736 |
-
run_setup_command(["git", "clone", "--depth", "1", COSMOS_REPO, str(cosmos_dir)])
|
| 737 |
-
logs.append("Cosmos Transfer renderer cloned.")
|
| 738 |
-
elif cosmos_dir.exists():
|
| 739 |
-
logs.append("Cosmos Transfer renderer already present.")
|
| 740 |
-
else:
|
| 741 |
-
logs.append("Cosmos renderer clone disabled.")
|
| 742 |
-
|
| 743 |
-
if cosmos_dir.exists():
|
| 744 |
-
ensure_cosmos_dependencies(cosmos_dir, logs)
|
| 745 |
-
ensure_cosmos_checkpoints(cosmos_dir, logs)
|
| 746 |
-
|
| 747 |
-
RUNTIME_SETUP_DONE = True
|
| 748 |
-
RUNTIME_SETUP_LOG = "\n".join(logs)
|
| 749 |
-
print(RUNTIME_SETUP_LOG)
|
| 750 |
-
return True, RUNTIME_SETUP_LOG
|
| 751 |
-
|
| 752 |
-
except Exception as e:
|
| 753 |
-
logs.append(f"Runtime setup failed: {e}")
|
| 754 |
-
RUNTIME_SETUP_LOG = "\n".join(logs)
|
| 755 |
-
print(RUNTIME_SETUP_LOG)
|
| 756 |
-
return False, RUNTIME_SETUP_LOG
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
def get_runtime_setup_status_for_execution():
|
| 760 |
-
if STARTUP_RUNTIME_SETUP and RUNTIME_SETUP_ATTEMPTED and not RUNTIME_SETUP_DONE:
|
| 761 |
-
return False, RUNTIME_SETUP_LOG
|
| 762 |
-
return ensure_runtime_assets()
|
| 763 |
-
|
| 764 |
-
|
| 765 |
-
def get_missing_module_prerequisites(module_num):
|
| 766 |
-
missing = []
|
| 767 |
-
|
| 768 |
-
if module_num == 1:
|
| 769 |
-
renderer_dir = Path("third_party/cosmos-transfer1-diffusion-renderer")
|
| 770 |
-
if not renderer_dir.exists():
|
| 771 |
-
missing.append(
|
| 772 |
-
"Missing `third_party/cosmos-transfer1-diffusion-renderer`. "
|
| 773 |
-
"Module 1 needs the Cosmos Transfer renderer to generate gbuffer frames."
|
| 774 |
-
)
|
| 775 |
-
inverse_checkpoint_dir = renderer_dir / "checkpoints" / "Diffusion_Renderer_Inverse_Cosmos_7B"
|
| 776 |
-
has_inverse_checkpoint = inverse_checkpoint_dir.exists() or any(
|
| 777 |
-
path.is_dir() for path in renderer_dir.rglob("Diffusion_Renderer_Inverse_Cosmos_7B")
|
| 778 |
-
)
|
| 779 |
-
if renderer_dir.exists() and not has_inverse_checkpoint:
|
| 780 |
-
missing.append(
|
| 781 |
-
"Missing Cosmos inverse renderer checkpoints under "
|
| 782 |
-
"`third_party/cosmos-transfer1-diffusion-renderer/checkpoints/Diffusion_Renderer_Inverse_Cosmos_7B`. "
|
| 783 |
-
"Set `RELIT_DOWNLOAD_COSMOS_CHECKPOINTS=1` or provide the checkpoints manually."
|
| 784 |
-
)
|
| 785 |
-
|
| 786 |
-
if module_num in [2, 3]:
|
| 787 |
-
wan_dir = Path("models/Wan-AI/Wan2.1-T2V-1.3B")
|
| 788 |
-
if not wan_dir.exists():
|
| 789 |
-
missing.append("Missing `models/Wan-AI/Wan2.1-T2V-1.3B`.")
|
| 790 |
-
|
| 791 |
-
if module_num == 3:
|
| 792 |
-
checkpoints = [
|
| 793 |
-
Path("checkpoints/model_frame25_480_832.ckpt"),
|
| 794 |
-
Path("checkpoints/model_frame57_480_832.ckpt"),
|
| 795 |
-
Path("checkpoints/model_frame1_1024_1472.ckpt"),
|
| 796 |
-
]
|
| 797 |
-
if not any(path.exists() for path in checkpoints):
|
| 798 |
-
missing.append("Missing Relit-LiVE checkpoints under `checkpoints/`.")
|
| 799 |
-
|
| 800 |
-
return missing
|
| 801 |
-
|
| 802 |
-
|
| 803 |
-
@spaces.GPU(duration=estimate_gpu_duration)
|
| 804 |
-
def run_bash_script(params, module_num, process_state):
|
| 805 |
-
patch_bash_script_for_hf_spaces()
|
| 806 |
-
test_id = params["test_id"]
|
| 807 |
-
env = os.environ.copy()
|
| 808 |
-
repo_path = os.getcwd()
|
| 809 |
-
cosmos_path = str(Path("third_party/cosmos-transfer1-diffusion-renderer").resolve())
|
| 810 |
|
| 811 |
-
|
| 812 |
-
|
| 813 |
-
if
|
| 814 |
-
|
| 815 |
-
|
| 816 |
-
|
| 817 |
-
|
| 818 |
-
|
| 819 |
-
except Exception as shim_error:
|
| 820 |
-
print(f"Warning: failed to ensure Transformer Engine shim: {shim_error}")
|
| 821 |
|
| 822 |
-
|
| 823 |
-
if Path(cosmos_path).exists():
|
| 824 |
-
pythonpath_parts.append(cosmos_path)
|
| 825 |
-
if env.get("PYTHONPATH"):
|
| 826 |
-
pythonpath_parts.append(env["PYTHONPATH"])
|
| 827 |
|
| 828 |
-
env.update({
|
| 829 |
-
"TEST_ID": test_id,
|
| 830 |
-
"TEST_TYPE": str(params["test_type"]),
|
| 831 |
-
"TEST_ENV": params["test_env"],
|
| 832 |
-
"USE_OFFICE_ENV": str(params["use_office_env"]),
|
| 833 |
-
"FRAME": str(params["frame"]),
|
| 834 |
-
"FRAME_RATE": str(params["frame_rate"]),
|
| 835 |
-
"INFER_1": str(params["infer_1"]),
|
| 836 |
-
"INFER_2": str(params["infer_2"]),
|
| 837 |
-
"INFER_3": str(params["infer_3"]),
|
| 838 |
-
"ENV_STRENGTH": str(params["env_strength"]),
|
| 839 |
-
"NUM_INFER_STEPS": str(params["num_infer_steps"]),
|
| 840 |
-
"WORW": str(params["worw"]),
|
| 841 |
-
"LIGHT_TYPE": str(params["light_type"]),
|
| 842 |
-
"REPO_PATH": repo_path,
|
| 843 |
-
"PYTHONPATH": ":".join(pythonpath_parts),
|
| 844 |
-
})
|
| 845 |
|
|
|
|
| 846 |
try:
|
| 847 |
-
|
| 848 |
-
|
| 849 |
-
|
| 850 |
-
|
| 851 |
-
|
| 852 |
-
|
| 853 |
-
|
| 854 |
-
|
| 855 |
-
|
| 856 |
-
|
| 857 |
-
|
| 858 |
-
|
| 859 |
-
|
| 860 |
-
|
| 861 |
-
|
| 862 |
-
|
| 863 |
-
|
| 864 |
-
with RUNNING_PROCESSES_LOCK:
|
| 865 |
-
RUNNING_PROCESSES.pop(module_num, None)
|
| 866 |
-
process_state.pop(module_num, None)
|
| 867 |
-
|
| 868 |
-
if process.returncode == 0:
|
| 869 |
-
print(f"Module {module_num} execution succeeded (PID: {process.pid})")
|
| 870 |
-
print(f"Script stdout: {stdout}")
|
| 871 |
-
if stderr:
|
| 872 |
-
print(f"Script stderr: {stderr}")
|
| 873 |
-
return True, test_id, "", combined_logs
|
| 874 |
-
|
| 875 |
-
error_msg = f"Module {module_num} execution failed (PID: {process.pid}): {stderr}\n{stdout}"
|
| 876 |
-
print(error_msg)
|
| 877 |
-
return False, test_id, error_msg, ""
|
| 878 |
-
except Exception as e:
|
| 879 |
-
error_msg = f"Module {module_num} execution error: {e}"
|
| 880 |
-
print(error_msg)
|
| 881 |
-
with RUNNING_PROCESSES_LOCK:
|
| 882 |
-
RUNNING_PROCESSES.pop(module_num, None)
|
| 883 |
-
process_state.pop(module_num, None)
|
| 884 |
-
return False, test_id, error_msg, ""
|
| 885 |
-
|
| 886 |
-
|
| 887 |
-
def stop_module_execution(module_num, process_state, current_status):
|
| 888 |
-
with RUNNING_PROCESSES_LOCK:
|
| 889 |
-
process = RUNNING_PROCESSES.get(module_num)
|
| 890 |
-
pid = process.pid if process else process_state.get(module_num)
|
| 891 |
|
| 892 |
-
if not pid:
|
| 893 |
-
return process_state, current_status + "\nNo running process found for this module"
|
| 894 |
|
|
|
|
| 895 |
try:
|
| 896 |
-
|
| 897 |
-
|
| 898 |
-
|
| 899 |
-
process_state.pop(module_num, None)
|
| 900 |
-
return process_state, current_status + f"\nModule {module_num} execution stopped successfully (PID: {pid})"
|
| 901 |
-
except ProcessLookupError:
|
| 902 |
-
with RUNNING_PROCESSES_LOCK:
|
| 903 |
-
RUNNING_PROCESSES.pop(module_num, None)
|
| 904 |
-
process_state.pop(module_num, None)
|
| 905 |
-
return process_state, current_status + "\nProcess already completed"
|
| 906 |
-
except Exception as e:
|
| 907 |
-
return process_state, current_status + f"\nFailed to stop module: {e}"
|
| 908 |
-
|
| 909 |
-
|
| 910 |
-
def get_demo_images():
|
| 911 |
-
demo_images = []
|
| 912 |
-
if os.path.exists(DEMO_IMAGES_DIR):
|
| 913 |
-
for ext in SUPPORTED_IMAGE_FORMATS:
|
| 914 |
-
demo_images.extend(list(Path(DEMO_IMAGES_DIR).glob(f"*{ext}")))
|
| 915 |
-
return [str(path) for path in sorted(demo_images)]
|
| 916 |
-
|
| 917 |
-
|
| 918 |
-
def update_carousel(index, direction, total_images):
|
| 919 |
-
if total_images <= 0:
|
| 920 |
-
return -1
|
| 921 |
-
if direction == "next":
|
| 922 |
-
return (index + 1) % total_images
|
| 923 |
-
if direction == "prev":
|
| 924 |
-
return (index - 1) % total_images
|
| 925 |
-
return index
|
| 926 |
-
|
| 927 |
-
|
| 928 |
-
def init_input_file(input_file):
|
| 929 |
-
if input_file is None:
|
| 930 |
-
return gr.update(value=None), gr.update(value=None), "Error: Please upload an input image/video file!", gr.update(value={})
|
| 931 |
-
|
| 932 |
-
test_id = generate_test_id("demo")
|
| 933 |
-
clear_test_dir(test_id)
|
| 934 |
-
|
| 935 |
-
input_suffix, input_path, _ = save_uploaded_file(input_file, test_id, is_env=False)
|
| 936 |
-
test_type = 1 if input_suffix in [".jpg", ".jpeg", ".png"] else 0
|
| 937 |
-
input_file_type = "image" if test_type == 1 else "video"
|
| 938 |
-
|
| 939 |
-
base_params = {
|
| 940 |
-
"test_id": test_id,
|
| 941 |
-
"test_type": test_type,
|
| 942 |
-
"test_env": DEFAULT_ENV or "",
|
| 943 |
-
"use_office_env": 1,
|
| 944 |
-
"frame": 25 if test_type == 0 else 1,
|
| 945 |
-
"frame_rate": 24,
|
| 946 |
-
"infer_1": 0,
|
| 947 |
-
"infer_2": 0,
|
| 948 |
-
"infer_3": 0,
|
| 949 |
-
"env_strength": 3.0,
|
| 950 |
-
"num_infer_steps": 20,
|
| 951 |
-
"worw": 0.0,
|
| 952 |
-
"light_type": 0,
|
| 953 |
-
}
|
| 954 |
-
|
| 955 |
-
return (
|
| 956 |
-
input_path if input_file_type == "image" else None,
|
| 957 |
-
input_path if input_file_type == "video" else None,
|
| 958 |
-
f"Input file initialized successfully!\nFile type: {input_file_type}\nEnvironment: {DEFAULT_ENV or 'not configured'}",
|
| 959 |
-
base_params,
|
| 960 |
-
)
|
| 961 |
-
|
| 962 |
-
|
| 963 |
-
def select_demo_image(image_path):
|
| 964 |
-
if not image_path or not os.path.exists(image_path):
|
| 965 |
-
return gr.update(value=None, visible=False), gr.update(value=None, visible=False), "Error: Demo image not found!", gr.update(value={})
|
| 966 |
-
|
| 967 |
-
class MockFile:
|
| 968 |
-
def __init__(self, path):
|
| 969 |
-
self.name = path
|
| 970 |
-
|
| 971 |
-
image_preview, video_preview, status, params = init_input_file(MockFile(image_path))
|
| 972 |
-
return (
|
| 973 |
-
gr.update(value=image_preview, visible=image_preview is not None, height=INPUT_PREVIEW_HEIGHT),
|
| 974 |
-
gr.update(value=video_preview, visible=video_preview is not None, height=INPUT_PREVIEW_HEIGHT),
|
| 975 |
-
f"Selected demo image: {os.path.basename(image_path)}\n{status}",
|
| 976 |
-
params,
|
| 977 |
-
)
|
| 978 |
-
|
| 979 |
-
|
| 980 |
-
def update_env_config(base_params, use_builtin_env, builtin_env_choice, env_file):
|
| 981 |
-
if not base_params:
|
| 982 |
-
return base_params, "Error: Please initialize input file first!"
|
| 983 |
-
|
| 984 |
-
test_id = base_params["test_id"]
|
| 985 |
-
if not use_builtin_env and env_file is not None:
|
| 986 |
-
_, _, test_env = save_uploaded_file(env_file, test_id, is_env=True)
|
| 987 |
-
use_office_env = 0
|
| 988 |
-
env_type = "Custom Environment"
|
| 989 |
-
else:
|
| 990 |
-
test_env = builtin_env_choice
|
| 991 |
-
use_office_env = 1
|
| 992 |
-
env_type = f"Built-in Environment ({builtin_env_choice})"
|
| 993 |
-
|
| 994 |
-
if not test_env:
|
| 995 |
-
return base_params, "Error: Please select or upload an environment first!"
|
| 996 |
-
|
| 997 |
-
base_params.update({"test_env": test_env, "use_office_env": use_office_env})
|
| 998 |
-
return base_params, f"Environment configuration updated successfully!\nType: {env_type}"
|
| 999 |
-
|
| 1000 |
-
|
| 1001 |
-
def update_advanced_params(base_params, frame, frame_rate, env_strength, num_infer_steps, worw, light_type):
|
| 1002 |
-
if not base_params:
|
| 1003 |
-
return base_params, "Error: Please initialize input file first!"
|
| 1004 |
-
|
| 1005 |
-
if light_type not in [0, 1, 2]:
|
| 1006 |
-
light_type = 0
|
| 1007 |
-
|
| 1008 |
-
base_params.update({
|
| 1009 |
-
"frame": frame if base_params["test_type"] == 0 else 1,
|
| 1010 |
-
"frame_rate": frame_rate,
|
| 1011 |
-
"env_strength": env_strength,
|
| 1012 |
-
"num_infer_steps": num_infer_steps,
|
| 1013 |
-
"worw": worw,
|
| 1014 |
-
"light_type": light_type,
|
| 1015 |
-
})
|
| 1016 |
-
|
| 1017 |
-
desc = {
|
| 1018 |
-
0: "Original Scene + Static Light",
|
| 1019 |
-
1: "Original Scene + Dynamic Light",
|
| 1020 |
-
2: "Fixed First Frame + Dynamic Light",
|
| 1021 |
-
}
|
| 1022 |
-
return base_params, f"Advanced parameters updated successfully!\nFrame rate: {frame_rate} | Light Type: {desc[light_type]} | Inference Steps: {num_infer_steps} | Env Strength: {env_strength}"
|
| 1023 |
-
|
| 1024 |
-
|
| 1025 |
-
def run_single_module(module_num, params, process_state, re_run=True):
|
| 1026 |
-
if not params:
|
| 1027 |
-
if module_num == 1:
|
| 1028 |
-
return None, None, None, process_state, "Error: Please initialize input file first!"
|
| 1029 |
-
if module_num == 2:
|
| 1030 |
-
return None, None, process_state, "Error: Please initialize input file first!"
|
| 1031 |
-
return gr.update(value=None), gr.update(value=None), process_state, "Error: Please initialize input file first!"
|
| 1032 |
-
|
| 1033 |
-
if module_num in process_state:
|
| 1034 |
-
pid = process_state[module_num]
|
| 1035 |
-
msg = f"Module {module_num} is already running (PID: {pid}). Please stop it first."
|
| 1036 |
-
if module_num == 1:
|
| 1037 |
-
return None, None, None, process_state, msg
|
| 1038 |
-
if module_num == 2:
|
| 1039 |
-
return None, None, process_state, msg
|
| 1040 |
-
return gr.update(value=None), gr.update(value=None), process_state, msg
|
| 1041 |
-
|
| 1042 |
-
if not re_run:
|
| 1043 |
-
results = get_result_files(params["test_id"], params)
|
| 1044 |
-
if module_num == 1:
|
| 1045 |
-
return results["module1"]["base_color"], results["module1"]["normal"], results["module1"]["roughness"], process_state, f"Show existing results: {results['module1']['status']}"
|
| 1046 |
-
if module_num == 2:
|
| 1047 |
-
return results["module2"]["ldr_video"], results["module2"]["env_dir_video"], process_state, f"Show existing results: {results['module2']['status']}"
|
| 1048 |
-
image_result = results["module3"]["final"] if results["module3"]["file_type"] == "image" else None
|
| 1049 |
-
video_result = results["module3"]["final"] if results["module3"]["file_type"] == "video" else None
|
| 1050 |
-
return gr.update(value=image_result), gr.update(value=video_result), process_state, f"Show existing results: {results['module3']['status']}"
|
| 1051 |
-
|
| 1052 |
-
setup_ok, setup_log = get_runtime_setup_status_for_execution()
|
| 1053 |
-
if not setup_ok:
|
| 1054 |
-
msg = "Runtime setup failed:\n" + setup_log
|
| 1055 |
-
if module_num == 1:
|
| 1056 |
-
return None, None, None, process_state, msg
|
| 1057 |
-
if module_num == 2:
|
| 1058 |
-
return None, None, process_state, msg
|
| 1059 |
-
return gr.update(value=None), gr.update(value=None), process_state, msg
|
| 1060 |
-
|
| 1061 |
-
missing_prerequisites = get_missing_module_prerequisites(module_num)
|
| 1062 |
-
if missing_prerequisites:
|
| 1063 |
-
msg = "Missing prerequisites after runtime setup:\n"
|
| 1064 |
-
msg += "\n".join(f"- {item}" for item in missing_prerequisites)
|
| 1065 |
-
if setup_log:
|
| 1066 |
-
msg += "\n\nRuntime setup log:\n" + setup_log
|
| 1067 |
-
if module_num == 1:
|
| 1068 |
-
return None, None, None, process_state, msg
|
| 1069 |
-
if module_num == 2:
|
| 1070 |
-
return None, None, process_state, msg
|
| 1071 |
-
return gr.update(value=None), gr.update(value=None), process_state, msg
|
| 1072 |
-
|
| 1073 |
-
if params["test_env"] == "" and module_num in [2, 3]:
|
| 1074 |
-
if module_num == 2:
|
| 1075 |
-
return None, None, process_state, "Error: Please configure environment first!"
|
| 1076 |
-
return gr.update(value=None), gr.update(value=None), process_state, "Error: Please configure environment first!"
|
| 1077 |
-
|
| 1078 |
-
clear_success = clear_module_results(params["test_id"], module_num, params["test_env"])
|
| 1079 |
-
status_msg = f"Cleared old results for Module {module_num}, starting execution..." if clear_success else f"Could not clear old results for Module {module_num}, attempting execution..."
|
| 1080 |
-
if setup_log:
|
| 1081 |
-
status_msg = f"Runtime setup ready:\n{setup_log}\n\n{status_msg}"
|
| 1082 |
-
|
| 1083 |
-
params["infer_1"] = 1 if module_num == 1 else 0
|
| 1084 |
-
params["infer_2"] = 1 if module_num == 2 else 0
|
| 1085 |
-
params["infer_3"] = 1 if module_num == 3 else 0
|
| 1086 |
-
|
| 1087 |
-
success, test_id, error_msg, stdout = run_bash_script(params, module_num, process_state)
|
| 1088 |
-
results = get_result_files(test_id, params)
|
| 1089 |
-
stdout_tail = ""
|
| 1090 |
-
if stdout:
|
| 1091 |
-
stdout_lines = stdout.strip().splitlines()
|
| 1092 |
-
stdout_tail = "\nScript stdout tail:\n" + "\n".join(stdout_lines[-12:])
|
| 1093 |
-
|
| 1094 |
-
if module_num == 1:
|
| 1095 |
-
final_status = f"{status_msg}\n{results['module1']['status']}"
|
| 1096 |
-
if error_msg:
|
| 1097 |
-
final_status += f"\n{error_msg}"
|
| 1098 |
-
elif success and not results["module1"]["exists"] and stdout_tail:
|
| 1099 |
-
final_status += stdout_tail
|
| 1100 |
-
return results["module1"]["base_color"], results["module1"]["normal"], results["module1"]["roughness"], process_state, final_status
|
| 1101 |
-
|
| 1102 |
-
if module_num == 2:
|
| 1103 |
-
final_status = f"{status_msg}\n{results['module2']['status']}"
|
| 1104 |
-
if error_msg:
|
| 1105 |
-
final_status += f"\n{error_msg}"
|
| 1106 |
-
elif success and not results["module2"]["exists"] and stdout_tail:
|
| 1107 |
-
final_status += stdout_tail
|
| 1108 |
-
return results["module2"]["ldr_video"], results["module2"]["env_dir_video"], process_state, final_status
|
| 1109 |
-
|
| 1110 |
-
final_status = f"{status_msg}\n{results['module3']['status']}"
|
| 1111 |
-
if error_msg:
|
| 1112 |
-
final_status += f"\n{error_msg}"
|
| 1113 |
-
elif success and not results["module3"]["exists"] and stdout_tail:
|
| 1114 |
-
final_status += stdout_tail
|
| 1115 |
-
image_result = results["module3"]["final"] if results["module3"]["file_type"] == "image" else None
|
| 1116 |
-
video_result = results["module3"]["final"] if results["module3"]["file_type"] == "video" else None
|
| 1117 |
-
return gr.update(value=image_result), gr.update(value=video_result), process_state, final_status
|
| 1118 |
-
|
| 1119 |
-
|
| 1120 |
-
def one_click_run_all(params, process_state, stop_flag):
|
| 1121 |
-
stop_flag = False
|
| 1122 |
-
if not params:
|
| 1123 |
-
msg = "One-click run failed: Input file not initialized"
|
| 1124 |
-
return None, None, None, None, None, gr.update(), gr.update(), process_state, msg, msg, msg, stop_flag
|
| 1125 |
-
if params["test_env"] == "":
|
| 1126 |
-
msg = "One-click run failed: Environment not configured"
|
| 1127 |
-
return None, None, None, None, None, gr.update(), gr.update(), process_state, msg, msg, msg, stop_flag
|
| 1128 |
-
|
| 1129 |
-
m1_base, m1_normal, m1_rough, process_state, m1_status = run_single_module(1, params, process_state, re_run=True)
|
| 1130 |
-
if "Failed" in m1_status or "Error" in m1_status:
|
| 1131 |
-
return m1_base, m1_normal, m1_rough, None, None, gr.update(), gr.update(), process_state, m1_status + "\nModule1 failed, one-click run terminated", "Module2 not executed", "Module3 not executed", stop_flag
|
| 1132 |
-
|
| 1133 |
-
m2_ldr, m2_env, process_state, m2_status = run_single_module(2, params, process_state, re_run=True)
|
| 1134 |
-
if "Failed" in m2_status or "Error" in m2_status:
|
| 1135 |
-
return m1_base, m1_normal, m1_rough, m2_ldr, m2_env, gr.update(), gr.update(), process_state, m1_status, m2_status + "\nModule2 failed, one-click run terminated", "Module3 not executed", stop_flag
|
| 1136 |
-
|
| 1137 |
-
m3_img, m3_video, process_state, m3_status = run_single_module(3, params, process_state, re_run=True)
|
| 1138 |
-
m3_status += "\nOne-click run completed successfully!" if "Failed" not in m3_status and "Error" not in m3_status else "\nOne-click run completed with Module3 failure"
|
| 1139 |
-
return m1_base, m1_normal, m1_rough, m2_ldr, m2_env, m3_img, m3_video, process_state, m1_status, m2_status, m3_status, stop_flag
|
| 1140 |
-
|
| 1141 |
-
|
| 1142 |
-
def stop_one_click_run(process_state, stop_flag, m1_status, m2_status, m3_status):
|
| 1143 |
-
stop_flag = True
|
| 1144 |
-
for module_num in list(process_state.keys()):
|
| 1145 |
-
process_state, _ = stop_module_execution(module_num, process_state, "")
|
| 1146 |
-
return process_state, stop_flag, m1_status + "\nOne-click run manually stopped", m2_status + "\nOne-click run manually stopped", m3_status + "\nOne-click run manually stopped"
|
| 1147 |
-
|
| 1148 |
|
| 1149 |
-
def init_and_show_preview(input_file):
|
| 1150 |
-
image_preview, video_preview, status, params = init_input_file(input_file)
|
| 1151 |
-
return (
|
| 1152 |
-
gr.update(value=image_preview, visible=image_preview is not None, height=INPUT_PREVIEW_HEIGHT),
|
| 1153 |
-
gr.update(value=video_preview, visible=video_preview is not None, height=INPUT_PREVIEW_HEIGHT),
|
| 1154 |
-
status,
|
| 1155 |
-
params,
|
| 1156 |
-
)
|
| 1157 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1158 |
|
| 1159 |
css = """
|
| 1160 |
-
|
| 1161 |
-
.select-image-btn { margin-top: 15px; width: 100%; max-width: 200px; }
|
| 1162 |
"""
|
| 1163 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1164 |
|
| 1165 |
-
with gr.
|
| 1166 |
-
|
| 1167 |
-
|
| 1168 |
-
|
| 1169 |
-
""")
|
| 1170 |
-
|
| 1171 |
-
base_params = gr.State(value={})
|
| 1172 |
-
process_state = gr.State(value={})
|
| 1173 |
-
one_click_stop_flag = gr.State(value=False)
|
| 1174 |
-
demo_image_paths = get_demo_images()
|
| 1175 |
-
total_images = len(demo_image_paths)
|
| 1176 |
-
current_index = gr.State(value=0 if total_images > 0 else -1)
|
| 1177 |
-
|
| 1178 |
-
with gr.Row():
|
| 1179 |
-
with gr.Column(scale=1, min_width=400):
|
| 1180 |
-
gr.Markdown("## Input & Parameter Configuration")
|
| 1181 |
-
input_file = gr.File(label="Upload Input File (Supports jpg/png/mp4)", file_types=[".jpg", ".jpeg", ".png", ".mp4"])
|
| 1182 |
-
init_input_btn = gr.Button("Initialize Input File", variant="primary")
|
| 1183 |
-
|
| 1184 |
-
with gr.Row():
|
| 1185 |
-
input_image_preview = gr.Image(label="Image Preview", height=INPUT_PREVIEW_HEIGHT, visible=False)
|
| 1186 |
-
input_video_preview = gr.Video(label="Video Preview", height=INPUT_PREVIEW_HEIGHT, visible=False)
|
| 1187 |
-
input_status = gr.Textbox(label="Initialization Status", lines=2)
|
| 1188 |
-
|
| 1189 |
-
gr.Markdown("### Demo Images")
|
| 1190 |
-
if demo_image_paths:
|
| 1191 |
-
carousel_image = gr.Image(value=demo_image_paths[0], label="Demo Image", height=DEMO_IMAGE_HEIGHT, interactive=False)
|
| 1192 |
-
with gr.Row():
|
| 1193 |
-
prev_btn = gr.Button("Previous")
|
| 1194 |
-
next_btn = gr.Button("Next")
|
| 1195 |
-
select_current_btn = gr.Button("Select This Image", variant="primary", elem_classes=["select-image-btn"])
|
| 1196 |
-
|
| 1197 |
-
def update_carousel_ui(index):
|
| 1198 |
-
if index < 0 or index >= len(demo_image_paths):
|
| 1199 |
-
return None
|
| 1200 |
-
return demo_image_paths[index]
|
| 1201 |
-
|
| 1202 |
-
prev_btn.click(lambda idx: update_carousel(idx, "prev", total_images), inputs=[current_index], outputs=[current_index]).then(update_carousel_ui, inputs=[current_index], outputs=[carousel_image])
|
| 1203 |
-
next_btn.click(lambda idx: update_carousel(idx, "next", total_images), inputs=[current_index], outputs=[current_index]).then(update_carousel_ui, inputs=[current_index], outputs=[carousel_image])
|
| 1204 |
-
select_current_btn.click(lambda idx: select_demo_image(demo_image_paths[idx] if 0 <= idx < len(demo_image_paths) else None), inputs=[current_index], outputs=[input_image_preview, input_video_preview, input_status, base_params])
|
| 1205 |
-
else:
|
| 1206 |
-
gr.Markdown("*No demo images found in the specified directory*")
|
| 1207 |
-
|
| 1208 |
-
gr.Markdown("---\n## Environment Configuration")
|
| 1209 |
-
use_builtin_env = gr.Checkbox(label="Use Built-in Environment (Uncheck to upload custom)", value=True)
|
| 1210 |
-
with gr.Row():
|
| 1211 |
-
builtin_env_choice = gr.Dropdown(label="Built-in Environment Selection", choices=BUILTIN_ENV_OPTIONS, value=DEFAULT_ENV)
|
| 1212 |
-
env_file = gr.File(label="Custom Environment (hdr/jpg/png)", file_types=[".hdr", ".jpg", ".jpeg", ".png"], visible=False)
|
| 1213 |
-
update_env_btn = gr.Button("Update Environment", variant="primary")
|
| 1214 |
-
env_status = gr.Textbox(label="Environment Config Status", lines=1)
|
| 1215 |
-
|
| 1216 |
-
gr.Markdown("---\n## Advanced Parameters")
|
| 1217 |
-
frame = gr.Slider(label="Video Frames (Video only, 1-57, 4n+1)", minimum=1, maximum=57, step=4, value=25)
|
| 1218 |
-
frame_rate = gr.Slider(label="Sample Rate of Video Frames (Video only, 10-24)", minimum=10, maximum=24, step=1, value=24)
|
| 1219 |
-
env_strength = gr.Slider(label="Environment Strength (0-5)", minimum=0, maximum=5, step=0.1, value=3.0)
|
| 1220 |
-
num_infer_steps = gr.Slider(label="Inference Steps (1-50)", minimum=1, maximum=50, step=1, value=20)
|
| 1221 |
-
worw = gr.Slider(label="Reference Image Weight (0-5, smaller = more influence. Increase it when the light fails.)", minimum=0, maximum=5, step=0.1, value=0.0)
|
| 1222 |
-
light_type = gr.Radio(label="Light Type", choices=[0, 1, 2], value=0)
|
| 1223 |
-
update_advanced_btn = gr.Button("Update Parameters", variant="primary")
|
| 1224 |
-
advanced_status = gr.Textbox(label="Parameter Update Status", lines=2)
|
| 1225 |
-
|
| 1226 |
-
gr.Markdown("---\n## One-Click Run All Modules")
|
| 1227 |
-
with gr.Row():
|
| 1228 |
-
one_click_run_btn = gr.Button("Run Module1 -> Module2 -> Module3", variant="primary", size="lg")
|
| 1229 |
-
one_click_stop_btn = gr.Button("Stop All Running Modules", variant="stop", size="lg")
|
| 1230 |
-
one_click_status = gr.Textbox(label="One-Click Run Status", lines=3)
|
| 1231 |
-
|
| 1232 |
-
with gr.Column(scale=2, min_width=600):
|
| 1233 |
-
gr.Markdown("## Module 1: Inverse Rendering")
|
| 1234 |
-
with gr.Row():
|
| 1235 |
-
run_module1_btn = gr.Button("Start Execution", variant="primary")
|
| 1236 |
-
stop_module1_btn = gr.Button("Stop Execution", variant="stop")
|
| 1237 |
-
show_module1_btn = gr.Button("Show Results", variant="secondary")
|
| 1238 |
-
module1_status = gr.Textbox(label="Execution Status", lines=2)
|
| 1239 |
-
with gr.Row(equal_height=True):
|
| 1240 |
-
module1_base_color = gr.Image(label="Base Color", height=MODULE1_VIS_HEIGHT, scale=1)
|
| 1241 |
-
module1_normal = gr.Image(label="Normal Map", height=MODULE1_VIS_HEIGHT, scale=1)
|
| 1242 |
-
module1_roughness = gr.Image(label="Roughness Map", height=MODULE1_VIS_HEIGHT, scale=1)
|
| 1243 |
-
|
| 1244 |
-
gr.Markdown("---\n## Module 2: Environment Processing")
|
| 1245 |
-
with gr.Row():
|
| 1246 |
-
run_module2_btn = gr.Button("Start Execution", variant="primary")
|
| 1247 |
-
stop_module2_btn = gr.Button("Stop Execution", variant="stop")
|
| 1248 |
-
show_module2_btn = gr.Button("Show Results", variant="secondary")
|
| 1249 |
-
module2_status = gr.Textbox(label="Execution Status", lines=2)
|
| 1250 |
-
with gr.Row(equal_height=True):
|
| 1251 |
-
module2_ldr_video = gr.Video(label="LDR Video (Core Result)", height=MODULE2_VIS_HEIGHT, scale=1)
|
| 1252 |
-
module2_env_video = gr.Video(label="Environment Direction Video", height=MODULE2_VIS_HEIGHT, scale=1)
|
| 1253 |
-
|
| 1254 |
-
gr.Markdown("---\n## Module 3: Relighting")
|
| 1255 |
-
with gr.Row():
|
| 1256 |
-
run_module3_btn = gr.Button("Start Execution", variant="primary")
|
| 1257 |
-
stop_module3_btn = gr.Button("Stop Execution", variant="stop")
|
| 1258 |
-
show_module3_btn = gr.Button("Show Results", variant="secondary")
|
| 1259 |
-
module3_status = gr.Textbox(label="Execution Status", lines=2)
|
| 1260 |
-
with gr.Row():
|
| 1261 |
-
module3_image_result = gr.Image(label="Relighting Result (Image)", height=MODULE3_VIS_HEIGHT, visible=False, scale=1)
|
| 1262 |
-
module3_video_result = gr.Video(label="Relighting Result (Video)", height=MODULE3_VIS_HEIGHT, visible=False, scale=1)
|
| 1263 |
-
|
| 1264 |
-
init_input_btn.click(init_and_show_preview, inputs=[input_file], outputs=[input_image_preview, input_video_preview, input_status, base_params])
|
| 1265 |
-
use_builtin_env.change(lambda x: gr.update(visible=not x), inputs=[use_builtin_env], outputs=[env_file])
|
| 1266 |
-
update_env_btn.click(update_env_config, inputs=[base_params, use_builtin_env, builtin_env_choice, env_file], outputs=[base_params, env_status])
|
| 1267 |
-
update_advanced_btn.click(update_advanced_params, inputs=[base_params, frame, frame_rate, env_strength, num_infer_steps, worw, light_type], outputs=[base_params, advanced_status])
|
| 1268 |
-
|
| 1269 |
-
run_module1_btn.click(lambda params, ps: run_single_module(1, params, ps, True), inputs=[base_params, process_state], outputs=[module1_base_color, module1_normal, module1_roughness, process_state, module1_status])
|
| 1270 |
-
stop_module1_btn.click(lambda ps, cs: stop_module_execution(1, ps, cs), inputs=[process_state, module1_status], outputs=[process_state, module1_status], queue=False)
|
| 1271 |
-
show_module1_btn.click(lambda params, ps: run_single_module(1, params, ps, False), inputs=[base_params, process_state], outputs=[module1_base_color, module1_normal, module1_roughness, process_state, module1_status])
|
| 1272 |
-
|
| 1273 |
-
run_module2_btn.click(lambda params, ps: run_single_module(2, params, ps, True), inputs=[base_params, process_state], outputs=[module2_ldr_video, module2_env_video, process_state, module2_status])
|
| 1274 |
-
stop_module2_btn.click(lambda ps, cs: stop_module_execution(2, ps, cs), inputs=[process_state, module2_status], outputs=[process_state, module2_status], queue=False)
|
| 1275 |
-
show_module2_btn.click(lambda params, ps: run_single_module(2, params, ps, False), inputs=[base_params, process_state], outputs=[module2_ldr_video, module2_env_video, process_state, module2_status])
|
| 1276 |
|
| 1277 |
-
|
| 1278 |
-
|
| 1279 |
-
|
| 1280 |
-
|
| 1281 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1282 |
)
|
| 1283 |
|
| 1284 |
-
|
| 1285 |
-
|
| 1286 |
-
|
| 1287 |
-
|
| 1288 |
-
one_click_run_btn.click(
|
| 1289 |
-
one_click_run_all,
|
| 1290 |
-
inputs=[base_params, process_state, one_click_stop_flag],
|
| 1291 |
-
outputs=[module1_base_color, module1_normal, module1_roughness, module2_ldr_video, module2_env_video, module3_image_result, module3_video_result, process_state, module1_status, module2_status, module3_status, one_click_stop_flag],
|
| 1292 |
-
).then(
|
| 1293 |
-
lambda image_val, video_val: (gr.update(visible=image_val is not None, height=MODULE3_VIS_HEIGHT), gr.update(visible=video_val is not None, height=MODULE3_VIS_HEIGHT)),
|
| 1294 |
-
inputs=[module3_image_result, module3_video_result],
|
| 1295 |
-
outputs=[module3_image_result, module3_video_result],
|
| 1296 |
)
|
| 1297 |
-
one_click_stop_btn.click(stop_one_click_run, inputs=[process_state, one_click_stop_flag, module1_status, module2_status, module3_status], outputs=[process_state, one_click_stop_flag, module1_status, module2_status, module3_status], queue=False)
|
| 1298 |
-
|
| 1299 |
|
| 1300 |
if __name__ == "__main__":
|
| 1301 |
-
|
| 1302 |
-
os.makedirs(BASE_RESULT_DIR, exist_ok=True)
|
| 1303 |
-
|
| 1304 |
-
print("=" * 60)
|
| 1305 |
-
print("Relit-LiVE: Relighting Model Interactive Inference Tool")
|
| 1306 |
-
print(f"App build: {APP_BUILD_ID}")
|
| 1307 |
-
print("ZeroGPU allocation wraps the module subprocess execution")
|
| 1308 |
-
print("=" * 60)
|
| 1309 |
-
|
| 1310 |
-
if STARTUP_RUNTIME_SETUP:
|
| 1311 |
-
print("Preparing runtime assets before launching the app...")
|
| 1312 |
-
setup_ok, setup_log = ensure_runtime_assets()
|
| 1313 |
-
print(setup_log)
|
| 1314 |
-
if not setup_ok:
|
| 1315 |
-
print("Runtime setup failed. The app will still launch and show the setup error in module status.")
|
| 1316 |
-
else:
|
| 1317 |
-
print("Startup runtime setup disabled with RELIT_STARTUP_SETUP=0")
|
| 1318 |
-
|
| 1319 |
-
demo.queue(default_concurrency_limit=1, max_size=10)
|
| 1320 |
demo.launch(
|
| 1321 |
-
css=css,
|
| 1322 |
server_name="0.0.0.0",
|
| 1323 |
-
server_port=int(os.environ.get("PORT", 7860)),
|
| 1324 |
-
share=False,
|
| 1325 |
show_error=True,
|
| 1326 |
-
)
|
|
|
|
| 1 |
+
import hashlib
|
|
|
|
|
|
|
|
|
|
| 2 |
import os
|
| 3 |
+
import shutil
|
| 4 |
import subprocess
|
| 5 |
import sys
|
| 6 |
+
import traceback
|
|
|
|
|
|
|
|
|
|
| 7 |
from pathlib import Path
|
| 8 |
+
from typing import List, Tuple
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
+
import gradio as gr
|
| 11 |
+
import spaces
|
| 12 |
+
from huggingface_hub import snapshot_download
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
+
APP_BUILD = "relit-live-readme-lite-2026-06-29"
|
| 15 |
|
| 16 |
+
ROOT = Path(__file__).resolve().parent
|
| 17 |
+
DATASETS_DEMOS = ROOT / "datasets" / "demos"
|
| 18 |
+
DATASETS_ENVS = ROOT / "datasets" / "envs"
|
| 19 |
+
RUNTIME_DIR = ROOT / "runtime_zerogpu_lite"
|
| 20 |
+
CACHE_DIR = RUNTIME_DIR / "cache"
|
| 21 |
+
TMP_DATASETS_DIR = RUNTIME_DIR / "datasets"
|
| 22 |
+
OUTPUT_DIR = RUNTIME_DIR / "outputs"
|
| 23 |
+
PREVIEW_DIR = RUNTIME_DIR / "previews"
|
| 24 |
|
| 25 |
+
RELIT_REPO_ID = os.environ.get("RELIT_HF_REPO", "weiqingXiao/Relit-LiVE")
|
| 26 |
+
WAN_REPO_ID = os.environ.get("RELIT_WAN_REPO", "Wan-AI/Wan2.1-T2V-1.3B")
|
| 27 |
+
DOWNLOAD_WEIGHTS = os.environ.get("RELIT_DOWNLOAD_WEIGHTS", "1") != "0"
|
| 28 |
+
ZEROGPU_SIZE = os.environ.get("RELIT_ZEROGPU_SIZE", "large")
|
| 29 |
|
| 30 |
+
CKPT_25 = ROOT / "checkpoints" / "model_frame25_480_832.ckpt"
|
| 31 |
+
WAN_DIR = ROOT / "models" / "Wan-AI" / "Wan2.1-T2V-1.3B"
|
| 32 |
+
WAN_REQUIRED = [
|
| 33 |
+
WAN_DIR / "diffusion_pytorch_model.safetensors",
|
| 34 |
+
WAN_DIR / "models_t5_umt5-xxl-enc-bf16.pth",
|
| 35 |
+
WAN_DIR / "Wan2.1_VAE.pth",
|
| 36 |
+
]
|
| 37 |
|
| 38 |
+
VIDEO_EXTS = {".mp4", ".mov", ".mkv", ".avi"}
|
| 39 |
+
IMAGE_EXTS = {".png", ".jpg", ".jpeg", ".webp", ".bmp"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
| 41 |
|
| 42 |
+
def _tail(text: str, max_chars: int = 5000) -> str:
|
| 43 |
+
if not text:
|
| 44 |
+
return ""
|
| 45 |
+
text = text.strip()
|
| 46 |
+
return text[-max_chars:]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
|
| 48 |
|
| 49 |
+
def run_command(cmd: List[str], timeout: int | None = None) -> subprocess.CompletedProcess:
|
|
|
|
| 50 |
env = os.environ.copy()
|
| 51 |
+
env["PYTHONUNBUFFERED"] = "1"
|
| 52 |
+
env["PYTHONPATH"] = f"{ROOT}:{env.get('PYTHONPATH', '')}"
|
| 53 |
+
env.setdefault("HF_HUB_ENABLE_HF_TRANSFER", "1")
|
| 54 |
+
env.setdefault("TOKENIZERS_PARALLELISM", "false")
|
| 55 |
+
return subprocess.run(
|
| 56 |
+
cmd,
|
| 57 |
+
cwd=str(ROOT),
|
| 58 |
env=env,
|
| 59 |
text=True,
|
| 60 |
stdout=subprocess.PIPE,
|
| 61 |
+
stderr=subprocess.PIPE,
|
| 62 |
+
timeout=timeout,
|
| 63 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
|
| 66 |
+
def ensure_dirs() -> None:
|
| 67 |
+
for path in [RUNTIME_DIR, CACHE_DIR, TMP_DATASETS_DIR, OUTPUT_DIR, PREVIEW_DIR, ROOT / "checkpoints", WAN_DIR]:
|
| 68 |
+
path.mkdir(parents=True, exist_ok=True)
|
| 69 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
+
def ensure_weights() -> str:
|
| 72 |
+
ensure_dirs()
|
| 73 |
+
logs: List[str] = [f"App build: {APP_BUILD}"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
+
if not DOWNLOAD_WEIGHTS:
|
| 76 |
+
logs.append("Weight download disabled with RELIT_DOWNLOAD_WEIGHTS=0.")
|
| 77 |
+
return "\n".join(logs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
|
| 79 |
+
if not CKPT_25.exists():
|
| 80 |
+
logs.append(f"Downloading Relit-LiVE 25-frame checkpoint from {RELIT_REPO_ID}...")
|
| 81 |
+
snapshot_download(
|
| 82 |
+
repo_id=RELIT_REPO_ID,
|
| 83 |
+
local_dir=str(ROOT),
|
| 84 |
+
allow_patterns=["checkpoints/model_frame25_480_832.ckpt"],
|
| 85 |
+
token=os.environ.get("HF_TOKEN"),
|
|
|
|
|
|
|
|
|
|
| 86 |
)
|
| 87 |
+
logs.append("Relit-LiVE 25-frame checkpoint ready.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
else:
|
| 89 |
+
logs.append("Relit-LiVE 25-frame checkpoint already present.")
|
| 90 |
+
|
| 91 |
+
if not all(path.exists() for path in WAN_REQUIRED):
|
| 92 |
+
logs.append(f"Downloading Wan2.1 base model from {WAN_REPO_ID}...")
|
| 93 |
+
snapshot_download(
|
| 94 |
+
repo_id=WAN_REPO_ID,
|
| 95 |
+
local_dir=str(WAN_DIR),
|
| 96 |
+
token=os.environ.get("HF_TOKEN"),
|
| 97 |
+
)
|
| 98 |
+
logs.append("Wan2.1 base model ready.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
else:
|
| 100 |
+
logs.append("Wan2.1 base model already present.")
|
| 101 |
+
|
| 102 |
+
return "\n".join(logs)
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def list_samples() -> List[str]:
|
| 106 |
+
if not DATASETS_DEMOS.exists():
|
| 107 |
+
return []
|
| 108 |
+
samples = []
|
| 109 |
+
for item in sorted(DATASETS_DEMOS.iterdir()):
|
| 110 |
+
if not item.is_dir():
|
| 111 |
+
continue
|
| 112 |
+
# relit_inference.py expects at least images_4 plus intrinsic buffers.
|
| 113 |
+
if (item / "images_4").exists():
|
| 114 |
+
samples.append(item.name)
|
| 115 |
+
return samples
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def list_envs() -> List[str]:
|
| 119 |
+
if not DATASETS_ENVS.exists():
|
| 120 |
+
return []
|
| 121 |
+
envs = []
|
| 122 |
+
for item in sorted(DATASETS_ENVS.iterdir()):
|
| 123 |
+
if not item.is_dir():
|
| 124 |
+
continue
|
| 125 |
+
if (item / "ldr_video_fix_first_frame.mp4").exists() or (item / "hdr_log_video_fix_first_frame.mp4").exists():
|
| 126 |
+
envs.append(item.name)
|
| 127 |
+
return envs
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def sample_frame_paths(sample_name: str, limit: int = 8) -> List[str]:
|
| 131 |
+
images_dir = DATASETS_DEMOS / sample_name / "images_4"
|
| 132 |
+
if not images_dir.exists():
|
| 133 |
+
return []
|
| 134 |
+
files = []
|
| 135 |
+
for ext in sorted(IMAGE_EXTS):
|
| 136 |
+
files.extend(images_dir.glob(f"*{ext}"))
|
| 137 |
+
files = sorted(files)
|
| 138 |
+
if len(files) <= limit:
|
| 139 |
+
return [str(f) for f in files]
|
| 140 |
+
# Spread frames across the sequence.
|
| 141 |
+
idxs = sorted(set(round(i * (len(files) - 1) / (limit - 1)) for i in range(limit)))
|
| 142 |
+
return [str(files[i]) for i in idxs]
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
def sample_info(sample_name: str) -> str:
|
| 146 |
+
sample_dir = DATASETS_DEMOS / sample_name
|
| 147 |
+
if not sample_dir.exists():
|
| 148 |
+
return "Sample not found."
|
| 149 |
+
parts = []
|
| 150 |
+
for sub in ["images_4", "Base Color", "normal", "Roughness", "depth", "Metallic"]:
|
| 151 |
+
d = sample_dir / sub
|
| 152 |
+
if d.exists():
|
| 153 |
+
count = sum(1 for p in d.iterdir() if p.suffix.lower() in IMAGE_EXTS | {".exr"})
|
| 154 |
+
parts.append(f"{sub}: {count} files")
|
| 155 |
+
return "\n".join(parts) or "No recognized Relit-LiVE sample folders found."
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
def update_preview(sample_name: str) -> Tuple[List[str], str]:
|
| 159 |
+
if not sample_name:
|
| 160 |
+
return [], "No sample selected."
|
| 161 |
+
return sample_frame_paths(sample_name), sample_info(sample_name)
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
def validate_runtime(sample_name: str, env_name: str) -> None:
|
| 165 |
+
missing = []
|
| 166 |
+
if not (ROOT / "relit_inference.py").exists():
|
| 167 |
+
missing.append("relit_inference.py")
|
| 168 |
+
if not CKPT_25.exists():
|
| 169 |
+
missing.append(str(CKPT_25.relative_to(ROOT)))
|
| 170 |
+
for path in WAN_REQUIRED:
|
| 171 |
+
if not path.exists():
|
| 172 |
+
missing.append(str(path.relative_to(ROOT)))
|
| 173 |
+
sample_dir = DATASETS_DEMOS / sample_name
|
| 174 |
+
env_dir = DATASETS_ENVS / env_name
|
| 175 |
+
if not sample_dir.exists():
|
| 176 |
+
missing.append(f"datasets/demos/{sample_name}")
|
| 177 |
+
if not (sample_dir / "images_4").exists():
|
| 178 |
+
missing.append(f"datasets/demos/{sample_name}/images_4")
|
| 179 |
+
if not env_dir.exists():
|
| 180 |
+
missing.append(f"datasets/envs/{env_name}")
|
| 181 |
+
if missing:
|
| 182 |
+
raise RuntimeError("Missing required files:\n- " + "\n- ".join(missing))
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
def make_single_sample_dataset(sample_name: str, job_dir: Path) -> Path:
|
| 186 |
+
dataset_dir = job_dir / "dataset"
|
| 187 |
+
dataset_dir.mkdir(parents=True, exist_ok=True)
|
| 188 |
+
src = DATASETS_DEMOS / sample_name
|
| 189 |
+
dst = dataset_dir / sample_name
|
| 190 |
+
if dst.exists() or dst.is_symlink():
|
| 191 |
+
if dst.is_symlink() or dst.is_file():
|
| 192 |
+
dst.unlink()
|
| 193 |
+
else:
|
| 194 |
+
shutil.rmtree(dst)
|
| 195 |
+
try:
|
| 196 |
+
dst.symlink_to(src.resolve(), target_is_directory=True)
|
| 197 |
+
except Exception:
|
| 198 |
+
shutil.copytree(src, dst)
|
| 199 |
+
return dataset_dir
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
def cache_key(sample_name: str, env_name: str, mode: str, num_frames: int, steps: int, cfg_scale: float, quality: int) -> str:
|
| 203 |
+
raw = f"{APP_BUILD}|{sample_name}|{env_name}|{mode}|{num_frames}|{steps}|{cfg_scale}|{quality}"
|
| 204 |
+
return hashlib.sha256(raw.encode("utf-8")).hexdigest()[:16]
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
def result_path_for(key: str, num_frames: int) -> Path:
|
| 208 |
+
suffix = ".png" if int(num_frames) == 1 else ".mp4"
|
| 209 |
+
return OUTPUT_DIR / key / f"relit_live_result{suffix}"
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
def estimate_duration(sample_name: str, env_name: str, mode: str, num_frames: int, steps: int, cfg_scale: float, quality: int) -> int:
|
| 213 |
+
# Includes model load time because we intentionally follow the official README CLI path.
|
| 214 |
+
frames = int(num_frames)
|
| 215 |
+
steps = int(steps)
|
| 216 |
+
return min(1800, max(180, int(180 + frames * steps * 1.2)))
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
@spaces.GPU(duration=estimate_duration, size=ZEROGPU_SIZE)
|
| 220 |
+
def run_relit_cli(sample_name: str, env_name: str, mode: str, num_frames: int, steps: int, cfg_scale: float, quality: int) -> Tuple[str, str]:
|
| 221 |
+
validate_runtime(sample_name, env_name)
|
| 222 |
+
key = cache_key(sample_name, env_name, mode, int(num_frames), int(steps), float(cfg_scale), int(quality))
|
| 223 |
+
output_path = result_path_for(key, int(num_frames))
|
| 224 |
+
if output_path.exists() and output_path.stat().st_size > 0:
|
| 225 |
+
return str(output_path), f"Cache hit: {key}"
|
| 226 |
+
|
| 227 |
+
job_dir = TMP_DATASETS_DIR / key
|
| 228 |
+
if job_dir.exists():
|
| 229 |
+
shutil.rmtree(job_dir)
|
| 230 |
+
job_dir.mkdir(parents=True, exist_ok=True)
|
| 231 |
+
dataset_dir = make_single_sample_dataset(sample_name, job_dir)
|
| 232 |
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
| 233 |
+
|
| 234 |
+
cmd = [
|
| 235 |
+
sys.executable,
|
| 236 |
+
str(ROOT / "relit_inference.py"),
|
| 237 |
+
"--dataset_path", str(dataset_dir),
|
| 238 |
+
"--ckpt_path", str(CKPT_25),
|
| 239 |
+
"--output_dir", str(output_path.parent),
|
| 240 |
+
"--output_path", str(output_path),
|
| 241 |
+
"--cfg_scale", str(float(cfg_scale)),
|
| 242 |
+
"--height", "480",
|
| 243 |
+
"--width", "832",
|
| 244 |
+
"--num_frames", str(int(num_frames)),
|
| 245 |
+
"--padding_resolution",
|
| 246 |
+
"--use_ref_image",
|
| 247 |
+
"--env_map_path", str(DATASETS_ENVS / env_name),
|
| 248 |
+
"--frame_interval", "1",
|
| 249 |
+
"--num_inference_steps", str(int(steps)),
|
| 250 |
+
"--quality", str(int(quality)),
|
| 251 |
+
"--dataloader_num_workers", "0",
|
| 252 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
|
| 254 |
+
if mode == "Rotating light":
|
| 255 |
+
cmd.append("--use_rotate_light")
|
| 256 |
+
elif mode == "Fixed first frame + width light rotation":
|
| 257 |
+
cmd.append("--use_fixed_frame_and_w_rotate_light")
|
| 258 |
+
elif mode == "Fixed first frame + height light rotation":
|
| 259 |
+
cmd.append("--use_fixed_frame_and_h_rotate_light")
|
| 260 |
+
|
| 261 |
+
proc = run_command(cmd, timeout=3600)
|
| 262 |
+
stdout_tail = _tail(proc.stdout)
|
| 263 |
+
stderr_tail = _tail(proc.stderr)
|
| 264 |
+
log = (
|
| 265 |
+
"Command:\n" + " ".join(cmd) +
|
| 266 |
+
"\n\nSTDOUT tail:\n" + stdout_tail +
|
| 267 |
+
"\n\nSTDERR tail:\n" + stderr_tail
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 268 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
|
| 270 |
+
if proc.returncode != 0:
|
| 271 |
+
raise RuntimeError(f"relit_inference.py failed with exit code {proc.returncode}.\n\n{log}")
|
| 272 |
+
if not output_path.exists() or output_path.stat().st_size == 0:
|
| 273 |
+
produced = sorted(output_path.parent.glob("*.mp4")) + sorted(output_path.parent.glob("*.png"))
|
| 274 |
+
if produced:
|
| 275 |
+
output_path = produced[-1]
|
| 276 |
+
else:
|
| 277 |
+
raise RuntimeError("relit_inference.py finished but no output file was produced.\n\n" + log)
|
|
|
|
|
|
|
| 278 |
|
| 279 |
+
return str(output_path), log
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 281 |
|
| 282 |
+
def run_demo(sample_name: str, env_name: str, mode: str, num_frames: int, steps: int, cfg_scale: float, quality: int):
|
| 283 |
try:
|
| 284 |
+
ensure_weights()
|
| 285 |
+
key = cache_key(sample_name, env_name, mode, int(num_frames), int(steps), float(cfg_scale), int(quality))
|
| 286 |
+
cached = result_path_for(key, int(num_frames))
|
| 287 |
+
if cached.exists() and cached.stat().st_size > 0:
|
| 288 |
+
status = f"â
Cached result loaded.\nKey: {key}"
|
| 289 |
+
if int(num_frames) == 1:
|
| 290 |
+
return gr.update(value=str(cached), visible=True), gr.update(value=None, visible=False), status
|
| 291 |
+
return gr.update(value=None, visible=False), gr.update(value=str(cached), visible=True), status
|
| 292 |
+
|
| 293 |
+
output, log = run_relit_cli(sample_name, env_name, mode, int(num_frames), int(steps), float(cfg_scale), int(quality))
|
| 294 |
+
status = "â
Relit-LiVE inference completed.\n\n" + _tail(log, 2500)
|
| 295 |
+
if int(num_frames) == 1 or output.lower().endswith(".png"):
|
| 296 |
+
return gr.update(value=output, visible=True), gr.update(value=None, visible=False), status
|
| 297 |
+
return gr.update(value=None, visible=False), gr.update(value=output, visible=True), status
|
| 298 |
+
except Exception as exc:
|
| 299 |
+
status = "â Inference failed.\n\n" + str(exc) + "\n\n" + traceback.format_exc()[-3000:]
|
| 300 |
+
return gr.update(value=None, visible=False), gr.update(value=None, visible=False), status
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 301 |
|
|
|
|
|
|
|
| 302 |
|
| 303 |
+
def startup_message() -> str:
|
| 304 |
try:
|
| 305 |
+
return ensure_weights()
|
| 306 |
+
except Exception as exc:
|
| 307 |
+
return "Runtime setup failed:\n" + str(exc)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 308 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 309 |
|
| 310 |
+
SAMPLES = list_samples()
|
| 311 |
+
ENVS = list_envs()
|
| 312 |
+
DEFAULT_SAMPLE = SAMPLES[0] if SAMPLES else None
|
| 313 |
+
DEFAULT_ENV = "Pink_Sunrise" if "Pink_Sunrise" in ENVS else (ENVS[0] if ENVS else None)
|
| 314 |
|
| 315 |
css = """
|
| 316 |
+
#notice {border: 1px solid #ddd; border-radius: 12px; padding: 12px;}
|
|
|
|
| 317 |
"""
|
| 318 |
|
| 319 |
+
with gr.Blocks(title="Relit-LiVE ZeroGPU README Demo", css=css) as demo:
|
| 320 |
+
gr.Markdown(
|
| 321 |
+
"# Relit-LiVE ZeroGPU Demo\n"
|
| 322 |
+
"This Space follows the repository README inference path: `python relit_inference.py`. "
|
| 323 |
+
"It uses curated samples already present in `datasets/demos` and environments from `datasets/envs`. "
|
| 324 |
+
"The full Gradio inverse-rendering pipeline is intentionally not used here."
|
| 325 |
+
)
|
| 326 |
|
| 327 |
+
with gr.Accordion("Runtime setup", open=False):
|
| 328 |
+
setup_box = gr.Textbox(value=startup_message(), label="Setup log", lines=10)
|
| 329 |
+
refresh_setup = gr.Button("Refresh setup check")
|
| 330 |
+
refresh_setup.click(fn=startup_message, inputs=[], outputs=[setup_box], queue=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
|
| 332 |
+
if not SAMPLES or not ENVS:
|
| 333 |
+
gr.Markdown(
|
| 334 |
+
"## Missing demo assets\n"
|
| 335 |
+
"This app expects the cloned repository to contain `datasets/demos/` and `datasets/envs/`."
|
| 336 |
+
)
|
| 337 |
+
else:
|
| 338 |
+
with gr.Row():
|
| 339 |
+
with gr.Column(scale=1):
|
| 340 |
+
sample = gr.Dropdown(SAMPLES, value=DEFAULT_SAMPLE, label="Demo sample")
|
| 341 |
+
env = gr.Dropdown(ENVS, value=DEFAULT_ENV, label="Lighting environment")
|
| 342 |
+
mode = gr.Radio(
|
| 343 |
+
[
|
| 344 |
+
"Basic relighting",
|
| 345 |
+
"Rotating light",
|
| 346 |
+
"Fixed first frame + width light rotation",
|
| 347 |
+
"Fixed first frame + height light rotation",
|
| 348 |
+
],
|
| 349 |
+
value="Basic relighting",
|
| 350 |
+
label="README inference mode",
|
| 351 |
+
)
|
| 352 |
+
num_frames = gr.Radio([1, 9, 17, 25], value=9, label="Frames")
|
| 353 |
+
steps = gr.Slider(8, 50, value=16, step=1, label="Inference steps")
|
| 354 |
+
cfg_scale = gr.Slider(1.0, 5.0, value=1.0, step=0.1, label="CFG scale")
|
| 355 |
+
quality = gr.Slider(5, 10, value=8, step=1, label="Video quality")
|
| 356 |
+
run_btn = gr.Button("Run README inference", variant="primary")
|
| 357 |
+
|
| 358 |
+
with gr.Column(scale=1):
|
| 359 |
+
preview = gr.Gallery(label="Input sample frames", columns=4, height=320, value=sample_frame_paths(DEFAULT_SAMPLE) if DEFAULT_SAMPLE else [])
|
| 360 |
+
info = gr.Textbox(label="Sample contents", lines=8, value=sample_info(DEFAULT_SAMPLE) if DEFAULT_SAMPLE else "")
|
| 361 |
+
|
| 362 |
+
with gr.Row():
|
| 363 |
+
result_image = gr.Image(label="Relit result", visible=False)
|
| 364 |
+
result_video = gr.Video(label="Relit result", visible=False)
|
| 365 |
+
|
| 366 |
+
status = gr.Textbox(label="Status / logs", lines=16)
|
| 367 |
+
|
| 368 |
+
sample.change(fn=update_preview, inputs=[sample], outputs=[preview, info], queue=False)
|
| 369 |
+
run_btn.click(
|
| 370 |
+
fn=run_demo,
|
| 371 |
+
inputs=[sample, env, mode, num_frames, steps, cfg_scale, quality],
|
| 372 |
+
outputs=[result_image, result_video, status],
|
| 373 |
)
|
| 374 |
|
| 375 |
+
gr.Markdown(
|
| 376 |
+
"---\n"
|
| 377 |
+
"**ZeroGPU scope:** this demo does not accept arbitrary uploads because arbitrary inputs require the full inverse-rendering pipeline and Cosmos. "
|
| 378 |
+
"Here we run the README `relit_inference.py` path on prepared Relit-LiVE samples."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 379 |
)
|
|
|
|
|
|
|
| 380 |
|
| 381 |
if __name__ == "__main__":
|
| 382 |
+
demo.queue(default_concurrency_limit=1, max_size=8)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 383 |
demo.launch(
|
|
|
|
| 384 |
server_name="0.0.0.0",
|
| 385 |
+
server_port=int(os.environ.get("PORT", "7860")),
|
|
|
|
| 386 |
show_error=True,
|
| 387 |
+
)
|