add RealPLKSR NomosWebPhoto and AnimeSharpV2 using spandrel engine

Signed-off-by: vladmandic <mandic00@live.com>
pull/4626/head
vladmandic 2026-02-05 14:06:17 +01:00
parent e5ebc4e5a2
commit dd80e15cfd
4 changed files with 76 additions and 8 deletions

View File

@ -2,14 +2,19 @@
## Update for 2026-02-05
Bugfix refresh
- fix: add metadata restore to always-on scripts
- fix: improve wildcard weights parsing, thanks @Tillerz
- fix: `anima` model detection
- refactor: reorganize `cli` scripts
- upscalers: hqx, icbi
- ui: add CTD-NT64Light theme, thanks @resonantsky
- **UI**
- ui: add CTD-NT64Light theme, thanks @resonantsky
- **Internal**
- refactor: reorganize `cli` scripts
- **Upscalers**
- add support for [spandrel](https://github.com/chaiNNer-org/spandrel)
upscaling engine with suport for new upscaling families
- add two new ai upscalers: *RealPLKSR NomosWebPhoto* and *RealPLKSR AnimeSharpV2*
- add two new interpolation methods: *HQX* and *ICB*
- **Fixes**
- fix: add metadata restore to always-on scripts
- fix: improve wildcard weights parsing, thanks @Tillerz
- fix: `anima` model detection
## Update for 2026-02-04

View File

@ -0,0 +1,55 @@
import os
import time
from PIL import Image
from modules.upscaler import Upscaler, UpscalerData
from modules import devices, paths
from modules.shared import log
MODELS = {
"Spandrel 4x RealPLKSR NomosWebPhoto": "https://huggingface.co/vladmandic/sdnext-upscalers/resolve/main/4xNomosWebPhoto_RealPLKSR.safetensors",
"Spandrel 2x RealPLKSR AnimeSharpV2": "https://huggingface.co/vladmandic/sdnext-upscalers/resolve/main/2x-AnimeSharpV2_RPLKSR_Sharp.pth",
}
class UpscalerSpandrel(Upscaler):
def __init__(self, dirname=None): # pylint: disable=unused-argument
super().__init__(False)
self.name = "Spandrel"
self.model_path = os.path.join(paths.models_path, 'Spandrel')
self.user_path = os.path.join(paths.models_path, 'Spandrel')
self.selected = None
self.model = None
self.scalers = []
for model_name, model_path in MODELS.items():
scaler = UpscalerData(name=model_name, path=model_path, upscaler=self)
self.scalers.append(scaler)
def process(self, img: Image.Image) -> Image.Image:
import torchvision.transforms.functional as TF
tensor = TF.to_tensor(img).unsqueeze(0).to(devices.device)
img = img.convert('RGB')
t0 = time.time()
with devices.inference_context():
tensor = self.model(tensor)
tensor = tensor.clamp(0, 1).squeeze(0).cpu()
t1 = time.time()
upscaled = TF.to_pil_image(tensor)
log.debug(f'Upscale: name="{self.selected}" input={img.size} output={upscaled.size} time={t1 - t0:.2f}')
return upscaled
def do_upscale(self, img: Image, selected_model=None):
from installer import install
if selected_model is None:
return img
install('spandrel')
try:
import spandrel
if (self.model is None) or (self.selected != selected_model):
self.selected = selected_model
model = self.find_model(selected_model)
self.model = spandrel.ModelLoader().load_from_file(model.local_data_path)
self.model.to(devices.device).eval()
return self.process(img)
except Exception as e:
log.error(f'Spandrel: {e}')
return img

View File

@ -1,3 +1,4 @@
import time
from PIL import Image
from modules.upscaler import Upscaler, UpscalerData
@ -30,12 +31,15 @@ class UpscalerAsymmetricVAE(Upscaler):
self.vae.eval()
self.selected = selected_model
shared.log.debug(f'Upscaler load: selected="{self.selected}" vae="{repo_id}"')
t0 = time.time()
img = img.resize((8 * (img.width // 8), 8 * (img.height // 8)), resample=Image.Resampling.LANCZOS).convert('RGB')
tensor = (F.pil_to_tensor(img).unsqueeze(0) / 255.0).to(device=devices.device, dtype=devices.dtype)
self.vae = self.vae.to(device=devices.device)
tensor = self.vae(tensor).sample
upscaled = F.to_pil_image(tensor.squeeze().clamp(0.0, 1.0).float().cpu())
self.vae = self.vae.to(device=devices.cpu)
t1 = time.time()
shared.log.debug(f'Upscale: name="{self.selected}" input={img.size} output={upscaled.size} time={t1 - t0:.2f}')
return upscaled
@ -73,6 +77,7 @@ class UpscalerWanUpscale(Upscaler):
self.selected = selected_model
shared.log.debug(f'Upscaler load: selected="{self.selected}" encode="{repo_encode}" decode="{repo_decode}"')
t0 = time.time()
self.vae_encode = self.vae_encode.to(device=devices.device)
tensor = (F.pil_to_tensor(img).unsqueeze(0).unsqueeze(2) / 255.0).to(device=devices.device, dtype=devices.dtype)
tensor = self.vae_encode.encode(tensor).latent_dist.mode()
@ -84,4 +89,6 @@ class UpscalerWanUpscale(Upscaler):
self.vae_decode.to(device=devices.cpu)
upscaled = F.to_pil_image(tensor.squeeze().clamp(0.0, 1.0).float().cpu())
t1 = time.time()
shared.log.debug(f'Upscale: name="{self.selected}" input={img.size} output={upscaled.size} time={t1 - t0:.2f}')
return upscaled

View File

@ -39,6 +39,7 @@ import modules.upscaler
import modules.upscaler_simple
import modules.upscaler_vae
import modules.upscaler_algo
import modules.upscaler_spandrel
import modules.extra_networks
import modules.ui_extra_networks
import modules.textual_inversion