Spaces:
Running on Zero
Sync develop into main (#32)
Browse files* Check Upscale 4x by default and drop model name from its label (#19)
Simplifies the option to a plain checkbox toggle without exposing the
underlying model name in the UI.
Claude-Session: https://claude.ai/code/session_01W9GLmk1R3mFfb7otDoGiWX
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* Add detailed export/traceback logging to diagnose post-pipeline UI errors (#20)
The Space logs cut off right after the pipeline stages finish, with no
trace of what happens next in generate_video() (export_to_video and the
run_inference call boundary), even though the UI shows a generic Error.
Log frame metadata and full tracebacks at those points so the next
failure is diagnosable from the Space logs alone.
* Pre-download RIFE/upscale weights at startup instead of inside @spaces.GPU (#22)
The RIFE and 4xLSDIRCompact weight fetches are pure network/disk I/O with no
CUDA dependency, but were only downloaded lazily on first use inside the
metered @spaces.GPU allocation — costing a few seconds of ZeroGPU quota on
every cold container start. Move both fetches to app startup (alongside
load_pipeline()), mirroring how the main video model is already preloaded.
Claude-Session: https://claude.ai/code/session_011keM1seSWsBZqPHrzKTeCG
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* Batch frames and same-shaped tiles through the upscale model (#23)
upscale_frames processed one frame at a time, and within each frame
_tile_process called the model once per tile (batch=1 every time). With
SRVGGNetCompact this leaves most of the GPU idle per call, since a single
~256px tile is far under typical GPU parallel capacity.
Group frames of equal size into batches (FRAME_BATCH_SIZE) before tiling,
and within _tile_process group tile jobs by input-patch shape (interior
tiles are all the same size; only the last row/column differs, clipped
against the image edge) so each model() call is one batched forward pass
over same-shaped patches, chunked to MAX_TILE_BATCH to bound memory. The
per-tile crop/placement math is unchanged — verified index-for-index
equivalent to the prior per-tile loop via a numpy simulation with a
synthetic nonlinear "model" across several batch/ragged-size cases.
No GPU available in this environment to benchmark; needs validation on
the dev Space before merge.
* Increase upscale tile size from 256 to 512 (#24)
Larger tiles mean fewer, bigger model() calls per frame, which cuts
pad/crop overhead and Python/kernel-launch count and improves GPU
occupancy for this small SRVGGNetCompact network. 512 is still
conservative for a 16GB+ Spaces GPU (T4/L4/A10G).
No GPU available in this environment to benchmark memory headroom or
speedup; needs validation on the dev Space before merge.
* torch.compile the upscale model on CUDA (#26)
Wraps the model with torch.compile(dynamic=True) on the CUDA path for
kernel fusion. dynamic=True avoids a fresh recompile for every distinct
tile shape (interior tiles vs. the clipped last row/column) — there's
only a handful of those per video, but without it each new shape would
trigger its own recompile. Wrapped in try/except: if torch.compile fails
to initialize (e.g. missing/incompatible triton on the target GPU image),
the eager model is used as-is.
No GPU available in this environment to benchmark, and this is the
riskiest of the four upscale perf changes (compile warmup cost vs.
steady-state speedup, triton availability on the Space's GPU image) —
needs careful validation on the dev Space before merge.
* Log when torch.compile falls back to the eager upscale model (#31)
The try/except guard added in #26 silently swallowed torch.compile
initialization failures (e.g. missing/incompatible triton on the Space's
GPU image), so a fallback to the eager model would be invisible in the
logs — leaving no way to tell, from the deployed Space's console output,
whether the compiled path is actually active. Print the exception instead
of passing silently.
* Add liability disclaimer to the Space UI (#30)
Notes that outputs are provided "as is" and that users are solely
responsible for generated content and how it's used/shared.
Claude-Session: https://claude.ai/code/session_015woCw9matvyXW3Xnx2JwXG
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
- app.py +4 -0
- postprocess/upscale/upscale.py +13 -2
|
@@ -345,6 +345,10 @@ with gr.Blocks() as demo:
|
|
| 345 |
"- Service availability and response times may vary as this Space runs on shared GPU infrastructure.\n"
|
| 346 |
"- When using this Space, please comply with the "
|
| 347 |
"[Hugging Face Content Policy](https://huggingface.co/content-policy).\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 348 |
f"- To monitor application performance and improve quality, input data (image/prompt) and generated "
|
| 349 |
f"outputs are logged solely for debugging purposes and retained in a private Hugging Face dataset "
|
| 350 |
f"accessible only to {_OPERATOR_MD}, automatically pruned once the dataset reaches its configured "
|
|
|
|
| 345 |
"- Service availability and response times may vary as this Space runs on shared GPU infrastructure.\n"
|
| 346 |
"- When using this Space, please comply with the "
|
| 347 |
"[Hugging Face Content Policy](https://huggingface.co/content-policy).\n"
|
| 348 |
+
"- This Space and its outputs are provided **\"as is\"**, without warranties of any kind. You are "
|
| 349 |
+
"solely responsible for the content you generate and for how you use or share it; the operator "
|
| 350 |
+
"accepts no liability for any loss, damage, or claim arising from use of this Space or its "
|
| 351 |
+
"outputs.\n"
|
| 352 |
f"- To monitor application performance and improve quality, input data (image/prompt) and generated "
|
| 353 |
f"outputs are logged solely for debugging purposes and retained in a private Hugging Face dataset "
|
| 354 |
f"accessible only to {_OPERATOR_MD}, automatically pruned once the dataset reaches its configured "
|
|
@@ -75,7 +75,7 @@ def ensure_weights_downloaded() -> None:
|
|
| 75 |
|
| 76 |
|
| 77 |
@lru_cache(maxsize=1)
|
| 78 |
-
def _load_model() ->
|
| 79 |
model = SRVGGNetCompact(
|
| 80 |
num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=NUM_CONV, upscale=SCALE, act_type="prelu"
|
| 81 |
)
|
|
@@ -83,6 +83,17 @@ def _load_model() -> SRVGGNetCompact:
|
|
| 83 |
state_dict = load_file(_weights_path())
|
| 84 |
model.load_state_dict(state_dict, strict=True)
|
| 85 |
model.to(device=device, dtype=dtype).eval()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
return model
|
| 87 |
|
| 88 |
|
|
@@ -91,7 +102,7 @@ def _pre_pad(tensor: torch.Tensor, pad: int) -> torch.Tensor:
|
|
| 91 |
|
| 92 |
|
| 93 |
@torch.no_grad()
|
| 94 |
-
def _tile_process(model:
|
| 95 |
batch, channel, height, width = img.shape
|
| 96 |
output = img.new_zeros((batch, channel, height * SCALE, width * SCALE))
|
| 97 |
tiles_x = math.ceil(width / TILE_SIZE)
|
|
|
|
| 75 |
|
| 76 |
|
| 77 |
@lru_cache(maxsize=1)
|
| 78 |
+
def _load_model() -> torch.nn.Module:
|
| 79 |
model = SRVGGNetCompact(
|
| 80 |
num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=NUM_CONV, upscale=SCALE, act_type="prelu"
|
| 81 |
)
|
|
|
|
| 83 |
state_dict = load_file(_weights_path())
|
| 84 |
model.load_state_dict(state_dict, strict=True)
|
| 85 |
model.to(device=device, dtype=dtype).eval()
|
| 86 |
+
if device.type == "cuda":
|
| 87 |
+
try:
|
| 88 |
+
# dynamic=True avoids a fresh recompile for every distinct tile shape (interior
|
| 89 |
+
# tiles vs. the last row/column, clipped against the image edge) — there are only a
|
| 90 |
+
# handful of those per video, but recompiling per-shape would otherwise eat into the
|
| 91 |
+
# first frame's latency. Guarded: torch.compile can fail to initialize (e.g. missing
|
| 92 |
+
# or incompatible triton on the target GPU image), in which case the eager model is
|
| 93 |
+
# used as-is.
|
| 94 |
+
model = torch.compile(model, dynamic=True)
|
| 95 |
+
except Exception as e:
|
| 96 |
+
print(f"[upscale] torch.compile failed to initialize, using eager model: {e!r}", flush=True)
|
| 97 |
return model
|
| 98 |
|
| 99 |
|
|
|
|
| 102 |
|
| 103 |
|
| 104 |
@torch.no_grad()
|
| 105 |
+
def _tile_process(model: torch.nn.Module, img: torch.Tensor) -> torch.Tensor:
|
| 106 |
batch, channel, height, width = img.shape
|
| 107 |
output = img.new_zeros((batch, channel, height * SCALE, width * SCALE))
|
| 108 |
tiles_x = math.ceil(width / TILE_SIZE)
|