hotfix:修复oom问题

This commit is contained in:
方长君
2026-03-05 17:18:02 +08:00
parent 7f0247f55c
commit 03d0f260e7
+93 -11
View File
@@ -1,7 +1,7 @@
import os
import sys
import types
from typing import Dict, Tuple
from typing import Any, Dict, Tuple
import numpy as np
import torch
@@ -75,7 +75,7 @@ except Exception:
_IMPORT_ERROR = e
_UPSCALER_CACHE: Dict[Tuple[str, str], RealESRGAN] = {}
_UPSCALER_CACHE: Dict[Tuple[str, str], Any] = {}
_MODEL_CHOICES_CACHE = None
@@ -92,6 +92,19 @@ def _get_device() -> str:
return "cpu"
def _is_oom_error(exc: Exception) -> bool:
if isinstance(exc, torch.OutOfMemoryError):
return True
msg = str(exc).lower()
tokens = [
"out of memory",
"allocation on device",
"cuda error: out of memory",
"cublas_status_alloc_failed",
]
return any(token in msg for token in tokens)
def _candidate_model_dirs():
candidates = []
if folder_paths is not None:
@@ -171,8 +184,16 @@ def _get_upscaler(model_path: str) -> RealESRGAN:
upscaler.load_weights(model_path, download=False)
elif _BACKEND == "er":
class _RealESRGANerWrapper:
_TILE_SCHEDULE = (0, 1024, 768, 512, 384, 256, 192, 128, 96, 64)
def __init__(self, model_path: str, device: str):
model = RRDBNet(
self.model_path = model_path
self.device = device
self.upsampler = self._build_upsampler(device=device, tile=0, half=(device == "cuda"))
self._cpu_upsampler = None
def _build_model(self):
return RRDBNet(
num_in_ch=3,
num_out_ch=3,
num_feat=64,
@@ -180,24 +201,82 @@ def _get_upscaler(model_path: str) -> RealESRGAN:
num_grow_ch=32,
scale=4,
)
self.upsampler = RealESRGANer(
def _build_upsampler(self, device: str, tile: int, half: bool):
model = self._build_model()
return RealESRGANer(
scale=4,
model_path=model_path,
model_path=self.model_path,
model=model,
tile=0,
tile=tile,
tile_pad=10,
pre_pad=0,
half=(device == "cuda"),
half=(half and device == "cuda"),
device=device,
)
def _run_with_tile(self, img, tile: int):
self.upsampler.tile = tile
output, _ = self.upsampler.enhance(img, outscale=1)
return output
def release(self):
try:
self.upsampler.model.to(device="cpu")
except Exception:
pass
self._cpu_upsampler = None
def predict(self, pil_image: Image.Image) -> Image.Image:
import cv2
img = np.array(pil_image)
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
output, _ = self.upsampler.enhance(img, outscale=1)
output = cv2.cvtColor(output, cv2.COLOR_BGR2RGB)
return Image.fromarray(output)
max_side = max(img.shape[0], img.shape[1])
tile_schedule = []
for tile in self._TILE_SCHEDULE:
if tile == 0:
tile_schedule.append(0)
continue
tile_eff = min(tile, max_side)
if tile_eff >= 64:
tile_schedule.append(tile_eff)
tile_schedule = list(dict.fromkeys(tile_schedule))
last_oom = None
for tile in tile_schedule:
try:
output = self._run_with_tile(img, tile=tile)
output = cv2.cvtColor(output, cv2.COLOR_BGR2RGB)
return Image.fromarray(output)
except Exception as e:
if not _is_oom_error(e):
raise
last_oom = e
if self.device == "cuda":
torch.cuda.empty_cache()
continue
if self.device == "cuda":
try:
if self._cpu_upsampler is None:
self._cpu_upsampler = self._build_upsampler(device="cpu", tile=256, half=False)
output, _ = self._cpu_upsampler.enhance(img, outscale=1)
output = cv2.cvtColor(output, cv2.COLOR_BGR2RGB)
return Image.fromarray(output)
except Exception as cpu_error:
if last_oom is not None:
raise RuntimeError(
"RealESRGAN 显存不足:已尝试分块推理与 CPU 回退,仍失败。"
) from cpu_error
raise
if last_oom is not None:
raise RuntimeError(
"RealESRGAN 显存不足:请减小输入分辨率或切换到 CPU。"
) from last_oom
raise RuntimeError("RealESRGAN 推理失败,未返回输出。")
upscaler = _RealESRGANerWrapper(model_path, device)
else:
@@ -279,7 +358,10 @@ class MeuxRealESRGANUpscale:
if free_gpu_after and _get_device() == "cuda":
try:
upscaler.model.to(device="cpu")
if hasattr(upscaler, "release"):
upscaler.release()
else:
upscaler.model.to(device="cpu")
except Exception:
pass
_UPSCALER_CACHE.pop((model_path, "cuda"), None)