Spaces:
Running on Zero
Running on Zero
Merge pull request #57 from some1galaxy/remove-upscale-pil-roundtrip
Browse filesRemove redundant numpy->PIL->numpy round trip between interpolation and upscale
- app.py +1 -5
- postprocess/upscale/upscale.py +13 -8
app.py
CHANGED
|
@@ -9,7 +9,6 @@ import numpy as np
|
|
| 9 |
import spaces
|
| 10 |
import torch
|
| 11 |
from diffusers.utils.export_utils import export_to_video
|
| 12 |
-
from PIL import Image
|
| 13 |
|
| 14 |
from logging_utils import (
|
| 15 |
LogUploader,
|
|
@@ -142,10 +141,7 @@ def _apply_upscale(frames, progress):
|
|
| 142 |
print_stage_start("upscaling")
|
| 143 |
t0 = _time.perf_counter()
|
| 144 |
try:
|
| 145 |
-
|
| 146 |
-
Image.fromarray((np.asarray(frame) * 255.0).round().astype(np.uint8)) for frame in frames
|
| 147 |
-
]
|
| 148 |
-
upscaled = upscale_frames(pil_frames, progress_callback=_report_upscale_progress)
|
| 149 |
print_stage_done("upscaling", _time.perf_counter() - t0)
|
| 150 |
return upscaled
|
| 151 |
except Exception as e:
|
|
|
|
| 9 |
import spaces
|
| 10 |
import torch
|
| 11 |
from diffusers.utils.export_utils import export_to_video
|
|
|
|
| 12 |
|
| 13 |
from logging_utils import (
|
| 14 |
LogUploader,
|
|
|
|
| 141 |
print_stage_start("upscaling")
|
| 142 |
t0 = _time.perf_counter()
|
| 143 |
try:
|
| 144 |
+
upscaled = upscale_frames(frames, progress_callback=_report_upscale_progress)
|
|
|
|
|
|
|
|
|
|
| 145 |
print_stage_done("upscaling", _time.perf_counter() - t0)
|
| 146 |
return upscaled
|
| 147 |
except Exception as e:
|
postprocess/upscale/upscale.py
CHANGED
|
@@ -2,9 +2,10 @@
|
|
| 2 |
|
| 3 |
Inference structure (pre-pad, tile, merge, crop) is adapted from xinntao/Real-ESRGAN's `RealESRGANer`
|
| 4 |
(BSD-3-Clause License, see `LICENSES/REAL-ESRGAN-LICENSE`, `realesrgan/utils.py`), trimmed to the single code path
|
| 5 |
-
this Space needs: one fixed compact model, RGB
|
| 6 |
-
|
| 7 |
-
|
|
|
|
| 8 |
|
| 9 |
The weights are `4xLSDIRCompact` (Philip Hofmann, CC BY 4.0, see `LICENSES/4xLSDIRCompact-LICENSE`) rather than
|
| 10 |
Real-ESRGAN's own `realesr-general-x4v3`: same `SRVGGNetCompact` architecture (just `num_conv=16` instead of 32,
|
|
@@ -145,16 +146,20 @@ def _tile_process(model: torch.nn.Module, img: torch.Tensor) -> torch.Tensor:
|
|
| 145 |
return output
|
| 146 |
|
| 147 |
|
| 148 |
-
def _frames_to_tensor(frames: list[
|
| 149 |
-
array = np.stack(
|
| 150 |
return torch.from_numpy(array).permute(0, 3, 1, 2).to(device=device, dtype=dtype)
|
| 151 |
|
| 152 |
|
| 153 |
def upscale_frames(
|
| 154 |
-
frames: list[
|
| 155 |
) -> list[Image.Image]:
|
| 156 |
"""Upscale every frame 4x with `4xLSDIRCompact`, tiled to bound peak memory.
|
| 157 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
Frames are processed in batches (of up to FRAME_BATCH_SIZE, split wherever frame size
|
| 159 |
changes) rather than one at a time, so the model sees a real batch dimension instead of
|
| 160 |
batch=1 on every call. progress_callback(done, total), if given, fires after each frame
|
|
@@ -167,10 +172,10 @@ def upscale_frames(
|
|
| 167 |
|
| 168 |
i = 0
|
| 169 |
while i < len(frames):
|
| 170 |
-
|
| 171 |
batch = [frames[i]]
|
| 172 |
i += 1
|
| 173 |
-
while i < len(frames) and len(batch) < FRAME_BATCH_SIZE and frames[i].
|
| 174 |
batch.append(frames[i])
|
| 175 |
i += 1
|
| 176 |
|
|
|
|
| 2 |
|
| 3 |
Inference structure (pre-pad, tile, merge, crop) is adapted from xinntao/Real-ESRGAN's `RealESRGANer`
|
| 4 |
(BSD-3-Clause License, see `LICENSES/REAL-ESRGAN-LICENSE`, `realesrgan/utils.py`), trimmed to the single code path
|
| 5 |
+
this Space needs: one fixed compact model, RGB frames in (numpy float32 `[0,1]`, `(H,W,C)` — the same
|
| 6 |
+
representation `_apply_interpolation` already produces, so no PIL round trip is needed at the boundary) and
|
| 7 |
+
`PIL.Image` out (no alpha channel, no 16-bit, no cv2/BGR round-trip, no `dni` model blending, no CLI
|
| 8 |
+
prefetch/IO-queue threads — none of those apply to frames coming straight out of the Wan 2.2 decode step).
|
| 9 |
|
| 10 |
The weights are `4xLSDIRCompact` (Philip Hofmann, CC BY 4.0, see `LICENSES/4xLSDIRCompact-LICENSE`) rather than
|
| 11 |
Real-ESRGAN's own `realesr-general-x4v3`: same `SRVGGNetCompact` architecture (just `num_conv=16` instead of 32,
|
|
|
|
| 146 |
return output
|
| 147 |
|
| 148 |
|
| 149 |
+
def _frames_to_tensor(frames: list[np.ndarray]) -> torch.Tensor:
|
| 150 |
+
array = np.stack(frames)
|
| 151 |
return torch.from_numpy(array).permute(0, 3, 1, 2).to(device=device, dtype=dtype)
|
| 152 |
|
| 153 |
|
| 154 |
def upscale_frames(
|
| 155 |
+
frames: list[np.ndarray], progress_callback: ProgressCallback | None = None
|
| 156 |
) -> list[Image.Image]:
|
| 157 |
"""Upscale every frame 4x with `4xLSDIRCompact`, tiled to bound peak memory.
|
| 158 |
|
| 159 |
+
frames are numpy float32 arrays in `[0,1]`, shape `(H,W,C)` — the same representation
|
| 160 |
+
`_apply_interpolation` already produces, so callers don't need to round-trip through
|
| 161 |
+
`PIL.Image` just to cross this boundary.
|
| 162 |
+
|
| 163 |
Frames are processed in batches (of up to FRAME_BATCH_SIZE, split wherever frame size
|
| 164 |
changes) rather than one at a time, so the model sees a real batch dimension instead of
|
| 165 |
batch=1 on every call. progress_callback(done, total), if given, fires after each frame
|
|
|
|
| 172 |
|
| 173 |
i = 0
|
| 174 |
while i < len(frames):
|
| 175 |
+
h, w = frames[i].shape[:2]
|
| 176 |
batch = [frames[i]]
|
| 177 |
i += 1
|
| 178 |
+
while i < len(frames) and len(batch) < FRAME_BATCH_SIZE and frames[i].shape[:2] == (h, w):
|
| 179 |
batch.append(frames[i])
|
| 180 |
i += 1
|
| 181 |
|