someone-in-the-world commited on
Commit
663dfc8
·
unverified ·
2 Parent(s): 3d9fab255d9178

Merge pull request #57 from some1galaxy/remove-upscale-pil-roundtrip

Browse files

Remove redundant numpy->PIL->numpy round trip between interpolation and upscale

Files changed (2) hide show
  1. app.py +1 -5
  2. 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
- pil_frames = [
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 `PIL.Image` in and out (no alpha channel, no 16-bit, no cv2/BGR
6
- round-trip, no `dni` model blending, no CLI prefetch/IO-queue threads — none of those apply to frames coming
7
- straight out of the Wan 2.2 decode step).
 
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[Image.Image]) -> torch.Tensor:
149
- array = np.stack([np.asarray(f.convert("RGB"), dtype=np.float32) / 255.0 for f in frames])
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[Image.Image], progress_callback: ProgressCallback | None = None
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
- w, h = frames[i].size
171
  batch = [frames[i]]
172
  i += 1
173
- while i < len(frames) and len(batch) < FRAME_BATCH_SIZE and frames[i].size == (w, h):
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