mirror of
https://github.com/AUTOMATIC1111/stable-diffusion-webui.git
synced 2026-01-09 10:41:15 -08:00
Merge branch 'dev' into 10141-gradio-user-exif
This commit is contained in:
commit
4147fd6b2f
104 changed files with 3450 additions and 1035 deletions
|
|
@ -14,7 +14,7 @@ from fastapi.encoders import jsonable_encoder
|
|||
from secrets import compare_digest
|
||||
|
||||
import modules.shared as shared
|
||||
from modules import sd_samplers, deepbooru, sd_hijack, images, scripts, ui, postprocessing
|
||||
from modules import sd_samplers, deepbooru, sd_hijack, images, scripts, ui, postprocessing, errors, restart
|
||||
from modules.api import models
|
||||
from modules.shared import opts
|
||||
from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, process_images
|
||||
|
|
@ -23,6 +23,7 @@ from modules.textual_inversion.preprocess import preprocess
|
|||
from modules.hypernetworks.hypernetwork import create_hypernetwork, train_hypernetwork
|
||||
from PIL import PngImagePlugin,Image
|
||||
from modules.sd_models import checkpoints_list, unload_model_weights, reload_model_weights
|
||||
from modules.sd_vae import vae_dict
|
||||
from modules.sd_models_config import find_checkpoint_config_near_filename
|
||||
from modules.realesrgan_model import get_realesrgan_models
|
||||
from modules import devices
|
||||
|
|
@ -108,7 +109,6 @@ def api_middleware(app: FastAPI):
|
|||
from rich.console import Console
|
||||
console = Console()
|
||||
except Exception:
|
||||
import traceback
|
||||
rich_available = False
|
||||
|
||||
@app.middleware("http")
|
||||
|
|
@ -139,11 +139,12 @@ def api_middleware(app: FastAPI):
|
|||
"errors": str(e),
|
||||
}
|
||||
if not isinstance(e, HTTPException): # do not print backtrace on known httpexceptions
|
||||
print(f"API error: {request.method}: {request.url} {err}")
|
||||
message = f"API error: {request.method}: {request.url} {err}"
|
||||
if rich_available:
|
||||
print(message)
|
||||
console.print_exception(show_locals=True, max_frames=2, extra_lines=1, suppress=[anyio, starlette], word_wrap=False, width=min([console.width, 200]))
|
||||
else:
|
||||
traceback.print_exc()
|
||||
errors.report(message, exc_info=True)
|
||||
return JSONResponse(status_code=vars(e).get('status_code', 500), content=jsonable_encoder(err))
|
||||
|
||||
@app.middleware("http")
|
||||
|
|
@ -188,7 +189,9 @@ class Api:
|
|||
self.add_api_route("/sdapi/v1/cmd-flags", self.get_cmd_flags, methods=["GET"], response_model=models.FlagsModel)
|
||||
self.add_api_route("/sdapi/v1/samplers", self.get_samplers, methods=["GET"], response_model=List[models.SamplerItem])
|
||||
self.add_api_route("/sdapi/v1/upscalers", self.get_upscalers, methods=["GET"], response_model=List[models.UpscalerItem])
|
||||
self.add_api_route("/sdapi/v1/latent-upscale-modes", self.get_latent_upscale_modes, methods=["GET"], response_model=List[models.LatentUpscalerModeItem])
|
||||
self.add_api_route("/sdapi/v1/sd-models", self.get_sd_models, methods=["GET"], response_model=List[models.SDModelItem])
|
||||
self.add_api_route("/sdapi/v1/sd-vae", self.get_sd_vaes, methods=["GET"], response_model=List[models.SDVaeItem])
|
||||
self.add_api_route("/sdapi/v1/hypernetworks", self.get_hypernetworks, methods=["GET"], response_model=List[models.HypernetworkItem])
|
||||
self.add_api_route("/sdapi/v1/face-restorers", self.get_face_restorers, methods=["GET"], response_model=List[models.FaceRestorerItem])
|
||||
self.add_api_route("/sdapi/v1/realesrgan-models", self.get_realesrgan_models, methods=["GET"], response_model=List[models.RealesrganItem])
|
||||
|
|
@ -206,6 +209,11 @@ class Api:
|
|||
self.add_api_route("/sdapi/v1/scripts", self.get_scripts_list, methods=["GET"], response_model=models.ScriptsList)
|
||||
self.add_api_route("/sdapi/v1/script-info", self.get_script_info, methods=["GET"], response_model=List[models.ScriptInfo])
|
||||
|
||||
if shared.cmd_opts.add_stop_route:
|
||||
self.add_api_route("/sdapi/v1/server-kill", self.kill_webui, methods=["POST"])
|
||||
self.add_api_route("/sdapi/v1/server-restart", self.restart_webui, methods=["POST"])
|
||||
self.add_api_route("/sdapi/v1/server-stop", self.stop_webui, methods=["POST"])
|
||||
|
||||
self.default_script_arg_txt2img = []
|
||||
self.default_script_arg_img2img = []
|
||||
|
||||
|
|
@ -278,7 +286,7 @@ class Api:
|
|||
script_args[0] = selectable_idx + 1
|
||||
|
||||
# Now check for always on scripts
|
||||
if request.alwayson_scripts and (len(request.alwayson_scripts) > 0):
|
||||
if request.alwayson_scripts:
|
||||
for alwayson_script_name in request.alwayson_scripts.keys():
|
||||
alwayson_script = self.get_script(alwayson_script_name, script_runner)
|
||||
if alwayson_script is None:
|
||||
|
|
@ -538,9 +546,20 @@ class Api:
|
|||
for upscaler in shared.sd_upscalers
|
||||
]
|
||||
|
||||
def get_latent_upscale_modes(self):
|
||||
return [
|
||||
{
|
||||
"name": upscale_mode,
|
||||
}
|
||||
for upscale_mode in [*(shared.latent_upscale_modes or {})]
|
||||
]
|
||||
|
||||
def get_sd_models(self):
|
||||
return [{"title": x.title, "model_name": x.model_name, "hash": x.shorthash, "sha256": x.sha256, "filename": x.filename, "config": find_checkpoint_config_near_filename(x)} for x in checkpoints_list.values()]
|
||||
|
||||
def get_sd_vaes(self):
|
||||
return [{"model_name": x, "filename": vae_dict[x]} for x in vae_dict.keys()]
|
||||
|
||||
def get_hypernetworks(self):
|
||||
return [{"name": name, "path": shared.hypernetworks[name]} for name in shared.hypernetworks]
|
||||
|
||||
|
|
@ -700,4 +719,16 @@ class Api:
|
|||
|
||||
def launch(self, server_name, port):
|
||||
self.app.include_router(self.router)
|
||||
uvicorn.run(self.app, host=server_name, port=port)
|
||||
uvicorn.run(self.app, host=server_name, port=port, timeout_keep_alive=0)
|
||||
|
||||
def kill_webui(self):
|
||||
restart.stop_program()
|
||||
|
||||
def restart_webui(self):
|
||||
if restart.is_restartable():
|
||||
restart.restart_program()
|
||||
return Response(status_code=501)
|
||||
|
||||
def stop_webui(request):
|
||||
shared.state.server_command = "stop"
|
||||
return Response("Stopping.")
|
||||
|
|
|
|||
|
|
@ -241,6 +241,9 @@ class UpscalerItem(BaseModel):
|
|||
model_url: Optional[str] = Field(title="URL")
|
||||
scale: Optional[float] = Field(title="Scale")
|
||||
|
||||
class LatentUpscalerModeItem(BaseModel):
|
||||
name: str = Field(title="Name")
|
||||
|
||||
class SDModelItem(BaseModel):
|
||||
title: str = Field(title="Title")
|
||||
model_name: str = Field(title="Model Name")
|
||||
|
|
@ -249,6 +252,10 @@ class SDModelItem(BaseModel):
|
|||
filename: str = Field(title="Filename")
|
||||
config: Optional[str] = Field(title="Config file")
|
||||
|
||||
class SDVaeItem(BaseModel):
|
||||
model_name: str = Field(title="Model Name")
|
||||
filename: str = Field(title="Filename")
|
||||
|
||||
class HypernetworkItem(BaseModel):
|
||||
name: str = Field(title="Name")
|
||||
path: Optional[str] = Field(title="Path")
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
from functools import wraps
|
||||
import html
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
import time
|
||||
|
||||
from modules import shared, progress
|
||||
from modules import shared, progress, errors
|
||||
|
||||
queue_lock = threading.Lock()
|
||||
|
||||
|
|
@ -25,7 +23,7 @@ def wrap_gradio_gpu_call(func, extra_outputs=None):
|
|||
def f(*args, **kwargs):
|
||||
|
||||
# if the first argument is a string that says "task(...)", it is treated as a job id
|
||||
if len(args) > 0 and type(args[0]) == str and args[0][0:5] == "task(" and args[0][-1] == ")":
|
||||
if args and type(args[0]) == str and args[0].startswith("task(") and args[0].endswith(")"):
|
||||
id_task = args[0]
|
||||
progress.add_task_to_queue(id_task)
|
||||
else:
|
||||
|
|
@ -59,16 +57,14 @@ def wrap_gradio_call(func, extra_outputs=None, add_stats=False):
|
|||
try:
|
||||
res = list(func(*args, **kwargs))
|
||||
except Exception as e:
|
||||
# When printing out our debug argument list, do not print out more than a MB of text
|
||||
max_debug_str_len = 131072 # (1024*1024)/8
|
||||
|
||||
print("Error completing request", file=sys.stderr)
|
||||
argStr = f"Arguments: {args} {kwargs}"
|
||||
print(argStr[:max_debug_str_len], file=sys.stderr)
|
||||
if len(argStr) > max_debug_str_len:
|
||||
print(f"(Argument list truncated at {max_debug_str_len}/{len(argStr)} characters)", file=sys.stderr)
|
||||
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
# When printing out our debug argument list,
|
||||
# do not print out more than a 100 KB of text
|
||||
max_debug_str_len = 131072
|
||||
message = "Error completing request"
|
||||
arg_str = f"Arguments: {args} {kwargs}"[:max_debug_str_len]
|
||||
if len(arg_str) > max_debug_str_len:
|
||||
arg_str += f" (Argument list truncated at {max_debug_str_len}/{len(arg_str)} characters)"
|
||||
errors.report(f"{message}\n{arg_str}", exc_info=True)
|
||||
|
||||
shared.state.job = ""
|
||||
shared.state.job_count = 0
|
||||
|
|
@ -111,4 +107,3 @@ def wrap_gradio_call(func, extra_outputs=None, add_stats=False):
|
|||
return tuple(res)
|
||||
|
||||
return f
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ parser.add_argument("--skip-python-version-check", action='store_true', help="la
|
|||
parser.add_argument("--skip-torch-cuda-test", action='store_true', help="launch.py argument: do not check if CUDA is able to work properly")
|
||||
parser.add_argument("--reinstall-xformers", action='store_true', help="launch.py argument: install the appropriate version of xformers even if you have some version already installed")
|
||||
parser.add_argument("--reinstall-torch", action='store_true', help="launch.py argument: install the appropriate version of torch even if you have some version already installed")
|
||||
parser.add_argument("--update-check", action='store_true', help="launch.py argument: chck for updates at startup")
|
||||
parser.add_argument("--update-check", action='store_true', help="launch.py argument: check for updates at startup")
|
||||
parser.add_argument("--test-server", action='store_true', help="launch.py argument: configure server for testing")
|
||||
parser.add_argument("--skip-prepare-environment", action='store_true', help="launch.py argument: skip all environment preparation")
|
||||
parser.add_argument("--skip-install", action='store_true', help="launch.py argument: skip installation of packages")
|
||||
|
|
@ -106,4 +106,4 @@ parser.add_argument("--skip-version-check", action='store_true', help="Do not ch
|
|||
parser.add_argument("--no-hashing", action='store_true', help="disable sha256 hashing of checkpoints to help loading performance", default=False)
|
||||
parser.add_argument("--no-download-sd-model", action='store_true', help="don't download SD1.5 model even if no model is found in --ckpt-dir", default=False)
|
||||
parser.add_argument('--subpath', type=str, help='customize the subpath for gradio, use with reverse proxy')
|
||||
parser.add_argument('--add-stop-route', action='store_true', help='add /_stop route to stop server')
|
||||
parser.add_argument('--add-stop-route', action='store_true', help='enable server stop/restart/kill via api')
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import cv2
|
||||
import torch
|
||||
|
||||
import modules.face_restoration
|
||||
import modules.shared
|
||||
from modules import shared, devices, modelloader
|
||||
from modules import shared, devices, modelloader, errors
|
||||
from modules.paths import models_path
|
||||
|
||||
# codeformer people made a choice to include modified basicsr library to their project which makes
|
||||
|
|
@ -22,9 +20,7 @@ codeformer = None
|
|||
|
||||
|
||||
def setup_model(dirname):
|
||||
global model_path
|
||||
if not os.path.exists(model_path):
|
||||
os.makedirs(model_path)
|
||||
os.makedirs(model_path, exist_ok=True)
|
||||
|
||||
path = modules.paths.paths.get("CodeFormer", None)
|
||||
if path is None:
|
||||
|
|
@ -105,8 +101,8 @@ def setup_model(dirname):
|
|||
restored_face = tensor2img(output, rgb2bgr=True, min_max=(-1, 1))
|
||||
del output
|
||||
torch.cuda.empty_cache()
|
||||
except Exception as error:
|
||||
print(f'\tFailed inference for CodeFormer: {error}', file=sys.stderr)
|
||||
except Exception:
|
||||
errors.report('Failed inference for CodeFormer', exc_info=True)
|
||||
restored_face = tensor2img(cropped_face_t, rgb2bgr=True, min_max=(-1, 1))
|
||||
|
||||
restored_face = restored_face.astype('uint8')
|
||||
|
|
@ -135,7 +131,6 @@ def setup_model(dirname):
|
|||
shared.face_restorers.append(codeformer)
|
||||
|
||||
except Exception:
|
||||
print("Error setting up CodeFormer:", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report("Error setting up CodeFormer", exc_info=True)
|
||||
|
||||
# sys.path = stored_sys_path
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ Supports saving and restoring webui and extensions from a known working set of c
|
|||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
import json
|
||||
import time
|
||||
import tqdm
|
||||
|
|
@ -13,7 +11,7 @@ from datetime import datetime
|
|||
from collections import OrderedDict
|
||||
import git
|
||||
|
||||
from modules import shared, extensions
|
||||
from modules import shared, extensions, errors
|
||||
from modules.paths_internal import script_path, config_states_dir
|
||||
|
||||
|
||||
|
|
@ -53,8 +51,7 @@ def get_webui_config():
|
|||
if os.path.exists(os.path.join(script_path, ".git")):
|
||||
webui_repo = git.Repo(script_path)
|
||||
except Exception:
|
||||
print(f"Error reading webui git info from {script_path}:", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error reading webui git info from {script_path}", exc_info=True)
|
||||
|
||||
webui_remote = None
|
||||
webui_commit_hash = None
|
||||
|
|
@ -134,8 +131,7 @@ def restore_webui_config(config):
|
|||
if os.path.exists(os.path.join(script_path, ".git")):
|
||||
webui_repo = git.Repo(script_path)
|
||||
except Exception:
|
||||
print(f"Error reading webui git info from {script_path}:", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error reading webui git info from {script_path}", exc_info=True)
|
||||
return
|
||||
|
||||
try:
|
||||
|
|
@ -143,8 +139,7 @@ def restore_webui_config(config):
|
|||
webui_repo.git.reset(webui_commit_hash, hard=True)
|
||||
print(f"* Restored webui to commit {webui_commit_hash}.")
|
||||
except Exception:
|
||||
print(f"Error restoring webui to commit {webui_commit_hash}:", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error restoring webui to commit{webui_commit_hash}")
|
||||
|
||||
|
||||
def restore_extension_config(config):
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import sys
|
||||
import contextlib
|
||||
from functools import lru_cache
|
||||
|
||||
import torch
|
||||
from modules import errors
|
||||
|
||||
|
|
@ -154,3 +156,19 @@ def test_for_nans(x, where):
|
|||
message += " Use --disable-nan-check commandline argument to disable this check."
|
||||
|
||||
raise NansException(message)
|
||||
|
||||
|
||||
@lru_cache
|
||||
def first_time_calculation():
|
||||
"""
|
||||
just do any calculation with pytorch layers - the first time this is done it allocaltes about 700MB of memory and
|
||||
spends about 2.7 seconds doing that, at least wih NVidia.
|
||||
"""
|
||||
|
||||
x = torch.zeros((1, 1)).to(device, dtype)
|
||||
linear = torch.nn.Linear(1, 1).to(device, dtype)
|
||||
linear(x)
|
||||
|
||||
x = torch.zeros((1, 1, 3, 3)).to(device, dtype)
|
||||
conv2d = torch.nn.Conv2d(1, 1, (3, 3)).to(device, dtype)
|
||||
conv2d(x)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,42 @@
|
|||
import sys
|
||||
import textwrap
|
||||
import traceback
|
||||
|
||||
|
||||
exception_records = []
|
||||
|
||||
|
||||
def record_exception():
|
||||
_, e, tb = sys.exc_info()
|
||||
if e is None:
|
||||
return
|
||||
|
||||
if exception_records and exception_records[-1] == e:
|
||||
return
|
||||
|
||||
exception_records.append((e, tb))
|
||||
|
||||
if len(exception_records) > 5:
|
||||
exception_records.pop(0)
|
||||
|
||||
|
||||
def report(message: str, *, exc_info: bool = False) -> None:
|
||||
"""
|
||||
Print an error message to stderr, with optional traceback.
|
||||
"""
|
||||
|
||||
record_exception()
|
||||
|
||||
for line in message.splitlines():
|
||||
print("***", line, file=sys.stderr)
|
||||
if exc_info:
|
||||
print(textwrap.indent(traceback.format_exc(), " "), file=sys.stderr)
|
||||
print("---", file=sys.stderr)
|
||||
|
||||
|
||||
def print_error_explanation(message):
|
||||
record_exception()
|
||||
|
||||
lines = message.strip().split("\n")
|
||||
max_len = max([len(x) for x in lines])
|
||||
|
||||
|
|
@ -12,9 +46,15 @@ def print_error_explanation(message):
|
|||
print('=' * max_len, file=sys.stderr)
|
||||
|
||||
|
||||
def display(e: Exception, task):
|
||||
def display(e: Exception, task, *, full_traceback=False):
|
||||
record_exception()
|
||||
|
||||
print(f"{task or 'error'}: {type(e).__name__}", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
te = traceback.TracebackException.from_exception(e)
|
||||
if full_traceback:
|
||||
# include frames leading up to the try-catch block
|
||||
te.stack = traceback.StackSummary(traceback.extract_stack()[:-2] + te.stack)
|
||||
print(*te.format(), sep="", file=sys.stderr)
|
||||
|
||||
message = str(e)
|
||||
if "copying a param with shape torch.Size([640, 1024]) from checkpoint, the shape in current model is torch.Size([640, 768])" in message:
|
||||
|
|
@ -28,6 +68,8 @@ already_displayed = {}
|
|||
|
||||
|
||||
def display_once(e: Exception, task):
|
||||
record_exception()
|
||||
|
||||
if task in already_displayed:
|
||||
return
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,13 @@
|
|||
import os
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
import git
|
||||
|
||||
from modules import shared
|
||||
from modules import shared, errors
|
||||
from modules.gitpython_hack import Repo
|
||||
from modules.paths_internal import extensions_dir, extensions_builtin_dir, script_path # noqa: F401
|
||||
|
||||
extensions = []
|
||||
|
||||
if not os.path.exists(extensions_dir):
|
||||
os.makedirs(extensions_dir)
|
||||
os.makedirs(extensions_dir, exist_ok=True)
|
||||
|
||||
|
||||
def active():
|
||||
|
|
@ -54,10 +50,9 @@ class Extension:
|
|||
repo = None
|
||||
try:
|
||||
if os.path.exists(os.path.join(self.path, ".git")):
|
||||
repo = git.Repo(self.path)
|
||||
repo = Repo(self.path)
|
||||
except Exception:
|
||||
print(f"Error reading github repository info from {self.path}:", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error reading github repository info from {self.path}", exc_info=True)
|
||||
|
||||
if repo is None or repo.bare:
|
||||
self.remote = None
|
||||
|
|
@ -72,8 +67,8 @@ class Extension:
|
|||
self.commit_hash = commit.hexsha
|
||||
self.version = self.commit_hash[:8]
|
||||
|
||||
except Exception as ex:
|
||||
print(f"Failed reading extension data from Git repository ({self.name}): {ex}", file=sys.stderr)
|
||||
except Exception:
|
||||
errors.report(f"Failed reading extension data from Git repository ({self.name})", exc_info=True)
|
||||
self.remote = None
|
||||
|
||||
self.have_info_from_repo = True
|
||||
|
|
@ -94,7 +89,7 @@ class Extension:
|
|||
return res
|
||||
|
||||
def check_updates(self):
|
||||
repo = git.Repo(self.path)
|
||||
repo = Repo(self.path)
|
||||
for fetch in repo.remote().fetch(dry_run=True):
|
||||
if fetch.flags != fetch.HEAD_UPTODATE:
|
||||
self.can_update = True
|
||||
|
|
@ -116,7 +111,7 @@ class Extension:
|
|||
self.status = "latest"
|
||||
|
||||
def fetch_and_reset_hard(self, commit='origin'):
|
||||
repo = git.Repo(self.path)
|
||||
repo = Repo(self.path)
|
||||
# Fix: `error: Your local changes to the following files would be overwritten by merge`,
|
||||
# because WSL2 Docker set 755 file permissions instead of 644, this results to the error.
|
||||
repo.git.fetch(all=True)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ class ExtraNetworkParams:
|
|||
else:
|
||||
self.positional.append(item)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.items == other.items
|
||||
|
||||
|
||||
class ExtraNetwork:
|
||||
def __init__(self, name):
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ class ExtraNetworkHypernet(extra_networks.ExtraNetwork):
|
|||
def activate(self, p, params_list):
|
||||
additional = shared.opts.sd_hypernetwork
|
||||
|
||||
if additional != "None" and additional in shared.hypernetworks and len([x for x in params_list if x.items[0] == additional]) == 0:
|
||||
if additional != "None" and additional in shared.hypernetworks and not any(x for x in params_list if x.items[0] == additional):
|
||||
hypernet_prompt_text = f"<hypernet:{additional}:{shared.opts.extra_networks_default_multiplier}>"
|
||||
p.all_prompts = [f"{prompt}{hypernet_prompt_text}" for prompt in p.all_prompts]
|
||||
params_list.append(extra_networks.ExtraNetworkParams(items=[additional, shared.opts.extra_networks_default_multiplier]))
|
||||
|
|
@ -17,7 +17,7 @@ class ExtraNetworkHypernet(extra_networks.ExtraNetwork):
|
|||
names = []
|
||||
multipliers = []
|
||||
for params in params_list:
|
||||
assert len(params.items) > 0
|
||||
assert params.items
|
||||
|
||||
names.append(params.items[0])
|
||||
multipliers.append(float(params.items[1]) if len(params.items) > 1 else 1.0)
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ def image_from_url_text(filedata):
|
|||
if filedata is None:
|
||||
return None
|
||||
|
||||
if type(filedata) == list and len(filedata) > 0 and type(filedata[0]) == dict and filedata[0].get("is_file", False):
|
||||
if type(filedata) == list and filedata and type(filedata[0]) == dict and filedata[0].get("is_file", False):
|
||||
filedata = filedata[0]
|
||||
|
||||
if type(filedata) == dict and filedata.get("is_file", False):
|
||||
|
|
@ -265,19 +265,30 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model
|
|||
else:
|
||||
prompt += ("" if prompt == "" else "\n") + line
|
||||
|
||||
if shared.opts.infotext_styles != "Ignore":
|
||||
found_styles, prompt, negative_prompt = shared.prompt_styles.extract_styles_from_prompt(prompt, negative_prompt)
|
||||
|
||||
if shared.opts.infotext_styles == "Apply":
|
||||
res["Styles array"] = found_styles
|
||||
elif shared.opts.infotext_styles == "Apply if any" and found_styles:
|
||||
res["Styles array"] = found_styles
|
||||
|
||||
res["Prompt"] = prompt
|
||||
res["Negative prompt"] = negative_prompt
|
||||
|
||||
for k, v in re_param.findall(lastline):
|
||||
if v[0] == '"' and v[-1] == '"':
|
||||
v = unquote(v)
|
||||
try:
|
||||
if v[0] == '"' and v[-1] == '"':
|
||||
v = unquote(v)
|
||||
|
||||
m = re_imagesize.match(v)
|
||||
if m is not None:
|
||||
res[f"{k}-1"] = m.group(1)
|
||||
res[f"{k}-2"] = m.group(2)
|
||||
else:
|
||||
res[k] = v
|
||||
m = re_imagesize.match(v)
|
||||
if m is not None:
|
||||
res[f"{k}-1"] = m.group(1)
|
||||
res[f"{k}-2"] = m.group(2)
|
||||
else:
|
||||
res[k] = v
|
||||
except Exception:
|
||||
print(f"Error parsing \"{k}: {v}\"")
|
||||
|
||||
# Missing CLIP skip means it was set to 1 (the default)
|
||||
if "Clip skip" not in res:
|
||||
|
|
@ -306,6 +317,18 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model
|
|||
if "RNG" not in res:
|
||||
res["RNG"] = "GPU"
|
||||
|
||||
if "Schedule type" not in res:
|
||||
res["Schedule type"] = "Automatic"
|
||||
|
||||
if "Schedule max sigma" not in res:
|
||||
res["Schedule max sigma"] = 0
|
||||
|
||||
if "Schedule min sigma" not in res:
|
||||
res["Schedule min sigma"] = 0
|
||||
|
||||
if "Schedule rho" not in res:
|
||||
res["Schedule rho"] = 0
|
||||
|
||||
return res
|
||||
|
||||
|
||||
|
|
@ -318,6 +341,10 @@ infotext_to_setting_name_mapping = [
|
|||
('Conditional mask weight', 'inpainting_mask_weight'),
|
||||
('Model hash', 'sd_model_checkpoint'),
|
||||
('ENSD', 'eta_noise_seed_delta'),
|
||||
('Schedule type', 'k_sched_type'),
|
||||
('Schedule max sigma', 'sigma_max'),
|
||||
('Schedule min sigma', 'sigma_min'),
|
||||
('Schedule rho', 'rho'),
|
||||
('Noise multiplier', 'initial_noise_multiplier'),
|
||||
('Eta', 'eta_ancestral'),
|
||||
('Eta DDIM', 'eta_ddim'),
|
||||
|
|
@ -330,6 +357,7 @@ infotext_to_setting_name_mapping = [
|
|||
('Token merging ratio hr', 'token_merging_ratio_hr'),
|
||||
('RNG', 'randn_source'),
|
||||
('NGMS', 's_min_uncond'),
|
||||
('Pad conds', 'pad_cond_uncond'),
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -421,7 +449,7 @@ def connect_paste(button, paste_fields, input_comp, override_settings_component,
|
|||
|
||||
vals_pairs = [f"{k}: {v}" for k, v in vals.items()]
|
||||
|
||||
return gr.Dropdown.update(value=vals_pairs, choices=vals_pairs, visible=len(vals_pairs) > 0)
|
||||
return gr.Dropdown.update(value=vals_pairs, choices=vals_pairs, visible=bool(vals_pairs))
|
||||
|
||||
paste_fields = paste_fields + [(override_settings_component, paste_settings)]
|
||||
|
||||
|
|
@ -438,5 +466,3 @@ def connect_paste(button, paste_fields, input_comp, override_settings_component,
|
|||
outputs=[],
|
||||
show_progress=False,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import facexlib
|
||||
import gfpgan
|
||||
|
||||
import modules.face_restoration
|
||||
from modules import paths, shared, devices, modelloader
|
||||
from modules import paths, shared, devices, modelloader, errors
|
||||
|
||||
model_dir = "GFPGAN"
|
||||
user_path = None
|
||||
|
|
@ -72,11 +70,8 @@ gfpgan_constructor = None
|
|||
|
||||
|
||||
def setup_model(dirname):
|
||||
global model_path
|
||||
if not os.path.exists(model_path):
|
||||
os.makedirs(model_path)
|
||||
|
||||
try:
|
||||
os.makedirs(model_path, exist_ok=True)
|
||||
from gfpgan import GFPGANer
|
||||
from facexlib import detection, parsing # noqa: F401
|
||||
global user_path
|
||||
|
|
@ -112,5 +107,4 @@ def setup_model(dirname):
|
|||
|
||||
shared.face_restorers.append(FaceRestorerGFPGAN())
|
||||
except Exception:
|
||||
print("Error setting up GFPGAN:", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report("Error setting up GFPGAN", exc_info=True)
|
||||
|
|
|
|||
42
modules/gitpython_hack.py
Normal file
42
modules/gitpython_hack.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import subprocess
|
||||
|
||||
import git
|
||||
|
||||
|
||||
class Git(git.Git):
|
||||
"""
|
||||
Git subclassed to never use persistent processes.
|
||||
"""
|
||||
|
||||
def _get_persistent_cmd(self, attr_name, cmd_name, *args, **kwargs):
|
||||
raise NotImplementedError(f"Refusing to use persistent process: {attr_name} ({cmd_name} {args} {kwargs})")
|
||||
|
||||
def get_object_header(self, ref: str | bytes) -> tuple[str, str, int]:
|
||||
ret = subprocess.check_output(
|
||||
[self.GIT_PYTHON_GIT_EXECUTABLE, "cat-file", "--batch-check"],
|
||||
input=self._prepare_ref(ref),
|
||||
cwd=self._working_dir,
|
||||
timeout=2,
|
||||
)
|
||||
return self._parse_object_header(ret)
|
||||
|
||||
def stream_object_data(self, ref: str) -> tuple[str, str, int, "Git.CatFileContentStream"]:
|
||||
# Not really streaming, per se; this buffers the entire object in memory.
|
||||
# Shouldn't be a problem for our use case, since we're only using this for
|
||||
# object headers (commit objects).
|
||||
ret = subprocess.check_output(
|
||||
[self.GIT_PYTHON_GIT_EXECUTABLE, "cat-file", "--batch"],
|
||||
input=self._prepare_ref(ref),
|
||||
cwd=self._working_dir,
|
||||
timeout=30,
|
||||
)
|
||||
bio = io.BytesIO(ret)
|
||||
hexsha, typename, size = self._parse_object_header(bio.readline())
|
||||
return (hexsha, typename, size, self.CatFileContentStream(size, bio))
|
||||
|
||||
|
||||
class Repo(git.Repo):
|
||||
GitCommandWrapperType = Git
|
||||
|
|
@ -2,8 +2,6 @@ import datetime
|
|||
import glob
|
||||
import html
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
import inspect
|
||||
|
||||
import modules.textual_inversion.dataset
|
||||
|
|
@ -11,7 +9,7 @@ import torch
|
|||
import tqdm
|
||||
from einops import rearrange, repeat
|
||||
from ldm.util import default
|
||||
from modules import devices, processing, sd_models, shared, sd_samplers, hashes, sd_hijack_checkpoint
|
||||
from modules import devices, processing, sd_models, shared, sd_samplers, hashes, sd_hijack_checkpoint, errors
|
||||
from modules.textual_inversion import textual_inversion, logging
|
||||
from modules.textual_inversion.learn_schedule import LearnRateScheduler
|
||||
from torch import einsum
|
||||
|
|
@ -325,17 +323,14 @@ def load_hypernetwork(name):
|
|||
if path is None:
|
||||
return None
|
||||
|
||||
hypernetwork = Hypernetwork()
|
||||
|
||||
try:
|
||||
hypernetwork = Hypernetwork()
|
||||
hypernetwork.load(path)
|
||||
return hypernetwork
|
||||
except Exception:
|
||||
print(f"Error loading hypernetwork {path}", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error loading hypernetwork {path}", exc_info=True)
|
||||
return None
|
||||
|
||||
return hypernetwork
|
||||
|
||||
|
||||
def load_hypernetworks(names, multipliers=None):
|
||||
already_loaded = {}
|
||||
|
|
@ -770,7 +765,7 @@ Last saved image: {html.escape(last_saved_image)}<br/>
|
|||
</p>
|
||||
"""
|
||||
except Exception:
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report("Exception in training hypernetwork", exc_info=True)
|
||||
finally:
|
||||
pbar.leave = False
|
||||
pbar.close()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import datetime
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import pytz
|
||||
import io
|
||||
|
|
@ -21,6 +19,8 @@ from modules import sd_samplers, shared, script_callbacks, errors
|
|||
from modules.paths_internal import roboto_ttf_file
|
||||
from modules.shared import opts
|
||||
|
||||
import modules.sd_vae as sd_vae
|
||||
|
||||
LANCZOS = (Image.Resampling.LANCZOS if hasattr(Image, 'Resampling') else Image.LANCZOS)
|
||||
|
||||
|
||||
|
|
@ -336,8 +336,20 @@ def sanitize_filename_part(text, replace_spaces=True):
|
|||
|
||||
|
||||
class FilenameGenerator:
|
||||
def get_vae_filename(self): #get the name of the VAE file.
|
||||
if sd_vae.loaded_vae_file is None:
|
||||
return "NoneType"
|
||||
file_name = os.path.basename(sd_vae.loaded_vae_file)
|
||||
split_file_name = file_name.split('.')
|
||||
if len(split_file_name) > 1 and split_file_name[0] == '':
|
||||
return split_file_name[1] # if the first character of the filename is "." then [1] is obtained.
|
||||
else:
|
||||
return split_file_name[0]
|
||||
|
||||
replacements = {
|
||||
'seed': lambda self: self.seed if self.seed is not None else '',
|
||||
'seed_first': lambda self: self.seed if self.p.batch_size == 1 else self.p.all_seeds[0],
|
||||
'seed_last': lambda self: NOTHING_AND_SKIP_PREVIOUS_TEXT if self.p.batch_size == 1 else self.p.all_seeds[-1],
|
||||
'steps': lambda self: self.p and self.p.steps,
|
||||
'cfg': lambda self: self.p and self.p.cfg_scale,
|
||||
'width': lambda self: self.image.width,
|
||||
|
|
@ -354,20 +366,23 @@ class FilenameGenerator:
|
|||
'prompt_no_styles': lambda self: self.prompt_no_style(),
|
||||
'prompt_spaces': lambda self: sanitize_filename_part(self.prompt, replace_spaces=False),
|
||||
'prompt_words': lambda self: self.prompt_words(),
|
||||
'batch_number': lambda self: NOTHING_AND_SKIP_PREVIOUS_TEXT if self.p.batch_size == 1 else self.p.batch_index + 1,
|
||||
'generation_number': lambda self: NOTHING_AND_SKIP_PREVIOUS_TEXT if self.p.n_iter == 1 and self.p.batch_size == 1 else self.p.iteration * self.p.batch_size + self.p.batch_index + 1,
|
||||
'batch_number': lambda self: NOTHING_AND_SKIP_PREVIOUS_TEXT if self.p.batch_size == 1 or self.zip else self.p.batch_index + 1,
|
||||
'batch_size': lambda self: self.p.batch_size,
|
||||
'generation_number': lambda self: NOTHING_AND_SKIP_PREVIOUS_TEXT if (self.p.n_iter == 1 and self.p.batch_size == 1) or self.zip else self.p.iteration * self.p.batch_size + self.p.batch_index + 1,
|
||||
'hasprompt': lambda self, *args: self.hasprompt(*args), # accepts formats:[hasprompt<prompt1|default><prompt2>..]
|
||||
'clip_skip': lambda self: opts.data["CLIP_stop_at_last_layers"],
|
||||
'denoising': lambda self: self.p.denoising_strength if self.p and self.p.denoising_strength else NOTHING_AND_SKIP_PREVIOUS_TEXT,
|
||||
'user': lambda self: self.p.user,
|
||||
'vae_filename': lambda self: self.get_vae_filename(),
|
||||
}
|
||||
default_time_format = '%Y%m%d%H%M%S'
|
||||
|
||||
def __init__(self, p, seed, prompt, image):
|
||||
def __init__(self, p, seed, prompt, image, zip=False):
|
||||
self.p = p
|
||||
self.seed = seed
|
||||
self.prompt = prompt
|
||||
self.image = image
|
||||
self.zip = zip
|
||||
|
||||
def hasprompt(self, *args):
|
||||
lower = self.prompt.lower()
|
||||
|
|
@ -391,7 +406,7 @@ class FilenameGenerator:
|
|||
|
||||
prompt_no_style = self.prompt
|
||||
for style in shared.prompt_styles.get_style_prompts(self.p.styles):
|
||||
if len(style) > 0:
|
||||
if style:
|
||||
for part in style.split("{prompt}"):
|
||||
prompt_no_style = prompt_no_style.replace(part, "").replace(", ,", ",").strip().strip(',')
|
||||
|
||||
|
|
@ -400,7 +415,7 @@ class FilenameGenerator:
|
|||
return sanitize_filename_part(prompt_no_style, replace_spaces=False)
|
||||
|
||||
def prompt_words(self):
|
||||
words = [x for x in re_nonletters.split(self.prompt or "") if len(x) > 0]
|
||||
words = [x for x in re_nonletters.split(self.prompt or "") if x]
|
||||
if len(words) == 0:
|
||||
words = ["empty"]
|
||||
return sanitize_filename_part(" ".join(words[0:opts.directories_max_prompt_words]), replace_spaces=False)
|
||||
|
|
@ -408,7 +423,7 @@ class FilenameGenerator:
|
|||
def datetime(self, *args):
|
||||
time_datetime = datetime.datetime.now()
|
||||
|
||||
time_format = args[0] if len(args) > 0 and args[0] != "" else self.default_time_format
|
||||
time_format = args[0] if (args and args[0] != "") else self.default_time_format
|
||||
try:
|
||||
time_zone = pytz.timezone(args[1]) if len(args) > 1 else None
|
||||
except pytz.exceptions.UnknownTimeZoneError:
|
||||
|
|
@ -447,8 +462,7 @@ class FilenameGenerator:
|
|||
replacement = fun(self, *pattern_args)
|
||||
except Exception:
|
||||
replacement = None
|
||||
print(f"Error adding [{pattern}] to filename", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error adding [{pattern}] to filename", exc_info=True)
|
||||
|
||||
if replacement == NOTHING_AND_SKIP_PREVIOUS_TEXT:
|
||||
continue
|
||||
|
|
@ -665,9 +679,10 @@ def read_info_from_image(image):
|
|||
items['exif comment'] = exif_comment
|
||||
geninfo = exif_comment
|
||||
|
||||
for field in ['jfif', 'jfif_version', 'jfif_unit', 'jfif_density', 'dpi', 'exif',
|
||||
'loop', 'background', 'timestamp', 'duration']:
|
||||
items.pop(field, None)
|
||||
for field in ['jfif', 'jfif_version', 'jfif_unit', 'jfif_density', 'dpi', 'exif',
|
||||
'loop', 'background', 'timestamp', 'duration', 'progressive', 'progression',
|
||||
'icc_profile', 'chromaticity']:
|
||||
items.pop(field, None)
|
||||
|
||||
if items.get("Software", None) == "NovelAI":
|
||||
try:
|
||||
|
|
@ -678,8 +693,7 @@ def read_info_from_image(image):
|
|||
Negative prompt: {json_info["uc"]}
|
||||
Steps: {json_info["steps"]}, Sampler: {sampler}, CFG scale: {json_info["scale"]}, Seed: {json_info["seed"]}, Size: {image.width}x{image.height}, Clip skip: 2, ENSD: 31337"""
|
||||
except Exception:
|
||||
print("Error parsing NovelAI image generation parameters:", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report("Error parsing NovelAI image generation parameters", exc_info=True)
|
||||
|
||||
return geninfo, items
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image, ImageOps, ImageFilter, ImageEnhance, ImageChops, UnidentifiedImageError
|
||||
|
|
@ -14,7 +15,7 @@ from modules.ui import plaintext_to_html
|
|||
import modules.scripts
|
||||
|
||||
|
||||
def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args):
|
||||
def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args, to_scale=False, scale_by=1.0):
|
||||
processing.fix_seed(p)
|
||||
|
||||
images = shared.listfiles(input_dir)
|
||||
|
|
@ -22,9 +23,10 @@ def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args):
|
|||
is_inpaint_batch = False
|
||||
if inpaint_mask_dir:
|
||||
inpaint_masks = shared.listfiles(inpaint_mask_dir)
|
||||
is_inpaint_batch = len(inpaint_masks) > 0
|
||||
if is_inpaint_batch:
|
||||
print(f"\nInpaint batch is enabled. {len(inpaint_masks)} masks found.")
|
||||
is_inpaint_batch = bool(inpaint_masks)
|
||||
|
||||
if is_inpaint_batch:
|
||||
print(f"\nInpaint batch is enabled. {len(inpaint_masks)} masks found.")
|
||||
|
||||
print(f"Will process {len(images)} images, creating {p.n_iter * p.batch_size} new images for each.")
|
||||
|
||||
|
|
@ -50,14 +52,31 @@ def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args):
|
|||
continue
|
||||
# Use the EXIF orientation of photos taken by smartphones.
|
||||
img = ImageOps.exif_transpose(img)
|
||||
|
||||
if to_scale:
|
||||
p.width = int(img.width * scale_by)
|
||||
p.height = int(img.height * scale_by)
|
||||
|
||||
p.init_images = [img] * p.batch_size
|
||||
|
||||
image_path = Path(image)
|
||||
if is_inpaint_batch:
|
||||
# try to find corresponding mask for an image using simple filename matching
|
||||
mask_image_path = os.path.join(inpaint_mask_dir, os.path.basename(image))
|
||||
# if not found use first one ("same mask for all images" use-case)
|
||||
if mask_image_path not in inpaint_masks:
|
||||
if len(inpaint_masks) == 1:
|
||||
mask_image_path = inpaint_masks[0]
|
||||
else:
|
||||
# try to find corresponding mask for an image using simple filename matching
|
||||
mask_image_dir = Path(inpaint_mask_dir)
|
||||
masks_found = list(mask_image_dir.glob(f"{image_path.stem}.*"))
|
||||
|
||||
if len(masks_found) == 0:
|
||||
print(f"Warning: mask is not found for {image_path} in {mask_image_dir}. Skipping it.")
|
||||
continue
|
||||
|
||||
# it should contain only 1 matching mask
|
||||
# otherwise user has many masks with the same name but different extensions
|
||||
mask_image_path = masks_found[0]
|
||||
|
||||
mask_image = Image.open(mask_image_path)
|
||||
p.image_mask = mask_image
|
||||
|
||||
|
|
@ -66,7 +85,7 @@ def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args):
|
|||
proc = process_images(p)
|
||||
|
||||
for n, processed_image in enumerate(proc.images):
|
||||
filename = os.path.basename(image)
|
||||
filename = image_path.name
|
||||
|
||||
if n > 0:
|
||||
left, right = os.path.splitext(filename)
|
||||
|
|
@ -93,7 +112,8 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s
|
|||
elif mode == 2: # inpaint
|
||||
image, mask = init_img_with_mask["image"], init_img_with_mask["mask"]
|
||||
alpha_mask = ImageOps.invert(image.split()[-1]).convert('L').point(lambda x: 255 if x > 0 else 0, mode='1')
|
||||
mask = ImageChops.lighter(alpha_mask, mask.convert('L')).convert('L')
|
||||
mask = mask.convert('L').point(lambda x: 255 if x > 128 else 0, mode='1')
|
||||
mask = ImageChops.lighter(alpha_mask, mask).convert('L')
|
||||
image = image.convert("RGB")
|
||||
elif mode == 3: # inpaint sketch
|
||||
image = inpaint_color_sketch
|
||||
|
|
@ -115,7 +135,7 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s
|
|||
if image is not None:
|
||||
image = ImageOps.exif_transpose(image)
|
||||
|
||||
if selected_scale_tab == 1:
|
||||
if selected_scale_tab == 1 and not is_batch:
|
||||
assert image, "Can't scale by because no image is selected"
|
||||
|
||||
width = int(image.width * scale_by)
|
||||
|
|
@ -172,7 +192,7 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s
|
|||
if is_batch:
|
||||
assert not shared.cmd_opts.hide_ui_dir_config, "Launched with --hide-ui-dir-config, batch img2img disabled"
|
||||
|
||||
process_batch(p, img2img_batch_input_dir, img2img_batch_output_dir, img2img_batch_inpaint_mask_dir, args)
|
||||
process_batch(p, img2img_batch_input_dir, img2img_batch_output_dir, img2img_batch_inpaint_mask_dir, args, to_scale=selected_scale_tab == 1, scale_by=scale_by)
|
||||
|
||||
processed = Processed(p, [], p.seed, "")
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import os
|
||||
import sys
|
||||
import traceback
|
||||
from collections import namedtuple
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
|
@ -216,8 +215,7 @@ class InterrogateModels:
|
|||
res += f", {match}"
|
||||
|
||||
except Exception:
|
||||
print("Error interrogating", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report("Error interrogating", exc_info=True)
|
||||
res += "<error>"
|
||||
|
||||
self.unload()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import platform
|
|||
import json
|
||||
from functools import lru_cache
|
||||
|
||||
from modules import cmd_args
|
||||
from modules import cmd_args, errors
|
||||
from modules.paths_internal import script_path, extensions_dir
|
||||
|
||||
args, _ = cmd_args.parser.parse_known_args()
|
||||
|
|
@ -68,7 +68,13 @@ def git_tag():
|
|||
try:
|
||||
return subprocess.check_output([git, "describe", "--tags"], shell=False, encoding='utf8').strip()
|
||||
except Exception:
|
||||
return "<none>"
|
||||
try:
|
||||
from pathlib import Path
|
||||
changelog_md = Path(__file__).parent.parent / "CHANGELOG.md"
|
||||
with changelog_md.open(encoding="utf-8") as file:
|
||||
return next((line.strip() for line in file if line.strip()), "<none>")
|
||||
except Exception:
|
||||
return "<none>"
|
||||
|
||||
|
||||
def run(command, desc=None, errdesc=None, custom_env=None, live: bool = default_command_live) -> str:
|
||||
|
|
@ -141,10 +147,10 @@ def git_clone(url, dir, name, commithash=None):
|
|||
return
|
||||
|
||||
run(f'"{git}" -C "{dir}" fetch', f"Fetching updates for {name}...", f"Couldn't fetch {name}")
|
||||
run(f'"{git}" -C "{dir}" checkout {commithash}', f"Checking out commit for {name} with hash: {commithash}...", f"Couldn't checkout commit {commithash} for {name}")
|
||||
run(f'"{git}" -C "{dir}" checkout {commithash}', f"Checking out commit for {name} with hash: {commithash}...", f"Couldn't checkout commit {commithash} for {name}", live=True)
|
||||
return
|
||||
|
||||
run(f'"{git}" clone "{url}" "{dir}"', f"Cloning {name} into {dir}...", f"Couldn't clone {name}")
|
||||
run(f'"{git}" clone "{url}" "{dir}"', f"Cloning {name} into {dir}...", f"Couldn't clone {name}", live=True)
|
||||
|
||||
if commithash is not None:
|
||||
run(f'"{git}" -C "{dir}" checkout {commithash}', None, "Couldn't checkout {name}'s hash: {commithash}")
|
||||
|
|
@ -188,7 +194,7 @@ def run_extension_installer(extension_dir):
|
|||
|
||||
print(run(f'"{python}" "{path_installer}"', errdesc=f"Error running install.py for extension {extension_dir}", custom_env=env))
|
||||
except Exception as e:
|
||||
print(e, file=sys.stderr)
|
||||
errors.report(str(e))
|
||||
|
||||
|
||||
def list_extensions(settings_file):
|
||||
|
|
@ -198,8 +204,8 @@ def list_extensions(settings_file):
|
|||
if os.path.isfile(settings_file):
|
||||
with open(settings_file, "r", encoding="utf8") as file:
|
||||
settings = json.load(file)
|
||||
except Exception as e:
|
||||
print(e, file=sys.stderr)
|
||||
except Exception:
|
||||
errors.report("Could not load settings", exc_info=True)
|
||||
|
||||
disabled_extensions = set(settings.get('disabled_extensions', []))
|
||||
disable_all_extensions = settings.get('disable_all_extensions', 'none')
|
||||
|
|
@ -223,23 +229,28 @@ def prepare_environment():
|
|||
torch_command = os.environ.get('TORCH_COMMAND', f"pip install torch==2.0.1 torchvision==0.15.2 --extra-index-url {torch_index_url}")
|
||||
requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt")
|
||||
|
||||
xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.17')
|
||||
xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.20')
|
||||
gfpgan_package = os.environ.get('GFPGAN_PACKAGE', "https://github.com/TencentARC/GFPGAN/archive/8d2447a2d918f8eba5a4a01463fd48e45126a379.zip")
|
||||
clip_package = os.environ.get('CLIP_PACKAGE', "https://github.com/openai/CLIP/archive/d50d76daa670286dd6cacf3bcd80b5e4823fc8e1.zip")
|
||||
openclip_package = os.environ.get('OPENCLIP_PACKAGE', "https://github.com/mlfoundations/open_clip/archive/bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b.zip")
|
||||
|
||||
stable_diffusion_repo = os.environ.get('STABLE_DIFFUSION_REPO', "https://github.com/Stability-AI/stablediffusion.git")
|
||||
taming_transformers_repo = os.environ.get('TAMING_TRANSFORMERS_REPO', "https://github.com/CompVis/taming-transformers.git")
|
||||
k_diffusion_repo = os.environ.get('K_DIFFUSION_REPO', 'https://github.com/crowsonkb/k-diffusion.git')
|
||||
codeformer_repo = os.environ.get('CODEFORMER_REPO', 'https://github.com/sczhou/CodeFormer.git')
|
||||
blip_repo = os.environ.get('BLIP_REPO', 'https://github.com/salesforce/BLIP.git')
|
||||
|
||||
stable_diffusion_commit_hash = os.environ.get('STABLE_DIFFUSION_COMMIT_HASH', "cf1d67a6fd5ea1aa600c4df58e5b47da45f6bdbf")
|
||||
taming_transformers_commit_hash = os.environ.get('TAMING_TRANSFORMERS_COMMIT_HASH', "24268930bf1dce879235a7fddd0b2355b84d7ea6")
|
||||
k_diffusion_commit_hash = os.environ.get('K_DIFFUSION_COMMIT_HASH', "c9fe758757e022f05ca5a53fa8fac28889e4f1cf")
|
||||
codeformer_commit_hash = os.environ.get('CODEFORMER_COMMIT_HASH', "c5b4593074ba6214284d6acd5f1719b6c5d739af")
|
||||
blip_commit_hash = os.environ.get('BLIP_COMMIT_HASH', "48211a1594f1321b00f14c9f7a5b4813144b2fb9")
|
||||
|
||||
try:
|
||||
# the existance of this file is a signal to webui.sh/bat that webui needs to be restarted when it stops execution
|
||||
os.remove(os.path.join(script_path, "tmp", "restart"))
|
||||
os.environ.setdefault('SD_WEBUI_RESTARTING ', '1')
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
if not args.skip_python_version_check:
|
||||
check_python_version()
|
||||
|
||||
|
|
@ -286,7 +297,6 @@ def prepare_environment():
|
|||
os.makedirs(os.path.join(script_path, dir_repos), exist_ok=True)
|
||||
|
||||
git_clone(stable_diffusion_repo, repo_dir('stable-diffusion-stability-ai'), "Stable Diffusion", stable_diffusion_commit_hash)
|
||||
git_clone(taming_transformers_repo, repo_dir('taming-transformers'), "Taming Transformers", taming_transformers_commit_hash)
|
||||
git_clone(k_diffusion_repo, repo_dir('k-diffusion'), "K-diffusion", k_diffusion_commit_hash)
|
||||
git_clone(codeformer_repo, repo_dir('CodeFormer'), "CodeFormer", codeformer_commit_hash)
|
||||
git_clone(blip_repo, repo_dir('BLIP'), "BLIP", blip_commit_hash)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import json
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from modules import errors
|
||||
|
||||
localizations = {}
|
||||
|
||||
|
|
@ -31,7 +30,6 @@ def localization_js(current_localization_name: str) -> str:
|
|||
with open(fn, "r", encoding="utf8") as file:
|
||||
data = json.load(file)
|
||||
except Exception:
|
||||
print(f"Error loading localization from {fn}:", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error loading localization from {fn}", exc_info=True)
|
||||
|
||||
return f"window.localization = {json.dumps(data)}"
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ def send_everything_to_cpu():
|
|||
|
||||
|
||||
def setup_for_low_vram(sd_model, use_medvram):
|
||||
sd_model.lowvram = True
|
||||
|
||||
parents = {}
|
||||
|
||||
def send_me_to_gpu(module, _):
|
||||
|
|
@ -96,3 +98,7 @@ def setup_for_low_vram(sd_model, use_medvram):
|
|||
diff_model.middle_block.register_forward_pre_hook(send_me_to_gpu)
|
||||
for block in diff_model.output_blocks:
|
||||
block.register_forward_pre_hook(send_me_to_gpu)
|
||||
|
||||
|
||||
def is_enabled(sd_model):
|
||||
return getattr(sd_model, 'lowvram', False)
|
||||
|
|
|
|||
|
|
@ -95,8 +95,7 @@ def cleanup_models():
|
|||
|
||||
def move_files(src_path: str, dest_path: str, ext_filter: str = None):
|
||||
try:
|
||||
if not os.path.exists(dest_path):
|
||||
os.makedirs(dest_path)
|
||||
os.makedirs(dest_path, exist_ok=True)
|
||||
if os.path.exists(src_path):
|
||||
for file in os.listdir(src_path):
|
||||
fullpath = os.path.join(src_path, file)
|
||||
|
|
|
|||
|
|
@ -230,9 +230,9 @@ class DDPM(pl.LightningModule):
|
|||
missing, unexpected = self.load_state_dict(sd, strict=False) if not only_model else self.model.load_state_dict(
|
||||
sd, strict=False)
|
||||
print(f"Restored from {path} with {len(missing)} missing and {len(unexpected)} unexpected keys")
|
||||
if len(missing) > 0:
|
||||
if missing:
|
||||
print(f"Missing Keys: {missing}")
|
||||
if len(unexpected) > 0:
|
||||
if unexpected:
|
||||
print(f"Unexpected Keys: {unexpected}")
|
||||
|
||||
def q_mean_variance(self, x_start, t):
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ assert sd_path is not None, f"Couldn't find Stable Diffusion in any of: {possibl
|
|||
|
||||
path_dirs = [
|
||||
(sd_path, 'ldm', 'Stable Diffusion', []),
|
||||
(os.path.join(sd_path, '../taming-transformers'), 'taming', 'Taming Transformers', []),
|
||||
(os.path.join(sd_path, '../CodeFormer'), 'inference_codeformer.py', 'CodeFormer', []),
|
||||
(os.path.join(sd_path, '../BLIP'), 'models/blip.py', 'BLIP', []),
|
||||
(os.path.join(sd_path, '../k-diffusion'), 'k_diffusion/sampling.py', 'k_diffusion', ["atstart"]),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import json
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
|
|
@ -6,14 +7,14 @@ import hashlib
|
|||
|
||||
import torch
|
||||
import numpy as np
|
||||
from PIL import Image, ImageFilter, ImageOps
|
||||
from PIL import Image, ImageOps
|
||||
import random
|
||||
import cv2
|
||||
from skimage import exposure
|
||||
from typing import Any, Dict, List
|
||||
|
||||
import modules.sd_hijack
|
||||
from modules import devices, prompt_parser, masking, sd_samplers, lowvram, generation_parameters_copypaste, extra_networks, sd_vae_approx, scripts, sd_samplers_common
|
||||
from modules import devices, prompt_parser, masking, sd_samplers, lowvram, generation_parameters_copypaste, extra_networks, sd_vae_approx, scripts, sd_samplers_common, sd_unet
|
||||
from modules.sd_hijack import model_hijack
|
||||
from modules.shared import opts, cmd_opts, state
|
||||
import modules.shared as shared
|
||||
|
|
@ -23,7 +24,6 @@ import modules.images as images
|
|||
import modules.styles
|
||||
import modules.sd_models as sd_models
|
||||
import modules.sd_vae as sd_vae
|
||||
import logging
|
||||
from ldm.data.util import AddMiDaS
|
||||
from ldm.models.diffusion.ddpm import LatentDepth2ImageDiffusion
|
||||
|
||||
|
|
@ -106,6 +106,9 @@ class StableDiffusionProcessing:
|
|||
"""
|
||||
The first set of paramaters: sd_models -> do_not_reload_embeddings represent the minimum required to create a StableDiffusionProcessing
|
||||
"""
|
||||
cached_uc = [None, None]
|
||||
cached_c = [None, None]
|
||||
|
||||
def __init__(self, sd_model=None, outpath_samples=None, outpath_grids=None, prompt: str = "", styles: List[str] = None, seed: int = -1, subseed: int = -1, subseed_strength: float = 0, seed_resize_from_h: int = -1, seed_resize_from_w: int = -1, seed_enable_extras: bool = True, sampler_name: str = None, batch_size: int = 1, n_iter: int = 1, steps: int = 50, cfg_scale: float = 7.0, width: int = 512, height: int = 512, restore_faces: bool = False, tiling: bool = False, do_not_save_samples: bool = False, do_not_save_grid: bool = False, extra_generation_params: Dict[Any, Any] = None, overlay_images: Any = None, negative_prompt: str = None, eta: float = None, do_not_reload_embeddings: bool = False, denoising_strength: float = 0, ddim_discretize: str = None, s_min_uncond: float = 0.0, s_churn: float = 0.0, s_tmax: float = None, s_tmin: float = 0.0, s_noise: float = 1.0, override_settings: Dict[str, Any] = None, override_settings_restore_afterwards: bool = True, sampler_index: int = None, script_args: list = None):
|
||||
if sampler_index is not None:
|
||||
print("sampler_index argument for StableDiffusionProcessing does not do anything; use sampler_name", file=sys.stderr)
|
||||
|
|
@ -171,12 +174,13 @@ class StableDiffusionProcessing:
|
|||
|
||||
self.prompts = None
|
||||
self.negative_prompts = None
|
||||
self.extra_network_data = None
|
||||
self.seeds = None
|
||||
self.subseeds = None
|
||||
|
||||
self.step_multiplier = 1
|
||||
self.cached_uc = [None, None]
|
||||
self.cached_c = [None, None]
|
||||
self.cached_uc = StableDiffusionProcessing.cached_uc
|
||||
self.cached_c = StableDiffusionProcessing.cached_c
|
||||
self.uc = None
|
||||
self.c = None
|
||||
|
||||
|
|
@ -290,8 +294,9 @@ class StableDiffusionProcessing:
|
|||
self.sampler = None
|
||||
self.c = None
|
||||
self.uc = None
|
||||
self.cached_c = [None, None]
|
||||
self.cached_uc = [None, None]
|
||||
if not opts.experimental_persistent_cond_cache:
|
||||
StableDiffusionProcessing.cached_c = [None, None]
|
||||
StableDiffusionProcessing.cached_uc = [None, None]
|
||||
|
||||
def get_token_merging_ratio(self, for_hr=False):
|
||||
if for_hr:
|
||||
|
|
@ -313,7 +318,7 @@ class StableDiffusionProcessing:
|
|||
self.all_prompts = [shared.prompt_styles.apply_styles_to_prompt(x, self.styles) for x in self.all_prompts]
|
||||
self.all_negative_prompts = [shared.prompt_styles.apply_negative_styles_to_prompt(x, self.styles) for x in self.all_negative_prompts]
|
||||
|
||||
def get_conds_with_caching(self, function, required_prompts, steps, cache):
|
||||
def get_conds_with_caching(self, function, required_prompts, steps, caches, extra_network_data):
|
||||
"""
|
||||
Returns the result of calling function(shared.sd_model, required_prompts, steps)
|
||||
using a cache to store the result if the same arguments have been used before.
|
||||
|
|
@ -322,27 +327,29 @@ class StableDiffusionProcessing:
|
|||
representing the previously used arguments, or None if no arguments
|
||||
have been used before. The second element is where the previously
|
||||
computed result is stored.
|
||||
|
||||
caches is a list with items described above.
|
||||
"""
|
||||
if cache[0] is not None and (required_prompts, steps, opts.CLIP_stop_at_last_layers, shared.sd_model.sd_checkpoint_info) == cache[0]:
|
||||
return cache[1]
|
||||
for cache in caches:
|
||||
if cache[0] is not None and (required_prompts, steps, opts.CLIP_stop_at_last_layers, shared.sd_model.sd_checkpoint_info, extra_network_data) == cache[0]:
|
||||
return cache[1]
|
||||
|
||||
cache = caches[0]
|
||||
|
||||
with devices.autocast():
|
||||
cache[1] = function(shared.sd_model, required_prompts, steps)
|
||||
|
||||
cache[0] = (required_prompts, steps, opts.CLIP_stop_at_last_layers, shared.sd_model.sd_checkpoint_info)
|
||||
cache[0] = (required_prompts, steps, opts.CLIP_stop_at_last_layers, shared.sd_model.sd_checkpoint_info, extra_network_data)
|
||||
return cache[1]
|
||||
|
||||
def setup_conds(self):
|
||||
sampler_config = sd_samplers.find_sampler_config(self.sampler_name)
|
||||
self.step_multiplier = 2 if sampler_config and sampler_config.options.get("second_order", False) else 1
|
||||
|
||||
self.uc = self.get_conds_with_caching(prompt_parser.get_learned_conditioning, self.negative_prompts, self.steps * self.step_multiplier, self.cached_uc)
|
||||
self.c = self.get_conds_with_caching(prompt_parser.get_multicond_learned_conditioning, self.prompts, self.steps * self.step_multiplier, self.cached_c)
|
||||
self.uc = self.get_conds_with_caching(prompt_parser.get_learned_conditioning, self.negative_prompts, self.steps * self.step_multiplier, [self.cached_uc], self.extra_network_data)
|
||||
self.c = self.get_conds_with_caching(prompt_parser.get_multicond_learned_conditioning, self.prompts, self.steps * self.step_multiplier, [self.cached_c], self.extra_network_data)
|
||||
|
||||
def parse_extra_network_prompts(self):
|
||||
self.prompts, extra_network_data = extra_networks.parse_prompts(self.prompts)
|
||||
|
||||
return extra_network_data
|
||||
self.prompts, self.extra_network_data = extra_networks.parse_prompts(self.prompts)
|
||||
|
||||
|
||||
class Processed:
|
||||
|
|
@ -591,6 +598,9 @@ def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments=None, iter
|
|||
|
||||
|
||||
def process_images(p: StableDiffusionProcessing) -> Processed:
|
||||
if p.scripts is not None:
|
||||
p.scripts.before_process(p)
|
||||
|
||||
stored_opts = {k: opts.data[k] for k in p.override_settings.keys()}
|
||||
|
||||
try:
|
||||
|
|
@ -676,10 +686,11 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
|||
if shared.opts.live_previews_enable and opts.show_progress_type == "Approx NN":
|
||||
sd_vae_approx.model()
|
||||
|
||||
sd_unet.apply_unet()
|
||||
|
||||
if state.job_count == -1:
|
||||
state.job_count = p.n_iter
|
||||
|
||||
extra_network_data = None
|
||||
for n in range(p.n_iter):
|
||||
p.iteration = n
|
||||
|
||||
|
|
@ -700,11 +711,11 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
|||
if len(p.prompts) == 0:
|
||||
break
|
||||
|
||||
extra_network_data = p.parse_extra_network_prompts()
|
||||
p.parse_extra_network_prompts()
|
||||
|
||||
if not p.disable_extra_networks:
|
||||
with devices.autocast():
|
||||
extra_networks.activate(p, extra_network_data)
|
||||
extra_networks.activate(p, p.extra_network_data)
|
||||
|
||||
if p.scripts is not None:
|
||||
p.scripts.process_batch(p, batch_number=n, prompts=p.prompts, seeds=p.seeds, subseeds=p.subseeds)
|
||||
|
|
@ -739,7 +750,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
|||
|
||||
del samples_ddim
|
||||
|
||||
if shared.cmd_opts.lowvram or shared.cmd_opts.medvram:
|
||||
if lowvram.is_enabled(shared.sd_model):
|
||||
lowvram.send_everything_to_cpu()
|
||||
|
||||
devices.torch_gc()
|
||||
|
|
@ -826,8 +837,8 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
|||
if opts.grid_save:
|
||||
images.save_image(grid, p.outpath_grids, "grid", p.all_seeds[0], p.all_prompts[0], opts.grid_format, info=infotext(), short_filename=not opts.grid_extended_filename, p=p, grid=True)
|
||||
|
||||
if not p.disable_extra_networks and extra_network_data:
|
||||
extra_networks.deactivate(p, extra_network_data)
|
||||
if not p.disable_extra_networks and p.extra_network_data:
|
||||
extra_networks.deactivate(p, p.extra_network_data)
|
||||
|
||||
devices.torch_gc()
|
||||
|
||||
|
|
@ -862,6 +873,8 @@ def old_hires_fix_first_pass_dimensions(width, height):
|
|||
|
||||
class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
|
||||
sampler = None
|
||||
cached_hr_uc = [None, None]
|
||||
cached_hr_c = [None, None]
|
||||
|
||||
def __init__(self, enable_hr: bool = False, denoising_strength: float = 0.75, firstphase_width: int = 0, firstphase_height: int = 0, hr_scale: float = 2.0, hr_upscaler: str = None, hr_second_pass_steps: int = 0, hr_resize_x: int = 0, hr_resize_y: int = 0, hr_sampler_name: str = None, hr_prompt: str = '', hr_negative_prompt: str = '', **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
|
@ -894,6 +907,8 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
|
|||
self.hr_negative_prompts = None
|
||||
self.hr_extra_network_data = None
|
||||
|
||||
self.cached_hr_uc = StableDiffusionProcessingTxt2Img.cached_hr_uc
|
||||
self.cached_hr_c = StableDiffusionProcessingTxt2Img.cached_hr_c
|
||||
self.hr_c = None
|
||||
self.hr_uc = None
|
||||
|
||||
|
|
@ -973,7 +988,8 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
|
|||
|
||||
latent_scale_mode = shared.latent_upscale_modes.get(self.hr_upscaler, None) if self.hr_upscaler is not None else shared.latent_upscale_modes.get(shared.latent_upscale_default_mode, "nearest")
|
||||
if self.enable_hr and latent_scale_mode is None:
|
||||
assert len([x for x in shared.sd_upscalers if x.name == self.hr_upscaler]) > 0, f"could not find upscaler named {self.hr_upscaler}"
|
||||
if not any(x.name == self.hr_upscaler for x in shared.sd_upscalers):
|
||||
raise Exception(f"could not find upscaler named {self.hr_upscaler}")
|
||||
|
||||
x = create_random_tensors([opt_C, self.height // opt_f, self.width // opt_f], seeds=seeds, subseeds=subseeds, subseed_strength=self.subseed_strength, seed_resize_from_h=self.seed_resize_from_h, seed_resize_from_w=self.seed_resize_from_w, p=self)
|
||||
samples = self.sampler.sample(self, x, conditioning, unconditional_conditioning, image_conditioning=self.txt2img_image_conditioning(x))
|
||||
|
|
@ -1056,6 +1072,9 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
|
|||
with devices.autocast():
|
||||
extra_networks.activate(self, self.hr_extra_network_data)
|
||||
|
||||
with devices.autocast():
|
||||
self.calculate_hr_conds()
|
||||
|
||||
sd_models.apply_token_merging(self.sd_model, self.get_token_merging_ratio(for_hr=True))
|
||||
|
||||
samples = self.sampler.sample_img2img(self, samples, noise, self.hr_c, self.hr_uc, steps=self.hr_second_pass_steps or self.steps, image_conditioning=image_conditioning)
|
||||
|
|
@ -1067,8 +1086,12 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
|
|||
return samples
|
||||
|
||||
def close(self):
|
||||
super().close()
|
||||
self.hr_c = None
|
||||
self.hr_uc = None
|
||||
if not opts.experimental_persistent_cond_cache:
|
||||
StableDiffusionProcessingTxt2Img.cached_hr_uc = [None, None]
|
||||
StableDiffusionProcessingTxt2Img.cached_hr_c = [None, None]
|
||||
|
||||
def setup_prompts(self):
|
||||
super().setup_prompts()
|
||||
|
|
@ -1095,12 +1118,31 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
|
|||
self.all_hr_prompts = [shared.prompt_styles.apply_styles_to_prompt(x, self.styles) for x in self.all_hr_prompts]
|
||||
self.all_hr_negative_prompts = [shared.prompt_styles.apply_negative_styles_to_prompt(x, self.styles) for x in self.all_hr_negative_prompts]
|
||||
|
||||
def calculate_hr_conds(self):
|
||||
if self.hr_c is not None:
|
||||
return
|
||||
|
||||
self.hr_uc = self.get_conds_with_caching(prompt_parser.get_learned_conditioning, self.hr_negative_prompts, self.steps * self.step_multiplier, [self.cached_hr_uc, self.cached_uc], self.hr_extra_network_data)
|
||||
self.hr_c = self.get_conds_with_caching(prompt_parser.get_multicond_learned_conditioning, self.hr_prompts, self.steps * self.step_multiplier, [self.cached_hr_c, self.cached_c], self.hr_extra_network_data)
|
||||
|
||||
def setup_conds(self):
|
||||
super().setup_conds()
|
||||
|
||||
self.hr_uc = None
|
||||
self.hr_c = None
|
||||
|
||||
if self.enable_hr:
|
||||
self.hr_uc = self.get_conds_with_caching(prompt_parser.get_learned_conditioning, self.hr_negative_prompts, self.steps * self.step_multiplier, self.cached_uc)
|
||||
self.hr_c = self.get_conds_with_caching(prompt_parser.get_multicond_learned_conditioning, self.hr_prompts, self.steps * self.step_multiplier, self.cached_c)
|
||||
if shared.opts.hires_fix_use_firstpass_conds:
|
||||
self.calculate_hr_conds()
|
||||
|
||||
elif lowvram.is_enabled(shared.sd_model): # if in lowvram mode, we need to calculate conds right away, before the cond NN is unloaded
|
||||
with devices.autocast():
|
||||
extra_networks.activate(self, self.hr_extra_network_data)
|
||||
|
||||
self.calculate_hr_conds()
|
||||
|
||||
with devices.autocast():
|
||||
extra_networks.activate(self, self.extra_network_data)
|
||||
|
||||
def parse_extra_network_prompts(self):
|
||||
res = super().parse_extra_network_prompts()
|
||||
|
|
@ -1117,7 +1159,7 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
|
|||
class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
|
||||
sampler = None
|
||||
|
||||
def __init__(self, init_images: list = None, resize_mode: int = 0, denoising_strength: float = 0.75, image_cfg_scale: float = None, mask: Any = None, mask_blur: int = 4, inpainting_fill: int = 0, inpaint_full_res: bool = True, inpaint_full_res_padding: int = 0, inpainting_mask_invert: int = 0, initial_noise_multiplier: float = None, **kwargs):
|
||||
def __init__(self, init_images: list = None, resize_mode: int = 0, denoising_strength: float = 0.75, image_cfg_scale: float = None, mask: Any = None, mask_blur: int = None, mask_blur_x: int = 4, mask_blur_y: int = 4, inpainting_fill: int = 0, inpaint_full_res: bool = True, inpaint_full_res_padding: int = 0, inpainting_mask_invert: int = 0, initial_noise_multiplier: float = None, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.init_images = init_images
|
||||
|
|
@ -1128,7 +1170,11 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
|
|||
self.image_mask = mask
|
||||
self.latent_mask = None
|
||||
self.mask_for_overlay = None
|
||||
self.mask_blur = mask_blur
|
||||
if mask_blur is not None:
|
||||
mask_blur_x = mask_blur
|
||||
mask_blur_y = mask_blur
|
||||
self.mask_blur_x = mask_blur_x
|
||||
self.mask_blur_y = mask_blur_y
|
||||
self.inpainting_fill = inpainting_fill
|
||||
self.inpaint_full_res = inpaint_full_res
|
||||
self.inpaint_full_res_padding = inpaint_full_res_padding
|
||||
|
|
@ -1150,8 +1196,17 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
|
|||
if self.inpainting_mask_invert:
|
||||
image_mask = ImageOps.invert(image_mask)
|
||||
|
||||
if self.mask_blur > 0:
|
||||
image_mask = image_mask.filter(ImageFilter.GaussianBlur(self.mask_blur))
|
||||
if self.mask_blur_x > 0:
|
||||
np_mask = np.array(image_mask)
|
||||
kernel_size = 2 * int(4 * self.mask_blur_x + 0.5) + 1
|
||||
np_mask = cv2.GaussianBlur(np_mask, (kernel_size, 1), self.mask_blur_x)
|
||||
image_mask = Image.fromarray(np_mask)
|
||||
|
||||
if self.mask_blur_y > 0:
|
||||
np_mask = np.array(image_mask)
|
||||
kernel_size = 2 * int(4 * self.mask_blur_y + 0.5) + 1
|
||||
np_mask = cv2.GaussianBlur(np_mask, (1, kernel_size), self.mask_blur_y)
|
||||
image_mask = Image.fromarray(np_mask)
|
||||
|
||||
if self.inpaint_full_res:
|
||||
self.mask_for_overlay = image_mask
|
||||
|
|
|
|||
|
|
@ -336,11 +336,11 @@ def parse_prompt_attention(text):
|
|||
round_brackets.append(len(res))
|
||||
elif text == '[':
|
||||
square_brackets.append(len(res))
|
||||
elif weight is not None and len(round_brackets) > 0:
|
||||
elif weight is not None and round_brackets:
|
||||
multiply_range(round_brackets.pop(), float(weight))
|
||||
elif text == ')' and len(round_brackets) > 0:
|
||||
elif text == ')' and round_brackets:
|
||||
multiply_range(round_brackets.pop(), round_bracket_multiplier)
|
||||
elif text == ']' and len(square_brackets) > 0:
|
||||
elif text == ']' and square_brackets:
|
||||
multiply_range(square_brackets.pop(), square_bracket_multiplier)
|
||||
else:
|
||||
parts = re.split(re_break, text)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
|
@ -9,7 +7,8 @@ from realesrgan import RealESRGANer
|
|||
|
||||
from modules.upscaler import Upscaler, UpscalerData
|
||||
from modules.shared import cmd_opts, opts
|
||||
from modules import modelloader
|
||||
from modules import modelloader, errors
|
||||
|
||||
|
||||
class UpscalerRealESRGAN(Upscaler):
|
||||
def __init__(self, path):
|
||||
|
|
@ -36,8 +35,7 @@ class UpscalerRealESRGAN(Upscaler):
|
|||
self.scalers.append(scaler)
|
||||
|
||||
except Exception:
|
||||
print("Error importing Real-ESRGAN:", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report("Error importing Real-ESRGAN", exc_info=True)
|
||||
self.enable = False
|
||||
self.scalers = []
|
||||
|
||||
|
|
@ -76,9 +74,8 @@ class UpscalerRealESRGAN(Upscaler):
|
|||
info.local_data_path = load_file_from_url(url=info.data_path, model_dir=self.model_download_path, progress=True)
|
||||
|
||||
return info
|
||||
except Exception as e:
|
||||
print(f"Error making Real-ESRGAN models list: {e}", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
except Exception:
|
||||
errors.report("Error making Real-ESRGAN models list", exc_info=True)
|
||||
return None
|
||||
|
||||
def load_models(self, _):
|
||||
|
|
@ -135,5 +132,4 @@ def get_realesrgan_models(scaler):
|
|||
]
|
||||
return models
|
||||
except Exception:
|
||||
print("Error making Real-ESRGAN models list:", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report("Error making Real-ESRGAN models list", exc_info=True)
|
||||
|
|
|
|||
23
modules/restart.py
Normal file
23
modules/restart.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from modules.paths_internal import script_path
|
||||
|
||||
|
||||
def is_restartable() -> bool:
|
||||
"""
|
||||
Return True if the webui is restartable (i.e. there is something watching to restart it with)
|
||||
"""
|
||||
return bool(os.environ.get('SD_WEBUI_RESTART'))
|
||||
|
||||
|
||||
def restart_program() -> None:
|
||||
"""creates file tmp/restart and immediately stops the process, which webui.bat/webui.sh interpret as a command to start webui again"""
|
||||
|
||||
(Path(script_path) / "tmp" / "restart").touch()
|
||||
|
||||
stop_program()
|
||||
|
||||
|
||||
def stop_program() -> None:
|
||||
os._exit(0)
|
||||
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
import pickle
|
||||
import collections
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import torch
|
||||
import numpy
|
||||
|
|
@ -11,7 +9,10 @@ import _codecs
|
|||
import zipfile
|
||||
import re
|
||||
|
||||
|
||||
# PyTorch 1.13 and later have _TypedStorage renamed to TypedStorage
|
||||
from modules import errors
|
||||
|
||||
TypedStorage = torch.storage.TypedStorage if hasattr(torch.storage, 'TypedStorage') else torch.storage._TypedStorage
|
||||
|
||||
def encode(*args):
|
||||
|
|
@ -136,17 +137,20 @@ def load_with_extra(filename, extra_handler=None, *args, **kwargs):
|
|||
check_pt(filename, extra_handler)
|
||||
|
||||
except pickle.UnpicklingError:
|
||||
print(f"Error verifying pickled file from {filename}:", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
print("-----> !!!! The file is most likely corrupted !!!! <-----", file=sys.stderr)
|
||||
print("You can skip this check with --disable-safe-unpickle commandline argument, but that is not going to help you.\n\n", file=sys.stderr)
|
||||
errors.report(
|
||||
f"Error verifying pickled file from {filename}\n"
|
||||
"-----> !!!! The file is most likely corrupted !!!! <-----\n"
|
||||
"You can skip this check with --disable-safe-unpickle commandline argument, but that is not going to help you.\n\n",
|
||||
exc_info=True,
|
||||
)
|
||||
return None
|
||||
|
||||
except Exception:
|
||||
print(f"Error verifying pickled file from {filename}:", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
print("\nThe file may be malicious, so the program is not going to read it.", file=sys.stderr)
|
||||
print("You can skip this check with --disable-safe-unpickle commandline argument.\n\n", file=sys.stderr)
|
||||
errors.report(
|
||||
f"Error verifying pickled file from {filename}\n"
|
||||
f"The file may be malicious, so the program is not going to read it.\n"
|
||||
f"You can skip this check with --disable-safe-unpickle commandline argument.\n\n",
|
||||
exc_info=True,
|
||||
)
|
||||
return None
|
||||
|
||||
return unsafe_torch_load(filename, *args, **kwargs)
|
||||
|
|
@ -190,4 +194,3 @@ with safe.Extra(handler):
|
|||
unsafe_torch_load = torch.load
|
||||
torch.load = load
|
||||
global_extra_handler = None
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import sys
|
||||
import traceback
|
||||
from collections import namedtuple
|
||||
import inspect
|
||||
import os
|
||||
from collections import namedtuple
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
from fastapi import FastAPI
|
||||
from gradio import Blocks
|
||||
|
||||
from modules import errors, timer
|
||||
|
||||
|
||||
def report_exception(c, job):
|
||||
print(f"Error executing callback {job} for {c.script}", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error executing callback {job} for {c.script}", exc_info=True)
|
||||
|
||||
|
||||
class ImageSaveParams:
|
||||
|
|
@ -111,6 +111,7 @@ callback_map = dict(
|
|||
callbacks_before_ui=[],
|
||||
callbacks_on_reload=[],
|
||||
callbacks_list_optimizers=[],
|
||||
callbacks_list_unets=[],
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -123,6 +124,7 @@ def app_started_callback(demo: Optional[Blocks], app: FastAPI):
|
|||
for c in callback_map['callbacks_app_started']:
|
||||
try:
|
||||
c.callback(demo, app)
|
||||
timer.startup_timer.record(os.path.basename(c.script))
|
||||
except Exception:
|
||||
report_exception(c, 'app_started_callback')
|
||||
|
||||
|
|
@ -271,16 +273,28 @@ def list_optimizers_callback():
|
|||
return res
|
||||
|
||||
|
||||
def list_unets_callback():
|
||||
res = []
|
||||
|
||||
for c in callback_map['callbacks_list_unets']:
|
||||
try:
|
||||
c.callback(res)
|
||||
except Exception:
|
||||
report_exception(c, 'list_unets')
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def add_callback(callbacks, fun):
|
||||
stack = [x for x in inspect.stack() if x.filename != __file__]
|
||||
filename = stack[0].filename if len(stack) > 0 else 'unknown file'
|
||||
filename = stack[0].filename if stack else 'unknown file'
|
||||
|
||||
callbacks.append(ScriptCallback(filename, fun))
|
||||
|
||||
|
||||
def remove_current_script_callbacks():
|
||||
stack = [x for x in inspect.stack() if x.filename != __file__]
|
||||
filename = stack[0].filename if len(stack) > 0 else 'unknown file'
|
||||
filename = stack[0].filename if stack else 'unknown file'
|
||||
if filename == 'unknown file':
|
||||
return
|
||||
for callback_list in callback_map.values():
|
||||
|
|
@ -430,3 +444,10 @@ def on_list_optimizers(callback):
|
|||
to it."""
|
||||
|
||||
add_callback(callback_map['callbacks_list_optimizers'], callback)
|
||||
|
||||
|
||||
def on_list_unets(callback):
|
||||
"""register a function to be called when UI is making a list of alternative options for unet.
|
||||
The function will be called with one argument, a list, and shall add objects of type modules.sd_unet.SdUnetOption to it."""
|
||||
|
||||
add_callback(callback_map['callbacks_list_unets'], callback)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import os
|
||||
import sys
|
||||
import traceback
|
||||
import importlib.util
|
||||
|
||||
from modules import errors
|
||||
|
||||
|
||||
def load_module(path):
|
||||
module_spec = importlib.util.spec_from_file_location(os.path.basename(path), path)
|
||||
|
|
@ -27,5 +27,4 @@ def preload_extensions(extensions_dir, parser):
|
|||
module.preload(parser)
|
||||
|
||||
except Exception:
|
||||
print(f"Error running preload() for {preload_script}", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error running preload() for {preload_script}", exc_info=True)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import os
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
import inspect
|
||||
from collections import namedtuple
|
||||
|
||||
import gradio as gr
|
||||
|
||||
from modules import shared, paths, script_callbacks, extensions, script_loading, scripts_postprocessing
|
||||
from modules import shared, paths, script_callbacks, extensions, script_loading, scripts_postprocessing, errors, timer
|
||||
|
||||
AlwaysVisible = object()
|
||||
|
||||
|
|
@ -20,6 +20,9 @@ class Script:
|
|||
name = None
|
||||
"""script's internal name derived from title"""
|
||||
|
||||
section = None
|
||||
"""name of UI section that the script's controls will be placed into"""
|
||||
|
||||
filename = None
|
||||
args_from = None
|
||||
args_to = None
|
||||
|
|
@ -82,6 +85,15 @@ class Script:
|
|||
|
||||
pass
|
||||
|
||||
def before_process(self, p, *args):
|
||||
"""
|
||||
This function is called very early before processing begins for AlwaysVisible scripts.
|
||||
You can modify the processing object (p) here, inject hooks, etc.
|
||||
args contains all values returned by components from ui()
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
def process(self, p, *args):
|
||||
"""
|
||||
This function is called before processing begins for AlwaysVisible scripts.
|
||||
|
|
@ -238,7 +250,7 @@ def load_scripts():
|
|||
|
||||
def register_scripts_from_module(module):
|
||||
for script_class in module.__dict__.values():
|
||||
if type(script_class) != type:
|
||||
if not inspect.isclass(script_class):
|
||||
continue
|
||||
|
||||
if issubclass(script_class, Script):
|
||||
|
|
@ -264,12 +276,12 @@ def load_scripts():
|
|||
register_scripts_from_module(script_module)
|
||||
|
||||
except Exception:
|
||||
print(f"Error loading script: {scriptfile.filename}", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error loading script: {scriptfile.filename}", exc_info=True)
|
||||
|
||||
finally:
|
||||
sys.path = syspath
|
||||
current_basedir = paths.script_path
|
||||
timer.startup_timer.record(scriptfile.filename)
|
||||
|
||||
global scripts_txt2img, scripts_img2img, scripts_postproc
|
||||
|
||||
|
|
@ -280,11 +292,9 @@ def load_scripts():
|
|||
|
||||
def wrap_call(func, filename, funcname, *args, default=None, **kwargs):
|
||||
try:
|
||||
res = func(*args, **kwargs)
|
||||
return res
|
||||
return func(*args, **kwargs)
|
||||
except Exception:
|
||||
print(f"Error calling: {filename}/{funcname}", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error calling: {filename}/{funcname}", exc_info=True)
|
||||
|
||||
return default
|
||||
|
||||
|
|
@ -297,6 +307,7 @@ class ScriptRunner:
|
|||
self.titles = []
|
||||
self.infotext_fields = []
|
||||
self.paste_field_names = []
|
||||
self.inputs = [None]
|
||||
|
||||
def initialize_scripts(self, is_img2img):
|
||||
from modules import scripts_auto_postprocessing
|
||||
|
|
@ -324,69 +335,73 @@ class ScriptRunner:
|
|||
self.scripts.append(script)
|
||||
self.selectable_scripts.append(script)
|
||||
|
||||
def setup_ui(self):
|
||||
def create_script_ui(self, script):
|
||||
import modules.api.models as api_models
|
||||
|
||||
script.args_from = len(self.inputs)
|
||||
script.args_to = len(self.inputs)
|
||||
|
||||
controls = wrap_call(script.ui, script.filename, "ui", script.is_img2img)
|
||||
|
||||
if controls is None:
|
||||
return
|
||||
|
||||
script.name = wrap_call(script.title, script.filename, "title", default=script.filename).lower()
|
||||
api_args = []
|
||||
|
||||
for control in controls:
|
||||
control.custom_script_source = os.path.basename(script.filename)
|
||||
|
||||
arg_info = api_models.ScriptArg(label=control.label or "")
|
||||
|
||||
for field in ("value", "minimum", "maximum", "step", "choices"):
|
||||
v = getattr(control, field, None)
|
||||
if v is not None:
|
||||
setattr(arg_info, field, v)
|
||||
|
||||
api_args.append(arg_info)
|
||||
|
||||
script.api_info = api_models.ScriptInfo(
|
||||
name=script.name,
|
||||
is_img2img=script.is_img2img,
|
||||
is_alwayson=script.alwayson,
|
||||
args=api_args,
|
||||
)
|
||||
|
||||
if script.infotext_fields is not None:
|
||||
self.infotext_fields += script.infotext_fields
|
||||
|
||||
if script.paste_field_names is not None:
|
||||
self.paste_field_names += script.paste_field_names
|
||||
|
||||
self.inputs += controls
|
||||
script.args_to = len(self.inputs)
|
||||
|
||||
def setup_ui_for_section(self, section, scriptlist=None):
|
||||
if scriptlist is None:
|
||||
scriptlist = self.alwayson_scripts
|
||||
|
||||
for script in scriptlist:
|
||||
if script.alwayson and script.section != section:
|
||||
continue
|
||||
|
||||
with gr.Group(visible=script.alwayson) as group:
|
||||
self.create_script_ui(script)
|
||||
|
||||
script.group = group
|
||||
|
||||
def prepare_ui(self):
|
||||
self.inputs = [None]
|
||||
|
||||
def setup_ui(self):
|
||||
self.titles = [wrap_call(script.title, script.filename, "title") or f"{script.filename} [error]" for script in self.selectable_scripts]
|
||||
|
||||
inputs = [None]
|
||||
inputs_alwayson = [True]
|
||||
|
||||
def create_script_ui(script, inputs, inputs_alwayson):
|
||||
script.args_from = len(inputs)
|
||||
script.args_to = len(inputs)
|
||||
|
||||
controls = wrap_call(script.ui, script.filename, "ui", script.is_img2img)
|
||||
|
||||
if controls is None:
|
||||
return
|
||||
|
||||
script.name = wrap_call(script.title, script.filename, "title", default=script.filename).lower()
|
||||
api_args = []
|
||||
|
||||
for control in controls:
|
||||
control.custom_script_source = os.path.basename(script.filename)
|
||||
|
||||
arg_info = api_models.ScriptArg(label=control.label or "")
|
||||
|
||||
for field in ("value", "minimum", "maximum", "step", "choices"):
|
||||
v = getattr(control, field, None)
|
||||
if v is not None:
|
||||
setattr(arg_info, field, v)
|
||||
|
||||
api_args.append(arg_info)
|
||||
|
||||
script.api_info = api_models.ScriptInfo(
|
||||
name=script.name,
|
||||
is_img2img=script.is_img2img,
|
||||
is_alwayson=script.alwayson,
|
||||
args=api_args,
|
||||
)
|
||||
|
||||
if script.infotext_fields is not None:
|
||||
self.infotext_fields += script.infotext_fields
|
||||
|
||||
if script.paste_field_names is not None:
|
||||
self.paste_field_names += script.paste_field_names
|
||||
|
||||
inputs += controls
|
||||
inputs_alwayson += [script.alwayson for _ in controls]
|
||||
script.args_to = len(inputs)
|
||||
|
||||
for script in self.alwayson_scripts:
|
||||
with gr.Group() as group:
|
||||
create_script_ui(script, inputs, inputs_alwayson)
|
||||
|
||||
script.group = group
|
||||
self.setup_ui_for_section(None)
|
||||
|
||||
dropdown = gr.Dropdown(label="Script", elem_id="script_list", choices=["None"] + self.titles, value="None", type="index")
|
||||
inputs[0] = dropdown
|
||||
self.inputs[0] = dropdown
|
||||
|
||||
for script in self.selectable_scripts:
|
||||
with gr.Group(visible=False) as group:
|
||||
create_script_ui(script, inputs, inputs_alwayson)
|
||||
|
||||
script.group = group
|
||||
self.setup_ui_for_section(None, self.selectable_scripts)
|
||||
|
||||
def select_script(script_index):
|
||||
selected_script = self.selectable_scripts[script_index - 1] if script_index>0 else None
|
||||
|
|
@ -411,6 +426,7 @@ class ScriptRunner:
|
|||
)
|
||||
|
||||
self.script_load_ctr = 0
|
||||
|
||||
def onload_script_visibility(params):
|
||||
title = params.get('Script', None)
|
||||
if title:
|
||||
|
|
@ -421,10 +437,10 @@ class ScriptRunner:
|
|||
else:
|
||||
return gr.update(visible=False)
|
||||
|
||||
self.infotext_fields.append( (dropdown, lambda x: gr.update(value=x.get('Script', 'None'))) )
|
||||
self.infotext_fields.extend( [(script.group, onload_script_visibility) for script in self.selectable_scripts] )
|
||||
self.infotext_fields.append((dropdown, lambda x: gr.update(value=x.get('Script', 'None'))))
|
||||
self.infotext_fields.extend([(script.group, onload_script_visibility) for script in self.selectable_scripts])
|
||||
|
||||
return inputs
|
||||
return self.inputs
|
||||
|
||||
def run(self, p, *args):
|
||||
script_index = args[0]
|
||||
|
|
@ -444,14 +460,21 @@ class ScriptRunner:
|
|||
|
||||
return processed
|
||||
|
||||
def before_process(self, p):
|
||||
for script in self.alwayson_scripts:
|
||||
try:
|
||||
script_args = p.script_args[script.args_from:script.args_to]
|
||||
script.before_process(p, *script_args)
|
||||
except Exception:
|
||||
errors.report(f"Error running before_process: {script.filename}", exc_info=True)
|
||||
|
||||
def process(self, p):
|
||||
for script in self.alwayson_scripts:
|
||||
try:
|
||||
script_args = p.script_args[script.args_from:script.args_to]
|
||||
script.process(p, *script_args)
|
||||
except Exception:
|
||||
print(f"Error running process: {script.filename}", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error running process: {script.filename}", exc_info=True)
|
||||
|
||||
def before_process_batch(self, p, **kwargs):
|
||||
for script in self.alwayson_scripts:
|
||||
|
|
@ -459,8 +482,7 @@ class ScriptRunner:
|
|||
script_args = p.script_args[script.args_from:script.args_to]
|
||||
script.before_process_batch(p, *script_args, **kwargs)
|
||||
except Exception:
|
||||
print(f"Error running before_process_batch: {script.filename}", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error running before_process_batch: {script.filename}", exc_info=True)
|
||||
|
||||
def process_batch(self, p, **kwargs):
|
||||
for script in self.alwayson_scripts:
|
||||
|
|
@ -468,8 +490,7 @@ class ScriptRunner:
|
|||
script_args = p.script_args[script.args_from:script.args_to]
|
||||
script.process_batch(p, *script_args, **kwargs)
|
||||
except Exception:
|
||||
print(f"Error running process_batch: {script.filename}", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error running process_batch: {script.filename}", exc_info=True)
|
||||
|
||||
def postprocess(self, p, processed):
|
||||
for script in self.alwayson_scripts:
|
||||
|
|
@ -477,8 +498,7 @@ class ScriptRunner:
|
|||
script_args = p.script_args[script.args_from:script.args_to]
|
||||
script.postprocess(p, processed, *script_args)
|
||||
except Exception:
|
||||
print(f"Error running postprocess: {script.filename}", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error running postprocess: {script.filename}", exc_info=True)
|
||||
|
||||
def postprocess_batch(self, p, images, **kwargs):
|
||||
for script in self.alwayson_scripts:
|
||||
|
|
@ -486,8 +506,7 @@ class ScriptRunner:
|
|||
script_args = p.script_args[script.args_from:script.args_to]
|
||||
script.postprocess_batch(p, *script_args, images=images, **kwargs)
|
||||
except Exception:
|
||||
print(f"Error running postprocess_batch: {script.filename}", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error running postprocess_batch: {script.filename}", exc_info=True)
|
||||
|
||||
def postprocess_image(self, p, pp: PostprocessImageArgs):
|
||||
for script in self.alwayson_scripts:
|
||||
|
|
@ -495,24 +514,21 @@ class ScriptRunner:
|
|||
script_args = p.script_args[script.args_from:script.args_to]
|
||||
script.postprocess_image(p, pp, *script_args)
|
||||
except Exception:
|
||||
print(f"Error running postprocess_batch: {script.filename}", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error running postprocess_image: {script.filename}", exc_info=True)
|
||||
|
||||
def before_component(self, component, **kwargs):
|
||||
for script in self.scripts:
|
||||
try:
|
||||
script.before_component(component, **kwargs)
|
||||
except Exception:
|
||||
print(f"Error running before_component: {script.filename}", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error running before_component: {script.filename}", exc_info=True)
|
||||
|
||||
def after_component(self, component, **kwargs):
|
||||
for script in self.scripts:
|
||||
try:
|
||||
script.after_component(component, **kwargs)
|
||||
except Exception:
|
||||
print(f"Error running after_component: {script.filename}", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error running after_component: {script.filename}", exc_info=True)
|
||||
|
||||
def reload_sources(self, cache):
|
||||
for si, script in list(enumerate(self.scripts)):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from torch.nn.functional import silu
|
|||
from types import MethodType
|
||||
|
||||
import modules.textual_inversion.textual_inversion
|
||||
from modules import devices, sd_hijack_optimizations, shared, script_callbacks, errors
|
||||
from modules import devices, sd_hijack_optimizations, shared, script_callbacks, errors, sd_unet
|
||||
from modules.hypernetworks import hypernetwork
|
||||
from modules.shared import cmd_opts
|
||||
from modules import sd_hijack_clip, sd_hijack_open_clip, sd_hijack_unet, sd_hijack_xlmr, xlmr
|
||||
|
|
@ -43,7 +43,7 @@ def list_optimizers():
|
|||
optimizers.extend(new_optimizers)
|
||||
|
||||
|
||||
def apply_optimizations():
|
||||
def apply_optimizations(option=None):
|
||||
global current_optimizer
|
||||
|
||||
undo_optimizations()
|
||||
|
|
@ -60,7 +60,7 @@ def apply_optimizations():
|
|||
current_optimizer.undo()
|
||||
current_optimizer = None
|
||||
|
||||
selection = shared.opts.cross_attention_optimization
|
||||
selection = option or shared.opts.cross_attention_optimization
|
||||
if selection == "Automatic" and len(optimizers) > 0:
|
||||
matching_optimizer = next(iter([x for x in optimizers if x.cmd_opt and getattr(shared.cmd_opts, x.cmd_opt, False)]), optimizers[0])
|
||||
else:
|
||||
|
|
@ -74,12 +74,13 @@ def apply_optimizations():
|
|||
matching_optimizer = optimizers[0]
|
||||
|
||||
if matching_optimizer is not None:
|
||||
print(f"Applying optimization: {matching_optimizer.name}... ", end='')
|
||||
print(f"Applying attention optimization: {matching_optimizer.name}... ", end='')
|
||||
matching_optimizer.apply()
|
||||
print("done.")
|
||||
current_optimizer = matching_optimizer
|
||||
return current_optimizer.name
|
||||
else:
|
||||
print("Disabling attention optimization")
|
||||
return ''
|
||||
|
||||
|
||||
|
|
@ -157,9 +158,9 @@ class StableDiffusionModelHijack:
|
|||
def __init__(self):
|
||||
self.embedding_db.add_embedding_dir(cmd_opts.embeddings_dir)
|
||||
|
||||
def apply_optimizations(self):
|
||||
def apply_optimizations(self, option=None):
|
||||
try:
|
||||
self.optimization_method = apply_optimizations()
|
||||
self.optimization_method = apply_optimizations(option)
|
||||
except Exception as e:
|
||||
errors.display(e, "applying cross attention optimization")
|
||||
undo_optimizations()
|
||||
|
|
@ -196,6 +197,11 @@ class StableDiffusionModelHijack:
|
|||
|
||||
self.layers = flatten(m)
|
||||
|
||||
if not hasattr(ldm.modules.diffusionmodules.openaimodel, 'copy_of_UNetModel_forward_for_webui'):
|
||||
ldm.modules.diffusionmodules.openaimodel.copy_of_UNetModel_forward_for_webui = ldm.modules.diffusionmodules.openaimodel.UNetModel.forward
|
||||
|
||||
ldm.modules.diffusionmodules.openaimodel.UNetModel.forward = sd_unet.UNetModel_forward
|
||||
|
||||
def undo_hijack(self, m):
|
||||
if type(m.cond_stage_model) == xlmr.BertSeriesModelWithTransformation:
|
||||
m.cond_stage_model = m.cond_stage_model.wrapped
|
||||
|
|
@ -217,6 +223,8 @@ class StableDiffusionModelHijack:
|
|||
self.layers = None
|
||||
self.clip = None
|
||||
|
||||
ldm.modules.diffusionmodules.openaimodel.UNetModel.forward = ldm.modules.diffusionmodules.openaimodel.copy_of_UNetModel_forward_for_webui
|
||||
|
||||
def apply_circular(self, enable):
|
||||
if self.circular_enabled == enable:
|
||||
return
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ class FrozenCLIPEmbedderWithCustomWordsBase(torch.nn.Module):
|
|||
chunk.multipliers += [weight] * emb_len
|
||||
position += embedding_length_in_tokens
|
||||
|
||||
if len(chunk.tokens) > 0 or len(chunks) == 0:
|
||||
if chunk.tokens or not chunks:
|
||||
next_chunk(is_last=True)
|
||||
|
||||
return chunks, token_count
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ def forward_old(self: sd_hijack_clip.FrozenCLIPEmbedderWithCustomWordsBase, text
|
|||
|
||||
self.hijack.comments += hijack_comments
|
||||
|
||||
if len(used_custom_terms) > 0:
|
||||
if used_custom_terms:
|
||||
embedding_names = ", ".join(f"{word} [{checksum}]" for word, checksum in used_custom_terms)
|
||||
self.hijack.comments.append(f"Used embeddings: {embedding_names}")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
from __future__ import annotations
|
||||
import math
|
||||
import sys
|
||||
import traceback
|
||||
import psutil
|
||||
|
||||
import torch
|
||||
|
|
@ -48,7 +46,7 @@ class SdOptimizationXformers(SdOptimization):
|
|||
priority = 100
|
||||
|
||||
def is_available(self):
|
||||
return shared.cmd_opts.force_enable_xformers or (shared.xformers_available and torch.version.cuda and (6, 0) <= torch.cuda.get_device_capability(shared.device) <= (9, 0))
|
||||
return shared.cmd_opts.force_enable_xformers or (shared.xformers_available and torch.cuda.is_available() and (6, 0) <= torch.cuda.get_device_capability(shared.device) <= (9, 0))
|
||||
|
||||
def apply(self):
|
||||
ldm.modules.attention.CrossAttention.forward = xformers_attention_forward
|
||||
|
|
@ -140,8 +138,7 @@ if shared.cmd_opts.xformers or shared.cmd_opts.force_enable_xformers:
|
|||
import xformers.ops
|
||||
shared.xformers_available = True
|
||||
except Exception:
|
||||
print("Cannot import xformers", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report("Cannot import xformers", exc_info=True)
|
||||
|
||||
|
||||
def get_available_vram():
|
||||
|
|
@ -605,7 +602,7 @@ def sdp_attnblock_forward(self, x):
|
|||
q, k, v = (rearrange(t, 'b c h w -> b (h w) c') for t in (q, k, v))
|
||||
dtype = q.dtype
|
||||
if shared.opts.upcast_attn:
|
||||
q, k = q.float(), k.float()
|
||||
q, k, v = q.float(), k.float(), v.float()
|
||||
q = q.contiguous()
|
||||
k = k.contiguous()
|
||||
v = v.contiguous()
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import ldm.modules.midas as midas
|
|||
|
||||
from ldm.util import instantiate_from_config
|
||||
|
||||
from modules import paths, shared, modelloader, devices, script_callbacks, sd_vae, sd_disable_initialization, errors, hashes, sd_models_config
|
||||
from modules import paths, shared, modelloader, devices, script_callbacks, sd_vae, sd_disable_initialization, errors, hashes, sd_models_config, sd_unet
|
||||
from modules.sd_hijack_inpainting import do_inpainting_hijack
|
||||
from modules.timer import Timer
|
||||
import tomesd
|
||||
|
|
@ -95,8 +95,7 @@ except Exception:
|
|||
|
||||
|
||||
def setup_model():
|
||||
if not os.path.exists(model_path):
|
||||
os.makedirs(model_path)
|
||||
os.makedirs(model_path, exist_ok=True)
|
||||
|
||||
enable_midas_autodownload()
|
||||
|
||||
|
|
@ -164,6 +163,7 @@ def model_hash(filename):
|
|||
|
||||
|
||||
def select_checkpoint():
|
||||
"""Raises `FileNotFoundError` if no checkpoints are found."""
|
||||
model_checkpoint = shared.opts.sd_model_checkpoint
|
||||
|
||||
checkpoint_info = checkpoint_alisases.get(model_checkpoint, None)
|
||||
|
|
@ -171,14 +171,14 @@ def select_checkpoint():
|
|||
return checkpoint_info
|
||||
|
||||
if len(checkpoints_list) == 0:
|
||||
print("No checkpoints found. When searching for checkpoints, looked at:", file=sys.stderr)
|
||||
error_message = "No checkpoints found. When searching for checkpoints, looked at:"
|
||||
if shared.cmd_opts.ckpt is not None:
|
||||
print(f" - file {os.path.abspath(shared.cmd_opts.ckpt)}", file=sys.stderr)
|
||||
print(f" - directory {model_path}", file=sys.stderr)
|
||||
error_message += f"\n - file {os.path.abspath(shared.cmd_opts.ckpt)}"
|
||||
error_message += f"\n - directory {model_path}"
|
||||
if shared.cmd_opts.ckpt_dir is not None:
|
||||
print(f" - directory {os.path.abspath(shared.cmd_opts.ckpt_dir)}", file=sys.stderr)
|
||||
print("Can't run without a checkpoint. Find and place a .ckpt or .safetensors file into any of those locations. The program will exit.", file=sys.stderr)
|
||||
exit(1)
|
||||
error_message += f"\n - directory {os.path.abspath(shared.cmd_opts.ckpt_dir)}"
|
||||
error_message += "Can't run without a checkpoint. Find and place a .ckpt or .safetensors file into any of those locations."
|
||||
raise FileNotFoundError(error_message)
|
||||
|
||||
checkpoint_info = next(iter(checkpoints_list.values()))
|
||||
if model_checkpoint is not None:
|
||||
|
|
@ -421,7 +421,7 @@ class SdModelData:
|
|||
try:
|
||||
load_model()
|
||||
except Exception as e:
|
||||
errors.display(e, "loading stable diffusion model")
|
||||
errors.display(e, "loading stable diffusion model", full_traceback=True)
|
||||
print("", file=sys.stderr)
|
||||
print("Stable diffusion model failed to load", file=sys.stderr)
|
||||
self.sd_model = None
|
||||
|
|
@ -506,6 +506,11 @@ def load_model(checkpoint_info=None, already_loaded_state_dict=None):
|
|||
|
||||
timer.record("scripts callbacks")
|
||||
|
||||
with devices.autocast(), torch.no_grad():
|
||||
sd_model.cond_stage_model_empty_prompt = sd_model.cond_stage_model([""])
|
||||
|
||||
timer.record("calculate empty prompt")
|
||||
|
||||
print(f"Model loaded in {timer.summary()}.")
|
||||
|
||||
return sd_model
|
||||
|
|
@ -525,6 +530,8 @@ def reload_model_weights(sd_model=None, info=None):
|
|||
if sd_model.sd_model_checkpoint == checkpoint_info.filename:
|
||||
return
|
||||
|
||||
sd_unet.apply_unet("None")
|
||||
|
||||
if shared.cmd_opts.lowvram or shared.cmd_opts.medvram:
|
||||
lowvram.send_everything_to_cpu()
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ samplers_k_diffusion = [
|
|||
('DPM++ 2S a', 'sample_dpmpp_2s_ancestral', ['k_dpmpp_2s_a'], {"uses_ensd": True, "second_order": True}),
|
||||
('DPM++ 2M', 'sample_dpmpp_2m', ['k_dpmpp_2m'], {}),
|
||||
('DPM++ SDE', 'sample_dpmpp_sde', ['k_dpmpp_sde'], {"second_order": True, "brownian_noise": True}),
|
||||
('DPM++ 2M SDE', 'sample_dpmpp_2m_sde', ['k_dpmpp_2m_sde_ka'], {"brownian_noise": True, 'discard_next_to_last_sigma': True}),
|
||||
('DPM++ 2M SDE', 'sample_dpmpp_2m_sde', ['k_dpmpp_2m_sde_ka'], {"brownian_noise": True}),
|
||||
('DPM fast', 'sample_dpm_fast', ['k_dpm_fast'], {"uses_ensd": True}),
|
||||
('DPM adaptive', 'sample_dpm_adaptive', ['k_dpm_ad'], {"uses_ensd": True}),
|
||||
('LMS Karras', 'sample_lms', ['k_lms_ka'], {'scheduler': 'karras'}),
|
||||
|
|
@ -29,7 +29,7 @@ samplers_k_diffusion = [
|
|||
('DPM++ 2S a Karras', 'sample_dpmpp_2s_ancestral', ['k_dpmpp_2s_a_ka'], {'scheduler': 'karras', "uses_ensd": True, "second_order": True}),
|
||||
('DPM++ 2M Karras', 'sample_dpmpp_2m', ['k_dpmpp_2m_ka'], {'scheduler': 'karras'}),
|
||||
('DPM++ SDE Karras', 'sample_dpmpp_sde', ['k_dpmpp_sde_ka'], {'scheduler': 'karras', "second_order": True, "brownian_noise": True}),
|
||||
('DPM++ 2M SDE Karras', 'sample_dpmpp_2m_sde', ['k_dpmpp_2m_sde_ka'], {'scheduler': 'karras', "brownian_noise": True, 'discard_next_to_last_sigma': True}),
|
||||
('DPM++ 2M SDE Karras', 'sample_dpmpp_2m_sde', ['k_dpmpp_2m_sde_ka'], {'scheduler': 'karras', "brownian_noise": True}),
|
||||
]
|
||||
|
||||
samplers_data_k_diffusion = [
|
||||
|
|
@ -44,6 +44,14 @@ sampler_extra_params = {
|
|||
'sample_dpm_2': ['s_churn', 's_tmin', 's_tmax', 's_noise'],
|
||||
}
|
||||
|
||||
k_diffusion_samplers_map = {x.name: x for x in samplers_data_k_diffusion}
|
||||
k_diffusion_scheduler = {
|
||||
'Automatic': None,
|
||||
'karras': k_diffusion.sampling.get_sigmas_karras,
|
||||
'exponential': k_diffusion.sampling.get_sigmas_exponential,
|
||||
'polyexponential': k_diffusion.sampling.get_sigmas_polyexponential
|
||||
}
|
||||
|
||||
|
||||
class CFGDenoiser(torch.nn.Module):
|
||||
"""
|
||||
|
|
@ -61,6 +69,7 @@ class CFGDenoiser(torch.nn.Module):
|
|||
self.init_latent = None
|
||||
self.step = 0
|
||||
self.image_cfg_scale = None
|
||||
self.padded_cond_uncond = False
|
||||
|
||||
def combine_denoised(self, x_out, conds_list, uncond, cond_scale):
|
||||
denoised_uncond = x_out[-uncond.shape[0]:]
|
||||
|
|
@ -125,6 +134,18 @@ class CFGDenoiser(torch.nn.Module):
|
|||
x_in = x_in[:-batch_size]
|
||||
sigma_in = sigma_in[:-batch_size]
|
||||
|
||||
self.padded_cond_uncond = False
|
||||
if shared.opts.pad_cond_uncond and tensor.shape[1] != uncond.shape[1]:
|
||||
empty = shared.sd_model.cond_stage_model_empty_prompt
|
||||
num_repeats = (tensor.shape[1] - uncond.shape[1]) // empty.shape[1]
|
||||
|
||||
if num_repeats < 0:
|
||||
tensor = torch.cat([tensor, empty.repeat((tensor.shape[0], -num_repeats, 1))], axis=1)
|
||||
self.padded_cond_uncond = True
|
||||
elif num_repeats > 0:
|
||||
uncond = torch.cat([uncond, empty.repeat((uncond.shape[0], num_repeats, 1))], axis=1)
|
||||
self.padded_cond_uncond = True
|
||||
|
||||
if tensor.shape[1] == uncond.shape[1] or skip_uncond:
|
||||
if is_edit_model:
|
||||
cond_in = torch.cat([tensor, uncond, uncond])
|
||||
|
|
@ -255,6 +276,13 @@ class KDiffusionSampler:
|
|||
|
||||
try:
|
||||
return func()
|
||||
except RecursionError:
|
||||
print(
|
||||
'Encountered RecursionError during sampling, returning last latent. '
|
||||
'rho >5 with a polyexponential scheduler may cause this error. '
|
||||
'You should try to use a smaller rho value instead.'
|
||||
)
|
||||
return self.last_latent
|
||||
except sd_samplers_common.InterruptedException:
|
||||
return self.last_latent
|
||||
|
||||
|
|
@ -294,6 +322,31 @@ class KDiffusionSampler:
|
|||
|
||||
if p.sampler_noise_scheduler_override:
|
||||
sigmas = p.sampler_noise_scheduler_override(steps)
|
||||
elif opts.k_sched_type != "Automatic":
|
||||
m_sigma_min, m_sigma_max = (self.model_wrap.sigmas[0].item(), self.model_wrap.sigmas[-1].item())
|
||||
sigma_min, sigma_max = (0.1, 10) if opts.use_old_karras_scheduler_sigmas else (m_sigma_min, m_sigma_max)
|
||||
sigmas_kwargs = {
|
||||
'sigma_min': sigma_min,
|
||||
'sigma_max': sigma_max,
|
||||
}
|
||||
|
||||
sigmas_func = k_diffusion_scheduler[opts.k_sched_type]
|
||||
p.extra_generation_params["Schedule type"] = opts.k_sched_type
|
||||
|
||||
if opts.sigma_min != m_sigma_min and opts.sigma_min != 0:
|
||||
sigmas_kwargs['sigma_min'] = opts.sigma_min
|
||||
p.extra_generation_params["Schedule min sigma"] = opts.sigma_min
|
||||
if opts.sigma_max != m_sigma_max and opts.sigma_max != 0:
|
||||
sigmas_kwargs['sigma_max'] = opts.sigma_max
|
||||
p.extra_generation_params["Schedule max sigma"] = opts.sigma_max
|
||||
|
||||
default_rho = 1. if opts.k_sched_type == "polyexponential" else 7.
|
||||
|
||||
if opts.k_sched_type != 'exponential' and opts.rho != 0 and opts.rho != default_rho:
|
||||
sigmas_kwargs['rho'] = opts.rho
|
||||
p.extra_generation_params["Schedule rho"] = opts.rho
|
||||
|
||||
sigmas = sigmas_func(n=steps, **sigmas_kwargs, device=shared.device)
|
||||
elif self.config is not None and self.config.options.get('scheduler', None) == 'karras':
|
||||
sigma_min, sigma_max = (0.1, 10) if opts.use_old_karras_scheduler_sigmas else (self.model_wrap.sigmas[0].item(), self.model_wrap.sigmas[-1].item())
|
||||
|
||||
|
|
@ -355,6 +408,9 @@ class KDiffusionSampler:
|
|||
|
||||
samples = self.launch_sampling(t_enc + 1, lambda: self.func(self.model_wrap_cfg, xi, extra_args=extra_args, disable=False, callback=self.callback_state, **extra_params_kwargs))
|
||||
|
||||
if self.model_wrap_cfg.padded_cond_uncond:
|
||||
p.extra_generation_params["Pad conds"] = True
|
||||
|
||||
return samples
|
||||
|
||||
def sample(self, p, x, conditioning, unconditional_conditioning, steps=None, image_conditioning=None):
|
||||
|
|
@ -388,5 +444,8 @@ class KDiffusionSampler:
|
|||
's_min_uncond': self.s_min_uncond
|
||||
}, disable=False, callback=self.callback_state, **extra_params_kwargs))
|
||||
|
||||
if self.model_wrap_cfg.padded_cond_uncond:
|
||||
p.extra_generation_params["Pad conds"] = True
|
||||
|
||||
return samples
|
||||
|
||||
|
|
|
|||
92
modules/sd_unet.py
Normal file
92
modules/sd_unet.py
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import torch.nn
|
||||
import ldm.modules.diffusionmodules.openaimodel
|
||||
|
||||
from modules import script_callbacks, shared, devices
|
||||
|
||||
unet_options = []
|
||||
current_unet_option = None
|
||||
current_unet = None
|
||||
|
||||
|
||||
def list_unets():
|
||||
new_unets = script_callbacks.list_unets_callback()
|
||||
|
||||
unet_options.clear()
|
||||
unet_options.extend(new_unets)
|
||||
|
||||
|
||||
def get_unet_option(option=None):
|
||||
option = option or shared.opts.sd_unet
|
||||
|
||||
if option == "None":
|
||||
return None
|
||||
|
||||
if option == "Automatic":
|
||||
name = shared.sd_model.sd_checkpoint_info.model_name
|
||||
|
||||
options = [x for x in unet_options if x.model_name == name]
|
||||
|
||||
option = options[0].label if options else "None"
|
||||
|
||||
return next(iter([x for x in unet_options if x.label == option]), None)
|
||||
|
||||
|
||||
def apply_unet(option=None):
|
||||
global current_unet_option
|
||||
global current_unet
|
||||
|
||||
new_option = get_unet_option(option)
|
||||
if new_option == current_unet_option:
|
||||
return
|
||||
|
||||
if current_unet is not None:
|
||||
print(f"Dectivating unet: {current_unet.option.label}")
|
||||
current_unet.deactivate()
|
||||
|
||||
current_unet_option = new_option
|
||||
if current_unet_option is None:
|
||||
current_unet = None
|
||||
|
||||
if not (shared.cmd_opts.lowvram or shared.cmd_opts.medvram):
|
||||
shared.sd_model.model.diffusion_model.to(devices.device)
|
||||
|
||||
return
|
||||
|
||||
shared.sd_model.model.diffusion_model.to(devices.cpu)
|
||||
devices.torch_gc()
|
||||
|
||||
current_unet = current_unet_option.create_unet()
|
||||
current_unet.option = current_unet_option
|
||||
print(f"Activating unet: {current_unet.option.label}")
|
||||
current_unet.activate()
|
||||
|
||||
|
||||
class SdUnetOption:
|
||||
model_name = None
|
||||
"""name of related checkpoint - this option will be selected automatically for unet if the name of checkpoint matches this"""
|
||||
|
||||
label = None
|
||||
"""name of the unet in UI"""
|
||||
|
||||
def create_unet(self):
|
||||
"""returns SdUnet object to be used as a Unet instead of built-in unet when making pictures"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class SdUnet(torch.nn.Module):
|
||||
def forward(self, x, timesteps, context, *args, **kwargs):
|
||||
raise NotImplementedError()
|
||||
|
||||
def activate(self):
|
||||
pass
|
||||
|
||||
def deactivate(self):
|
||||
pass
|
||||
|
||||
|
||||
def UNetModel_forward(self, x, timesteps=None, context=None, *args, **kwargs):
|
||||
if current_unet is not None:
|
||||
return current_unet.forward(x, timesteps, context, *args, **kwargs)
|
||||
|
||||
return ldm.modules.diffusionmodules.openaimodel.copy_of_UNetModel_forward_for_webui(self, x, timesteps, context, *args, **kwargs)
|
||||
|
||||
|
|
@ -44,19 +44,6 @@ restricted_opts = {
|
|||
"outdir_init_images"
|
||||
}
|
||||
|
||||
ui_reorder_categories = [
|
||||
"inpaint",
|
||||
"sampler",
|
||||
"checkboxes",
|
||||
"hires_fix",
|
||||
"dimensions",
|
||||
"cfg",
|
||||
"seed",
|
||||
"batch",
|
||||
"override_settings",
|
||||
"scripts",
|
||||
]
|
||||
|
||||
# https://huggingface.co/datasets/freddyaboulton/gradio-theme-subdomains/resolve/main/subdomains.json
|
||||
gradio_hf_hub_themes = [
|
||||
"gradio/glass",
|
||||
|
|
@ -273,6 +260,10 @@ class OptionInfo:
|
|||
self.comment_after += f"<span class='info'>({info})</span>"
|
||||
return self
|
||||
|
||||
def html(self, html):
|
||||
self.comment_after += html
|
||||
return self
|
||||
|
||||
def needs_restart(self):
|
||||
self.comment_after += " <span class='info'>(requires restart)</span>"
|
||||
return self
|
||||
|
|
@ -318,6 +309,7 @@ options_templates.update(options_section(('saving-images', "Saving images/grids"
|
|||
"grid_extended_filename": OptionInfo(False, "Add extended info (seed, prompt) to filename when saving grid"),
|
||||
"grid_only_if_multiple": OptionInfo(True, "Do not save grids consisting of one picture"),
|
||||
"grid_prevent_empty_spots": OptionInfo(False, "Prevent empty spots in grid (when set to autodetect)"),
|
||||
"grid_zip_filename_pattern": OptionInfo("", "Archive filename pattern", component_args=hide_dirs).link("wiki", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Custom-Images-Filename-Name-and-Subdirectory"),
|
||||
"n_rows": OptionInfo(-1, "Grid row count; use -1 for autodetect and 0 for it to be same as batch size", gr.Slider, {"minimum": -1, "maximum": 16, "step": 1}),
|
||||
|
||||
"enable_pnginfo": OptionInfo(True, "Save text information about generation parameters as chunks to png files"),
|
||||
|
|
@ -407,6 +399,7 @@ options_templates.update(options_section(('sd', "Stable Diffusion"), {
|
|||
"sd_vae_checkpoint_cache": OptionInfo(0, "VAE Checkpoints to cache in RAM", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
|
||||
"sd_vae": OptionInfo("Automatic", "SD VAE", gr.Dropdown, lambda: {"choices": shared_items.sd_vae_items()}, refresh=shared_items.refresh_vae_list).info("choose VAE model: Automatic = use one with same filename as checkpoint; None = use VAE from checkpoint"),
|
||||
"sd_vae_as_default": OptionInfo(True, "Ignore selected VAE for stable diffusion checkpoints that have their own .vae.pt next to them"),
|
||||
"sd_unet": OptionInfo("Automatic", "SD Unet", gr.Dropdown, lambda: {"choices": shared_items.sd_unet_items()}, refresh=shared_items.refresh_unet_list).info("choose Unet model: Automatic = use one with same filename as checkpoint; None = use Unet from checkpoint"),
|
||||
"inpainting_mask_weight": OptionInfo(1.0, "Inpainting conditioning mask strength", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
|
||||
"initial_noise_multiplier": OptionInfo(1.0, "Noise multiplier for img2img", gr.Slider, {"minimum": 0.5, "maximum": 1.5, "step": 0.01}),
|
||||
"img2img_color_correction": OptionInfo(False, "Apply color correction to img2img results to match original colors."),
|
||||
|
|
@ -416,17 +409,19 @@ options_templates.update(options_section(('sd', "Stable Diffusion"), {
|
|||
"enable_emphasis": OptionInfo(True, "Enable emphasis").info("use (text) to make model pay more attention to text and [text] to make it pay less attention"),
|
||||
"enable_batch_seeds": OptionInfo(True, "Make K-diffusion samplers produce same images in a batch as when making a single image"),
|
||||
"comma_padding_backtrack": OptionInfo(20, "Prompt word wrap length limit", gr.Slider, {"minimum": 0, "maximum": 74, "step": 1}).info("in tokens - for texts shorter than specified, if they don't fit into 75 token limit, move them to the next 75 token chunk"),
|
||||
"CLIP_stop_at_last_layers": OptionInfo(1, "Clip skip", gr.Slider, {"minimum": 1, "maximum": 12, "step": 1}).link("wiki", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#clip-skip").info("ignore last layers of CLIP nrtwork; 1 ignores none, 2 ignores one layer"),
|
||||
"CLIP_stop_at_last_layers": OptionInfo(1, "Clip skip", gr.Slider, {"minimum": 1, "maximum": 12, "step": 1}).link("wiki", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#clip-skip").info("ignore last layers of CLIP network; 1 ignores none, 2 ignores one layer"),
|
||||
"upcast_attn": OptionInfo(False, "Upcast cross attention layer to float32"),
|
||||
"randn_source": OptionInfo("GPU", "Random number generator source.", gr.Radio, {"choices": ["GPU", "CPU"]}).info("changes seeds drastically; use CPU to produce the same picture across different vidocard vendors"),
|
||||
"randn_source": OptionInfo("GPU", "Random number generator source.", gr.Radio, {"choices": ["GPU", "CPU"]}).info("changes seeds drastically; use CPU to produce the same picture across different videocard vendors"),
|
||||
}))
|
||||
|
||||
options_templates.update(options_section(('optimizations', "Optimizations"), {
|
||||
"cross_attention_optimization": OptionInfo("Automatic", "Cross attention optimization", gr.Dropdown, lambda: {"choices": shared_items.cross_attention_optimizations()}),
|
||||
"s_min_uncond": OptionInfo(0, "Negative Guidance minimum sigma", gr.Slider, {"minimum": 0.0, "maximum": 4.0, "step": 0.01}).link("PR", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/9177").info("skip negative prompt for some steps when the image is almost ready; 0=disable, higher=faster"),
|
||||
"s_min_uncond": OptionInfo(0.0, "Negative Guidance minimum sigma", gr.Slider, {"minimum": 0.0, "maximum": 4.0, "step": 0.01}).link("PR", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/9177").info("skip negative prompt for some steps when the image is almost ready; 0=disable, higher=faster"),
|
||||
"token_merging_ratio": OptionInfo(0.0, "Token merging ratio", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}).link("PR", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/9256").info("0=disable, higher=faster"),
|
||||
"token_merging_ratio_img2img": OptionInfo(0.0, "Token merging ratio for img2img", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}).info("only applies if non-zero and overrides above"),
|
||||
"token_merging_ratio_hr": OptionInfo(0.0, "Token merging ratio for high-res pass", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}).info("only applies if non-zero and overrides above"),
|
||||
"pad_cond_uncond": OptionInfo(False, "Pad prompt/negative prompt to be same length").info("improves performance when prompt and negative prompt have different lengths; changes seeds"),
|
||||
"experimental_persistent_cond_cache": OptionInfo(False, "persistent cond cache").info("Experimental, keep cond caches across jobs, reduce overhead."),
|
||||
}))
|
||||
|
||||
options_templates.update(options_section(('compatibility', "Compatibility"), {
|
||||
|
|
@ -435,6 +430,7 @@ options_templates.update(options_section(('compatibility', "Compatibility"), {
|
|||
"no_dpmpp_sde_batch_determinism": OptionInfo(False, "Do not make DPM++ SDE deterministic across different batch sizes."),
|
||||
"use_old_hires_fix_width_height": OptionInfo(False, "For hires fix, use width/height sliders to set final resolution rather than first pass (disables Upscale by, Resize width/height to)."),
|
||||
"dont_fix_second_order_samplers_schedule": OptionInfo(False, "Do not fix prompt schedule for second order samplers."),
|
||||
"hires_fix_use_firstpass_conds": OptionInfo(False, "For hires fix, calculate conds of second pass using extra networks of first pass."),
|
||||
}))
|
||||
|
||||
options_templates.update(options_section(('interrogate', "Interrogate Options"), {
|
||||
|
|
@ -488,9 +484,10 @@ options_templates.update(options_section(('ui', "User interface"), {
|
|||
"quicksettings_list": OptionInfo(["sd_model_checkpoint"], "Quicksettings list", ui_components.DropdownMulti, lambda: {"choices": list(opts.data_labels.keys())}).js("info", "settingsHintsShowQuicksettings").info("setting entries that appear at the top of page rather than in settings tab").needs_restart(),
|
||||
"ui_tab_order": OptionInfo([], "UI tab order", ui_components.DropdownMulti, lambda: {"choices": list(tab_names)}).needs_restart(),
|
||||
"hidden_tabs": OptionInfo([], "Hidden UI tabs", ui_components.DropdownMulti, lambda: {"choices": list(tab_names)}).needs_restart(),
|
||||
"ui_reorder": OptionInfo(", ".join(ui_reorder_categories), "txt2img/img2img UI item order").needs_restart(),
|
||||
"ui_reorder_list": OptionInfo([], "txt2img/img2img UI item order", ui_components.DropdownMulti, lambda: {"choices": list(shared_items.ui_reorder_categories())}).info("selected items appear first").needs_restart(),
|
||||
"hires_fix_show_sampler": OptionInfo(False, "Hires fix: show hires sampler selection").needs_restart(),
|
||||
"hires_fix_show_prompts": OptionInfo(False, "Hires fix: show hires prompt and negative prompt").needs_restart(),
|
||||
"disable_token_counters": OptionInfo(False, "Disable prompt token counters").needs_restart(),
|
||||
}))
|
||||
|
||||
options_templates.update(options_section(('infotext', "Infotext"), {
|
||||
|
|
@ -498,7 +495,14 @@ options_templates.update(options_section(('infotext', "Infotext"), {
|
|||
"add_model_name_to_info": OptionInfo(True, "Add model name to generation information"),
|
||||
"add_user_name_to_info": OptionInfo(False, "Add user name to generation information when authenticated"),
|
||||
"add_version_to_infotext": OptionInfo(True, "Add program version to generation information"),
|
||||
"disable_weights_auto_swap": OptionInfo(True, "When reading generation parameters from text into UI (from PNG info or pasted text), do not change the selected model/checkpoint."),
|
||||
"disable_weights_auto_swap": OptionInfo(True, "Disregard checkpoint information from pasted infotext").info("when reading generation parameters from text into UI"),
|
||||
"infotext_styles": OptionInfo("Apply if any", "Infer styles from prompts of pasted infotext", gr.Radio, {"choices": ["Ignore", "Apply", "Discard", "Apply if any"]}).info("when reading generation parameters from text into UI)").html("""<ul style='margin-left: 1.5em'>
|
||||
<li>Ignore: keep prompt and styles dropdown as it is.</li>
|
||||
<li>Apply: remove style text from prompt, always replace styles dropdown value with found styles (even if none are found).</li>
|
||||
<li>Discard: remove style text from prompt, keep styles dropdown as it is.</li>
|
||||
<li>Apply if any: remove style text from prompt; if any styles are found in prompt, put them into styles dropdown, otherwise keep it as it is.</li>
|
||||
</ul>"""),
|
||||
|
||||
}))
|
||||
|
||||
options_templates.update(options_section(('ui', "Live previews"), {
|
||||
|
|
@ -520,6 +524,10 @@ options_templates.update(options_section(('sampler-params', "Sampler parameters"
|
|||
's_churn': OptionInfo(0.0, "sigma churn", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
|
||||
's_tmin': OptionInfo(0.0, "sigma tmin", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
|
||||
's_noise': OptionInfo(1.0, "sigma noise", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
|
||||
'k_sched_type': OptionInfo("Automatic", "scheduler type", gr.Dropdown, {"choices": ["Automatic", "karras", "exponential", "polyexponential"]}).info("lets you override the noise schedule for k-diffusion samplers; choosing Automatic disables the three parameters below"),
|
||||
'sigma_min': OptionInfo(0.0, "sigma min", gr.Number).info("0 = default (~0.03); minimum noise strength for k-diffusion noise scheduler"),
|
||||
'sigma_max': OptionInfo(0.0, "sigma max", gr.Number).info("0 = default (~14.6); maximum noise strength for k-diffusion noise schedule"),
|
||||
'rho': OptionInfo(0.0, "rho", gr.Number).info("0 = default (7 for karras, 1 for polyexponential); higher values result in a more steep noise schedule (decreases faster)"),
|
||||
'eta_noise_seed_delta': OptionInfo(0, "Eta noise seed delta", gr.Number, {"precision": 0}).info("ENSD; does not improve anything, just produces different results for ancestral samplers - only useful for reproducing images"),
|
||||
'always_discard_next_to_last_sigma': OptionInfo(False, "Always discard next-to-last sigma").link("PR", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/6044"),
|
||||
'uni_pc_variant': OptionInfo("bh1", "UniPC variant", gr.Radio, {"choices": ["bh1", "bh2", "vary_coeff"]}),
|
||||
|
|
@ -635,6 +643,10 @@ class Options:
|
|||
if self.data.get('quicksettings') is not None and self.data.get('quicksettings_list') is None:
|
||||
self.data['quicksettings_list'] = [i.strip() for i in self.data.get('quicksettings').split(',')]
|
||||
|
||||
# 1.4.0 ui_reorder
|
||||
if isinstance(self.data.get('ui_reorder'), str) and self.data.get('ui_reorder') and "ui_reorder_list" not in self.data:
|
||||
self.data['ui_reorder_list'] = [i.strip() for i in self.data.get('ui_reorder').split(',')]
|
||||
|
||||
bad_settings = 0
|
||||
for k, v in self.data.items():
|
||||
info = self.data_labels.get(k, None)
|
||||
|
|
|
|||
|
|
@ -29,3 +29,41 @@ def cross_attention_optimizations():
|
|||
return ["Automatic"] + [x.title() for x in modules.sd_hijack.optimizers] + ["None"]
|
||||
|
||||
|
||||
def sd_unet_items():
|
||||
import modules.sd_unet
|
||||
|
||||
return ["Automatic"] + [x.label for x in modules.sd_unet.unet_options] + ["None"]
|
||||
|
||||
|
||||
def refresh_unet_list():
|
||||
import modules.sd_unet
|
||||
|
||||
modules.sd_unet.list_unets()
|
||||
|
||||
|
||||
ui_reorder_categories_builtin_items = [
|
||||
"inpaint",
|
||||
"sampler",
|
||||
"checkboxes",
|
||||
"hires_fix",
|
||||
"dimensions",
|
||||
"cfg",
|
||||
"seed",
|
||||
"batch",
|
||||
"override_settings",
|
||||
]
|
||||
|
||||
|
||||
def ui_reorder_categories():
|
||||
from modules import scripts
|
||||
|
||||
yield from ui_reorder_categories_builtin_items
|
||||
|
||||
sections = {}
|
||||
for script in scripts.scripts_txt2img.scripts + scripts.scripts_img2img.scripts:
|
||||
if isinstance(script.section, str):
|
||||
sections[script.section] = 1
|
||||
|
||||
yield from sections
|
||||
|
||||
yield "scripts"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import csv
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import typing
|
||||
import shutil
|
||||
|
||||
|
|
@ -28,6 +29,44 @@ def apply_styles_to_prompt(prompt, styles):
|
|||
return prompt
|
||||
|
||||
|
||||
re_spaces = re.compile(" +")
|
||||
|
||||
|
||||
def extract_style_text_from_prompt(style_text, prompt):
|
||||
stripped_prompt = re.sub(re_spaces, " ", prompt.strip())
|
||||
stripped_style_text = re.sub(re_spaces, " ", style_text.strip())
|
||||
if "{prompt}" in stripped_style_text:
|
||||
left, right = stripped_style_text.split("{prompt}", 2)
|
||||
if stripped_prompt.startswith(left) and stripped_prompt.endswith(right):
|
||||
prompt = stripped_prompt[len(left):len(stripped_prompt)-len(right)]
|
||||
return True, prompt
|
||||
else:
|
||||
if stripped_prompt.endswith(stripped_style_text):
|
||||
prompt = stripped_prompt[:len(stripped_prompt)-len(stripped_style_text)]
|
||||
|
||||
if prompt.endswith(', '):
|
||||
prompt = prompt[:-2]
|
||||
|
||||
return True, prompt
|
||||
|
||||
return False, prompt
|
||||
|
||||
|
||||
def extract_style_from_prompts(style: PromptStyle, prompt, negative_prompt):
|
||||
if not style.prompt and not style.negative_prompt:
|
||||
return False, prompt, negative_prompt
|
||||
|
||||
match_positive, extracted_positive = extract_style_text_from_prompt(style.prompt, prompt)
|
||||
if not match_positive:
|
||||
return False, prompt, negative_prompt
|
||||
|
||||
match_negative, extracted_negative = extract_style_text_from_prompt(style.negative_prompt, negative_prompt)
|
||||
if not match_negative:
|
||||
return False, prompt, negative_prompt
|
||||
|
||||
return True, extracted_positive, extracted_negative
|
||||
|
||||
|
||||
class StyleDatabase:
|
||||
def __init__(self, path: str):
|
||||
self.no_style = PromptStyle("None", "", "")
|
||||
|
|
@ -67,10 +106,34 @@ class StyleDatabase:
|
|||
if os.path.exists(path):
|
||||
shutil.copy(path, f"{path}.bak")
|
||||
|
||||
fd = os.open(path, os.O_RDWR|os.O_CREAT)
|
||||
fd = os.open(path, os.O_RDWR | os.O_CREAT)
|
||||
with os.fdopen(fd, "w", encoding="utf-8-sig", newline='') as file:
|
||||
# _fields is actually part of the public API: typing.NamedTuple is a replacement for collections.NamedTuple,
|
||||
# and collections.NamedTuple has explicit documentation for accessing _fields. Same goes for _asdict()
|
||||
writer = csv.DictWriter(file, fieldnames=PromptStyle._fields)
|
||||
writer.writeheader()
|
||||
writer.writerows(style._asdict() for k, style in self.styles.items())
|
||||
writer.writerows(style._asdict() for k, style in self.styles.items())
|
||||
|
||||
def extract_styles_from_prompt(self, prompt, negative_prompt):
|
||||
extracted = []
|
||||
|
||||
applicable_styles = list(self.styles.values())
|
||||
|
||||
while True:
|
||||
found_style = None
|
||||
|
||||
for style in applicable_styles:
|
||||
is_match, new_prompt, new_neg_prompt = extract_style_from_prompts(style, prompt, negative_prompt)
|
||||
if is_match:
|
||||
found_style = style
|
||||
prompt = new_prompt
|
||||
negative_prompt = new_neg_prompt
|
||||
break
|
||||
|
||||
if not found_style:
|
||||
break
|
||||
|
||||
applicable_styles.remove(found_style)
|
||||
extracted.append(found_style.name)
|
||||
|
||||
return list(reversed(extracted)), prompt, negative_prompt
|
||||
|
|
|
|||
162
modules/sysinfo.py
Normal file
162
modules/sysinfo.py
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
import json
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import platform
|
||||
import hashlib
|
||||
import pkg_resources
|
||||
import psutil
|
||||
import re
|
||||
|
||||
import launch
|
||||
from modules import paths_internal, timer
|
||||
|
||||
checksum_token = "DontStealMyGamePlz__WINNERS_DONT_USE_DRUGS__DONT_COPY_THAT_FLOPPY"
|
||||
environment_whitelist = {
|
||||
"GIT",
|
||||
"INDEX_URL",
|
||||
"WEBUI_LAUNCH_LIVE_OUTPUT",
|
||||
"GRADIO_ANALYTICS_ENABLED",
|
||||
"PYTHONPATH",
|
||||
"TORCH_INDEX_URL",
|
||||
"TORCH_COMMAND",
|
||||
"REQS_FILE",
|
||||
"XFORMERS_PACKAGE",
|
||||
"GFPGAN_PACKAGE",
|
||||
"CLIP_PACKAGE",
|
||||
"OPENCLIP_PACKAGE",
|
||||
"STABLE_DIFFUSION_REPO",
|
||||
"K_DIFFUSION_REPO",
|
||||
"CODEFORMER_REPO",
|
||||
"BLIP_REPO",
|
||||
"STABLE_DIFFUSION_COMMIT_HASH",
|
||||
"K_DIFFUSION_COMMIT_HASH",
|
||||
"CODEFORMER_COMMIT_HASH",
|
||||
"BLIP_COMMIT_HASH",
|
||||
"COMMANDLINE_ARGS",
|
||||
"IGNORE_CMD_ARGS_ERRORS",
|
||||
}
|
||||
|
||||
|
||||
def pretty_bytes(num, suffix="B"):
|
||||
for unit in ["", "K", "M", "G", "T", "P", "E", "Z", "Y"]:
|
||||
if abs(num) < 1024 or unit == 'Y':
|
||||
return f"{num:.0f}{unit}{suffix}"
|
||||
num /= 1024
|
||||
|
||||
|
||||
def get():
|
||||
res = get_dict()
|
||||
|
||||
text = json.dumps(res, ensure_ascii=False, indent=4)
|
||||
|
||||
h = hashlib.sha256(text.encode("utf8"))
|
||||
text = text.replace(checksum_token, h.hexdigest())
|
||||
|
||||
return text
|
||||
|
||||
|
||||
re_checksum = re.compile(r'"Checksum": "([0-9a-fA-F]{64})"')
|
||||
|
||||
|
||||
def check(x):
|
||||
m = re.search(re_checksum, x)
|
||||
if not m:
|
||||
return False
|
||||
|
||||
replaced = re.sub(re_checksum, f'"Checksum": "{checksum_token}"', x)
|
||||
|
||||
h = hashlib.sha256(replaced.encode("utf8"))
|
||||
return h.hexdigest() == m.group(1)
|
||||
|
||||
|
||||
def get_dict():
|
||||
ram = psutil.virtual_memory()
|
||||
|
||||
res = {
|
||||
"Platform": platform.platform(),
|
||||
"Python": platform.python_version(),
|
||||
"Version": launch.git_tag(),
|
||||
"Commit": launch.commit_hash(),
|
||||
"Script path": paths_internal.script_path,
|
||||
"Data path": paths_internal.data_path,
|
||||
"Extensions dir": paths_internal.extensions_dir,
|
||||
"Checksum": checksum_token,
|
||||
"Commandline": sys.argv,
|
||||
"Torch env info": get_torch_sysinfo(),
|
||||
"Exceptions": get_exceptions(),
|
||||
"CPU": {
|
||||
"model": platform.processor(),
|
||||
"count logical": psutil.cpu_count(logical=True),
|
||||
"count physical": psutil.cpu_count(logical=False),
|
||||
},
|
||||
"RAM": {
|
||||
x: pretty_bytes(getattr(ram, x, 0)) for x in ["total", "used", "free", "active", "inactive", "buffers", "cached", "shared"] if getattr(ram, x, 0) != 0
|
||||
},
|
||||
"Extensions": get_extensions(enabled=True),
|
||||
"Inactive extensions": get_extensions(enabled=False),
|
||||
"Environment": get_environment(),
|
||||
"Config": get_config(),
|
||||
"Startup": timer.startup_record,
|
||||
"Packages": sorted([f"{pkg.key}=={pkg.version}" for pkg in pkg_resources.working_set]),
|
||||
}
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def format_traceback(tb):
|
||||
return [[f"{x.filename}, line {x.lineno}, {x.name}", x.line] for x in traceback.extract_tb(tb)]
|
||||
|
||||
|
||||
def get_exceptions():
|
||||
try:
|
||||
from modules import errors
|
||||
|
||||
return [{"exception": str(e), "traceback": format_traceback(tb)} for e, tb in reversed(errors.exception_records)]
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
def get_environment():
|
||||
return {k: os.environ[k] for k in sorted(os.environ) if k in environment_whitelist}
|
||||
|
||||
|
||||
re_newline = re.compile(r"\r*\n")
|
||||
|
||||
|
||||
def get_torch_sysinfo():
|
||||
try:
|
||||
import torch.utils.collect_env
|
||||
info = torch.utils.collect_env.get_env_info()._asdict()
|
||||
|
||||
return {k: re.split(re_newline, str(v)) if "\n" in str(v) else v for k, v in info.items()}
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
def get_extensions(*, enabled):
|
||||
|
||||
try:
|
||||
from modules import extensions
|
||||
|
||||
def to_json(x: extensions.Extension):
|
||||
return {
|
||||
"name": x.name,
|
||||
"path": x.path,
|
||||
"version": x.version,
|
||||
"branch": x.branch,
|
||||
"remote": x.remote,
|
||||
}
|
||||
|
||||
return [to_json(x) for x in extensions.extensions if not x.is_builtin and x.enabled == enabled]
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
def get_config():
|
||||
try:
|
||||
from modules import shared
|
||||
return shared.opts.data
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
|
@ -77,27 +77,27 @@ def focal_point(im, settings):
|
|||
pois = []
|
||||
|
||||
weight_pref_total = 0
|
||||
if len(corner_points) > 0:
|
||||
if corner_points:
|
||||
weight_pref_total += settings.corner_points_weight
|
||||
if len(entropy_points) > 0:
|
||||
if entropy_points:
|
||||
weight_pref_total += settings.entropy_points_weight
|
||||
if len(face_points) > 0:
|
||||
if face_points:
|
||||
weight_pref_total += settings.face_points_weight
|
||||
|
||||
corner_centroid = None
|
||||
if len(corner_points) > 0:
|
||||
if corner_points:
|
||||
corner_centroid = centroid(corner_points)
|
||||
corner_centroid.weight = settings.corner_points_weight / weight_pref_total
|
||||
pois.append(corner_centroid)
|
||||
|
||||
entropy_centroid = None
|
||||
if len(entropy_points) > 0:
|
||||
if entropy_points:
|
||||
entropy_centroid = centroid(entropy_points)
|
||||
entropy_centroid.weight = settings.entropy_points_weight / weight_pref_total
|
||||
pois.append(entropy_centroid)
|
||||
|
||||
face_centroid = None
|
||||
if len(face_points) > 0:
|
||||
if face_points:
|
||||
face_centroid = centroid(face_points)
|
||||
face_centroid.weight = settings.face_points_weight / weight_pref_total
|
||||
pois.append(face_centroid)
|
||||
|
|
@ -187,7 +187,7 @@ def image_face_points(im, settings):
|
|||
except Exception:
|
||||
continue
|
||||
|
||||
if len(faces) > 0:
|
||||
if faces:
|
||||
rects = [[f[0], f[1], f[0] + f[2], f[1] + f[3]] for f in faces]
|
||||
return [PointOfInterest((r[0] +r[2]) // 2, (r[1] + r[3]) // 2, size=abs(r[0]-r[2]), weight=1/len(rects)) for r in rects]
|
||||
return []
|
||||
|
|
@ -298,8 +298,7 @@ def download_and_cache_models(dirname):
|
|||
download_url = 'https://github.com/opencv/opencv_zoo/blob/91fb0290f50896f38a0ab1e558b74b16bc009428/models/face_detection_yunet/face_detection_yunet_2022mar.onnx?raw=true'
|
||||
model_file_name = 'face_detection_yunet.onnx'
|
||||
|
||||
if not os.path.exists(dirname):
|
||||
os.makedirs(dirname)
|
||||
os.makedirs(dirname, exist_ok=True)
|
||||
|
||||
cache_file = os.path.join(dirname, model_file_name)
|
||||
if not os.path.exists(cache_file):
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class DatasetEntry:
|
|||
|
||||
class PersonalizedBase(Dataset):
|
||||
def __init__(self, data_root, width, height, repeats, flip_p=0.5, placeholder_token="*", model=None, cond_model=None, device=None, template_file=None, include_cond=False, batch_size=1, gradient_step=1, shuffle_tags=False, tag_drop_out=0, latent_sampling_method='once', varsize=False, use_weight=False):
|
||||
re_word = re.compile(shared.opts.dataset_filename_word_regex) if len(shared.opts.dataset_filename_word_regex) > 0 else None
|
||||
re_word = re.compile(shared.opts.dataset_filename_word_regex) if shared.opts.dataset_filename_word_regex else None
|
||||
|
||||
self.placeholder_token = placeholder_token
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ def save_pic_with_caption(image, index, params: PreprocessParams, existing_capti
|
|||
caption += shared.interrogator.generate_caption(image)
|
||||
|
||||
if params.process_caption_deepbooru:
|
||||
if len(caption) > 0:
|
||||
if caption:
|
||||
caption += ", "
|
||||
caption += deepbooru.model.tag_multi(image)
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ def save_pic_with_caption(image, index, params: PreprocessParams, existing_capti
|
|||
|
||||
caption = caption.strip()
|
||||
|
||||
if len(caption) > 0:
|
||||
if caption:
|
||||
with open(os.path.join(params.dstdir, f"{basename}.txt"), "w", encoding="utf8") as file:
|
||||
file.write(caption)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import os
|
||||
import sys
|
||||
import traceback
|
||||
from collections import namedtuple
|
||||
|
||||
import torch
|
||||
|
|
@ -14,7 +12,7 @@ import numpy as np
|
|||
from PIL import Image, PngImagePlugin
|
||||
from torch.utils.tensorboard import SummaryWriter
|
||||
|
||||
from modules import shared, devices, sd_hijack, processing, sd_models, images, sd_samplers, sd_hijack_checkpoint
|
||||
from modules import shared, devices, sd_hijack, processing, sd_models, images, sd_samplers, sd_hijack_checkpoint, errors
|
||||
import modules.textual_inversion.dataset
|
||||
from modules.textual_inversion.learn_schedule import LearnRateScheduler
|
||||
|
||||
|
|
@ -120,16 +118,29 @@ class EmbeddingDatabase:
|
|||
self.embedding_dirs.clear()
|
||||
|
||||
def register_embedding(self, embedding, model):
|
||||
self.word_embeddings[embedding.name] = embedding
|
||||
|
||||
ids = model.cond_stage_model.tokenize([embedding.name])[0]
|
||||
return self.register_embedding_by_name(embedding, model, embedding.name)
|
||||
|
||||
def register_embedding_by_name(self, embedding, model, name):
|
||||
ids = model.cond_stage_model.tokenize([name])[0]
|
||||
first_id = ids[0]
|
||||
if first_id not in self.ids_lookup:
|
||||
self.ids_lookup[first_id] = []
|
||||
|
||||
self.ids_lookup[first_id] = sorted(self.ids_lookup[first_id] + [(ids, embedding)], key=lambda x: len(x[0]), reverse=True)
|
||||
|
||||
if name in self.word_embeddings:
|
||||
# remove old one from the lookup list
|
||||
lookup = [x for x in self.ids_lookup[first_id] if x[1].name!=name]
|
||||
else:
|
||||
lookup = self.ids_lookup[first_id]
|
||||
if embedding is not None:
|
||||
lookup += [(ids, embedding)]
|
||||
self.ids_lookup[first_id] = sorted(lookup, key=lambda x: len(x[0]), reverse=True)
|
||||
if embedding is None:
|
||||
# unregister embedding with specified name
|
||||
if name in self.word_embeddings:
|
||||
del self.word_embeddings[name]
|
||||
if len(self.ids_lookup[first_id])==0:
|
||||
del self.ids_lookup[first_id]
|
||||
return None
|
||||
self.word_embeddings[name] = embedding
|
||||
return embedding
|
||||
|
||||
def get_expected_shape(self):
|
||||
|
|
@ -207,8 +218,7 @@ class EmbeddingDatabase:
|
|||
|
||||
self.load_from_file(fullfn, fn)
|
||||
except Exception:
|
||||
print(f"Error loading embedding {fn}:", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error loading embedding {fn}", exc_info=True)
|
||||
continue
|
||||
|
||||
def load_textual_inversion_embeddings(self, force_reload=False):
|
||||
|
|
@ -241,7 +251,7 @@ class EmbeddingDatabase:
|
|||
if self.previously_displayed_embeddings != displayed_embeddings:
|
||||
self.previously_displayed_embeddings = displayed_embeddings
|
||||
print(f"Textual inversion embeddings loaded({len(self.word_embeddings)}): {', '.join(self.word_embeddings.keys())}")
|
||||
if len(self.skipped_embeddings) > 0:
|
||||
if self.skipped_embeddings:
|
||||
print(f"Textual inversion embeddings skipped({len(self.skipped_embeddings)}): {', '.join(self.skipped_embeddings.keys())}")
|
||||
|
||||
def find_embedding_at_position(self, tokens, offset):
|
||||
|
|
@ -632,8 +642,7 @@ Last saved image: {html.escape(last_saved_image)}<br/>
|
|||
filename = os.path.join(shared.cmd_opts.embeddings_dir, f'{embedding_name}.pt')
|
||||
save_embedding(embedding, optimizer, checkpoint, embedding_name, filename, remove_cached_checksum=True)
|
||||
except Exception:
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
pass
|
||||
errors.report("Error training embedding", exc_info=True)
|
||||
finally:
|
||||
pbar.leave = False
|
||||
pbar.close()
|
||||
|
|
|
|||
|
|
@ -1,11 +1,30 @@
|
|||
import time
|
||||
|
||||
|
||||
class TimerSubcategory:
|
||||
def __init__(self, timer, category):
|
||||
self.timer = timer
|
||||
self.category = category
|
||||
self.start = None
|
||||
self.original_base_category = timer.base_category
|
||||
|
||||
def __enter__(self):
|
||||
self.start = time.time()
|
||||
self.timer.base_category = self.original_base_category + self.category + "/"
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
elapsed_for_subcategroy = time.time() - self.start
|
||||
self.timer.base_category = self.original_base_category
|
||||
self.timer.add_time_to_record(self.original_base_category + self.category, elapsed_for_subcategroy)
|
||||
self.timer.record(self.category)
|
||||
|
||||
|
||||
class Timer:
|
||||
def __init__(self):
|
||||
self.start = time.time()
|
||||
self.records = {}
|
||||
self.total = 0
|
||||
self.base_category = ''
|
||||
|
||||
def elapsed(self):
|
||||
end = time.time()
|
||||
|
|
@ -13,18 +32,29 @@ class Timer:
|
|||
self.start = end
|
||||
return res
|
||||
|
||||
def record(self, category, extra_time=0):
|
||||
e = self.elapsed()
|
||||
def add_time_to_record(self, category, amount):
|
||||
if category not in self.records:
|
||||
self.records[category] = 0
|
||||
|
||||
self.records[category] += e + extra_time
|
||||
self.records[category] += amount
|
||||
|
||||
def record(self, category, extra_time=0):
|
||||
e = self.elapsed()
|
||||
|
||||
self.add_time_to_record(self.base_category + category, e + extra_time)
|
||||
|
||||
self.total += e + extra_time
|
||||
|
||||
def subcategory(self, name):
|
||||
self.elapsed()
|
||||
|
||||
subcat = TimerSubcategory(self, name)
|
||||
return subcat
|
||||
|
||||
def summary(self):
|
||||
res = f"{self.total:.1f}s"
|
||||
|
||||
additions = [x for x in self.records.items() if x[1] >= 0.1]
|
||||
additions = [(category, time_taken) for category, time_taken in self.records.items() if time_taken >= 0.1 and '/' not in category]
|
||||
if not additions:
|
||||
return res
|
||||
|
||||
|
|
@ -34,5 +64,13 @@ class Timer:
|
|||
|
||||
return res
|
||||
|
||||
def dump(self):
|
||||
return {'total': self.total, 'records': self.records}
|
||||
|
||||
def reset(self):
|
||||
self.__init__()
|
||||
|
||||
|
||||
startup_timer = Timer()
|
||||
|
||||
startup_record = None
|
||||
|
|
|
|||
396
modules/ui.py
396
modules/ui.py
|
|
@ -1,21 +1,23 @@
|
|||
import datetime
|
||||
import json
|
||||
import mimetypes
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
from functools import reduce
|
||||
import warnings
|
||||
|
||||
import gradio as gr
|
||||
import gradio.routes
|
||||
import gradio.utils
|
||||
import numpy as np
|
||||
from PIL import Image, PngImagePlugin # noqa: F401
|
||||
from modules.call_queue import wrap_gradio_gpu_call, wrap_queued_call, wrap_gradio_call
|
||||
|
||||
from modules import sd_hijack, sd_models, localization, script_callbacks, ui_extensions, deepbooru, sd_vae, extra_networks, ui_common, ui_postprocessing, progress, ui_loadsave
|
||||
from modules import sd_hijack, sd_models, script_callbacks, ui_extensions, deepbooru, sd_vae, extra_networks, ui_common, ui_postprocessing, progress, ui_loadsave, errors, shared_items, ui_settings, timer, sysinfo
|
||||
from modules.ui_components import FormRow, FormGroup, ToolButton, FormHTML
|
||||
from modules.paths import script_path, data_path
|
||||
from modules.paths import script_path
|
||||
from modules.ui_common import create_refresh_button
|
||||
from modules.ui_gradio_extensions import reload_javascript
|
||||
|
||||
|
||||
from modules.shared import opts, cmd_opts
|
||||
|
||||
|
|
@ -35,6 +37,8 @@ import modules.hypernetworks.ui
|
|||
from modules.generation_parameters_copypaste import image_from_url_text
|
||||
import modules.extras
|
||||
|
||||
create_setting_component = ui_settings.create_setting_component
|
||||
|
||||
warnings.filterwarnings("default" if opts.show_warnings else "ignore", category=UserWarning)
|
||||
|
||||
# this is a fix for Windows users. Without it, javascript files will be served with text/html content-type and the browser will not show any UI
|
||||
|
|
@ -76,6 +80,7 @@ extra_networks_symbol = '\U0001F3B4' # 🎴
|
|||
switch_values_symbol = '\U000021C5' # ⇅
|
||||
restore_progress_symbol = '\U0001F300' # 🌀
|
||||
detect_image_size_symbol = '\U0001F4D0' # 📐
|
||||
up_down_symbol = '\u2195\ufe0f' # ↕️
|
||||
|
||||
|
||||
def plaintext_to_html(text):
|
||||
|
|
@ -231,9 +236,8 @@ def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info:
|
|||
res = all_seeds[index if 0 <= index < len(all_seeds) else 0]
|
||||
|
||||
except json.decoder.JSONDecodeError:
|
||||
if gen_info_string != '':
|
||||
print("Error parsing JSON generation info:", file=sys.stderr)
|
||||
print(gen_info_string, file=sys.stderr)
|
||||
if gen_info_string:
|
||||
errors.report(f"Error parsing JSON generation info: {gen_info_string}")
|
||||
|
||||
return [res, gr_show(False)]
|
||||
|
||||
|
|
@ -368,25 +372,6 @@ def apply_setting(key, value):
|
|||
return getattr(opts, key)
|
||||
|
||||
|
||||
def create_refresh_button(refresh_component, refresh_method, refreshed_args, elem_id):
|
||||
def refresh():
|
||||
refresh_method()
|
||||
args = refreshed_args() if callable(refreshed_args) else refreshed_args
|
||||
|
||||
for k, v in args.items():
|
||||
setattr(refresh_component, k, v)
|
||||
|
||||
return gr.update(**(args or {}))
|
||||
|
||||
refresh_button = ToolButton(value=refresh_symbol, elem_id=elem_id)
|
||||
refresh_button.click(
|
||||
fn=refresh,
|
||||
inputs=[],
|
||||
outputs=[refresh_component]
|
||||
)
|
||||
return refresh_button
|
||||
|
||||
|
||||
def create_output_panel(tabname, outdir):
|
||||
return ui_common.create_output_panel(tabname, outdir)
|
||||
|
||||
|
|
@ -405,27 +390,17 @@ def create_sampler_and_steps_selection(choices, tabname):
|
|||
|
||||
|
||||
def ordered_ui_categories():
|
||||
user_order = {x.strip(): i * 2 + 1 for i, x in enumerate(shared.opts.ui_reorder.split(","))}
|
||||
user_order = {x.strip(): i * 2 + 1 for i, x in enumerate(shared.opts.ui_reorder_list)}
|
||||
|
||||
for _, category in sorted(enumerate(shared.ui_reorder_categories), key=lambda x: user_order.get(x[1], x[0] * 2 + 0)):
|
||||
for _, category in sorted(enumerate(shared_items.ui_reorder_categories()), key=lambda x: user_order.get(x[1], x[0] * 2 + 0)):
|
||||
yield category
|
||||
|
||||
|
||||
def get_value_for_setting(key):
|
||||
value = getattr(opts, key)
|
||||
|
||||
info = opts.data_labels[key]
|
||||
args = info.component_args() if callable(info.component_args) else info.component_args or {}
|
||||
args = {k: v for k, v in args.items() if k not in {'precision'}}
|
||||
|
||||
return gr.update(value=value, **args)
|
||||
|
||||
|
||||
def create_override_settings_dropdown(tabname, row):
|
||||
dropdown = gr.Dropdown([], label="Override settings", visible=False, elem_id=f"{tabname}_override_settings", multiselect=True)
|
||||
|
||||
dropdown.change(
|
||||
fn=lambda x: gr.Dropdown.update(visible=len(x) > 0),
|
||||
fn=lambda x: gr.Dropdown.update(visible=bool(x)),
|
||||
inputs=[dropdown],
|
||||
outputs=[dropdown],
|
||||
)
|
||||
|
|
@ -456,6 +431,8 @@ def create_ui():
|
|||
|
||||
with gr.Row().style(equal_height=False):
|
||||
with gr.Column(variant='compact', elem_id="txt2img_settings"):
|
||||
modules.scripts.scripts_txt2img.prepare_ui()
|
||||
|
||||
for category in ordered_ui_categories():
|
||||
if category == "sampler":
|
||||
steps, sampler_index = create_sampler_and_steps_selection(samplers, "txt2img")
|
||||
|
|
@ -524,6 +501,9 @@ def create_ui():
|
|||
with FormGroup(elem_id="txt2img_script_container"):
|
||||
custom_inputs = modules.scripts.scripts_txt2img.setup_ui()
|
||||
|
||||
else:
|
||||
modules.scripts.scripts_txt2img.setup_ui_for_section(category)
|
||||
|
||||
hr_resolution_preview_inputs = [enable_hr, width, height, hr_scale, hr_resize_x, hr_resize_y]
|
||||
|
||||
for component in hr_resolution_preview_inputs:
|
||||
|
|
@ -616,7 +596,8 @@ def create_ui():
|
|||
outputs=[
|
||||
txt2img_prompt,
|
||||
txt_prompt_img
|
||||
]
|
||||
],
|
||||
show_progress=False,
|
||||
)
|
||||
|
||||
enable_hr.change(
|
||||
|
|
@ -641,6 +622,7 @@ def create_ui():
|
|||
(subseed_strength, "Variation seed strength"),
|
||||
(seed_resize_from_w, "Seed resize from-1"),
|
||||
(seed_resize_from_h, "Seed resize from-2"),
|
||||
(txt2img_prompt_styles, lambda d: d["Styles array"] if isinstance(d.get("Styles array"), list) else gr.update()),
|
||||
(denoising_strength, "Denoising strength"),
|
||||
(enable_hr, lambda d: "Denoising strength" in d),
|
||||
(hr_options, lambda d: gr.Row.update(visible="Denoising strength" in d)),
|
||||
|
|
@ -779,6 +761,8 @@ def create_ui():
|
|||
with FormRow():
|
||||
resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", choices=["Just resize", "Crop and resize", "Resize and fill", "Just resize (latent upscale)"], type="index", value="Just resize")
|
||||
|
||||
modules.scripts.scripts_img2img.prepare_ui()
|
||||
|
||||
for category in ordered_ui_categories():
|
||||
if category == "sampler":
|
||||
steps, sampler_index = create_sampler_and_steps_selection(samplers_for_img2img, "img2img")
|
||||
|
|
@ -789,7 +773,7 @@ def create_ui():
|
|||
selected_scale_tab = gr.State(value=0)
|
||||
|
||||
with gr.Tabs():
|
||||
with gr.Tab(label="Resize to") as tab_scale_to:
|
||||
with gr.Tab(label="Resize to", elem_id="img2img_tab_resize_to") as tab_scale_to:
|
||||
with FormRow():
|
||||
with gr.Column(elem_id="img2img_column_size", scale=4):
|
||||
width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="img2img_width")
|
||||
|
|
@ -798,7 +782,7 @@ def create_ui():
|
|||
res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="img2img_res_switch_btn")
|
||||
detect_image_size_btn = ToolButton(value=detect_image_size_symbol, elem_id="img2img_detect_image_size_btn")
|
||||
|
||||
with gr.Tab(label="Resize by") as tab_scale_by:
|
||||
with gr.Tab(label="Resize by", elem_id="img2img_tab_resize_by") as tab_scale_by:
|
||||
scale_by = gr.Slider(minimum=0.05, maximum=4.0, step=0.05, label="Scale", value=1.0, elem_id="img2img_scale")
|
||||
|
||||
with FormRow():
|
||||
|
|
@ -888,6 +872,8 @@ def create_ui():
|
|||
inputs=[],
|
||||
outputs=[inpaint_controls, mask_alpha],
|
||||
)
|
||||
else:
|
||||
modules.scripts.scripts_img2img.setup_ui_for_section(category)
|
||||
|
||||
img2img_gallery, generation_info, html_info, html_log = create_output_panel("img2img", opts.outdir_img2img_samples)
|
||||
|
||||
|
|
@ -902,7 +888,8 @@ def create_ui():
|
|||
outputs=[
|
||||
img2img_prompt,
|
||||
img2img_prompt_img
|
||||
]
|
||||
],
|
||||
show_progress=False,
|
||||
)
|
||||
|
||||
img2img_args = dict(
|
||||
|
|
@ -1051,6 +1038,7 @@ def create_ui():
|
|||
(subseed_strength, "Variation seed strength"),
|
||||
(seed_resize_from_w, "Seed resize from-1"),
|
||||
(seed_resize_from_h, "Seed resize from-2"),
|
||||
(img2img_prompt_styles, lambda d: d["Styles array"] if isinstance(d.get("Styles array"), list) else gr.update()),
|
||||
(denoising_strength, "Denoising strength"),
|
||||
(mask_blur, "Mask blur"),
|
||||
*modules.scripts.scripts_img2img.infotext_fields
|
||||
|
|
@ -1460,195 +1448,10 @@ def create_ui():
|
|||
outputs=[],
|
||||
)
|
||||
|
||||
def create_setting_component(key, is_quicksettings=False):
|
||||
def fun():
|
||||
return opts.data[key] if key in opts.data else opts.data_labels[key].default
|
||||
|
||||
info = opts.data_labels[key]
|
||||
t = type(info.default)
|
||||
|
||||
args = info.component_args() if callable(info.component_args) else info.component_args
|
||||
|
||||
if info.component is not None:
|
||||
comp = info.component
|
||||
elif t == str:
|
||||
comp = gr.Textbox
|
||||
elif t == int:
|
||||
comp = gr.Number
|
||||
elif t == bool:
|
||||
comp = gr.Checkbox
|
||||
else:
|
||||
raise Exception(f'bad options item type: {t} for key {key}')
|
||||
|
||||
elem_id = f"setting_{key}"
|
||||
|
||||
if info.refresh is not None:
|
||||
if is_quicksettings:
|
||||
res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
|
||||
create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}")
|
||||
else:
|
||||
with FormRow():
|
||||
res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
|
||||
create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}")
|
||||
else:
|
||||
res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
|
||||
|
||||
return res
|
||||
|
||||
loadsave = ui_loadsave.UiLoadsave(cmd_opts.ui_config_file)
|
||||
|
||||
components = []
|
||||
component_dict = {}
|
||||
shared.settings_components = component_dict
|
||||
|
||||
script_callbacks.ui_settings_callback()
|
||||
opts.reorder()
|
||||
|
||||
def run_settings(*args):
|
||||
changed = []
|
||||
|
||||
for key, value, comp in zip(opts.data_labels.keys(), args, components):
|
||||
assert comp == dummy_component or opts.same_type(value, opts.data_labels[key].default), f"Bad value for setting {key}: {value}; expecting {type(opts.data_labels[key].default).__name__}"
|
||||
|
||||
for key, value, comp in zip(opts.data_labels.keys(), args, components):
|
||||
if comp == dummy_component:
|
||||
continue
|
||||
|
||||
if opts.set(key, value):
|
||||
changed.append(key)
|
||||
|
||||
try:
|
||||
opts.save(shared.config_filename)
|
||||
except RuntimeError:
|
||||
return opts.dumpjson(), f'{len(changed)} settings changed without save: {", ".join(changed)}.'
|
||||
return opts.dumpjson(), f'{len(changed)} settings changed{": " if len(changed) > 0 else ""}{", ".join(changed)}.'
|
||||
|
||||
def run_settings_single(value, key):
|
||||
if not opts.same_type(value, opts.data_labels[key].default):
|
||||
return gr.update(visible=True), opts.dumpjson()
|
||||
|
||||
if not opts.set(key, value):
|
||||
return gr.update(value=getattr(opts, key)), opts.dumpjson()
|
||||
|
||||
opts.save(shared.config_filename)
|
||||
|
||||
return get_value_for_setting(key), opts.dumpjson()
|
||||
|
||||
with gr.Blocks(analytics_enabled=False) as settings_interface:
|
||||
with gr.Row():
|
||||
with gr.Column(scale=6):
|
||||
settings_submit = gr.Button(value="Apply settings", variant='primary', elem_id="settings_submit")
|
||||
with gr.Column():
|
||||
restart_gradio = gr.Button(value='Reload UI', variant='primary', elem_id="settings_restart_gradio")
|
||||
|
||||
result = gr.HTML(elem_id="settings_result")
|
||||
|
||||
quicksettings_names = opts.quicksettings_list
|
||||
quicksettings_names = {x: i for i, x in enumerate(quicksettings_names) if x != 'quicksettings'}
|
||||
|
||||
quicksettings_list = []
|
||||
|
||||
previous_section = None
|
||||
current_tab = None
|
||||
current_row = None
|
||||
with gr.Tabs(elem_id="settings"):
|
||||
for i, (k, item) in enumerate(opts.data_labels.items()):
|
||||
section_must_be_skipped = item.section[0] is None
|
||||
|
||||
if previous_section != item.section and not section_must_be_skipped:
|
||||
elem_id, text = item.section
|
||||
|
||||
if current_tab is not None:
|
||||
current_row.__exit__()
|
||||
current_tab.__exit__()
|
||||
|
||||
gr.Group()
|
||||
current_tab = gr.TabItem(elem_id=f"settings_{elem_id}", label=text)
|
||||
current_tab.__enter__()
|
||||
current_row = gr.Column(variant='compact')
|
||||
current_row.__enter__()
|
||||
|
||||
previous_section = item.section
|
||||
|
||||
if k in quicksettings_names and not shared.cmd_opts.freeze_settings:
|
||||
quicksettings_list.append((i, k, item))
|
||||
components.append(dummy_component)
|
||||
elif section_must_be_skipped:
|
||||
components.append(dummy_component)
|
||||
else:
|
||||
component = create_setting_component(k)
|
||||
component_dict[k] = component
|
||||
components.append(component)
|
||||
|
||||
if current_tab is not None:
|
||||
current_row.__exit__()
|
||||
current_tab.__exit__()
|
||||
|
||||
with gr.TabItem("Defaults", id="defaults", elem_id="settings_tab_defaults"):
|
||||
loadsave.create_ui()
|
||||
|
||||
with gr.TabItem("Actions", id="actions", elem_id="settings_tab_actions"):
|
||||
request_notifications = gr.Button(value='Request browser notifications', elem_id="request_notifications")
|
||||
download_localization = gr.Button(value='Download localization template', elem_id="download_localization")
|
||||
reload_script_bodies = gr.Button(value='Reload custom script bodies (No ui updates, No restart)', variant='secondary', elem_id="settings_reload_script_bodies")
|
||||
with gr.Row():
|
||||
unload_sd_model = gr.Button(value='Unload SD checkpoint to free VRAM', elem_id="sett_unload_sd_model")
|
||||
reload_sd_model = gr.Button(value='Reload the last SD checkpoint back into VRAM', elem_id="sett_reload_sd_model")
|
||||
|
||||
with gr.TabItem("Licenses", id="licenses", elem_id="settings_tab_licenses"):
|
||||
gr.HTML(shared.html("licenses.html"), elem_id="licenses")
|
||||
|
||||
gr.Button(value="Show all pages", elem_id="settings_show_all_pages")
|
||||
|
||||
|
||||
def unload_sd_weights():
|
||||
modules.sd_models.unload_model_weights()
|
||||
|
||||
def reload_sd_weights():
|
||||
modules.sd_models.reload_model_weights()
|
||||
|
||||
unload_sd_model.click(
|
||||
fn=unload_sd_weights,
|
||||
inputs=[],
|
||||
outputs=[]
|
||||
)
|
||||
|
||||
reload_sd_model.click(
|
||||
fn=reload_sd_weights,
|
||||
inputs=[],
|
||||
outputs=[]
|
||||
)
|
||||
|
||||
request_notifications.click(
|
||||
fn=lambda: None,
|
||||
inputs=[],
|
||||
outputs=[],
|
||||
_js='function(){}'
|
||||
)
|
||||
|
||||
download_localization.click(
|
||||
fn=lambda: None,
|
||||
inputs=[],
|
||||
outputs=[],
|
||||
_js='download_localization'
|
||||
)
|
||||
|
||||
def reload_scripts():
|
||||
modules.scripts.reload_script_body_only()
|
||||
reload_javascript() # need to refresh the html page
|
||||
|
||||
reload_script_bodies.click(
|
||||
fn=reload_scripts,
|
||||
inputs=[],
|
||||
outputs=[]
|
||||
)
|
||||
|
||||
restart_gradio.click(
|
||||
fn=shared.state.request_restart,
|
||||
_js='restart_reload',
|
||||
inputs=[],
|
||||
outputs=[],
|
||||
)
|
||||
settings = ui_settings.UiSettings()
|
||||
settings.create_ui(loadsave, dummy_component)
|
||||
|
||||
interfaces = [
|
||||
(txt2img_interface, "txt2img", "txt2img"),
|
||||
|
|
@ -1660,7 +1463,7 @@ def create_ui():
|
|||
]
|
||||
|
||||
interfaces += script_callbacks.ui_tabs_callback()
|
||||
interfaces += [(settings_interface, "Settings", "settings")]
|
||||
interfaces += [(settings.interface, "Settings", "settings")]
|
||||
|
||||
extensions_interface = ui_extensions.create_ui()
|
||||
interfaces += [(extensions_interface, "Extensions", "extensions")]
|
||||
|
|
@ -1670,10 +1473,7 @@ def create_ui():
|
|||
shared.tab_names.append(label)
|
||||
|
||||
with gr.Blocks(theme=shared.gradio_theme, analytics_enabled=False, title="Stable Diffusion") as demo:
|
||||
with gr.Row(elem_id="quicksettings", variant="compact"):
|
||||
for _i, k, _item in sorted(quicksettings_list, key=lambda x: quicksettings_names.get(x[1], x[0])):
|
||||
component = create_setting_component(k, is_quicksettings=True)
|
||||
component_dict[k] = component
|
||||
settings.add_quicksettings()
|
||||
|
||||
parameters_copypaste.connect_paste_params_buttons()
|
||||
|
||||
|
|
@ -1701,58 +1501,20 @@ def create_ui():
|
|||
gr.Audio(interactive=False, value=os.path.join(script_path, "notification.mp3"), elem_id="audio_notification", visible=False)
|
||||
|
||||
footer = shared.html("footer.html")
|
||||
footer = footer.format(versions=versions_html())
|
||||
footer = footer.format(versions=versions_html(), api_docs="/docs" if shared.cmd_opts.api else "https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/API")
|
||||
gr.HTML(footer, elem_id="footer")
|
||||
|
||||
text_settings = gr.Textbox(elem_id="settings_json", value=lambda: opts.dumpjson(), visible=False)
|
||||
settings_submit.click(
|
||||
fn=wrap_gradio_call(run_settings, extra_outputs=[gr.update()]),
|
||||
inputs=components,
|
||||
outputs=[text_settings, result],
|
||||
)
|
||||
|
||||
for _i, k, _item in quicksettings_list:
|
||||
component = component_dict[k]
|
||||
info = opts.data_labels[k]
|
||||
|
||||
change_handler = component.release if hasattr(component, 'release') else component.change
|
||||
change_handler(
|
||||
fn=lambda value, k=k: run_settings_single(value, key=k),
|
||||
inputs=[component],
|
||||
outputs=[component, text_settings],
|
||||
show_progress=info.refresh is not None,
|
||||
)
|
||||
settings.add_functionality(demo)
|
||||
|
||||
update_image_cfg_scale_visibility = lambda: gr.update(visible=shared.sd_model and shared.sd_model.cond_stage_key == "edit")
|
||||
text_settings.change(fn=update_image_cfg_scale_visibility, inputs=[], outputs=[image_cfg_scale])
|
||||
settings.text_settings.change(fn=update_image_cfg_scale_visibility, inputs=[], outputs=[image_cfg_scale])
|
||||
demo.load(fn=update_image_cfg_scale_visibility, inputs=[], outputs=[image_cfg_scale])
|
||||
|
||||
button_set_checkpoint = gr.Button('Change checkpoint', elem_id='change_checkpoint', visible=False)
|
||||
button_set_checkpoint.click(
|
||||
fn=lambda value, _: run_settings_single(value, key='sd_model_checkpoint'),
|
||||
_js="function(v){ var res = desiredCheckpointName; desiredCheckpointName = ''; return [res || v, null]; }",
|
||||
inputs=[component_dict['sd_model_checkpoint'], dummy_component],
|
||||
outputs=[component_dict['sd_model_checkpoint'], text_settings],
|
||||
)
|
||||
|
||||
component_keys = [k for k in opts.data_labels.keys() if k in component_dict]
|
||||
|
||||
def get_settings_values():
|
||||
return [get_value_for_setting(key) for key in component_keys]
|
||||
|
||||
demo.load(
|
||||
fn=get_settings_values,
|
||||
inputs=[],
|
||||
outputs=[component_dict[k] for k in component_keys],
|
||||
queue=False,
|
||||
)
|
||||
|
||||
def modelmerger(*args):
|
||||
try:
|
||||
results = modules.extras.run_modelmerger(*args)
|
||||
except Exception as e:
|
||||
print("Error loading/saving model file:", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report("Error loading/saving model file", exc_info=True)
|
||||
modules.sd_models.list_models() # to remove the potentially missing models from the list
|
||||
return [*[gr.Dropdown.update(choices=modules.sd_models.checkpoint_tiles()) for _ in range(4)], f"Error merging checkpoints: {e}"]
|
||||
return results
|
||||
|
|
@ -1780,7 +1542,7 @@ def create_ui():
|
|||
primary_model_name,
|
||||
secondary_model_name,
|
||||
tertiary_model_name,
|
||||
component_dict['sd_model_checkpoint'],
|
||||
settings.component_dict['sd_model_checkpoint'],
|
||||
modelmerger_result,
|
||||
]
|
||||
)
|
||||
|
|
@ -1794,70 +1556,6 @@ def create_ui():
|
|||
return demo
|
||||
|
||||
|
||||
def webpath(fn):
|
||||
if fn.startswith(script_path):
|
||||
web_path = os.path.relpath(fn, script_path).replace('\\', '/')
|
||||
else:
|
||||
web_path = os.path.abspath(fn)
|
||||
|
||||
return f'file={web_path}?{os.path.getmtime(fn)}'
|
||||
|
||||
|
||||
def javascript_html():
|
||||
# Ensure localization is in `window` before scripts
|
||||
head = f'<script type="text/javascript">{localization.localization_js(shared.opts.localization)}</script>\n'
|
||||
|
||||
script_js = os.path.join(script_path, "script.js")
|
||||
head += f'<script type="text/javascript" src="{webpath(script_js)}"></script>\n'
|
||||
|
||||
for script in modules.scripts.list_scripts("javascript", ".js"):
|
||||
head += f'<script type="text/javascript" src="{webpath(script.path)}"></script>\n'
|
||||
|
||||
for script in modules.scripts.list_scripts("javascript", ".mjs"):
|
||||
head += f'<script type="module" src="{webpath(script.path)}"></script>\n'
|
||||
|
||||
if cmd_opts.theme:
|
||||
head += f'<script type="text/javascript">set_theme(\"{cmd_opts.theme}\");</script>\n'
|
||||
|
||||
return head
|
||||
|
||||
|
||||
def css_html():
|
||||
head = ""
|
||||
|
||||
def stylesheet(fn):
|
||||
return f'<link rel="stylesheet" property="stylesheet" href="{webpath(fn)}">'
|
||||
|
||||
for cssfile in modules.scripts.list_files_with_name("style.css"):
|
||||
if not os.path.isfile(cssfile):
|
||||
continue
|
||||
|
||||
head += stylesheet(cssfile)
|
||||
|
||||
if os.path.exists(os.path.join(data_path, "user.css")):
|
||||
head += stylesheet(os.path.join(data_path, "user.css"))
|
||||
|
||||
return head
|
||||
|
||||
|
||||
def reload_javascript():
|
||||
js = javascript_html()
|
||||
css = css_html()
|
||||
|
||||
def template_response(*args, **kwargs):
|
||||
res = shared.GradioTemplateResponseOriginal(*args, **kwargs)
|
||||
res.body = res.body.replace(b'</head>', f'{js}</head>'.encode("utf8"))
|
||||
res.body = res.body.replace(b'</body>', f'{css}</body>'.encode("utf8"))
|
||||
res.init_headers()
|
||||
return res
|
||||
|
||||
gradio.routes.templates.TemplateResponse = template_response
|
||||
|
||||
|
||||
if not hasattr(shared, 'GradioTemplateResponseOriginal'):
|
||||
shared.GradioTemplateResponseOriginal = gradio.routes.templates.TemplateResponse
|
||||
|
||||
|
||||
def versions_html():
|
||||
import torch
|
||||
import launch
|
||||
|
|
@ -1901,3 +1599,17 @@ def setup_ui_api(app):
|
|||
app.add_api_route("/internal/quicksettings-hint", quicksettings_hint, methods=["GET"], response_model=List[QuicksettingsHint])
|
||||
|
||||
app.add_api_route("/internal/ping", lambda: {}, methods=["GET"])
|
||||
|
||||
app.add_api_route("/internal/profile-startup", lambda: timer.startup_record, methods=["GET"])
|
||||
|
||||
def download_sysinfo(attachment=False):
|
||||
from fastapi.responses import PlainTextResponse
|
||||
|
||||
text = sysinfo.get()
|
||||
filename = f"sysinfo-{datetime.datetime.utcnow().strftime('%Y-%m-%d-%H-%M')}.txt"
|
||||
|
||||
return PlainTextResponse(text, headers={'Content-Disposition': f'{"attachment" if attachment else "inline"}; filename="{filename}"'})
|
||||
|
||||
app.add_api_route("/internal/sysinfo", download_sysinfo, methods=["GET"])
|
||||
app.add_api_route("/internal/sysinfo-download", lambda: download_sysinfo(attachment=True), methods=["GET"])
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,11 @@ import subprocess as sp
|
|||
from modules import call_queue, shared
|
||||
from modules.generation_parameters_copypaste import image_from_url_text
|
||||
import modules.images
|
||||
from modules.ui_components import ToolButton
|
||||
|
||||
|
||||
folder_symbol = '\U0001f4c2' # 📂
|
||||
refresh_symbol = '\U0001f504' # 🔄
|
||||
|
||||
|
||||
def update_generation_info(generation_info, html_info, img_index):
|
||||
|
|
@ -50,9 +53,10 @@ def save_files(js_data, images, do_make_zip, index):
|
|||
save_to_dirs = shared.opts.use_save_to_dirs_for_ui
|
||||
extension: str = shared.opts.samples_format
|
||||
start_index = 0
|
||||
only_one = False
|
||||
|
||||
if index > -1 and shared.opts.save_selected_only and (index >= data["index_of_first_image"]): # ensures we are looking at a specific non-grid picture, and we have save_selected_only
|
||||
|
||||
only_one = True
|
||||
images = [images[index]]
|
||||
start_index = index
|
||||
|
||||
|
|
@ -70,6 +74,7 @@ def save_files(js_data, images, do_make_zip, index):
|
|||
is_grid = image_index < p.index_of_first_image
|
||||
i = 0 if is_grid else (image_index - p.index_of_first_image)
|
||||
|
||||
p.batch_index = image_index-1
|
||||
fullfn, txt_fullfn = modules.images.save_image(image, path, "", seed=p.all_seeds[i], prompt=p.all_prompts[i], extension=extension, info=p.infotexts[image_index], grid=is_grid, p=p, save_to_dirs=save_to_dirs)
|
||||
|
||||
filename = os.path.relpath(fullfn, path)
|
||||
|
|
@ -83,7 +88,10 @@ def save_files(js_data, images, do_make_zip, index):
|
|||
|
||||
# Make Zip
|
||||
if do_make_zip:
|
||||
zip_filepath = os.path.join(path, "images.zip")
|
||||
zip_fileseed = p.all_seeds[index-1] if only_one else p.all_seeds[0]
|
||||
namegen = modules.images.FilenameGenerator(p, zip_fileseed, p.all_prompts[0], image, True)
|
||||
zip_filename = namegen.apply(shared.opts.grid_zip_filename_pattern or "[datetime]_[[model_name]]_[seed]-[seed_last]")
|
||||
zip_filepath = os.path.join(path, f"{zip_filename}.zip")
|
||||
|
||||
from zipfile import ZipFile
|
||||
with ZipFile(zip_filepath, "w") as zip_file:
|
||||
|
|
@ -211,3 +219,23 @@ Requested path was: {f}
|
|||
))
|
||||
|
||||
return result_gallery, generation_info if tabname != "extras" else html_info_x, html_info, html_log
|
||||
|
||||
|
||||
def create_refresh_button(refresh_component, refresh_method, refreshed_args, elem_id):
|
||||
def refresh():
|
||||
refresh_method()
|
||||
args = refreshed_args() if callable(refreshed_args) else refreshed_args
|
||||
|
||||
for k, v in args.items():
|
||||
setattr(refresh_component, k, v)
|
||||
|
||||
return gr.update(**(args or {}))
|
||||
|
||||
refresh_button = ToolButton(value=refresh_symbol, elem_id=elem_id)
|
||||
refresh_button.click(
|
||||
fn=refresh,
|
||||
inputs=[],
|
||||
outputs=[refresh_component]
|
||||
)
|
||||
return refresh_button
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import json
|
||||
import os.path
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime
|
||||
import traceback
|
||||
|
||||
import git
|
||||
|
||||
|
|
@ -13,7 +11,7 @@ import html
|
|||
import shutil
|
||||
import errno
|
||||
|
||||
from modules import extensions, shared, paths, config_states
|
||||
from modules import extensions, shared, paths, config_states, errors, restart
|
||||
from modules.paths_internal import config_states_dir
|
||||
from modules.call_queue import wrap_gradio_gpu_call
|
||||
|
||||
|
|
@ -46,13 +44,16 @@ def apply_and_restart(disable_list, update_list, disable_all):
|
|||
try:
|
||||
ext.fetch_and_reset_hard()
|
||||
except Exception:
|
||||
print(f"Error getting updates for {ext.name}:", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error getting updates for {ext.name}", exc_info=True)
|
||||
|
||||
shared.opts.disabled_extensions = disabled
|
||||
shared.opts.disable_all_extensions = disable_all
|
||||
shared.opts.save(shared.config_filename)
|
||||
shared.state.request_restart()
|
||||
|
||||
if restart.is_restartable():
|
||||
restart.restart_program()
|
||||
else:
|
||||
restart.stop_program()
|
||||
|
||||
|
||||
def save_config_state(name):
|
||||
|
|
@ -113,8 +114,7 @@ def check_updates(id_task, disable_list):
|
|||
if 'FETCH_HEAD' not in str(e):
|
||||
raise
|
||||
except Exception:
|
||||
print(f"Error checking updates for {ext.name}:", file=sys.stderr)
|
||||
print(traceback.format_exc(), file=sys.stderr)
|
||||
errors.report(f"Error checking updates for {ext.name}", exc_info=True)
|
||||
|
||||
shared.state.nextjob()
|
||||
|
||||
|
|
@ -138,7 +138,10 @@ def extension_table():
|
|||
<table id="extensions">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><abbr title="Use checkbox to enable the extension; it will be enabled or disabled when you click apply button">Extension</abbr></th>
|
||||
<th>
|
||||
<input class="gr-check-radio gr-checkbox all_extensions_toggle" type="checkbox" {'checked="checked"' if all(ext.enabled for ext in extensions.extensions) else ''} onchange="toggle_all_extensions(event)" />
|
||||
<abbr title="Use checkbox to enable the extension; it will be enabled or disabled when you click apply button">Extension</abbr>
|
||||
</th>
|
||||
<th>URL</th>
|
||||
<th>Branch</th>
|
||||
<th>Version</th>
|
||||
|
|
@ -170,7 +173,7 @@ def extension_table():
|
|||
|
||||
code += f"""
|
||||
<tr>
|
||||
<td><label{style}><input class="gr-check-radio gr-checkbox" name="enable_{html.escape(ext.name)}" type="checkbox" {'checked="checked"' if ext.enabled else ''}>{html.escape(ext.name)}</label></td>
|
||||
<td><label{style}><input class="gr-check-radio gr-checkbox extension_toggle" name="enable_{html.escape(ext.name)}" type="checkbox" {'checked="checked"' if ext.enabled else ''} onchange="toggle_extension(event)" />{html.escape(ext.name)}</label></td>
|
||||
<td>{remote}</td>
|
||||
<td>{ext.branch}</td>
|
||||
<td>{version_link}</td>
|
||||
|
|
@ -325,6 +328,11 @@ def normalize_git_url(url):
|
|||
def install_extension_from_url(dirname, url, branch_name=None):
|
||||
check_access()
|
||||
|
||||
if isinstance(dirname, str):
|
||||
dirname = dirname.strip()
|
||||
if isinstance(url, str):
|
||||
url = url.strip()
|
||||
|
||||
assert url, 'No URL specified'
|
||||
|
||||
if dirname is None or dirname == "":
|
||||
|
|
@ -337,7 +345,8 @@ def install_extension_from_url(dirname, url, branch_name=None):
|
|||
assert not os.path.exists(target_dir), f'Extension directory already exists: {target_dir}'
|
||||
|
||||
normalized_url = normalize_git_url(url)
|
||||
assert len([x for x in extensions.extensions if normalize_git_url(x.remote) == normalized_url]) == 0, 'Extension with this URL is already installed'
|
||||
if any(x for x in extensions.extensions if normalize_git_url(x.remote) == normalized_url):
|
||||
raise Exception(f'Extension with this URL is already installed: {url}')
|
||||
|
||||
tmpdir = os.path.join(paths.data_path, "tmp", dirname)
|
||||
|
||||
|
|
@ -453,7 +462,7 @@ def refresh_available_extensions_from_data(hide_tags, sort_column, filter_text="
|
|||
existing = installed_extension_urls.get(normalize_git_url(url), None)
|
||||
extension_tags = extension_tags + ["installed"] if existing else extension_tags
|
||||
|
||||
if len([x for x in extension_tags if x in tags_to_hide]) > 0:
|
||||
if any(x for x in extension_tags if x in tags_to_hide):
|
||||
hidden += 1
|
||||
continue
|
||||
|
||||
|
|
@ -490,8 +499,14 @@ def refresh_available_extensions_from_data(hide_tags, sort_column, filter_text="
|
|||
|
||||
|
||||
def preload_extensions_git_metadata():
|
||||
t0 = time.time()
|
||||
for extension in extensions.extensions:
|
||||
extension.read_info_from_repo()
|
||||
print(
|
||||
f"preload_extensions_git_metadata for "
|
||||
f"{len(extensions.extensions)} extensions took "
|
||||
f"{time.time() - t0:.2f}s"
|
||||
)
|
||||
|
||||
|
||||
def create_ui():
|
||||
|
|
@ -506,7 +521,8 @@ def create_ui():
|
|||
with gr.TabItem("Installed", id="installed"):
|
||||
|
||||
with gr.Row(elem_id="extensions_installed_top"):
|
||||
apply = gr.Button(value="Apply and restart UI", variant="primary")
|
||||
apply_label = ("Apply and restart UI" if restart.is_restartable() else "Apply and quit")
|
||||
apply = gr.Button(value=apply_label, variant="primary")
|
||||
check = gr.Button(value="Check for updates")
|
||||
extensions_disable_all = gr.Radio(label="Disable all extensions", choices=["none", "extra", "all"], value=shared.opts.disable_all_extensions, elem_id="extensions_disable_all")
|
||||
extensions_disabled_list = gr.Text(elem_id="extensions_disabled_list", visible=False).style(container=False)
|
||||
|
|
@ -555,9 +571,9 @@ def create_ui():
|
|||
available_extensions_table = gr.HTML()
|
||||
|
||||
refresh_available_extensions_button.click(
|
||||
fn=modules.ui.wrap_gradio_call(refresh_available_extensions, extra_outputs=[gr.update(), gr.update(), gr.update()]),
|
||||
fn=modules.ui.wrap_gradio_call(refresh_available_extensions, extra_outputs=[gr.update(), gr.update(), gr.update(), gr.update()]),
|
||||
inputs=[available_extensions_index, hide_tags, sort_column],
|
||||
outputs=[available_extensions_index, available_extensions_table, hide_tags, install_result, search_extensions_text],
|
||||
outputs=[available_extensions_index, available_extensions_table, hide_tags, search_extensions_text, install_result],
|
||||
)
|
||||
|
||||
install_extension_button.click(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ from pathlib import Path
|
|||
|
||||
from modules import shared
|
||||
from modules.images import read_info_from_image, save_image_with_geninfo
|
||||
from modules.ui import up_down_symbol
|
||||
import gradio as gr
|
||||
import json
|
||||
import html
|
||||
|
|
@ -185,6 +186,8 @@ class ExtraNetworksPage:
|
|||
if search_only and shared.opts.extra_networks_hidden_models == "Never":
|
||||
return ""
|
||||
|
||||
sort_keys = " ".join([html.escape(f'data-sort-{k}={v}') for k, v in item.get("sort_keys", {}).items()]).strip()
|
||||
|
||||
args = {
|
||||
"background_image": background_image,
|
||||
"style": f"'display: none; {height}{width}'",
|
||||
|
|
@ -198,10 +201,23 @@ class ExtraNetworksPage:
|
|||
"search_term": item.get("search_term", ""),
|
||||
"metadata_button": metadata_button,
|
||||
"search_only": " search_only" if search_only else "",
|
||||
"sort_keys": sort_keys,
|
||||
}
|
||||
|
||||
return self.card_page.format(**args)
|
||||
|
||||
def get_sort_keys(self, path):
|
||||
"""
|
||||
List of default keys used for sorting in the UI.
|
||||
"""
|
||||
pth = Path(path)
|
||||
stat = pth.stat()
|
||||
return {
|
||||
"date_created": int(stat.st_ctime or 0),
|
||||
"date_modified": int(stat.st_mtime or 0),
|
||||
"name": pth.name.lower(),
|
||||
}
|
||||
|
||||
def find_preview(self, path):
|
||||
"""
|
||||
Find a preview PNG for a given path (without extension) and call link_preview on it.
|
||||
|
|
@ -296,6 +312,8 @@ def create_ui(container, button, tabname):
|
|||
page_elem.change(fn=lambda: None, _js='function(){applyExtraNetworkFilter(' + json.dumps(tabname) + '); return []}', inputs=[], outputs=[])
|
||||
|
||||
gr.Textbox('', show_label=False, elem_id=tabname+"_extra_search", placeholder="Search...", visible=False)
|
||||
gr.Dropdown(choices=['Default Sort', 'Date Created', 'Date Modified', 'Name'], value='Default Sort', elem_id=tabname+"_extra_sort", multiselect=False, visible=False, show_label=False, interactive=True)
|
||||
gr.Button(up_down_symbol, elem_id=tabname+"_extra_sortorder")
|
||||
button_refresh = gr.Button('Refresh', elem_id=tabname+"_extra_refresh")
|
||||
|
||||
ui.button_save_preview = gr.Button('Save preview', elem_id=tabname+"_save_preview", visible=False)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage):
|
|||
|
||||
def list_items(self):
|
||||
checkpoint: sd_models.CheckpointInfo
|
||||
for name, checkpoint in sd_models.checkpoints_list.items():
|
||||
for index, (name, checkpoint) in enumerate(sd_models.checkpoints_list.items()):
|
||||
path, ext = os.path.splitext(checkpoint.filename)
|
||||
yield {
|
||||
"name": checkpoint.name_for_extra,
|
||||
|
|
@ -24,6 +24,8 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage):
|
|||
"search_term": self.search_terms_from_path(checkpoint.filename) + " " + (checkpoint.sha256 or ""),
|
||||
"onclick": '"' + html.escape(f"""return selectCheckpoint({json.dumps(name)})""") + '"',
|
||||
"local_preview": f"{path}.{shared.opts.samples_format}",
|
||||
"sort_keys": {'default': index, **self.get_sort_keys(checkpoint.filename)},
|
||||
|
||||
}
|
||||
|
||||
def allowed_directories_for_previews(self):
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class ExtraNetworksPageHypernetworks(ui_extra_networks.ExtraNetworksPage):
|
|||
shared.reload_hypernetworks()
|
||||
|
||||
def list_items(self):
|
||||
for name, path in shared.hypernetworks.items():
|
||||
for index, (name, path) in enumerate(shared.hypernetworks.items()):
|
||||
path, ext = os.path.splitext(path)
|
||||
|
||||
yield {
|
||||
|
|
@ -23,6 +23,8 @@ class ExtraNetworksPageHypernetworks(ui_extra_networks.ExtraNetworksPage):
|
|||
"search_term": self.search_terms_from_path(path),
|
||||
"prompt": json.dumps(f"<hypernet:{name}:") + " + opts.extra_networks_default_multiplier + " + json.dumps(">"),
|
||||
"local_preview": f"{path}.preview.{shared.opts.samples_format}",
|
||||
"sort_keys": {'default': index, **self.get_sort_keys(path + ext)},
|
||||
|
||||
}
|
||||
|
||||
def allowed_directories_for_previews(self):
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage):
|
|||
sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings(force_reload=True)
|
||||
|
||||
def list_items(self):
|
||||
for embedding in sd_hijack.model_hijack.embedding_db.word_embeddings.values():
|
||||
for index, embedding in enumerate(sd_hijack.model_hijack.embedding_db.word_embeddings.values()):
|
||||
path, ext = os.path.splitext(embedding.filename)
|
||||
yield {
|
||||
"name": embedding.name,
|
||||
|
|
@ -23,6 +23,8 @@ class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage):
|
|||
"search_term": self.search_terms_from_path(embedding.filename),
|
||||
"prompt": json.dumps(embedding.name),
|
||||
"local_preview": f"{path}.preview.{shared.opts.samples_format}",
|
||||
"sort_keys": {'default': index, **self.get_sort_keys(embedding.filename)},
|
||||
|
||||
}
|
||||
|
||||
def allowed_directories_for_previews(self):
|
||||
|
|
|
|||
69
modules/ui_gradio_extensions.py
Normal file
69
modules/ui_gradio_extensions.py
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import os
|
||||
import gradio as gr
|
||||
|
||||
from modules import localization, shared, scripts
|
||||
from modules.paths import script_path, data_path
|
||||
|
||||
|
||||
def webpath(fn):
|
||||
if fn.startswith(script_path):
|
||||
web_path = os.path.relpath(fn, script_path).replace('\\', '/')
|
||||
else:
|
||||
web_path = os.path.abspath(fn)
|
||||
|
||||
return f'file={web_path}?{os.path.getmtime(fn)}'
|
||||
|
||||
|
||||
def javascript_html():
|
||||
# Ensure localization is in `window` before scripts
|
||||
head = f'<script type="text/javascript">{localization.localization_js(shared.opts.localization)}</script>\n'
|
||||
|
||||
script_js = os.path.join(script_path, "script.js")
|
||||
head += f'<script type="text/javascript" src="{webpath(script_js)}"></script>\n'
|
||||
|
||||
for script in scripts.list_scripts("javascript", ".js"):
|
||||
head += f'<script type="text/javascript" src="{webpath(script.path)}"></script>\n'
|
||||
|
||||
for script in scripts.list_scripts("javascript", ".mjs"):
|
||||
head += f'<script type="module" src="{webpath(script.path)}"></script>\n'
|
||||
|
||||
if shared.cmd_opts.theme:
|
||||
head += f'<script type="text/javascript">set_theme(\"{shared.cmd_opts.theme}\");</script>\n'
|
||||
|
||||
return head
|
||||
|
||||
|
||||
def css_html():
|
||||
head = ""
|
||||
|
||||
def stylesheet(fn):
|
||||
return f'<link rel="stylesheet" property="stylesheet" href="{webpath(fn)}">'
|
||||
|
||||
for cssfile in scripts.list_files_with_name("style.css"):
|
||||
if not os.path.isfile(cssfile):
|
||||
continue
|
||||
|
||||
head += stylesheet(cssfile)
|
||||
|
||||
if os.path.exists(os.path.join(data_path, "user.css")):
|
||||
head += stylesheet(os.path.join(data_path, "user.css"))
|
||||
|
||||
return head
|
||||
|
||||
|
||||
def reload_javascript():
|
||||
js = javascript_html()
|
||||
css = css_html()
|
||||
|
||||
def template_response(*args, **kwargs):
|
||||
res = shared.GradioTemplateResponseOriginal(*args, **kwargs)
|
||||
res.body = res.body.replace(b'</head>', f'{js}</head>'.encode("utf8"))
|
||||
res.body = res.body.replace(b'</body>', f'{css}</body>'.encode("utf8"))
|
||||
res.init_headers()
|
||||
return res
|
||||
|
||||
gr.routes.templates.TemplateResponse = template_response
|
||||
|
||||
|
||||
if not hasattr(shared, 'GradioTemplateResponseOriginal'):
|
||||
shared.GradioTemplateResponseOriginal = gr.routes.templates.TemplateResponse
|
||||
289
modules/ui_settings.py
Normal file
289
modules/ui_settings.py
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
import gradio as gr
|
||||
|
||||
from modules import ui_common, shared, script_callbacks, scripts, sd_models, sysinfo
|
||||
from modules.call_queue import wrap_gradio_call
|
||||
from modules.shared import opts
|
||||
from modules.ui_components import FormRow
|
||||
from modules.ui_gradio_extensions import reload_javascript
|
||||
|
||||
|
||||
def get_value_for_setting(key):
|
||||
value = getattr(opts, key)
|
||||
|
||||
info = opts.data_labels[key]
|
||||
args = info.component_args() if callable(info.component_args) else info.component_args or {}
|
||||
args = {k: v for k, v in args.items() if k not in {'precision'}}
|
||||
|
||||
return gr.update(value=value, **args)
|
||||
|
||||
|
||||
def create_setting_component(key, is_quicksettings=False):
|
||||
def fun():
|
||||
return opts.data[key] if key in opts.data else opts.data_labels[key].default
|
||||
|
||||
info = opts.data_labels[key]
|
||||
t = type(info.default)
|
||||
|
||||
args = info.component_args() if callable(info.component_args) else info.component_args
|
||||
|
||||
if info.component is not None:
|
||||
comp = info.component
|
||||
elif t == str:
|
||||
comp = gr.Textbox
|
||||
elif t == int:
|
||||
comp = gr.Number
|
||||
elif t == bool:
|
||||
comp = gr.Checkbox
|
||||
else:
|
||||
raise Exception(f'bad options item type: {t} for key {key}')
|
||||
|
||||
elem_id = f"setting_{key}"
|
||||
|
||||
if info.refresh is not None:
|
||||
if is_quicksettings:
|
||||
res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
|
||||
ui_common.create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}")
|
||||
else:
|
||||
with FormRow():
|
||||
res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
|
||||
ui_common.create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}")
|
||||
else:
|
||||
res = comp(label=info.label, value=fun(), elem_id=elem_id, **(args or {}))
|
||||
|
||||
return res
|
||||
|
||||
|
||||
class UiSettings:
|
||||
submit = None
|
||||
result = None
|
||||
interface = None
|
||||
components = None
|
||||
component_dict = None
|
||||
dummy_component = None
|
||||
quicksettings_list = None
|
||||
quicksettings_names = None
|
||||
text_settings = None
|
||||
|
||||
def run_settings(self, *args):
|
||||
changed = []
|
||||
|
||||
for key, value, comp in zip(opts.data_labels.keys(), args, self.components):
|
||||
assert comp == self.dummy_component or opts.same_type(value, opts.data_labels[key].default), f"Bad value for setting {key}: {value}; expecting {type(opts.data_labels[key].default).__name__}"
|
||||
|
||||
for key, value, comp in zip(opts.data_labels.keys(), args, self.components):
|
||||
if comp == self.dummy_component:
|
||||
continue
|
||||
|
||||
if opts.set(key, value):
|
||||
changed.append(key)
|
||||
|
||||
try:
|
||||
opts.save(shared.config_filename)
|
||||
except RuntimeError:
|
||||
return opts.dumpjson(), f'{len(changed)} settings changed without save: {", ".join(changed)}.'
|
||||
return opts.dumpjson(), f'{len(changed)} settings changed{": " if changed else ""}{", ".join(changed)}.'
|
||||
|
||||
def run_settings_single(self, value, key):
|
||||
if not opts.same_type(value, opts.data_labels[key].default):
|
||||
return gr.update(visible=True), opts.dumpjson()
|
||||
|
||||
if not opts.set(key, value):
|
||||
return gr.update(value=getattr(opts, key)), opts.dumpjson()
|
||||
|
||||
opts.save(shared.config_filename)
|
||||
|
||||
return get_value_for_setting(key), opts.dumpjson()
|
||||
|
||||
def create_ui(self, loadsave, dummy_component):
|
||||
self.components = []
|
||||
self.component_dict = {}
|
||||
self.dummy_component = dummy_component
|
||||
|
||||
shared.settings_components = self.component_dict
|
||||
|
||||
script_callbacks.ui_settings_callback()
|
||||
opts.reorder()
|
||||
|
||||
with gr.Blocks(analytics_enabled=False) as settings_interface:
|
||||
with gr.Row():
|
||||
with gr.Column(scale=6):
|
||||
self.submit = gr.Button(value="Apply settings", variant='primary', elem_id="settings_submit")
|
||||
with gr.Column():
|
||||
restart_gradio = gr.Button(value='Reload UI', variant='primary', elem_id="settings_restart_gradio")
|
||||
|
||||
self.result = gr.HTML(elem_id="settings_result")
|
||||
|
||||
self.quicksettings_names = opts.quicksettings_list
|
||||
self.quicksettings_names = {x: i for i, x in enumerate(self.quicksettings_names) if x != 'quicksettings'}
|
||||
|
||||
self.quicksettings_list = []
|
||||
|
||||
previous_section = None
|
||||
current_tab = None
|
||||
current_row = None
|
||||
with gr.Tabs(elem_id="settings"):
|
||||
for i, (k, item) in enumerate(opts.data_labels.items()):
|
||||
section_must_be_skipped = item.section[0] is None
|
||||
|
||||
if previous_section != item.section and not section_must_be_skipped:
|
||||
elem_id, text = item.section
|
||||
|
||||
if current_tab is not None:
|
||||
current_row.__exit__()
|
||||
current_tab.__exit__()
|
||||
|
||||
gr.Group()
|
||||
current_tab = gr.TabItem(elem_id=f"settings_{elem_id}", label=text)
|
||||
current_tab.__enter__()
|
||||
current_row = gr.Column(variant='compact')
|
||||
current_row.__enter__()
|
||||
|
||||
previous_section = item.section
|
||||
|
||||
if k in self.quicksettings_names and not shared.cmd_opts.freeze_settings:
|
||||
self.quicksettings_list.append((i, k, item))
|
||||
self.components.append(dummy_component)
|
||||
elif section_must_be_skipped:
|
||||
self.components.append(dummy_component)
|
||||
else:
|
||||
component = create_setting_component(k)
|
||||
self.component_dict[k] = component
|
||||
self.components.append(component)
|
||||
|
||||
if current_tab is not None:
|
||||
current_row.__exit__()
|
||||
current_tab.__exit__()
|
||||
|
||||
with gr.TabItem("Defaults", id="defaults", elem_id="settings_tab_defaults"):
|
||||
loadsave.create_ui()
|
||||
|
||||
with gr.TabItem("Sysinfo", id="sysinfo", elem_id="settings_tab_sysinfo"):
|
||||
gr.HTML('<a href="./internal/sysinfo-download" class="sysinfo_big_link" download>Download system info</a><br /><a href="./internal/sysinfo">(or open as text in a new page)</a>', elem_id="sysinfo_download")
|
||||
|
||||
with gr.Row():
|
||||
with gr.Column(scale=1):
|
||||
sysinfo_check_file = gr.File(label="Check system info for validity", type='binary')
|
||||
with gr.Column(scale=1):
|
||||
sysinfo_check_output = gr.HTML("", elem_id="sysinfo_validity")
|
||||
with gr.Column(scale=100):
|
||||
pass
|
||||
|
||||
with gr.TabItem("Actions", id="actions", elem_id="settings_tab_actions"):
|
||||
request_notifications = gr.Button(value='Request browser notifications', elem_id="request_notifications")
|
||||
download_localization = gr.Button(value='Download localization template', elem_id="download_localization")
|
||||
reload_script_bodies = gr.Button(value='Reload custom script bodies (No ui updates, No restart)', variant='secondary', elem_id="settings_reload_script_bodies")
|
||||
with gr.Row():
|
||||
unload_sd_model = gr.Button(value='Unload SD checkpoint to free VRAM', elem_id="sett_unload_sd_model")
|
||||
reload_sd_model = gr.Button(value='Reload the last SD checkpoint back into VRAM', elem_id="sett_reload_sd_model")
|
||||
|
||||
with gr.TabItem("Licenses", id="licenses", elem_id="settings_tab_licenses"):
|
||||
gr.HTML(shared.html("licenses.html"), elem_id="licenses")
|
||||
|
||||
gr.Button(value="Show all pages", elem_id="settings_show_all_pages")
|
||||
|
||||
self.text_settings = gr.Textbox(elem_id="settings_json", value=lambda: opts.dumpjson(), visible=False)
|
||||
|
||||
unload_sd_model.click(
|
||||
fn=sd_models.unload_model_weights,
|
||||
inputs=[],
|
||||
outputs=[]
|
||||
)
|
||||
|
||||
reload_sd_model.click(
|
||||
fn=sd_models.reload_model_weights,
|
||||
inputs=[],
|
||||
outputs=[]
|
||||
)
|
||||
|
||||
request_notifications.click(
|
||||
fn=lambda: None,
|
||||
inputs=[],
|
||||
outputs=[],
|
||||
_js='function(){}'
|
||||
)
|
||||
|
||||
download_localization.click(
|
||||
fn=lambda: None,
|
||||
inputs=[],
|
||||
outputs=[],
|
||||
_js='download_localization'
|
||||
)
|
||||
|
||||
def reload_scripts():
|
||||
scripts.reload_script_body_only()
|
||||
reload_javascript() # need to refresh the html page
|
||||
|
||||
reload_script_bodies.click(
|
||||
fn=reload_scripts,
|
||||
inputs=[],
|
||||
outputs=[]
|
||||
)
|
||||
|
||||
restart_gradio.click(
|
||||
fn=shared.state.request_restart,
|
||||
_js='restart_reload',
|
||||
inputs=[],
|
||||
outputs=[],
|
||||
)
|
||||
|
||||
def check_file(x):
|
||||
if x is None:
|
||||
return ''
|
||||
|
||||
if sysinfo.check(x.decode('utf8', errors='ignore')):
|
||||
return 'Valid'
|
||||
|
||||
return 'Invalid'
|
||||
|
||||
sysinfo_check_file.change(
|
||||
fn=check_file,
|
||||
inputs=[sysinfo_check_file],
|
||||
outputs=[sysinfo_check_output],
|
||||
)
|
||||
|
||||
self.interface = settings_interface
|
||||
|
||||
def add_quicksettings(self):
|
||||
with gr.Row(elem_id="quicksettings", variant="compact"):
|
||||
for _i, k, _item in sorted(self.quicksettings_list, key=lambda x: self.quicksettings_names.get(x[1], x[0])):
|
||||
component = create_setting_component(k, is_quicksettings=True)
|
||||
self.component_dict[k] = component
|
||||
|
||||
def add_functionality(self, demo):
|
||||
self.submit.click(
|
||||
fn=wrap_gradio_call(lambda *args: self.run_settings(*args), extra_outputs=[gr.update()]),
|
||||
inputs=self.components,
|
||||
outputs=[self.text_settings, self.result],
|
||||
)
|
||||
|
||||
for _i, k, _item in self.quicksettings_list:
|
||||
component = self.component_dict[k]
|
||||
info = opts.data_labels[k]
|
||||
|
||||
change_handler = component.release if hasattr(component, 'release') else component.change
|
||||
change_handler(
|
||||
fn=lambda value, k=k: self.run_settings_single(value, key=k),
|
||||
inputs=[component],
|
||||
outputs=[component, self.text_settings],
|
||||
show_progress=info.refresh is not None,
|
||||
)
|
||||
|
||||
button_set_checkpoint = gr.Button('Change checkpoint', elem_id='change_checkpoint', visible=False)
|
||||
button_set_checkpoint.click(
|
||||
fn=lambda value, _: self.run_settings_single(value, key='sd_model_checkpoint'),
|
||||
_js="function(v){ var res = desiredCheckpointName; desiredCheckpointName = ''; return [res || v, null]; }",
|
||||
inputs=[self.component_dict['sd_model_checkpoint'], self.dummy_component],
|
||||
outputs=[self.component_dict['sd_model_checkpoint'], self.text_settings],
|
||||
)
|
||||
|
||||
component_keys = [k for k in opts.data_labels.keys() if k in self.component_dict]
|
||||
|
||||
def get_settings_values():
|
||||
return [get_value_for_setting(key) for key in component_keys]
|
||||
|
||||
demo.load(
|
||||
fn=get_settings_values,
|
||||
inputs=[],
|
||||
outputs=[self.component_dict[k] for k in component_keys],
|
||||
queue=False,
|
||||
)
|
||||
|
|
@ -31,7 +31,7 @@ def check_tmp_file(gradio, filename):
|
|||
return False
|
||||
|
||||
|
||||
def save_pil_to_file(self, pil_image, dir=None):
|
||||
def save_pil_to_file(self, pil_image, dir=None, format="png"):
|
||||
already_saved_as = getattr(pil_image, 'already_saved_as', None)
|
||||
if already_saved_as and os.path.isfile(already_saved_as):
|
||||
register_tmp_file(shared.demo, already_saved_as)
|
||||
|
|
|
|||
|
|
@ -53,8 +53,8 @@ class Upscaler:
|
|||
|
||||
def upscale(self, img: PIL.Image, scale, selected_model: str = None):
|
||||
self.scale = scale
|
||||
dest_w = int(img.width * scale)
|
||||
dest_h = int(img.height * scale)
|
||||
dest_w = int((img.width * scale) // 8 * 8)
|
||||
dest_h = int((img.height * scale) // 8 * 8)
|
||||
|
||||
for _ in range(3):
|
||||
shape = (img.width, img.height)
|
||||
|
|
@ -77,7 +77,7 @@ class Upscaler:
|
|||
pass
|
||||
|
||||
def find_models(self, ext_filter=None) -> list:
|
||||
return modelloader.load_models(model_path=self.model_path, model_url=self.model_url, command_path=self.user_path)
|
||||
return modelloader.load_models(model_path=self.model_path, model_url=self.model_url, command_path=self.user_path, ext_filter=ext_filter)
|
||||
|
||||
def update_status(self, prompt):
|
||||
print(f"\nextras: {prompt}", file=shared.progress_print_out)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue