Spaces:
Paused
Paused
daKhosa commited on
Commit ·
afd702d
1
Parent(s): b840bd7
Fix ZeroGPU GPU access: switch from subprocess.Popen to multiprocessing.Process
Browse filesThe spaces library only patches multiprocessing.Process.start() to register
child PIDs with the GPU broker. subprocess.Popen children are not registered
and get no GPU access (CUDA init fails with "No CUDA GPUs are available").
Changes:
- generate.py: extract run_generation() callable + _worker_entrypoint for
multiprocessing pickling; add immediate torch.zeros(1, device='cuda') right
after signal received to establish CUDA context before lease expires
- app.py: import _worker_entrypoint, use multiprocessing.get_context('spawn')
.Process instead of subprocess.Popen; sleep 0.8s in GPU lease to give
worker time to init CUDA; proc.join() instead of proc.wait()
- app.py +26 -27
- generate.py +101 -72
app.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
| 1 |
"""
|
| 2 |
Sulphur — Image to Video (HF Spaces).
|
| 3 |
|
| 4 |
-
POC-proven pattern: subprocess is pre-spawned from the main Gradio process
|
| 5 |
-
|
| 6 |
-
|
|
|
|
|
|
|
| 7 |
Billing cost: 1 second per generation regardless of inference time.
|
| 8 |
"""
|
| 9 |
|
|
@@ -11,8 +13,6 @@ import json
|
|
| 11 |
import multiprocessing
|
| 12 |
import os
|
| 13 |
import shutil
|
| 14 |
-
import subprocess
|
| 15 |
-
import sys
|
| 16 |
import tempfile
|
| 17 |
import threading
|
| 18 |
import time
|
|
@@ -21,13 +21,17 @@ from pathlib import Path
|
|
| 21 |
import gradio as gr
|
| 22 |
import spaces
|
| 23 |
|
|
|
|
|
|
|
| 24 |
_HF_TOKEN = os.environ.get("HF_TOKEN")
|
| 25 |
_PERSISTENT = Path("/data") if Path("/data").exists() else Path(tempfile.gettempdir())
|
| 26 |
WAN2GP_ROOT = _PERSISTENT / "Wan2GP"
|
| 27 |
CKPTS_DIR = WAN2GP_ROOT / "ckpts"
|
| 28 |
LORAS_DIR = WAN2GP_ROOT / "loras" / "ltx2"
|
| 29 |
FINETUNES_DIR = WAN2GP_ROOT / "finetunes"
|
| 30 |
-
|
|
|
|
|
|
|
| 31 |
|
| 32 |
SULPHUR_ASSETS = [
|
| 33 |
("SulphurAI/Sulphur-2-base", "sulphur_distil_bf16.safetensors", CKPTS_DIR),
|
|
@@ -86,6 +90,7 @@ def setup():
|
|
| 86 |
if not (WAN2GP_ROOT / "shared" / "api.py").exists():
|
| 87 |
WAN2GP_ROOT.mkdir(parents=True, exist_ok=True)
|
| 88 |
print("[setup] Cloning Wan2GP...")
|
|
|
|
| 89 |
subprocess.run(
|
| 90 |
["git", "clone", "--depth=1",
|
| 91 |
"https://github.com/deepbeepmeep/Wan2GP.git", str(WAN2GP_ROOT)],
|
|
@@ -126,7 +131,7 @@ RESOLUTIONS = ["832x480", "480x832", "640x640", "1024x576", "576x1024"]
|
|
| 126 |
def _signal_cuda_init(signal_path):
|
| 127 |
"""Acquire a 1-second GPU lease just long enough for the worker to init CUDA."""
|
| 128 |
Path(signal_path).write_text("go")
|
| 129 |
-
time.sleep(0.
|
| 130 |
|
| 131 |
|
| 132 |
def generate_video(image, prompt, resolution, steps, guidance_scale, frames, seed):
|
|
@@ -140,25 +145,17 @@ def generate_video(image, prompt, resolution, steps, guidance_scale, frames, see
|
|
| 140 |
out_dir = tempfile.mkdtemp()
|
| 141 |
out_file = os.path.join(out_dir, "output.mp4")
|
| 142 |
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
"--signal-path", signal_path,
|
| 155 |
-
"--result-path", result_path,
|
| 156 |
-
]
|
| 157 |
-
|
| 158 |
-
env = {**os.environ, "WAN2GP_ROOT": str(WAN2GP_ROOT)}
|
| 159 |
-
|
| 160 |
-
# Spawn from main Gradio process (not inside ZeroGPU daemon — avoids daemon-child restriction)
|
| 161 |
-
proc = subprocess.Popen(cmd, env=env)
|
| 162 |
|
| 163 |
log_lines = ["Worker spawned, acquiring GPU lease..."]
|
| 164 |
yield None, "\n".join(log_lines)
|
|
@@ -191,7 +188,9 @@ def generate_video(image, prompt, resolution, steps, guidance_scale, frames, see
|
|
| 191 |
else:
|
| 192 |
yield None, "\n".join(log_lines)
|
| 193 |
|
| 194 |
-
proc.
|
|
|
|
|
|
|
| 195 |
|
| 196 |
for path in (signal_path, result_path):
|
| 197 |
try:
|
|
|
|
| 1 |
"""
|
| 2 |
Sulphur — Image to Video (HF Spaces).
|
| 3 |
|
| 4 |
+
POC-proven pattern: subprocess is pre-spawned from the main Gradio process via
|
| 5 |
+
multiprocessing.Process (NOT subprocess.Popen — ZeroGPU only grants GPU access
|
| 6 |
+
to children spawned through the patched multiprocessing API).
|
| 7 |
+
@spaces.GPU(duration=1) signals CUDA initialisation; the worker retains the
|
| 8 |
+
context for the full generation.
|
| 9 |
Billing cost: 1 second per generation regardless of inference time.
|
| 10 |
"""
|
| 11 |
|
|
|
|
| 13 |
import multiprocessing
|
| 14 |
import os
|
| 15 |
import shutil
|
|
|
|
|
|
|
| 16 |
import tempfile
|
| 17 |
import threading
|
| 18 |
import time
|
|
|
|
| 21 |
import gradio as gr
|
| 22 |
import spaces
|
| 23 |
|
| 24 |
+
from generate import _worker_entrypoint
|
| 25 |
+
|
| 26 |
_HF_TOKEN = os.environ.get("HF_TOKEN")
|
| 27 |
_PERSISTENT = Path("/data") if Path("/data").exists() else Path(tempfile.gettempdir())
|
| 28 |
WAN2GP_ROOT = _PERSISTENT / "Wan2GP"
|
| 29 |
CKPTS_DIR = WAN2GP_ROOT / "ckpts"
|
| 30 |
LORAS_DIR = WAN2GP_ROOT / "loras" / "ltx2"
|
| 31 |
FINETUNES_DIR = WAN2GP_ROOT / "finetunes"
|
| 32 |
+
|
| 33 |
+
# Propagate to spawned worker processes
|
| 34 |
+
os.environ["WAN2GP_ROOT"] = str(WAN2GP_ROOT)
|
| 35 |
|
| 36 |
SULPHUR_ASSETS = [
|
| 37 |
("SulphurAI/Sulphur-2-base", "sulphur_distil_bf16.safetensors", CKPTS_DIR),
|
|
|
|
| 90 |
if not (WAN2GP_ROOT / "shared" / "api.py").exists():
|
| 91 |
WAN2GP_ROOT.mkdir(parents=True, exist_ok=True)
|
| 92 |
print("[setup] Cloning Wan2GP...")
|
| 93 |
+
import subprocess
|
| 94 |
subprocess.run(
|
| 95 |
["git", "clone", "--depth=1",
|
| 96 |
"https://github.com/deepbeepmeep/Wan2GP.git", str(WAN2GP_ROOT)],
|
|
|
|
| 131 |
def _signal_cuda_init(signal_path):
|
| 132 |
"""Acquire a 1-second GPU lease just long enough for the worker to init CUDA."""
|
| 133 |
Path(signal_path).write_text("go")
|
| 134 |
+
time.sleep(0.8)
|
| 135 |
|
| 136 |
|
| 137 |
def generate_video(image, prompt, resolution, steps, guidance_scale, frames, seed):
|
|
|
|
| 145 |
out_dir = tempfile.mkdtemp()
|
| 146 |
out_file = os.path.join(out_dir, "output.mp4")
|
| 147 |
|
| 148 |
+
# Use multiprocessing.Process — ZeroGPU patches this to grant GPU access to children.
|
| 149 |
+
# subprocess.Popen does NOT get GPU access (not patched by the spaces library).
|
| 150 |
+
ctx = multiprocessing.get_context("spawn")
|
| 151 |
+
proc = ctx.Process(
|
| 152 |
+
target=_worker_entrypoint,
|
| 153 |
+
args=(image, prompt, out_file, "sulphur-2",
|
| 154 |
+
int(steps), float(guidance_scale), int(frames),
|
| 155 |
+
resolution, int(seed), signal_path, result_path),
|
| 156 |
+
daemon=False,
|
| 157 |
+
)
|
| 158 |
+
proc.start()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
|
| 160 |
log_lines = ["Worker spawned, acquiring GPU lease..."]
|
| 161 |
yield None, "\n".join(log_lines)
|
|
|
|
| 188 |
else:
|
| 189 |
yield None, "\n".join(log_lines)
|
| 190 |
|
| 191 |
+
proc.join(timeout=10)
|
| 192 |
+
if proc.is_alive():
|
| 193 |
+
proc.terminate()
|
| 194 |
|
| 195 |
for path in (signal_path, result_path):
|
| 196 |
try:
|
generate.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
"""
|
| 2 |
-
HF Spaces
|
| 3 |
-
Called as a subprocess from app.py.
|
| 4 |
Waits for --signal-path before touching CUDA, writes incremental JSON to --result-path.
|
|
|
|
| 5 |
"""
|
| 6 |
|
| 7 |
import argparse
|
|
@@ -30,7 +30,7 @@ _log_entries = []
|
|
| 30 |
_result_path = None
|
| 31 |
|
| 32 |
|
| 33 |
-
def
|
| 34 |
ts = time.strftime("%H:%M:%S")
|
| 35 |
entry = f"[{ts}] {msg}"
|
| 36 |
_log_entries.append(entry)
|
|
@@ -43,72 +43,70 @@ def p(msg):
|
|
| 43 |
pass
|
| 44 |
|
| 45 |
|
| 46 |
-
def
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
args = parse_args()
|
| 66 |
-
_result_path = args.result_path
|
| 67 |
-
|
| 68 |
-
if args.signal_path:
|
| 69 |
-
p("Waiting for GPU lease signal...")
|
| 70 |
-
while not Path(args.signal_path).exists():
|
| 71 |
time.sleep(0.05)
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
defaults = DEFAULTS.get(model_type, DEFAULTS["sulphur_2_base"])
|
| 76 |
|
| 77 |
-
image_path = str(Path(
|
| 78 |
if not Path(image_path).exists():
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
json.dump({"log": _log_entries, "done": True, "error": "image not found"}, f)
|
| 83 |
-
sys.exit(1)
|
| 84 |
|
| 85 |
-
|
| 86 |
-
if not
|
| 87 |
try:
|
| 88 |
from PIL import Image as _PIL
|
| 89 |
img = _PIL.open(image_path)
|
| 90 |
iw, ih = img.size
|
| 91 |
if ih > iw:
|
| 92 |
-
tw = 480
|
| 93 |
-
th = round(ih / iw * tw / 32) * 32
|
| 94 |
else:
|
| 95 |
-
th = 480
|
| 96 |
-
|
| 97 |
-
resolution
|
| 98 |
-
p(f"Auto-detected resolution: {resolution} (from {iw}x{ih} input)")
|
| 99 |
except Exception:
|
| 100 |
pass
|
| 101 |
|
| 102 |
task = {
|
| 103 |
"model_type": model_type,
|
| 104 |
"base_model_type": model_type,
|
| 105 |
-
"prompt":
|
| 106 |
"image_start": image_path,
|
| 107 |
-
"num_inference_steps":
|
| 108 |
-
"guidance_scale":
|
| 109 |
-
"resolution":
|
| 110 |
-
"video_length":
|
| 111 |
-
"seed":
|
| 112 |
"image_prompt_type": "S",
|
| 113 |
"input_video_strength": 1.0,
|
| 114 |
"activated_loras": [
|
|
@@ -118,28 +116,27 @@ def main():
|
|
| 118 |
"loras_multipliers": ["0.5", "1.0"],
|
| 119 |
}
|
| 120 |
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
|
| 127 |
sys.path.insert(0, str(WAN2GP_ROOT))
|
| 128 |
os.chdir(WAN2GP_ROOT)
|
| 129 |
|
| 130 |
from shared.api import WanGPSession
|
| 131 |
|
| 132 |
-
output_dir = Path(
|
| 133 |
output_dir.mkdir(parents=True, exist_ok=True)
|
| 134 |
|
| 135 |
-
|
| 136 |
session = WanGPSession(root=WAN2GP_ROOT, output_dir=output_dir, console_output=True)
|
| 137 |
|
| 138 |
-
|
| 139 |
result = session.run_task(task)
|
| 140 |
|
| 141 |
output_file = None
|
| 142 |
-
|
| 143 |
if result.artifacts:
|
| 144 |
src = result.artifacts[0].path
|
| 145 |
if src and Path(src).exists():
|
|
@@ -149,26 +146,58 @@ def main():
|
|
| 149 |
candidates = sorted(output_dir.glob("**/*.mp4"), key=lambda f: f.stat().st_mtime, reverse=True)
|
| 150 |
if candidates:
|
| 151 |
output_file = str(candidates[0])
|
| 152 |
-
|
| 153 |
|
| 154 |
if output_file:
|
| 155 |
import shutil
|
| 156 |
-
shutil.copy2(output_file,
|
| 157 |
-
|
| 158 |
else:
|
| 159 |
-
|
| 160 |
if result.errors:
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
json.dump({"log": _log_entries, "done": True, "error": "no output"}, f)
|
| 165 |
-
sys.exit(1)
|
| 166 |
|
| 167 |
session.close()
|
|
|
|
| 168 |
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
|
| 173 |
|
| 174 |
if __name__ == "__main__":
|
|
|
|
| 1 |
"""
|
| 2 |
+
HF Spaces generate module — called via multiprocessing.Process from app.py.
|
|
|
|
| 3 |
Waits for --signal-path before touching CUDA, writes incremental JSON to --result-path.
|
| 4 |
+
Also usable as a CLI script.
|
| 5 |
"""
|
| 6 |
|
| 7 |
import argparse
|
|
|
|
| 30 |
_result_path = None
|
| 31 |
|
| 32 |
|
| 33 |
+
def _p(msg):
|
| 34 |
ts = time.strftime("%H:%M:%S")
|
| 35 |
entry = f"[{ts}] {msg}"
|
| 36 |
_log_entries.append(entry)
|
|
|
|
| 43 |
pass
|
| 44 |
|
| 45 |
|
| 46 |
+
def _done(error=None):
|
| 47 |
+
if _result_path:
|
| 48 |
+
with open(_result_path, "w") as f:
|
| 49 |
+
data = {"log": _log_entries, "done": True}
|
| 50 |
+
if error:
|
| 51 |
+
data["error"] = error
|
| 52 |
+
json.dump(data, f)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def run_generation(image, prompt, output, model="sulphur-2", steps=None,
|
| 56 |
+
guidance_scale=None, frames=None, resolution=None, seed=-1,
|
| 57 |
+
signal_path=None, result_path=None):
|
| 58 |
+
global _log_entries, _result_path
|
| 59 |
+
_log_entries = []
|
| 60 |
+
_result_path = result_path
|
| 61 |
+
|
| 62 |
+
if signal_path:
|
| 63 |
+
_p("Waiting for GPU lease signal...")
|
| 64 |
+
while not Path(signal_path).exists():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
time.sleep(0.05)
|
| 66 |
+
_p("Signal received — initialising CUDA context immediately...")
|
| 67 |
+
try:
|
| 68 |
+
import torch
|
| 69 |
+
_ = torch.zeros(1, device="cuda")
|
| 70 |
+
_p(f"CUDA ready: {torch.cuda.get_device_name(0)}")
|
| 71 |
+
except Exception as exc:
|
| 72 |
+
_p(f"CUDA init failed: {exc}")
|
| 73 |
+
_done(error=str(exc))
|
| 74 |
+
return
|
| 75 |
+
|
| 76 |
+
model_type = MODEL_SHORTHANDS.get(model, model)
|
| 77 |
defaults = DEFAULTS.get(model_type, DEFAULTS["sulphur_2_base"])
|
| 78 |
|
| 79 |
+
image_path = str(Path(image.strip()).resolve())
|
| 80 |
if not Path(image_path).exists():
|
| 81 |
+
_p(f"Fatal: image not found: {image_path}")
|
| 82 |
+
_done(error="image not found")
|
| 83 |
+
return
|
|
|
|
|
|
|
| 84 |
|
| 85 |
+
res = resolution or defaults["resolution"]
|
| 86 |
+
if not resolution:
|
| 87 |
try:
|
| 88 |
from PIL import Image as _PIL
|
| 89 |
img = _PIL.open(image_path)
|
| 90 |
iw, ih = img.size
|
| 91 |
if ih > iw:
|
| 92 |
+
tw = 480; th = round(ih / iw * tw / 32) * 32
|
|
|
|
| 93 |
else:
|
| 94 |
+
th = 480; tw = round(iw / ih * th / 32) * 32
|
| 95 |
+
res = f"{tw}x{th}"
|
| 96 |
+
_p(f"Auto-detected resolution: {res} (from {iw}x{ih} input)")
|
|
|
|
| 97 |
except Exception:
|
| 98 |
pass
|
| 99 |
|
| 100 |
task = {
|
| 101 |
"model_type": model_type,
|
| 102 |
"base_model_type": model_type,
|
| 103 |
+
"prompt": prompt,
|
| 104 |
"image_start": image_path,
|
| 105 |
+
"num_inference_steps": steps or defaults["num_inference_steps"],
|
| 106 |
+
"guidance_scale": guidance_scale or defaults["guidance_scale"],
|
| 107 |
+
"resolution": res,
|
| 108 |
+
"video_length": frames or defaults["video_length"],
|
| 109 |
+
"seed": seed,
|
| 110 |
"image_prompt_type": "S",
|
| 111 |
"input_video_strength": 1.0,
|
| 112 |
"activated_loras": [
|
|
|
|
| 116 |
"loras_multipliers": ["0.5", "1.0"],
|
| 117 |
}
|
| 118 |
|
| 119 |
+
_p(f"Model: {model_type}")
|
| 120 |
+
_p(f"Image: {image_path}")
|
| 121 |
+
_p(f"Steps: {task['num_inference_steps']} Guidance: {task['guidance_scale']}")
|
| 122 |
+
_p(f"Resolution: {task['resolution']} Frames: {task['video_length']}")
|
| 123 |
+
_p(f"Prompt: {prompt[:80]}")
|
| 124 |
|
| 125 |
sys.path.insert(0, str(WAN2GP_ROOT))
|
| 126 |
os.chdir(WAN2GP_ROOT)
|
| 127 |
|
| 128 |
from shared.api import WanGPSession
|
| 129 |
|
| 130 |
+
output_dir = Path(output).parent
|
| 131 |
output_dir.mkdir(parents=True, exist_ok=True)
|
| 132 |
|
| 133 |
+
_p("Starting WanGPSession...")
|
| 134 |
session = WanGPSession(root=WAN2GP_ROOT, output_dir=output_dir, console_output=True)
|
| 135 |
|
| 136 |
+
_p("Running generation...")
|
| 137 |
result = session.run_task(task)
|
| 138 |
|
| 139 |
output_file = None
|
|
|
|
| 140 |
if result.artifacts:
|
| 141 |
src = result.artifacts[0].path
|
| 142 |
if src and Path(src).exists():
|
|
|
|
| 146 |
candidates = sorted(output_dir.glob("**/*.mp4"), key=lambda f: f.stat().st_mtime, reverse=True)
|
| 147 |
if candidates:
|
| 148 |
output_file = str(candidates[0])
|
| 149 |
+
_p(f"Found output via dir scan: {output_file}")
|
| 150 |
|
| 151 |
if output_file:
|
| 152 |
import shutil
|
| 153 |
+
shutil.copy2(output_file, output)
|
| 154 |
+
_p(f"Done: {output}")
|
| 155 |
else:
|
| 156 |
+
_p(f"No output found in {output_dir}")
|
| 157 |
if result.errors:
|
| 158 |
+
_p(f"Errors: {result.errors}")
|
| 159 |
+
_done(error="no output produced")
|
| 160 |
+
return
|
|
|
|
|
|
|
| 161 |
|
| 162 |
session.close()
|
| 163 |
+
_done()
|
| 164 |
|
| 165 |
+
|
| 166 |
+
# Top-level function required for multiprocessing.Process pickling
|
| 167 |
+
def _worker_entrypoint(image, prompt, output, model, steps, guidance_scale,
|
| 168 |
+
frames, resolution, seed, signal_path, result_path):
|
| 169 |
+
run_generation(
|
| 170 |
+
image=image, prompt=prompt, output=output, model=model,
|
| 171 |
+
steps=steps, guidance_scale=guidance_scale, frames=frames,
|
| 172 |
+
resolution=resolution, seed=seed,
|
| 173 |
+
signal_path=signal_path, result_path=result_path,
|
| 174 |
+
)
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
def parse_args():
|
| 178 |
+
ap = argparse.ArgumentParser()
|
| 179 |
+
ap.add_argument("--image", required=True)
|
| 180 |
+
ap.add_argument("--prompt", required=True)
|
| 181 |
+
ap.add_argument("--output", required=True)
|
| 182 |
+
ap.add_argument("--model", default="sulphur-2")
|
| 183 |
+
ap.add_argument("--steps", type=int, default=None)
|
| 184 |
+
ap.add_argument("--guidance_scale", type=float, default=None)
|
| 185 |
+
ap.add_argument("--frames", type=int, default=None)
|
| 186 |
+
ap.add_argument("--resolution", default=None)
|
| 187 |
+
ap.add_argument("--seed", type=int, default=-1)
|
| 188 |
+
ap.add_argument("--signal-path", default=None, dest="signal_path")
|
| 189 |
+
ap.add_argument("--result-path", default=None, dest="result_path")
|
| 190 |
+
return ap.parse_args()
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
def main():
|
| 194 |
+
args = parse_args()
|
| 195 |
+
run_generation(
|
| 196 |
+
image=args.image, prompt=args.prompt, output=args.output,
|
| 197 |
+
model=args.model, steps=args.steps, guidance_scale=args.guidance_scale,
|
| 198 |
+
frames=args.frames, resolution=args.resolution, seed=args.seed,
|
| 199 |
+
signal_path=args.signal_path, result_path=args.result_path,
|
| 200 |
+
)
|
| 201 |
|
| 202 |
|
| 203 |
if __name__ == "__main__":
|