cleanup upscaler settings

This commit is contained in:
Vladimir Mandic
2023-09-27 09:22:26 -04:00
parent 76894461d2
commit d67152cab4
9 changed files with 31 additions and 66 deletions
+5 -3
View File
@@ -45,17 +45,19 @@ Upgrades are still possible and supported, but above is recommended for best exp
faster loading, wider compatibility and support for embeddings with multiple vectors
information about used embedding is now also added to image metadata
- **Upscalers**:
- fix long outstanding memory leak in legacy code, amazing this went undetected for so long
- more high quality upscalers available by default
*SwinIR:2, ESRGAN:12, RealESRGAN:6, SCUNet:2*
- two additional latent upscalers based on SD upscale models when using Diffusers backend
*SD Upscale 2x, SD Upscale 4x*
Note: Recommended usage for *SD Upscale* is by using second pass instead of upscaler
as it allows for tuning of prompt, seed, sampler settings which are used to guide upscaler
- unified init/download/execute/progress code
- easier installation
- available in **xyz grid**
- upscalers are available in **xyz grid**
- simplified *settings->postprocessing->upscalers*
- allow upscale-only as part of **txt2img** and **img2img** workflows
simply set *denoising strength* to 0 so hires does not get triggered
- unified init/download/execute/progress code
- easier installation
- **Samplers**:
- moved ui options to submenu
- default list for new installs is now all samplers, list can be modified in settings
+11 -23
View File
@@ -20,18 +20,18 @@ cached_ldsr_model: torch.nn.Module = None
# Create LDSR Class
class LDSR:
def load_model_from_config(self, half_attention):
global cached_ldsr_model
global cached_ldsr_model # pylint: disable=global-statement
if shared.opts.ldsr_cached and cached_ldsr_model is not None:
shared.log.info("LDSR Loading model from cache")
if cached_ldsr_model is not None:
shared.log.info(f"Upscaler cached: type=LDSR model={self.modelPath}")
model: torch.nn.Module = cached_ldsr_model
else:
shared.log.info(f"LDSR Loading model from {self.modelPath}")
_, extension = os.path.splitext(self.modelPath)
if extension.lower() == ".safetensors":
pl_sd = safetensors.torch.load_file(self.modelPath, device="cpu")
else:
pl_sd = torch.load(self.modelPath, map_location="cpu")
shared.log.info(f"Upscaler loaded: type=LDSR model={self.modelPath}")
sd = pl_sd["state_dict"] if "state_dict" in pl_sd else pl_sd
config = OmegaConf.load(self.yamlPath)
config.model.target = "ldm.models.diffusion.ddpm.LatentDiffusionV1"
@@ -42,13 +42,9 @@ class LDSR:
model = model.half()
if shared.cmd_opts.opt_channelslast:
model = model.to(memory_format=torch.channels_last)
sd_hijack.model_hijack.hijack(model) # apply optimization
model.eval()
if shared.opts.ldsr_cached:
cached_ldsr_model = model
cached_ldsr_model = model
return {"model": model}
def __init__(self, model_path, yaml_path):
@@ -58,18 +54,15 @@ class LDSR:
@staticmethod
def run(model, selected_path, custom_steps, eta):
example = get_cond(selected_path)
n_runs = 1
guider = None
ckwargs = None
ddim_use_x0_pred = False
temperature = 1.
eta = eta
eta = eta # pylint: disable=self-assigning-variable
custom_shape = None
height, width = example["image"].shape[1:3]
split_input = height >= 128 and width >= 128
if split_input:
ks = 128
stride = 64
@@ -105,14 +98,9 @@ class LDSR:
def super_resolution(self, image, steps=100, target_scale=2, half_attention=False):
model = self.load_model_from_config(half_attention)
# Run settings
diffusion_steps = int(steps)
eta = 1.0
gc.collect()
devices.torch_gc()
im_og = image
width_og, height_og = im_og.size
# If we can adjust the max upscale size, then the 4 below should be our variable
@@ -121,7 +109,6 @@ class LDSR:
hd = height_og * down_sample_rate
width_downsampled_pre = int(np.ceil(wd))
height_downsampled_pre = int(np.ceil(hd))
if down_sample_rate != 1:
shared.log.info(f'LDSR Downsampling from [{width_og}, {height_og}] to [{width_downsampled_pre}, {height_downsampled_pre}]')
im_og = im_og.resize((width_downsampled_pre, height_downsampled_pre), Image.LANCZOS)
@@ -141,13 +128,14 @@ class LDSR:
sample = sample.numpy().astype(np.uint8)
sample = np.transpose(sample, (0, 2, 3, 1))
a = Image.fromarray(sample[0])
# remove padding
a = a.crop((0, 0) + tuple(np.array(im_og.size) * 4))
del model
gc.collect()
devices.torch_gc()
if shared.opts.upscaler_unload:
del model
cached_ldsr_model = None
shared.log.debug(f"Upscaler unloaded: type=LDSR model={self.modelPath}")
devices.torch_gc(force=True)
return a
@@ -67,9 +67,6 @@ class UpscalerLDSR(Upscaler):
def on_ui_settings():
import gradio as gr
shared.opts.add_option("ldsr_steps", shared.OptionInfo(100, "LDSR processing steps. Lower = faster", gr.Slider, {"minimum": 1, "maximum": 200, "step": 1}, section=('postprocessing', "Postprocessing")))
shared.opts.add_option("ldsr_cached", shared.OptionInfo(False, "Cache LDSR model in memory", gr.Checkbox, {"interactive": True}, section=('postprocessing', "Postprocessing")))
shared.opts.add_option("ldsr_steps", shared.OptionInfo(100, "LDSR processing steps", gr.Slider, {"minimum": 1, "maximum": 200, "step": 1}, section=('postprocessing', "Postprocessing")))
script_callbacks.on_ui_settings(on_ui_settings)
+2 -2
View File
@@ -188,10 +188,10 @@ def upscale_without_tiling(model, img):
def esrgan_upscale(model, img):
if opts.ESRGAN_tile == 0:
if opts.upscaler_tile_size == 0:
return upscale_without_tiling(model, img)
grid = images.split_grid(img, opts.ESRGAN_tile, opts.ESRGAN_tile, opts.ESRGAN_tile_overlap)
grid = images.split_grid(img, opts.upscaler_tile_size, opts.upscaler_tile_size, opts.upscaler_tile_overlap)
newtiles = []
scale_factor = 1
+2 -2
View File
@@ -55,8 +55,8 @@ class UpscalerRealESRGAN(Upscaler):
model_path=info.local_data_path,
model=info.model(),
half=not opts.no_half and not opts.upcast_sampling,
tile=opts.ESRGAN_tile,
tile_pad=opts.ESRGAN_tile_overlap,
tile=opts.upscaler_tile_size,
tile_pad=opts.upscaler_tile_overlap,
device=device,
)
self.models[info.local_data_path] = upsampler
+3 -13
View File
@@ -39,8 +39,8 @@ class UpscalerSCUNet(Upscaler):
def tiled_inference(img, model):
# test the image tile by tile
h, w = img.shape[2:]
tile = opts.SCUNET_tile
tile_overlap = opts.SCUNET_tile_overlap
tile = opts.upscaler_tile_size
tile_overlap = opts.upscaler_tile_overlap
if tile == 0:
return model(img)
assert tile % 8 == 0, "tile size should be a multiple of window_size"
@@ -72,7 +72,7 @@ class UpscalerSCUNet(Upscaler):
model = self.load_model(selected_file)
if model is None:
return img
tile = opts.SCUNET_tile
tile = opts.upscaler_tile_size
h, w = img.height, img.width
np_img = np.array(img)
np_img = np_img[:, :, ::-1] # RGB to BGR
@@ -95,13 +95,3 @@ class UpscalerSCUNet(Upscaler):
log.debug(f"Upscaler unloaded: type={self.name} model={selected_file}")
devices.torch_gc(force=True)
return img
def on_ui_settings():
import gradio as gr
from modules import shared
shared.opts.add_option("SCUNET_tile", shared.OptionInfo(256, "Tile size for SCUNET upscalers", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}, section=('postprocessing', "Postprocessing")).info("0 = no tiling"))
shared.opts.add_option("SCUNET_tile_overlap", shared.OptionInfo(8, "Tile overlap for SCUNET upscalers", gr.Slider, {"minimum": 0, "maximum": 64, "step": 1}, section=('postprocessing', "Postprocessing")).info("Low values = visible seam"))
script_callbacks.on_ui_settings(on_ui_settings)
+2 -11
View File
@@ -85,8 +85,8 @@ def upscale(
window_size=8,
scale=4,
):
tile = tile or shared.opts.SWIN_tile
tile_overlap = tile_overlap or shared.opts.SWIN_tile_overlap
tile = tile or shared.opts.upscaler_tile_size
tile_overlap = tile_overlap or shared.opts.upscaler_tile_overlap
img = np.array(img)
img = img[:, :, ::-1]
img = np.moveaxis(img, 2, 0) / 255
@@ -140,12 +140,3 @@ def inference(img, model, tile, tile_overlap, window_size, scale):
progress.update(task, advance=1, description="Upscaling")
output = E.div_(W)
return output
def on_ui_settings():
import gradio as gr
shared.opts.add_option("SWIN_tile", shared.OptionInfo(192, "Tile size for SwinIR upscaler", gr.Slider, {"minimum": 16, "maximum": 512, "step": 16}, section=('postprocessing', "Postprocessing")))
shared.opts.add_option("SWIN_tile_overlap", shared.OptionInfo(8, "Tile overlap for SwinIR upscaler", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}, section=('postprocessing', "Postprocessing")))
script_callbacks.on_ui_settings(on_ui_settings)
+3 -6
View File
@@ -614,13 +614,10 @@ options_templates.update(options_section(('postprocessing', "Postprocessing"), {
"postprocessing_sep_upscalers": OptionInfo("<h2>Upscaling</h2>", "", gr.HTML),
"upscaler_unload": OptionInfo(False, "Unload upscaler after processing"),
'upscaling_max_images_in_cache': OptionInfo(5, "Maximum number of images in upscaling cache", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1, "visible": False}),
# 'upscaling_max_images_in_cache': OptionInfo(5, "Maximum number of images in upscaling cache", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1, "visible": False}),
"upscaler_for_img2img": OptionInfo("None", "Default upscaler for image resize operations", gr.Dropdown, lambda: {"choices": [x.name for x in sd_upscalers]}),
# "realesrgan_enabled_models": OptionInfo(["R-ESRGAN 4x+", "R-ESRGAN 4x+ Anime6B"], "Real-ESRGAN available models", gr.CheckboxGroup, lambda: {"choices": shared_items.realesrgan_models_names()}),
"ESRGAN_tile": OptionInfo(192, "Tile size for ESRGAN upscalers", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}),
"ESRGAN_tile_overlap": OptionInfo(8, "Tile overlap in pixels for ESRGAN upscalers", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}),
"SCUNET_tile": OptionInfo(256, "Tile size for SCUNET upscalers", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}),
"SCUNET_tile_overlap": OptionInfo(8, "Tile overlap for SCUNET upscalers", gr.Slider, {"minimum": 0, "maximum": 64, "step": 1}),
"upscaler_tile_size": OptionInfo(192, "Upscaler tile size", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}),
"upscaler_tile_overlap": OptionInfo(8, "Upscaler tile overlap", gr.Slider, {"minimum": 0, "maximum": 64, "step": 1}),
}))
options_templates.update(options_section(('training', "Training"), {
+2 -2
View File
@@ -28,8 +28,8 @@ class Upscaler:
if models is None:
models = modules.shared.readfile('html/upscalers.json')
self.mod_pad_h = None
self.tile_size = modules.shared.opts.ESRGAN_tile
self.tile_pad = modules.shared.opts.ESRGAN_tile_overlap
self.tile_size = modules.shared.opts.upscaler_tile_size
self.tile_pad = modules.shared.opts.upscaler_tile_overlap
self.device = modules.shared.device
self.img = None
self.output = None