mirror of
https://github.com/vladmandic/automatic
synced 2026-08-25 22:20:46 +02:00
update upscaler workflow
Signed-off-by: Vladimir Mandic <mandic00@live.com>
This commit is contained in:
@@ -116,6 +116,7 @@ Plus quite a lot more, see full [changelog](https://github.com/vladmandic/automa
|
||||
- api: auth via remote-ip
|
||||
- krea2: fallback to base pipeline/transformer for nunchaku-lite
|
||||
- torchsde: handle obsolete dependency
|
||||
- upscaler: avoid unnecessary multi-pass
|
||||
|
||||
## Update for 2026-08-07
|
||||
|
||||
|
||||
+3
-3
@@ -20,13 +20,13 @@
|
||||
"RealESRGAN": [
|
||||
["4x General V3", "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.5.0/realesr-general-x4v3.pth"],
|
||||
["4x General WDN V3", "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.5.0/realesr-general-wdn-x4v3.pth"],
|
||||
["AnimeVideo V3", "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.5.0/realesr-animevideov3.pth"],
|
||||
["4x AnimeVideo V3", "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.5.0/realesr-animevideov3.pth"],
|
||||
["4x+ Anime6B", "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth"],
|
||||
["4x+", "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth"],
|
||||
["2x+", "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.1/RealESRGAN_x2plus.pth"]
|
||||
],
|
||||
"SCUNet": [
|
||||
["GAN", "https://github.com/cszn/KAIR/releases/download/v1.0/scunet_color_real_gan.pth"],
|
||||
["PSNR", "https://github.com/cszn/KAIR/releases/download/v1.0/scunet_color_real_psnr.pth"]
|
||||
["GAN 1x", "https://github.com/cszn/KAIR/releases/download/v1.0/scunet_color_real_gan.pth"],
|
||||
["PSNR 1x", "https://github.com/cszn/KAIR/releases/download/v1.0/scunet_color_real_psnr.pth"]
|
||||
]
|
||||
}
|
||||
|
||||
Submodule extensions-builtin/sd-extension-chainner updated: 78f29b45f2...ef38d47839
+1
-1
Submodule extensions-builtin/sdnq updated: c5d505a96d...21d60c8c3f
@@ -145,7 +145,7 @@ class UpscalerESRGAN(Upscaler):
|
||||
log.debug(f"Upscaler cached: type={self.name} model={info.local_data_path}")
|
||||
return self.models[info.local_data_path]
|
||||
state_dict = torch.load(info.local_data_path, map_location='cpu' if devices.device.type in {'mps', 'cpu'} else None)
|
||||
log.info(f"Upscaler loaded: type={self.name} model={info.local_data_path}")
|
||||
log.info(f'Upscaler loaded: type="{self.name}" model="{info.local_data_path}"')
|
||||
|
||||
if "params_ema" in state_dict:
|
||||
state_dict = state_dict["params_ema"]
|
||||
|
||||
@@ -29,7 +29,7 @@ class UpscalerRealESRGAN(Upscaler):
|
||||
scaler.model = lambda: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=4, act_type='prelu')
|
||||
elif scaler.name == 'RealESRGAN 4x General WDN V3':
|
||||
scaler.model = lambda: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=4, act_type='prelu')
|
||||
elif scaler.name == 'RealESRGAN AnimeVideo V3':
|
||||
elif scaler.name == 'RealESRGAN 4x AnimeVideo V3':
|
||||
scaler.model=lambda: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=16, upscale=4, act_type='prelu')
|
||||
elif scaler.name == 'RealESRGAN 4x+':
|
||||
scaler.model = lambda: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=4)
|
||||
@@ -47,27 +47,28 @@ class UpscalerRealESRGAN(Upscaler):
|
||||
except Exception:
|
||||
log.error("Error importing Real-ESRGAN:")
|
||||
return img
|
||||
info = self.find_model(selected_model)
|
||||
if info is None or not os.path.exists(info.local_data_path):
|
||||
model = self.find_model(selected_model)
|
||||
if model is None or not os.path.exists(model.local_data_path):
|
||||
return img
|
||||
if self.models.get(info.local_data_path, None) is not None:
|
||||
log.debug(f"Upscaler cached: type={self.name} model={info.local_data_path}")
|
||||
upsampler=self.models[info.local_data_path]
|
||||
if self.models.get(model.local_data_path, None) is not None:
|
||||
log.debug(f"Upscaler cached: type={self.name} model={model.local_data_path}")
|
||||
upsampler=self.models[model.local_data_path]
|
||||
else:
|
||||
upsampler = RealESRGANer(
|
||||
name=info.name,
|
||||
scale=info.scale,
|
||||
model_path=info.local_data_path,
|
||||
model=info.model(),
|
||||
half=not opts.no_half and not opts.upcast_sampling,
|
||||
tile=opts.upscaler_tile_size,
|
||||
tile_pad=opts.upscaler_tile_overlap,
|
||||
device=device,
|
||||
)
|
||||
self.models[info.local_data_path] = upsampler
|
||||
upsampled = upsampler.enhance(np.array(img), outscale=info.scale)[0]
|
||||
if opts.upscaler_unload and info.local_data_path in self.models:
|
||||
del self.models[info.local_data_path]
|
||||
kwargs = {
|
||||
'name': model.name,
|
||||
'scale': model.scale,
|
||||
'model_path': model.local_data_path,
|
||||
'model': model.model(),
|
||||
'half': not opts.no_half and not opts.upcast_sampling,
|
||||
'tile': opts.upscaler_tile_size,
|
||||
'tile_pad': opts.upscaler_tile_overlap,
|
||||
'device': device,
|
||||
}
|
||||
upsampler = RealESRGANer(**kwargs)
|
||||
self.models[model.local_data_path] = upsampler
|
||||
upsampled = upsampler.enhance(np.array(img), outscale=model.scale)[0]
|
||||
if opts.upscaler_unload and model.local_data_path in self.models:
|
||||
del self.models[model.local_data_path]
|
||||
log.debug(f"Upscaler unloaded: type={self.name} model={selected_model}")
|
||||
devices.torch_gc(force=True)
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ class RealESRGANer:
|
||||
from modules.modelloader import load_file_from_url
|
||||
model_path = load_file_from_url(url=model_path, model_dir=os.path.join(ROOT_DIR, 'weights'), progress=True, file_name=None)
|
||||
loadnet = torch.load(model_path, map_location=torch.device('cpu'))
|
||||
log.info(f"Upscaler loaded: type={self.name} model={model_path}")
|
||||
log.info(f'Upscaler loaded: type="{self.name}" model="{model_path}"')
|
||||
|
||||
# prefer to use params_ema
|
||||
if 'params_ema' in loadnet:
|
||||
|
||||
@@ -27,7 +27,7 @@ class UpscalerSCUNet(Upscaler):
|
||||
model = net(in_nc=3, config=[4, 4, 4, 4, 4, 4, 4], dim=64)
|
||||
model.load_state_dict(torch.load(info.local_data_path), strict=True)
|
||||
model.eval()
|
||||
log.info(f"Upscaler loaded: type={self.name} model={info.local_data_path}")
|
||||
log.info(f'Upscaler loaded: type="{self.name}" model="{info.local_data_path}"')
|
||||
for _, v in model.named_parameters():
|
||||
v.requires_grad = False
|
||||
model = model.to(devices.device)
|
||||
@@ -41,9 +41,11 @@ class UpscalerSCUNet(Upscaler):
|
||||
# test the image tile by tile
|
||||
h, w = img.shape[2:]
|
||||
tile = opts.upscaler_tile_size
|
||||
tile = 0
|
||||
tile_overlap = opts.upscaler_tile_overlap
|
||||
if tile == 0:
|
||||
return model(img)
|
||||
output = model(img)
|
||||
return output
|
||||
assert tile % 8 == 0, "tile size should be a multiple of window_size"
|
||||
sf = 1
|
||||
stride = tile - tile_overlap
|
||||
@@ -73,6 +75,7 @@ class UpscalerSCUNet(Upscaler):
|
||||
model = self.load_model(selected_file)
|
||||
if model is None:
|
||||
return img
|
||||
sf = 1
|
||||
tile = opts.upscaler_tile_size
|
||||
h, w = img.height, img.width
|
||||
np_img = np.array(img)
|
||||
@@ -84,7 +87,7 @@ class UpscalerSCUNet(Upscaler):
|
||||
_img[:, :, :h, :w] = torch_img # pad image
|
||||
torch_img = _img
|
||||
torch_output = self.tiled_inference(torch_img, model).squeeze(0)
|
||||
torch_output = torch_output[:, :h * 1, :w * 1] # remove padding, if any
|
||||
torch_output = torch_output[:, :h * sf, :w * sf] # remove padding, if any
|
||||
np_output: np.ndarray = torch_output.float().cpu().clamp_(0, 1).numpy()
|
||||
del torch_img, torch_output
|
||||
devices.torch_gc()
|
||||
|
||||
@@ -58,12 +58,12 @@ class UpscalerSwinIR(Upscaler):
|
||||
model.load_state_dict(pretrained_model[param], strict=True)
|
||||
else:
|
||||
model.load_state_dict(pretrained_model, strict=True)
|
||||
log.info(f"Upscaler loaded: type={self.name} model={info.local_data_path} param={param}")
|
||||
log.info(f'Upscaler loaded: type="{self.name}" model="{info.local_data_path}" param="{param}"')
|
||||
model = compile_upscaler(model)
|
||||
self.models[info.local_data_path] = model
|
||||
return model
|
||||
except Exception as e:
|
||||
log.error(f'Upscaler invalid parameters: type={self.name} model={info.local_data_path} {e}')
|
||||
log.error(f'Upscaler invalid parameters: type="{self.name}" model="{info.local_data_path}" error="{e}"')
|
||||
return model
|
||||
|
||||
def do_upscale(self, img, selected_model): # pylint: disable=arguments-differ
|
||||
|
||||
+13
-14
@@ -38,7 +38,7 @@ class Upscaler:
|
||||
self.device = shared.device
|
||||
self.img = None
|
||||
self.output = None
|
||||
self.scale = 1
|
||||
self.scale = 4
|
||||
self.half = not shared.cmd_opts.no_half
|
||||
self.pre_pad = 0
|
||||
self.mod_scale = None
|
||||
@@ -116,11 +116,11 @@ class Upscaler:
|
||||
for _ in range(3):
|
||||
shape = (img.width, img.height)
|
||||
img = self.do_upscale(img, selected_model)
|
||||
if shape == (img.width, img.height):
|
||||
if shape == (img.width, img.height): # no change, no point of running another iteration
|
||||
break
|
||||
if img.width >= (dest_w - 8) and img.height >= (dest_h - 8):
|
||||
if (abs(img.width - dest_w) <= 12 or img.width >= dest_w) or (abs(img.height - dest_h) <= 12 or img.height >= dest_h): # close enough, do not run one more iteration
|
||||
break
|
||||
if img.width != dest_w or img.height != dest_h:
|
||||
if abs(img.width - dest_w) > 8 or abs(img.height - dest_h) > 8:
|
||||
from modules.image import sharpfin
|
||||
img = sharpfin.resize(img, (int(dest_w), int(dest_h)))
|
||||
shared.state.end(jobid)
|
||||
@@ -158,30 +158,29 @@ class UpscalerData:
|
||||
custom: bool = False
|
||||
name = None
|
||||
data_path = None
|
||||
scale: int = 1
|
||||
scale: int = 2
|
||||
scaler: Upscaler | None = None
|
||||
model: None
|
||||
|
||||
def __init__(self, name: str, path: str | None = None, upscaler: Upscaler | None = None, scale: int = 1, model=None):
|
||||
def __init__(self, name: str, path: str | None = None, upscaler: Upscaler | None = None, scale: int = 0, model=None):
|
||||
self.name = name
|
||||
self.data_path = path
|
||||
self.local_data_path = path
|
||||
self.scaler = upscaler
|
||||
if scale > 0:
|
||||
self.scale = scale
|
||||
elif '2x' in name.lower():
|
||||
elif '1x' in name.lower() or 'x1' in name.lower():
|
||||
self.scale = 1
|
||||
elif '2x' in name.lower() or 'x2' in name.lower():
|
||||
self.scale = 2
|
||||
elif '3x' in name.lower():
|
||||
elif '3x' in name.lower() or 'x3' in name.lower():
|
||||
self.scale = 3
|
||||
elif '4x' in name.lower():
|
||||
elif '4x' in name.lower() or 'x4' in name.lower():
|
||||
self.scale = 4
|
||||
elif '4x' in name.lower():
|
||||
self.scale = 4
|
||||
elif '8x' in name.lower():
|
||||
elif '8x' in name.lower() or 'x8' in name.lower():
|
||||
self.scale = 8
|
||||
else:
|
||||
self.scale = 1
|
||||
self.scale = scale
|
||||
self.scale = 2 # default scale to 2 if not specified
|
||||
self.model = model
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@@ -13,7 +13,7 @@ MODELS = {
|
||||
"Spandrel 2x RealPLKSR AnimeSharpV2": "https://huggingface.co/vladmandic/sdnext-upscalers/resolve/main/2x-AnimeSharpV2_RPLKSR_Sharp.pth",
|
||||
"Spandrel 2x RealESRGAN Compact": "https://huggingface.co/vladmandic/sdnext-upscalers/resolve/main/RealESRGAN-2x-Compact.pth",
|
||||
"Spandrel 2x RealESRGAN UltraCompact": "https://huggingface.co/vladmandic/sdnext-upscalers/resolve/main/RealESRGAN-2x-UltraCompact.pth",
|
||||
"Spandrel 2x RealSAFMN++": "https://huggingface.co/vladmandic/sdnext-upscalers/resolve/main/Real-SAFMN++.pth",
|
||||
"Spandrel 4x RealSAFMN++": "https://huggingface.co/vladmandic/sdnext-upscalers/resolve/main/Real-SAFMN++.pth",
|
||||
"Spandrel 2x RealSAFMN": "https://huggingface.co/vladmandic/sdnext-upscalers/resolve/main/Real-SAFMN-x2.pth",
|
||||
"Spandrel 4x RealSAFMN": "https://huggingface.co/vladmandic/sdnext-upscalers/resolve/main/Real-SAFMN-x4-v2.pth",
|
||||
"Spandrel 2x SAFMN PureScale": "https://huggingface.co/vladmandic/sdnext-upscalers/resolve/main/2x_SAFMN_PureScale.pth",
|
||||
@@ -37,9 +37,9 @@ class UpscalerSpandrel(Upscaler):
|
||||
scaler = UpscalerData(name=k, path=v, upscaler=self)
|
||||
self.scalers.append(scaler)
|
||||
else:
|
||||
for s in self.scalers: # update name of existing scaler if it was found
|
||||
if os.path.basename(s.data_path) == fn:
|
||||
s.name = k
|
||||
for i, _s in enumerate(self.scalers): # update name of existing scaler if it was found
|
||||
if os.path.basename(self.scalers[i].data_path) == fn:
|
||||
self.scalers[i].name = k
|
||||
break
|
||||
|
||||
def process(self, img: Image.Image, output_type='pil', quiet=False):
|
||||
|
||||
@@ -11,8 +11,8 @@ class UpscalerAsymmetricVAE(Upscaler):
|
||||
self.vae = None
|
||||
self.selected = None
|
||||
self.scalers = [
|
||||
UpscalerData("Asymmetric VAE v1", None, self),
|
||||
UpscalerData("Asymmetric VAE v2", None, self),
|
||||
UpscalerData("Asymmetric VAE 2x v1", None, self),
|
||||
UpscalerData("Asymmetric VAE 2x v2", None, self),
|
||||
]
|
||||
|
||||
def do_upscale(self, img: Image.Image, selected_model=None): # pylint: disable=arguments-differ
|
||||
@@ -52,7 +52,7 @@ class UpscalerWanUpscale(Upscaler):
|
||||
self.vae_decode = None
|
||||
self.selected = None
|
||||
self.scalers = [
|
||||
UpscalerData("WAN Asymmetric Upscale", None, self),
|
||||
UpscalerData("WAN Asymmetric Upscale 2x", None, self),
|
||||
]
|
||||
|
||||
def do_upscale(self, img: Image.Image, selected_model=None): # pylint: disable=arguments-differ
|
||||
|
||||
Reference in New Issue
Block a user