├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── PULL_REQUEST_TEMPLATE │ └── pull_request_template.md ├── .gitignore ├── CODEOWNERS ├── README.md ├── artists.csv ├── core ├── __init__.py ├── cmdargs.py ├── devicelib.py ├── imagelib.py ├── installing.py ├── jobs.py ├── memmon.py ├── modellib.py ├── options.py ├── paths.py ├── plugins.py ├── printing.py ├── promptlib.py └── webui.py ├── environment-wsl2.yaml ├── launch.py ├── old ├── extras.py ├── gradio │ ├── NgrokPlugin.py │ ├── generation_parameters_copypaste.py │ ├── ngrok.py │ └── ui.py ├── javascript │ ├── aspectRatioOverlay.js │ ├── contextMenus.js │ ├── dragdrop.js │ ├── edit-attention.js │ ├── hints.js │ ├── imageMaskFix.js │ ├── imageviewer.js │ ├── notification.js │ ├── progressbar.js │ ├── textualInversion.js │ └── ui.js ├── stable_diffusion_auto1111 │ ├── CFGDenoiser.py │ ├── CheckpointInfo.py │ ├── CheckpointLoader.py │ ├── Hypernetwork.py │ ├── HypernetworkLoader.py │ ├── HypernetworkModule.py │ ├── SDAttention.py │ ├── SDCheckpointLoader.py │ ├── SDConstants.py │ ├── SDEmbeddingLoader.py │ ├── SDJob.py │ ├── SDJob_img2img.py │ ├── SDJob_train_embedding.py │ ├── SDJob_txt2img.py │ ├── SDSampler.py │ ├── SDSampler_K.py │ ├── SDSampler_Vanilla.py │ ├── SDUtil.py │ ├── StableDiffusionPlugin.py │ ├── TextInv64.py │ ├── TextInvDataset.py │ ├── TextinvLearnSchedule.py │ ├── TextinvUI_old.py │ ├── __init__.py │ └── optimizations.py ├── styles.py └── upscaler.py ├── plug-models └── deepbooru │ └── Put your deepbooru release project folder here.txt ├── plugins ├── ArtistPlugin.py ├── BSRGANPlugin.py ├── BSRGANPlugin_arch.py ├── Codeforme_vqgan_arch │ ├── __init__.py │ └── vqgan_arch.py ├── CodeformerPlugin.py ├── CodeformerPlugin_arch.py ├── DeepbooruPlugin.py ├── ESRGANPlugin.py ├── ESRGANPlugin_arch.py ├── GFPGANPlugin.py ├── InterrogatePlugin.py ├── LDSRPlugin.py ├── LDSRPlugin_arch.py ├── LanczosUpscaler.py ├── RealESRGANPlugin.py ├── SafetyPlugin.py ├── ScunetPlugin.py ├── ScunetPlugin_arch.py ├── SwinirPlugin.py ├── SwinirPlugin_arch.py ├── SwinirPlugin_arch_v2.py ├── XFormersPlugin.py ├── __init__.py ├── params.txt ├── prompt_style_plugin │ ├── PromptStylePlugin.py │ └── __init__.py ├── sd_1111_plugin │ ├── SDAttention.py │ ├── SDJob.py │ ├── SDPlugin.py │ ├── __init__.py │ ├── devices.py │ ├── image_embedding.py │ ├── images.py │ ├── img2img.py │ ├── interrogate.py │ ├── masking.py │ ├── modelloader.py │ ├── modelsplit.py │ ├── options.py │ ├── prompt_parser.py │ ├── safe.py │ ├── sd_hijack.py │ ├── sd_hijack_inpainting.py │ ├── sd_hijack_optimizations.py │ ├── sd_hypernetwork.py │ ├── sd_models.py │ ├── sd_paths.py │ ├── sd_samplers.py │ ├── sd_textinv.py │ ├── sd_textinv_dataset.py │ ├── sd_textinv_learn_schedule.py │ ├── sd_textinv_preprocess.py │ └── txt2img.py └── sd_diffusers_plugin │ ├── README.md │ └── StableDiffusionPluginHF.py ├── requirements.txt ├── requirements_versions.txt ├── screenshot.png ├── script.js ├── scripts ├── custom_code.py ├── img2imgalt.py ├── loopback.py ├── outpainting_mk_2.py ├── poor_mans_outpainting.py ├── prompt_matrix.py ├── prompts_from_file.py ├── sd_upscale.py └── xy_grid.py ├── style.css ├── textual_inversion_templates ├── hypernetwork.txt ├── none.txt ├── style.txt ├── style_filewords.txt ├── subject.txt └── subject_filewords.txt ├── webui.bat └── webui.sh /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug-report 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Windows, Linux] 28 | - Browser [e.g. chrome, safari] 29 | - Commit revision [looks like this: e68484500f76a33ba477d5a99340ab30451e557b; can be seen when launching webui.bat, or obtained manually by running `git rev-parse HEAD`] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: 'suggestion' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Please read the [contributing wiki page](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Contributing) before submitting a pull request! 2 | 3 | If you have a large change, pay special attention to this paragraph: 4 | 5 | > Before making changes, if you think that your feature will result in more than 100 lines changing, find me and talk to me about the feature you are proposing. It pains me to reject the hard work someone else did, but I won't add everything to the repo, and it's better if the rejection happens before you have to waste time working on the feature. 6 | 7 | Otherwise, after making sure you're following the rules described in wiki page, remove this section and continue on. 8 | 9 | **Describe what this pull request is trying to achieve.** 10 | 11 | A clear and concise description of what you're trying to accomplish with this, so your intent doesn't have to be extracted from your code. 12 | 13 | **Additional notes and description of your changes** 14 | 15 | More technical discussion about your changes go here, plus anything that a maintainer might have to specifically take a look at, or be wary of. 16 | 17 | **Environment this was tested in** 18 | 19 | List the environment you have developed / tested this on. As per the contributing page, changes should be able to work on Windows out of the box. 20 | - OS: [e.g. Windows, Linux] 21 | - Browser [e.g. chrome, safari] 22 | - Graphics card [e.g. NVIDIA RTX 2080 8GB, AMD RX 6600 8GB] 23 | 24 | **Screenshots or videos of your changes** 25 | 26 | If applicable, screenshots or a video showing off your changes. If it edits an existing UI, it should ideally contain a comparison of what used to be there, before your changes were made. 27 | 28 | This is **required** for anything that touches the user interface. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.ckpt 3 | *.pth 4 | /ESRGAN/* 5 | /SwinIR/* 6 | /repositories 7 | /venv 8 | /tmp 9 | /model.ckpt 10 | /models/**/* 11 | /GFPGANv1.3.pth 12 | /gfpgan/weights/*.pth 13 | /ui-config.json 14 | /outputs 15 | /config.json 16 | /log 17 | /webui.settings.bat 18 | /embeddings 19 | /styles.csv 20 | /styles.csv.bak 21 | /webui-user.bat 22 | /webui-user.sh 23 | /interrogate 24 | /user.css 25 | /.idea 26 | notification.mp3 27 | /SwinIR 28 | /textual_inversion 29 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @AUTOMATIC1111 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Project moved ➡ 2 | 3 | https://github.com/stablecore-ai/stable-core 4 | -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oxysoft/stable-diffusion-webui/14c1272b5e436bb3eaeb9713a07ea50443e9970e/core/__init__.py -------------------------------------------------------------------------------- /core/devicelib.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | 3 | import torch 4 | 5 | from core import printing 6 | 7 | 8 | def get_optimal_device(): 9 | if torch.cuda.is_available(): 10 | return torch.device("cuda") 11 | 12 | if has_mps: 13 | return torch.device("mps") 14 | 15 | return cpu 16 | 17 | 18 | def torch_gc(): 19 | if torch.cuda.is_available(): 20 | torch.cuda.empty_cache() 21 | torch.cuda.ipc_collect() 22 | 23 | 24 | def enable_tf32(): 25 | if torch.cuda.is_available(): 26 | torch.backends.cuda.matmul.allow_tf32 = True 27 | torch.backends.cudnn.allow_tf32 = True 28 | 29 | def set_precision(precision): 30 | global dtype 31 | global dtype_vae 32 | 33 | if precision == 'full': 34 | dtype = torch.float32 35 | dtype_vae = torch.float32 36 | elif precision == 'autocast': 37 | dtype = torch.float16 38 | dtype_vae = torch.float16 39 | 40 | def randn(seed, shape): 41 | # Pytorch currently doesn't handle setting randomness correctly when the metal backend is used. 42 | if device.type == 'mps': 43 | generator = torch.Generator(device=cpu) 44 | generator.manual_seed(seed) 45 | noise = torch.randn(shape, generator=generator, device=cpu).to(device) 46 | return noise 47 | 48 | torch.manual_seed(seed) 49 | return torch.randn(shape, device=device) 50 | 51 | 52 | def randn_without_seed(shape): 53 | # Pytorch currently doesn't handle setting randomness correctly when the metal backend is used. 54 | if device.type == 'mps': 55 | generator = torch.Generator(device=cpu) 56 | noise = torch.randn(shape, generator=generator, device=cpu).to(device) 57 | return noise 58 | 59 | return torch.randn(shape, device=device) 60 | 61 | 62 | def autocast(enable=True): 63 | if not enable: return contextlib.nullcontext() 64 | if dtype == torch.float32: return contextlib.nullcontext() # or cargs.precision == "full" 65 | 66 | return torch.autocast("cuda") 67 | 68 | 69 | # has_mps is only available in nightly pytorch (for now), `getattr` for compatibility 70 | has_mps = getattr(torch, 'has_mps', False) 71 | cpu = torch.device("cpu") 72 | printing.run(enable_tf32, "Enabling TF32") 73 | 74 | dtype = torch.float16 75 | dtype_vae = torch.float16 76 | 77 | # TODO figure out a device assignment token or some crap that modules can use 78 | 79 | # device = device_gfpgan = device_bsrgan = device_esrgan = device_scunet = device_codeformer = get_optimal_device() 80 | # device, \ 81 | # device_gfpgan, \ 82 | # device_bsrgan, \ 83 | # device_esrgan, \ 84 | # device_scunet, \ 85 | # device_codeformer = \ 86 | # (cpu if x in cargs.use_cpu else get_optimal_device() 87 | # for x 88 | # in ['stable_diffusion', 'GFPGAN', 'BSRGAN', 'ESRGAN', 'SCUNet', 'CodeFormer']) 89 | 90 | # device = device 91 | 92 | device = get_optimal_device() -------------------------------------------------------------------------------- /core/installing.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | import os 3 | import shutil 4 | import subprocess 5 | import sys 6 | 7 | from core.paths import repodir 8 | 9 | git = os.environ.get('GIT', "git") 10 | python = sys.executable 11 | 12 | 13 | def is_installed(package): 14 | try: 15 | spec = importlib.util.find_spec(package) 16 | except ModuleNotFoundError: 17 | return False 18 | 19 | return spec is not None 20 | 21 | 22 | def extract_arg(args, name): 23 | return [x for x in args if x != name], name in args 24 | 25 | 26 | def check_run(command): 27 | result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 28 | return result.returncode == 0 29 | 30 | 31 | def repo_dir(name): 32 | return repodir / name 33 | 34 | 35 | def run_python(code, desc=None, errdesc=None): 36 | return run(f'"{python}" -c "{code}"', desc, errdesc) 37 | 38 | 39 | def run_pip(args, desc=None): 40 | return run(f'"{python}" -m pip {args} --prefer-binary', desc=f"Installing {desc}", errdesc=f"Couldn't install {desc}") 41 | 42 | 43 | def check_run_python(code): 44 | return check_run(f'"{python}" -c "{code}"') 45 | 46 | 47 | def run(command, desc=None, errdesc=None): 48 | if desc is not None: 49 | print(desc) 50 | 51 | result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 52 | 53 | if result.returncode != 0: 54 | message = f"""{errdesc or 'Error running command'}. 55 | Command: {command} 56 | Error code: {result.returncode} 57 | stdout: {result.stdout.decode(encoding="utf8", errors="ignore") if len(result.stdout) > 0 else ''} 58 | stderr: {result.stderr.decode(encoding="utf8", errors="ignore") if len(result.stderr) > 0 else ''} 59 | """ 60 | raise RuntimeError(message) 61 | 62 | return result.stdout.decode(encoding="utf8", errors="ignore") 63 | 64 | 65 | def git_clone(url, dir, name, commithash=None): 66 | # TODO clone into temporary dir and move if successful 67 | 68 | if os.path.exists(dir): 69 | if commithash is None: 70 | return 71 | 72 | current_hash = run(f'"{git}" -C {dir} rev-parse HEAD', None, f"Couldn't determine {name}'s hash: {commithash}").strip() 73 | if current_hash == commithash: 74 | return 75 | 76 | run(f'"{git}" -C {dir} fetch', f"Fetching updates for {name}...", f"Couldn't fetch {name}") 77 | run(f'"{git}" -C {dir} checkout {commithash}', f"Checking out commint for {name} with hash: {commithash}...", f"Couldn't checkout commit {commithash} for {name}") 78 | return 79 | 80 | run(f'"{git}" clone "{url}" "{dir}"', f"Cloning {name} into {dir}...", f"Couldn't clone {name}") 81 | 82 | if commithash is not None: 83 | run(f'"{git}" -C {dir} checkout {commithash}', None, "Couldn't checkout {name}'s hash: {commithash}") 84 | 85 | sys.path.append(dir) 86 | 87 | 88 | def move_files(src_path: str, dest_path: str, ext_filter: str = None): 89 | try: 90 | if not os.path.exists(dest_path): 91 | os.makedirs(dest_path) 92 | if os.path.exists(src_path): 93 | for file in os.listdir(src_path): 94 | fullpath = os.path.join(src_path, file) 95 | if os.path.isfile(fullpath): 96 | if ext_filter is not None: 97 | if ext_filter not in file: 98 | continue 99 | print(f"Moving {file} from {src_path} to {dest_path}.") 100 | try: 101 | shutil.move(fullpath, dest_path) 102 | except: 103 | pass 104 | if len(os.listdir(src_path)) == 0: 105 | print(f"Removing empty folder: {src_path}") 106 | shutil.rmtree(src_path, True) 107 | except: 108 | pass 109 | -------------------------------------------------------------------------------- /core/memmon.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import time 3 | from collections import defaultdict 4 | 5 | import torch 6 | 7 | 8 | class MemUsageMonitor(threading.Thread): 9 | run_flag = None 10 | device = None 11 | disabled = False 12 | opts = None 13 | data = None 14 | 15 | def __init__(self, name, device, opts): 16 | threading.Thread.__init__(self) 17 | self.name = name 18 | self.device = device 19 | self.opts = opts 20 | 21 | self.daemon = True 22 | self.run_flag = threading.Event() 23 | self.data = defaultdict(int) 24 | 25 | try: 26 | torch.cuda.mem_get_info() 27 | torch.cuda.memory_stats(self.device) 28 | except Exception as e: # AMD or whatever 29 | print(f"Warning: caught exception '{e}', memory monitor disabled") 30 | self.disabled = True 31 | 32 | def run(self): 33 | if self.disabled: 34 | return 35 | 36 | while True: 37 | self.run_flag.wait() 38 | 39 | torch.cuda.reset_peak_memory_stats() 40 | self.data.clear() 41 | 42 | if self.opts.memmon_poll_rate <= 0: 43 | self.run_flag.clear() 44 | continue 45 | 46 | self.data["min_free"] = torch.cuda.mem_get_info()[0] 47 | 48 | while self.run_flag.is_set(): 49 | free, total = torch.cuda.mem_get_info() # calling with self.device errors, torch bug? 50 | self.data["min_free"] = min(self.data["min_free"], free) 51 | 52 | time.sleep(1 / self.opts.memmon_poll_rate) 53 | 54 | def dump_debug(self): 55 | print(self, 'recorded data:') 56 | for k, v in self.read().items(): 57 | print(k, -(v // -(1024 ** 2))) 58 | 59 | print(self, 'raw torch memory stats:') 60 | tm = torch.cuda.memory_stats(self.device) 61 | for k, v in tm.items(): 62 | if 'bytes' not in k: 63 | continue 64 | print('\t' if 'peak' in k else '', k, -(v // -(1024 ** 2))) 65 | 66 | print(torch.cuda.memory_summary()) 67 | 68 | def monitor(self): 69 | self.run_flag.set() 70 | 71 | def read(self): 72 | if not self.disabled: 73 | free, total = torch.cuda.mem_get_info() 74 | self.data["total"] = total 75 | 76 | torch_stats = torch.cuda.memory_stats(self.device) 77 | self.data["active_peak"] = torch_stats["active_bytes.all.peak"] 78 | self.data["reserved_peak"] = torch_stats["reserved_bytes.all.peak"] 79 | self.data["system_peak"] = total - self.data["min_free"] 80 | 81 | return self.data 82 | 83 | def stop(self): 84 | self.run_flag.clear() 85 | return self.read() 86 | -------------------------------------------------------------------------------- /core/paths.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | 5 | # script_path = Path(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) 6 | rootdir = Path(__file__).resolve().parent.parent 7 | modeldir = rootdir / "models" 8 | repodir = rootdir / "plugin-repos" 9 | plugindir = rootdir / "modules" 10 | embeddingdir = rootdir / "embeddings" 11 | 12 | sys.path.insert(0, rootdir.as_posix()) 13 | 14 | # search for directory of stable diffusion in following places 15 | # path_dirs = [ 16 | # (sd_path, 'ldm', 'Stable Diffusion', []), 17 | # (os.path.join(sd_path, '../taming-transformers'), 'taming', 'Taming Transformers', []), 18 | # (os.path.join(sd_path, '../CodeFormer'), 'inference_codeformer.py', 'CodeFormer', []), 19 | # (os.path.join(sd_path, '../BLIP'), 'models/blip.py', 'BLIP', []), 20 | # (os.path.join(sd_path, '../k-diffusion'), 'k_diffusion/SDSampler.py', 'k_diffusion', ["atstart"]), 21 | # ] 22 | # 23 | # paths = {} 24 | # 25 | # for d, must_exist, what, options in path_dirs: 26 | # path = Path(script_path, d, must_exist).resolve() 27 | # 28 | # if not path.exists(): 29 | # print(f"Warning: {what} not found at path {path}", file=sys.stderr) 30 | # else: 31 | # d = os.path.abspath(d) 32 | # if "atstart" in options: 33 | # sys.path.insert(0, d) 34 | # else: 35 | # sys.path.append(d) 36 | # 37 | # paths[what] = d 38 | 39 | # Convert above loop to a function ( 40 | 41 | 42 | -------------------------------------------------------------------------------- /core/printing.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import traceback 3 | 4 | def print_bp(msg): 5 | print(f' - {msg}') 6 | 7 | def printerr(msg): 8 | import sys 9 | print(msg, file=sys.stderr) 10 | 11 | 12 | def printerr_bp(msg): 13 | print(f' - {msg}', file=sys.stderr) 14 | 15 | 16 | def run(code, task): 17 | try: 18 | code() 19 | except Exception as e: 20 | print(f"{task}: {type(e).__name__}", file=sys.stderr) 21 | print(traceback.format_exc(), file=sys.stderr) 22 | 23 | progress_print_out = sys.stdout 24 | -------------------------------------------------------------------------------- /core/webui.py: -------------------------------------------------------------------------------- 1 | import json 2 | import threading 3 | 4 | import core 5 | from core import jobs 6 | import modules 7 | from flask import Flask, jsonify, request 8 | import flask_socketio as fsock 9 | 10 | queue_lock = threading.Lock() 11 | 12 | app = Flask(__name__) 13 | app.config['SECRET_KEY'] = 'secret!' 14 | 15 | socketio = fsock.SocketIO(app) 16 | 17 | 18 | def emit(event, *args, **kwargs): 19 | with queue_lock: 20 | socketio.emit(event, *args, **kwargs) 21 | 22 | 23 | @app.route('/') 24 | def index(): 25 | return "Hello from stable-core!" 26 | 27 | 28 | # A route to list all plugins 29 | @app.route('/plugins') 30 | def list_plugins(): 31 | import core.plugins 32 | return jsonify(core.plugins.list_ids()) 33 | 34 | 35 | @socketio.on('connect') 36 | def connect(): 37 | print('Client connected') 38 | 39 | 40 | @socketio.on('disconnect') 41 | def disconnect(): 42 | print('Client disconnected') 43 | 44 | 45 | @socketio.on('list_plugins') 46 | def list_plugins(): 47 | """ Send an array of plugin IDs """ 48 | import core.plugins 49 | data = [extract_dict(x, 'id') for x in core.plugins.plugins] 50 | emit('list_plugins', jsonify(data)) 51 | 52 | 53 | @socketio.on('call_plugin') 54 | def call_plugin(js): 55 | """ 56 | An API message with socketio to call a plugin and optionally add a job 57 | """ 58 | import core.plugins 59 | 60 | msg = json.loads(js) 61 | pid = msg['plugin_id'] 62 | fname = msg['plugin_func'] 63 | args = msg['args'] 64 | kwargs = msg['kwargs'] 65 | 66 | core.plugins.invoke(pid, fname, *args, **kwargs) 67 | 68 | 69 | @socketio.on('list_jobs') 70 | def list_jobs(): 71 | return jsonify(jobs.queue.list_ids()) 72 | 73 | 74 | @socketio.on('is_job_processing') 75 | def is_job_processing(id): 76 | return jsonify(jobs.is_running(id)) 77 | 78 | @socketio.on('abort_job') 79 | def abort_job(id): 80 | if jobs.is_running(id): 81 | jobs.queue.abort(id) 82 | 83 | @socketio.on('is_processing') 84 | def is_processing(): 85 | return jobs.any_running() 86 | 87 | def extract_dict(obj, *names): 88 | return {x: getattr(obj, x) for x in names} 89 | 90 | # def wrap_gradio_gpu_call(func, extra_outputs=None): 91 | # def f(*args, **kwargs): 92 | # devicelib.torch_gc() 93 | # 94 | # shared.state.sampling_step = 0 95 | # shared.state.job_count = -1 96 | # shared.state.job_no = 0 97 | # shared.state.job_timestamp = shared.state.get_job_timestamp() 98 | # shared.state.current_latent = None 99 | # shared.state.current_image = None 100 | # shared.state.current_image_sampling_step = 0 101 | # shared.state.skipped = False 102 | # shared.state.interrupted = False 103 | # shared.state.textinfo = None 104 | # 105 | # with queue_lock: 106 | # res = func(*args, **kwargs) 107 | # 108 | # shared.state.job = "" 109 | # shared.state.job_count = 0 110 | # 111 | # devicelib.torch_gc() 112 | # 113 | # return res 114 | # 115 | # return gradio.ui.wrap_gradio_call(f, extra_outputs=extra_outputs) 116 | 117 | 118 | # def launch_gradio(): 119 | # while 1: 120 | # demo = gradio.ui.create_ui(wrap_gradio_gpu_call=wrap_gradio_gpu_call) 121 | # 122 | # app, local_url, share_url = demo.launch( 123 | # share=cmd_opts.share, 124 | # server_name="0.0.0.0" if cmd_opts.listen else None, 125 | # server_port=cmd_opts.port, 126 | # debug=cmd_opts.gradio_debug, 127 | # auth=[tuple(cred.split(':')) for cred in cmd_opts.gradio_auth.strip('"').split(',')] if cmd_opts.gradio_auth else None, 128 | # inbrowser=cmd_opts.autolaunch, 129 | # prevent_thread_lock=True) 130 | # 131 | # app.add_middleware(GZipMiddleware, minimum_size=1000) 132 | # 133 | # while 1: 134 | # time.sleep(0.5) 135 | # if getattr(demo, 'do_restart', False): 136 | # time.sleep(0.5) 137 | # demo.close() 138 | # time.sleep(0.5) 139 | # break 140 | # 141 | # print('Reloading modules: modules.ui') 142 | # importlib.reload(gradio.ui) 143 | # 144 | # print('Restarting Gradio') 145 | -------------------------------------------------------------------------------- /environment-wsl2.yaml: -------------------------------------------------------------------------------- 1 | name: automatic 2 | channels: 3 | - pytorch 4 | - defaults 5 | dependencies: 6 | - python=3.10 7 | - pip=22.2.2 8 | - cudatoolkit=11.3 9 | - pytorch=1.12.1 10 | - torchvision=0.13.1 11 | - numpy=1.23.1 -------------------------------------------------------------------------------- /launch.py: -------------------------------------------------------------------------------- 1 | # this scripts installs necessary requirements and launches main program in webui.py 2 | import importlib 3 | import os 4 | import signal 5 | import sys 6 | import shlex 7 | 8 | import transformers 9 | 10 | from core.cmdargs import cargs 11 | from core.paths import repodir 12 | from core.installing import is_installed, run, git, git_clone, run_python, run_pip, repo_dir, python 13 | 14 | from core.paths import rootdir 15 | 16 | taming_transformers_commit_hash = os.environ.get('TAMING_TRANSFORMERS_COMMIT_HASH', "24268930bf1dce879235a7fddd0b2355b84d7ea6") 17 | torch_command = os.environ.get('TORCH_COMMAND', "pip install torch==1.12.1+cu113 torchvision==0.13.1+cu113 --extra-index-url https://download.pytorch.org/whl/cu113") 18 | clip_package = os.environ.get('CLIP_PACKAGE', "git+https://github.com/openai/CLIP.git@d50d76daa670286dd6cacf3bcd80b5e4823fc8e1") 19 | requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt") 20 | commandline_args = os.environ.get('COMMANDLINE_ARGS', "") 21 | 22 | 23 | def print_info(): 24 | try: 25 | commit = run(f"{git} rev-parse HEAD").strip() 26 | except Exception: 27 | commit = "" 28 | print(f"Python {sys.version}") 29 | print(f"Commit hash: {commit}") 30 | 31 | 32 | def install_core(): 33 | """ 34 | Install all core requirements 35 | """ 36 | 37 | os.makedirs(repodir, exist_ok=True) 38 | 39 | if not is_installed("torch") or not is_installed("torchvision"): 40 | run(f'"{python}" -m {torch_command}', "Installing torch and torchvision", "Couldn't install torch") 41 | 42 | if not '--skip-torch-cuda-test' in args: 43 | run_python("import torch; assert torch.cuda.is_available(), 'Torch is not able to use GPU; add --skip-torch-cuda-test to COMMANDLINE_ARGS variable to disable this check'") 44 | 45 | if not is_installed("clip"): 46 | run_pip(f"install {clip_package}", "clip") 47 | 48 | git_clone("https://github.com/CompVis/taming-transformers.git", repodir / "taming-transformers", "Taming Transformers", taming_transformers_commit_hash) 49 | 50 | 51 | def install_webui(): 52 | run_pip(f"install -r {requirements_file}", "requirements for Web UI") 53 | 54 | 55 | def start_webui(): 56 | print(f"Launching Web UI with arguments: {' '.join(sys.argv[1:])}") 57 | from core import webui 58 | webui.app.run() 59 | 60 | 61 | def sigint_handler(sig, frame): 62 | print(f'Interrupted with signal {sig} in {frame}') 63 | os._exit(0) 64 | 65 | 66 | xformers_available = False 67 | transformers.logging.set_verbosity_error() 68 | try: 69 | from transformers import logging, CLIPModel 70 | logging.set_verbosity_error() 71 | except Exception: 72 | pass 73 | 74 | if __name__ == "__main__": 75 | print_info() 76 | 77 | # Prepare CTRL-C handler 78 | signal.signal(signal.SIGINT, sigint_handler) 79 | 80 | # Memory monitor 81 | from core import options, devicelib, memmon 82 | 83 | mem_mon = memmon.MemUsageMonitor("MemMon", devicelib.device, options.opts) # TODO remove options 84 | mem_mon.start() 85 | 86 | # Prepare args for use 87 | from core import cmdargs 88 | 89 | args = shlex.split(commandline_args) 90 | sys.argv += args 91 | cargs = cmdargs.parser.parse_args(args) 92 | 93 | # Prepare core 94 | # ---------------------------------------- 95 | devicelib.set_precision(cargs.precision) 96 | 97 | # Prepare plugin system 98 | # ---------------------------------------- 99 | from core import plugins, options, paths 100 | 101 | # Iterate all directories in paths.repodir TODO this should be handled automatically by plugin installations 102 | for d in paths.repodir.iterdir(): 103 | sys.path.insert(0, d.as_posix()) 104 | 105 | sys.path.insert(0, (rootdir / "plugin-repos" / "stable_diffusion" / "ldm").as_posix()) 106 | 107 | # TODO git clone modules from a user list 108 | plugins.load_all(paths.plugindir) 109 | 110 | # Installations 111 | # ---------------------------------------- 112 | install_core() 113 | plugins.broadcast("install") 114 | print("Installations complete") 115 | 116 | # Dry run, only install and exit. 117 | # ---------------------------------------- 118 | # plugins.broadcast("load") 119 | # plugins.job(SDJob_txt2img(prompt="Beautiful painting of an ultra contorted landscape by Greg Ruktowsky and Salvador Dali. airbrushed, 70s prog rock album cover, psychedelic, elaborate, complex", 120 | # cfg=7.75, 121 | # steps=22, 122 | # sampler='euler-a', 123 | # )) 124 | 125 | # plugins.get("stable_diffusion_auto1111").txt2img() 126 | plugins.get("stable_diffusion_auto2222").load() 127 | # plugins.get("stable_diffusion_auto1111").load() 128 | 129 | plugins.get("stable_diffusion_auto2222").txt2img("Beautiful painting of an ultra contorted landscape by Greg Ruktowsky and Salvador Dali. airbrushed, 70s prog rock album cover, psychedelic, elaborate, complex") 130 | # plugins.get("stable_diffusion_auto1111").txt2img(SDJob_txt2img(prompt="Beautiful painting of an ultra contorted landscape by Greg Ruktowsky and Salvador Dali. airbrushed, 70s prog rock album cover, psychedelic, elaborate, complex")) 131 | 132 | if cargs.dry: 133 | print("Exiting because of --dry argument") 134 | exit(0) 135 | 136 | # Start server 137 | # ---------------------------------------- 138 | 139 | install_webui() 140 | start_webui() 141 | -------------------------------------------------------------------------------- /old/gradio/NgrokPlugin.py: -------------------------------------------------------------------------------- 1 | from core.installing import is_installed, run_pip 2 | 3 | 4 | class NgrokPlugin: 5 | def install(self, args): 6 | if not is_installed("pyngrok"): 7 | run_pip("install pyngrok", "ngrok") -------------------------------------------------------------------------------- /old/gradio/generation_parameters_copypaste.py: -------------------------------------------------------------------------------- 1 | import re 2 | import gradio as gr 3 | 4 | re_param_code = r"\s*([\w ]+):\s*([^,]+)(?:,|$)" 5 | re_param = re.compile(re_param_code) 6 | re_params = re.compile(r"^(?:" + re_param_code + "){3,}$") 7 | re_imagesize = re.compile(r"^(\d+)x(\d+)$") 8 | type_of_gr_update = type(gr.update()) 9 | 10 | 11 | def parse_generation_parameters(x: str): 12 | """parses generation parameters string, the one you see in text field under the picture in UI: 13 | ``` 14 | girl with an artist's beret, determined, blue eyes, desert scene, computer monitors, heavy makeup, by Alphonse Mucha and Charlie Bowater, ((eyeshadow)), (coquettish), detailed, intricate 15 | Negative prompt: ugly, fat, obese, chubby, (((deformed))), [blurry], bad anatomy, disfigured, poorly drawn face, mutation, mutated, (extra_limb), (ugly), (poorly drawn hands), messy drawing 16 | Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model hash: 45dee52b 17 | ``` 18 | 19 | returns a dict with field values 20 | """ 21 | 22 | res = {} 23 | 24 | prompt = "" 25 | negative_prompt = "" 26 | 27 | done_with_prompt = False 28 | 29 | *lines, lastline = x.strip().split("\n") 30 | if not re_params.match(lastline): 31 | lines.enqueue(lastline) 32 | lastline = '' 33 | 34 | for i, line in enumerate(lines): 35 | line = line.strip() 36 | if line.startswith("Negative prompt:"): 37 | done_with_prompt = True 38 | line = line[16:].strip() 39 | 40 | if done_with_prompt: 41 | negative_prompt += ("" if negative_prompt == "" else "\n") + line 42 | else: 43 | prompt += ("" if prompt == "" else "\n") + line 44 | 45 | if len(prompt) > 0: 46 | res["Prompt"] = prompt 47 | 48 | if len(negative_prompt) > 0: 49 | res["Negative prompt"] = negative_prompt 50 | 51 | for k, v in re_param.findall(lastline): 52 | m = re_imagesize.match(v) 53 | if m is not None: 54 | res[k+"-1"] = m.group(1) 55 | res[k+"-2"] = m.group(2) 56 | else: 57 | res[k] = v 58 | 59 | return res 60 | 61 | 62 | def connect_paste(button, paste_fields, input_comp, js=None): 63 | def paste_func(prompt): 64 | params = parse_generation_parameters(prompt) 65 | res = [] 66 | 67 | for output, key in paste_fields: 68 | if callable(key): 69 | v = key(params) 70 | else: 71 | v = params.get(key, None) 72 | 73 | if v is None: 74 | res.append(gr.update()) 75 | elif isinstance(v, type_of_gr_update): 76 | res.append(v) 77 | else: 78 | try: 79 | valtype = type(output.value) 80 | val = valtype(v) 81 | res.append(gr.update(value=val)) 82 | except Exception: 83 | res.append(gr.update()) 84 | 85 | return res 86 | 87 | button.click( 88 | fn=paste_func, 89 | _js=js, 90 | inputs=[input_comp], 91 | outputs=[x[0] for x in paste_fields], 92 | ) 93 | -------------------------------------------------------------------------------- /old/gradio/ngrok.py: -------------------------------------------------------------------------------- 1 | from pyngrok import ngrok, conf, exception 2 | 3 | 4 | def connect(token, port): 5 | if token == None: 6 | token = 'None' 7 | conf.get_default().auth_token = token 8 | try: 9 | public_url = ngrok.connect(port).public_url 10 | except exception.PyngrokNgrokError: 11 | print(f'Invalid ngrok authtoken, ngrok connection aborted.\n' 12 | f'Your token: {token}, get the right one on https://dashboard.ngrok.com/get-started/your-authtoken') 13 | else: 14 | print(f'ngrok connected to localhost:{port}! URL: {public_url}\n' 15 | 'You can use this link after the launch is complete.') 16 | -------------------------------------------------------------------------------- /old/javascript/aspectRatioOverlay.js: -------------------------------------------------------------------------------- 1 | 2 | let currentWidth = null; 3 | let currentHeight = null; 4 | let arFrameTimeout = setTimeout(function(){},0); 5 | 6 | function dimensionChange(e,dimname){ 7 | 8 | if(dimname == 'Width'){ 9 | currentWidth = e.target.value*1.0 10 | } 11 | if(dimname == 'Height'){ 12 | currentHeight = e.target.value*1.0 13 | } 14 | 15 | var inImg2img = Boolean(gradioApp().querySelector("button.rounded-t-lg.border-gray-200")) 16 | 17 | if(!inImg2img){ 18 | return; 19 | } 20 | 21 | var img2imgMode = gradioApp().querySelector('#mode_img2img.tabs > div > button.rounded-t-lg.border-gray-200') 22 | if(img2imgMode){ 23 | img2imgMode=img2imgMode.innerText 24 | }else{ 25 | return; 26 | } 27 | 28 | var redrawImage = gradioApp().querySelector('div[data-testid=image] img'); 29 | var inpaintImage = gradioApp().querySelector('#img2maskimg div[data-testid=image] img') 30 | 31 | var targetElement = null; 32 | 33 | if(img2imgMode=='img2img' && redrawImage){ 34 | targetElement = redrawImage; 35 | }else if(img2imgMode=='Inpaint' && inpaintImage){ 36 | targetElement = inpaintImage; 37 | } 38 | 39 | if(targetElement){ 40 | 41 | var arPreviewRect = gradioApp().querySelector('#imageARPreview'); 42 | if(!arPreviewRect){ 43 | arPreviewRect = document.createElement('div') 44 | arPreviewRect.id = "imageARPreview"; 45 | gradioApp().getRootNode().appendChild(arPreviewRect) 46 | } 47 | 48 | 49 | 50 | var viewportOffset = targetElement.getBoundingClientRect(); 51 | 52 | viewportscale = Math.min( targetElement.clientWidth/targetElement.naturalWidth, targetElement.clientHeight/targetElement.naturalHeight ) 53 | 54 | scaledx = targetElement.naturalWidth*viewportscale 55 | scaledy = targetElement.naturalHeight*viewportscale 56 | 57 | cleintRectTop = (viewportOffset.top+window.scrollY) 58 | cleintRectLeft = (viewportOffset.left+window.scrollX) 59 | cleintRectCentreY = cleintRectTop + (targetElement.clientHeight/2) 60 | cleintRectCentreX = cleintRectLeft + (targetElement.clientWidth/2) 61 | 62 | viewRectTop = cleintRectCentreY-(scaledy/2) 63 | viewRectLeft = cleintRectCentreX-(scaledx/2) 64 | arRectWidth = scaledx 65 | arRectHeight = scaledy 66 | 67 | arscale = Math.min( arRectWidth/currentWidth, arRectHeight/currentHeight ) 68 | arscaledx = currentWidth*arscale 69 | arscaledy = currentHeight*arscale 70 | 71 | arRectTop = cleintRectCentreY-(arscaledy/2) 72 | arRectLeft = cleintRectCentreX-(arscaledx/2) 73 | arRectWidth = arscaledx 74 | arRectHeight = arscaledy 75 | 76 | arPreviewRect.style.top = arRectTop+'px'; 77 | arPreviewRect.style.left = arRectLeft+'px'; 78 | arPreviewRect.style.width = arRectWidth+'px'; 79 | arPreviewRect.style.height = arRectHeight+'px'; 80 | 81 | clearTimeout(arFrameTimeout); 82 | arFrameTimeout = setTimeout(function(){ 83 | arPreviewRect.style.display = 'none'; 84 | },2000); 85 | 86 | arPreviewRect.style.display = 'block'; 87 | 88 | } 89 | 90 | } 91 | 92 | 93 | onUiUpdate(function(){ 94 | var arPreviewRect = gradioApp().querySelector('#imageARPreview'); 95 | if(arPreviewRect){ 96 | arPreviewRect.style.display = 'none'; 97 | } 98 | var inImg2img = Boolean(gradioApp().querySelector("button.rounded-t-lg.border-gray-200")) 99 | if(inImg2img){ 100 | let inputs = gradioApp().querySelectorAll('input'); 101 | inputs.forEach(function(e){ 102 | let parentLabel = e.parentElement.querySelector('label') 103 | if(parentLabel && parentLabel.innerText){ 104 | if(!e.classList.contains('scrollwatch')){ 105 | if(parentLabel.innerText == 'Width' || parentLabel.innerText == 'Height'){ 106 | e.addEventListener('input', function(e){dimensionChange(e,parentLabel.innerText)} ) 107 | e.classList.add('scrollwatch') 108 | } 109 | if(parentLabel.innerText == 'Width'){ 110 | currentWidth = e.value*1.0 111 | } 112 | if(parentLabel.innerText == 'Height'){ 113 | currentHeight = e.value*1.0 114 | } 115 | } 116 | } 117 | }) 118 | } 119 | }); 120 | -------------------------------------------------------------------------------- /old/javascript/contextMenus.js: -------------------------------------------------------------------------------- 1 | 2 | contextMenuInit = function(){ 3 | let eventListenerApplied=false; 4 | let menuSpecs = new Map(); 5 | 6 | const uid = function(){ 7 | return Date.now().toString(36) + Math.random().toString(36).substr(2); 8 | } 9 | 10 | function showContextMenu(event,element,menuEntries){ 11 | let posx = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; 12 | let posy = event.clientY + document.body.scrollTop + document.documentElement.scrollTop; 13 | 14 | let oldMenu = gradioApp().querySelector('#context-menu') 15 | if(oldMenu){ 16 | oldMenu.remove() 17 | } 18 | 19 | let tabButton = uiCurrentTab 20 | let baseStyle = window.getComputedStyle(tabButton) 21 | 22 | const contextMenu = document.createElement('nav') 23 | contextMenu.id = "context-menu" 24 | contextMenu.style.background = baseStyle.background 25 | contextMenu.style.color = baseStyle.color 26 | contextMenu.style.fontFamily = baseStyle.fontFamily 27 | contextMenu.style.top = posy+'px' 28 | contextMenu.style.left = posx+'px' 29 | 30 | 31 | 32 | const contextMenuList = document.createElement('ul') 33 | contextMenuList.className = 'context-menu-items'; 34 | contextMenu.append(contextMenuList); 35 | 36 | menuEntries.forEach(function(entry){ 37 | let contextMenuEntry = document.createElement('a') 38 | contextMenuEntry.innerHTML = entry['name'] 39 | contextMenuEntry.addEventListener("click", function(e) { 40 | entry['func'](); 41 | }) 42 | contextMenuList.append(contextMenuEntry); 43 | 44 | }) 45 | 46 | gradioApp().getRootNode().appendChild(contextMenu) 47 | 48 | let menuWidth = contextMenu.offsetWidth + 4; 49 | let menuHeight = contextMenu.offsetHeight + 4; 50 | 51 | let windowWidth = window.innerWidth; 52 | let windowHeight = window.innerHeight; 53 | 54 | if ( (windowWidth - posx) < menuWidth ) { 55 | contextMenu.style.left = windowWidth - menuWidth + "px"; 56 | } 57 | 58 | if ( (windowHeight - posy) < menuHeight ) { 59 | contextMenu.style.top = windowHeight - menuHeight + "px"; 60 | } 61 | 62 | } 63 | 64 | function appendContextMenuOption(targetEmementSelector,entryName,entryFunction){ 65 | 66 | currentItems = menuSpecs.get(targetEmementSelector) 67 | 68 | if(!currentItems){ 69 | currentItems = [] 70 | menuSpecs.set(targetEmementSelector,currentItems); 71 | } 72 | let newItem = {'id':targetEmementSelector+'_'+uid(), 73 | 'name':entryName, 74 | 'func':entryFunction, 75 | 'isNew':true} 76 | 77 | currentItems.push(newItem) 78 | return newItem['id'] 79 | } 80 | 81 | function removeContextMenuOption(uid){ 82 | menuSpecs.forEach(function(v,k) { 83 | let index = -1 84 | v.forEach(function(e,ei){if(e['id']==uid){index=ei}}) 85 | if(index>=0){ 86 | v.splice(index, 1); 87 | } 88 | }) 89 | } 90 | 91 | function addContextMenuEventListener(){ 92 | if(eventListenerApplied){ 93 | return; 94 | } 95 | gradioApp().addEventListener("click", function(e) { 96 | let source = e.composedPath()[0] 97 | if(source.id && source.indexOf('check_progress')>-1){ 98 | return 99 | } 100 | 101 | let oldMenu = gradioApp().querySelector('#context-menu') 102 | if(oldMenu){ 103 | oldMenu.remove() 104 | } 105 | }); 106 | gradioApp().addEventListener("contextmenu", function(e) { 107 | let oldMenu = gradioApp().querySelector('#context-menu') 108 | if(oldMenu){ 109 | oldMenu.remove() 110 | } 111 | menuSpecs.forEach(function(v,k) { 112 | if(e.composedPath()[0].matches(k)){ 113 | showContextMenu(e,e.composedPath()[0],v) 114 | e.preventDefault() 115 | return 116 | } 117 | }) 118 | }); 119 | eventListenerApplied=true 120 | 121 | } 122 | 123 | return [appendContextMenuOption, removeContextMenuOption, addContextMenuEventListener] 124 | } 125 | 126 | initResponse = contextMenuInit(); 127 | appendContextMenuOption = initResponse[0]; 128 | removeContextMenuOption = initResponse[1]; 129 | addContextMenuEventListener = initResponse[2]; 130 | 131 | (function(){ 132 | //Start example Context Menu Items 133 | let generateOnRepeat = function(genbuttonid,interruptbuttonid){ 134 | let genbutton = gradioApp().querySelector(genbuttonid); 135 | let interruptbutton = gradioApp().querySelector(interruptbuttonid); 136 | if(!interruptbutton.offsetParent){ 137 | genbutton.click(); 138 | } 139 | clearInterval(window.generateOnRepeatInterval) 140 | window.generateOnRepeatInterval = setInterval(function(){ 141 | if(!interruptbutton.offsetParent){ 142 | genbutton.click(); 143 | } 144 | }, 145 | 500) 146 | } 147 | 148 | appendContextMenuOption('#txt2img_generate','Generate forever',function(){ 149 | generateOnRepeat('#txt2img_generate','#txt2img_interrupt'); 150 | }) 151 | appendContextMenuOption('#img2img_generate','Generate forever',function(){ 152 | generateOnRepeat('#img2img_generate','#img2img_interrupt'); 153 | }) 154 | 155 | let cancelGenerateForever = function(){ 156 | clearInterval(window.generateOnRepeatInterval) 157 | } 158 | 159 | appendContextMenuOption('#txt2img_interrupt','Cancel generate forever',cancelGenerateForever) 160 | appendContextMenuOption('#txt2img_generate', 'Cancel generate forever',cancelGenerateForever) 161 | appendContextMenuOption('#img2img_interrupt','Cancel generate forever',cancelGenerateForever) 162 | appendContextMenuOption('#img2img_generate', 'Cancel generate forever',cancelGenerateForever) 163 | 164 | appendContextMenuOption('#roll','Roll three', 165 | function(){ 166 | let rollbutton = get_uiCurrentTabContent().querySelector('#roll'); 167 | setTimeout(function(){rollbutton.click()},100) 168 | setTimeout(function(){rollbutton.click()},200) 169 | setTimeout(function(){rollbutton.click()},300) 170 | } 171 | ) 172 | })(); 173 | //End example Context Menu Items 174 | 175 | onUiUpdate(function(){ 176 | addContextMenuEventListener() 177 | }); 178 | -------------------------------------------------------------------------------- /old/javascript/dragdrop.js: -------------------------------------------------------------------------------- 1 | // allows drag-dropping files into gradio image elements, and also pasting images from clipboard 2 | 3 | function isValidImageList( files ) { 4 | return files && files?.length === 1 && ['image/png', 'image/gif', 'image/jpeg'].includes(files[0].type); 5 | } 6 | 7 | function dropReplaceImage( imgWrap, files ) { 8 | if ( ! isValidImageList( files ) ) { 9 | return; 10 | } 11 | 12 | imgWrap.querySelector('.modify-upload button + button, .touch-none + div button + button')?.click(); 13 | const callback = () => { 14 | const fileInput = imgWrap.querySelector('input[type="file"]'); 15 | if ( fileInput ) { 16 | fileInput.files = files; 17 | fileInput.dispatchEvent(new Event('change')); 18 | } 19 | }; 20 | 21 | if ( imgWrap.closest('#pnginfo_image') ) { 22 | // special treatment for PNG Info tab, wait for fetch request to finish 23 | const oldFetch = window.fetch; 24 | window.fetch = async (input, options) => { 25 | const response = await oldFetch(input, options); 26 | if ( 'api/predict/' === input ) { 27 | const content = await response.text(); 28 | window.fetch = oldFetch; 29 | window.requestAnimationFrame( () => callback() ); 30 | return new Response(content, { 31 | status: response.status, 32 | statusText: response.statusText, 33 | headers: response.headers 34 | }) 35 | } 36 | return response; 37 | }; 38 | } else { 39 | window.requestAnimationFrame( () => callback() ); 40 | } 41 | } 42 | 43 | window.document.addEventListener('dragover', e => { 44 | const target = e.composedPath()[0]; 45 | const imgWrap = target.closest('[data-testid="image"]'); 46 | if ( !imgWrap ) { 47 | return; 48 | } 49 | e.stopPropagation(); 50 | e.preventDefault(); 51 | e.dataTransfer.dropEffect = 'copy'; 52 | }); 53 | 54 | window.document.addEventListener('drop', e => { 55 | const target = e.composedPath()[0]; 56 | const imgWrap = target.closest('[data-testid="image"]'); 57 | if ( !imgWrap ) { 58 | return; 59 | } 60 | e.stopPropagation(); 61 | e.preventDefault(); 62 | const files = e.dataTransfer.files; 63 | dropReplaceImage( imgWrap, files ); 64 | }); 65 | 66 | window.addEventListener('paste', e => { 67 | const files = e.clipboardData.files; 68 | if ( ! isValidImageList( files ) ) { 69 | return; 70 | } 71 | 72 | const visibleImageFields = [...gradioApp().querySelectorAll('[data-testid="image"]')] 73 | .filter(el => uiElementIsVisible(el)); 74 | if ( ! visibleImageFields.length ) { 75 | return; 76 | } 77 | 78 | const firstFreeImageField = visibleImageFields 79 | .filter(el => el.querySelector('input[type=file]'))?.[0]; 80 | 81 | dropReplaceImage( 82 | firstFreeImageField ? 83 | firstFreeImageField : 84 | visibleImageFields[visibleImageFields.length - 1] 85 | , files ); 86 | }); 87 | -------------------------------------------------------------------------------- /old/javascript/edit-attention.js: -------------------------------------------------------------------------------- 1 | addEventListener('keydown', (event) => { 2 | let target = event.originalTarget || event.composedPath()[0]; 3 | if (!target.hasAttribute("placeholder")) return; 4 | if (!target.placeholder.toLowerCase().includes("prompt")) return; 5 | 6 | let plus = "ArrowUp" 7 | let minus = "ArrowDown" 8 | if (event.key != plus && event.key != minus) return; 9 | 10 | selectionStart = target.selectionStart; 11 | selectionEnd = target.selectionEnd; 12 | if(selectionStart == selectionEnd) return; 13 | 14 | event.preventDefault(); 15 | 16 | if (selectionStart == 0 || target.value[selectionStart - 1] != "(") { 17 | target.value = target.value.slice(0, selectionStart) + 18 | "(" + target.value.slice(selectionStart, selectionEnd) + ":1.0)" + 19 | target.value.slice(selectionEnd); 20 | 21 | target.focus(); 22 | target.selectionStart = selectionStart + 1; 23 | target.selectionEnd = selectionEnd + 1; 24 | 25 | } else { 26 | end = target.value.slice(selectionEnd + 1).indexOf(")") + 1; 27 | weight = parseFloat(target.value.slice(selectionEnd + 1, selectionEnd + 1 + end)); 28 | if (isNaN(weight)) return; 29 | if (event.key == minus) weight -= 0.1; 30 | if (event.key == plus) weight += 0.1; 31 | 32 | weight = parseFloat(weight.toPrecision(12)); 33 | 34 | target.value = target.value.slice(0, selectionEnd + 1) + 35 | weight + 36 | target.value.slice(selectionEnd + 1 + end - 1); 37 | 38 | target.focus(); 39 | target.selectionStart = selectionStart; 40 | target.selectionEnd = selectionEnd; 41 | } 42 | // Since we've modified a Gradio Textbox component manually, we need to simulate an `input` DOM event to ensure its 43 | // internal Svelte data binding remains in sync. 44 | target.dispatchEvent(new Event("input", { bubbles: true })); 45 | }); 46 | -------------------------------------------------------------------------------- /old/javascript/imageMaskFix.js: -------------------------------------------------------------------------------- 1 | /** 2 | * temporary fix for https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/668 3 | * @see https://github.com/gradio-app/gradio/issues/1721 4 | */ 5 | window.addEventListener( 'resize', () => imageMaskResize()); 6 | function imageMaskResize() { 7 | const canvases = gradioApp().querySelectorAll('#img2maskimg .touch-none canvas'); 8 | if ( ! canvases.length ) { 9 | canvases_fixed = false; 10 | window.removeEventListener( 'resize', imageMaskResize ); 11 | return; 12 | } 13 | 14 | const wrapper = canvases[0].closest('.touch-none'); 15 | const previewImage = wrapper.previousElementSibling; 16 | 17 | if ( ! previewImage.complete ) { 18 | previewImage.addEventListener( 'load', () => imageMaskResize()); 19 | return; 20 | } 21 | 22 | const w = previewImage.width; 23 | const h = previewImage.height; 24 | const nw = previewImage.naturalWidth; 25 | const nh = previewImage.naturalHeight; 26 | const portrait = nh > nw; 27 | const factor = portrait; 28 | 29 | const wW = Math.min(w, portrait ? h/nh*nw : w/nw*nw); 30 | const wH = Math.min(h, portrait ? h/nh*nh : w/nw*nh); 31 | 32 | wrapper.style.width = `${wW}px`; 33 | wrapper.style.height = `${wH}px`; 34 | wrapper.style.left = `${(w-wW)/2}px`; 35 | wrapper.style.top = `${(h-wH)/2}px`; 36 | 37 | canvases.forEach( c => { 38 | c.style.width = c.style.height = ''; 39 | c.style.maxWidth = '100%'; 40 | c.style.maxHeight = '100%'; 41 | c.style.objectFit = 'contain'; 42 | }); 43 | } 44 | 45 | onUiUpdate(() => imageMaskResize()); -------------------------------------------------------------------------------- /old/javascript/notification.js: -------------------------------------------------------------------------------- 1 | // Monitors the gallery and sends a browser notification when the leading image is new. 2 | 3 | let lastHeadImg = null; 4 | 5 | notificationButton = null 6 | 7 | onUiUpdate(function(){ 8 | if(notificationButton == null){ 9 | notificationButton = gradioApp().getElementById('request_notifications') 10 | 11 | if(notificationButton != null){ 12 | notificationButton.addEventListener('click', function (evt) { 13 | Notification.requestPermission(); 14 | },true); 15 | } 16 | } 17 | 18 | const galleryPreviews = gradioApp().querySelectorAll('img.h-full.w-full.overflow-hidden'); 19 | 20 | if (galleryPreviews == null) return; 21 | 22 | const headImg = galleryPreviews[0]?.src; 23 | 24 | if (headImg == null || headImg == lastHeadImg) return; 25 | 26 | lastHeadImg = headImg; 27 | 28 | // play notification sound if available 29 | gradioApp().querySelector('#audio_notification audio')?.play(); 30 | 31 | if (document.hasFocus()) return; 32 | 33 | // Multiple copies of the images are in the DOM when one is selected. Dedup with a Set to get the real number generated. 34 | const imgs = new Set(Array.from(galleryPreviews).map(img => img.src)); 35 | 36 | const notification = new Notification( 37 | 'Stable Diffusion', 38 | { 39 | body: `Generated ${imgs.size > 1 ? imgs.size - 1 : 1} image${imgs.size > 1 ? 's' : ''}`, 40 | icon: headImg, 41 | image: headImg, 42 | } 43 | ); 44 | 45 | notification.onclick = function(_){ 46 | parent.focus(); 47 | this.close(); 48 | }; 49 | }); 50 | -------------------------------------------------------------------------------- /old/javascript/progressbar.js: -------------------------------------------------------------------------------- 1 | // code related to showing and updating progressbar shown as the image is being made 2 | global_progressbars = {} 3 | 4 | function check_progressbar(id_part, id_progressbar, id_progressbar_span, id_skip, id_interrupt, id_preview, id_gallery){ 5 | var progressbar = gradioApp().getElementById(id_progressbar) 6 | var skip = id_skip ? gradioApp().getElementById(id_skip) : null 7 | var interrupt = gradioApp().getElementById(id_interrupt) 8 | 9 | if(opts.show_progress_in_title && progressbar && progressbar.offsetParent){ 10 | if(progressbar.innerText){ 11 | let newtitle = 'Stable Diffusion - ' + progressbar.innerText 12 | if(document.title != newtitle){ 13 | document.title = newtitle; 14 | } 15 | }else{ 16 | let newtitle = 'Stable Diffusion' 17 | if(document.title != newtitle){ 18 | document.title = newtitle; 19 | } 20 | } 21 | } 22 | 23 | if(progressbar!= null && progressbar != global_progressbars[id_progressbar]){ 24 | global_progressbars[id_progressbar] = progressbar 25 | 26 | var mutationObserver = new MutationObserver(function(m){ 27 | preview = gradioApp().getElementById(id_preview) 28 | gallery = gradioApp().getElementById(id_gallery) 29 | 30 | if(preview != null && gallery != null){ 31 | preview.style.width = gallery.clientWidth + "px" 32 | preview.style.height = gallery.clientHeight + "px" 33 | 34 | var progressDiv = gradioApp().querySelectorAll('#' + id_progressbar_span).length > 0; 35 | if(!progressDiv){ 36 | if (skip) { 37 | skip.style.display = "none" 38 | } 39 | interrupt.style.display = "none" 40 | } 41 | } 42 | 43 | window.setTimeout(function() { requestMoreProgress(id_part, id_progressbar_span, id_skip, id_interrupt) }, 500) 44 | }); 45 | mutationObserver.observe( progressbar, { childList:true, subtree:true }) 46 | } 47 | } 48 | 49 | onUiUpdate(function(){ 50 | check_progressbar('txt2img', 'txt2img_progressbar', 'txt2img_progress_span', 'txt2img_skip', 'txt2img_interrupt', 'txt2img_preview', 'txt2img_gallery') 51 | check_progressbar('img2img', 'img2img_progressbar', 'img2img_progress_span', 'img2img_skip', 'img2img_interrupt', 'img2img_preview', 'img2img_gallery') 52 | check_progressbar('ti', 'ti_progressbar', 'ti_progress_span', '', 'ti_interrupt', 'ti_preview', 'ti_gallery') 53 | }) 54 | 55 | function requestMoreProgress(id_part, id_progressbar_span, id_skip, id_interrupt){ 56 | btn = gradioApp().getElementById(id_part+"_check_progress"); 57 | if(btn==null) return; 58 | 59 | btn.click(); 60 | var progressDiv = gradioApp().querySelectorAll('#' + id_progressbar_span).length > 0; 61 | var skip = id_skip ? gradioApp().getElementById(id_skip) : null 62 | var interrupt = gradioApp().getElementById(id_interrupt) 63 | if(progressDiv && interrupt){ 64 | if (skip) { 65 | skip.style.display = "block" 66 | } 67 | interrupt.style.display = "block" 68 | } 69 | } 70 | 71 | function requestProgress(id_part){ 72 | btn = gradioApp().getElementById(id_part+"_check_progress_initial"); 73 | if(btn==null) return; 74 | 75 | btn.click(); 76 | } 77 | -------------------------------------------------------------------------------- /old/javascript/textualInversion.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | function start_training_textual_inversion(){ 4 | requestProgress('ti') 5 | gradioApp().querySelector('#ti_error').innerHTML='' 6 | 7 | return args_to_array(arguments) 8 | } 9 | -------------------------------------------------------------------------------- /old/stable_diffusion_auto1111/CFGDenoiser.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | from core import promptlib 4 | from core.cmdargs import cargs 5 | 6 | 7 | class CFGDenoiser(torch.nn.Module): 8 | """ 9 | magic code by katherine crowson and auto1111 san 10 | """ 11 | 12 | def __init__(self, model, plugin): 13 | super().__init__() 14 | self.inner_model = model 15 | self.plugin = plugin 16 | self.mask = None 17 | self.nmask = None 18 | self.init_latent = None 19 | self.step = 0 20 | 21 | def forward(self, x, sigma, uncond, cond, cond_scale): 22 | opt = self.plugin.opt 23 | batch_cond_uncond = opt.always_batch_cond_uncond or not opt.lowvram and not opt.medvram 24 | 25 | conds_list, tensor = promptlib.reconstruct_multicond_batch(cond, self.step) 26 | uncond = promptlib.reconstruct_cond_batch(uncond, self.step) 27 | 28 | batch_size = len(conds_list) 29 | repeats = [len(conds_list[i]) for i in range(batch_size)] 30 | 31 | x_in = torch.cat([torch.stack([x[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [x]) 32 | sigma_in = torch.cat([torch.stack([sigma[i] for _ in range(n)]) for i, n in enumerate(repeats)] + [sigma]) 33 | 34 | if tensor.shape[1] == uncond.shape[1]: 35 | cond_in = torch.cat([tensor, uncond]) 36 | 37 | if batch_cond_uncond: 38 | x_out = self.inner_model(x_in, sigma_in, cond=cond_in) 39 | else: 40 | x_out = torch.zeros_like(x_in) 41 | for batch_offset in range(0, x_out.shape[0], batch_size): 42 | a = batch_offset 43 | b = a + batch_size 44 | x_out[a:b] = self.inner_model(x_in[a:b], sigma_in[a:b], cond=cond_in[a:b]) 45 | else: 46 | x_out = torch.zeros_like(x_in) 47 | batch_size = batch_size * 2 if batch_cond_uncond else batch_size 48 | for batch_offset in range(0, tensor.shape[0], batch_size): 49 | a = batch_offset 50 | b = min(a + batch_size, tensor.shape[0]) 51 | x_out[a:b] = self.inner_model(x_in[a:b], sigma_in[a:b], cond=tensor[a:b]) 52 | 53 | x_out[-uncond.shape[0]:] = self.inner_model(x_in[-uncond.shape[0]:], sigma_in[-uncond.shape[0]:], cond=uncond) 54 | 55 | denoised_uncond = x_out[-uncond.shape[0]:] 56 | denoised = torch.clone(denoised_uncond) 57 | 58 | for i, conds in enumerate(conds_list): 59 | for cond_index, weight in conds: 60 | denoised[i] += (x_out[cond_index] - denoised_uncond[i]) * (weight * cond_scale) 61 | 62 | if self.mask is not None: 63 | denoised = self.init_latent * self.mask + self.nmask * denoised 64 | 65 | self.step += 1 66 | 67 | return denoised -------------------------------------------------------------------------------- /old/stable_diffusion_auto1111/CheckpointInfo.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | class CheckpointInfo: 4 | def __init__(self, path, hash, config): 5 | self.path = path 6 | self.hash = hash 7 | 8 | self.configpath = config 9 | self.id = Path(path).relative_to(path.parent.parent).stem 10 | self.title = f'{self.id} [{hash}]' 11 | 12 | def __setattr__(self, name, value): 13 | self.__dict__[name] = value 14 | 15 | def __getattr__(self, name): 16 | return self.__dict__[name] 17 | 18 | def __str__(self): 19 | return f'CheckpointInfo({self.id}, {self.title}, {self.path}, {self.hash}, {self.configpath})' 20 | 21 | def __repr__(self): 22 | return self.id -------------------------------------------------------------------------------- /old/stable_diffusion_auto1111/CheckpointLoader.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from core import paths 4 | from core.printing import printerr 5 | from CheckpointInfo import CheckpointInfo 6 | from modules.stable_diffusion_auto1111.CheckpointInfo import CheckpointInfo 7 | 8 | class CheckpointLoader: 9 | """ 10 | A repository of models in installation_dir/models/ 11 | Automatically detects all models in the directory and provides a list of CheckpointInfo 12 | A default model ID can be given to use as a default when none is specified by the user configuration. 13 | """ 14 | 15 | def __init__(self, subdirname, config, defaults=None, extensions=None): 16 | if extensions is None: 17 | extensions = ['ckpt', 'pt'] 18 | if defaults is None: 19 | defaults = ["model"] 20 | 21 | self.all = [] 22 | self.config = config 23 | self.dirpath = paths.modeldir / subdirname 24 | self.default_ids = defaults 25 | self.extensions = extensions 26 | 27 | self.reload() 28 | 29 | def get(self, id) -> CheckpointInfo | None: 30 | for info in self.all: 31 | if info.id == id: 32 | return info 33 | return None 34 | 35 | def get_default(self) -> CheckpointInfo | None: 36 | # Go through the list of defaults first 37 | for id in self.default_ids: 38 | info = self.get(id) 39 | if info is not None: 40 | return info 41 | 42 | # Return the first model in the repository 43 | if len(self.all) > 0: 44 | return self.all[0] 45 | 46 | return None 47 | 48 | def get_closest(self, searchstr) -> Any | None: 49 | applicable = sorted([info for info in self.all if searchstr in info.id], key=lambda x: len(x.id)) 50 | if len(applicable) > 0: 51 | return applicable[0] 52 | return None 53 | 54 | def get_ids(self) -> list: 55 | return sorted([x.id for x in self.all]) 56 | 57 | def reload(self): 58 | self.all.clear() 59 | self.add(self.dirpath) 60 | 61 | def add(self, path): 62 | def add_file(path): 63 | confpath = path.with_suffix(".yaml") 64 | if not confpath.is_file(): 65 | confpath = self.config 66 | 67 | self.all.append(CheckpointInfo(path, model_hash(path), confpath)) 68 | 69 | def add_dir(dirpath): 70 | for e in self.extensions: 71 | for p in dirpath.glob(f"*.{e}"): 72 | add_file(p) 73 | 74 | if path.exists(): 75 | if path.is_dir(): 76 | add_dir(path) 77 | elif path.is_file(): 78 | add_file(path) 79 | else: 80 | printerr(f"Path does not exist: {path}") 81 | -------------------------------------------------------------------------------- /old/stable_diffusion_auto1111/Hypernetwork.py: -------------------------------------------------------------------------------- 1 | from HypernetworkModule import HypernetworkModule 2 | 3 | 4 | class Hypernetwork: 5 | def __init__(self, path=None, enable_sizes=None): 6 | self.path = path 7 | self.id = path.stem 8 | self.layers = {} 9 | self.step = 0 10 | self.sd_checkpoint = None 11 | self.sd_checkpoint_name = None 12 | 13 | for size in enable_sizes or []: 14 | self.layers[size] = (HypernetworkModule(size), HypernetworkModule(size)) 15 | 16 | def weights(self): 17 | res = [] 18 | 19 | for k, layers in self.layers.items(): 20 | for layer in layers: 21 | layer.train() 22 | res += [layer.linear1.weight, layer.linear1.bias, layer.linear2.weight, layer.linear2.bias] 23 | 24 | return res -------------------------------------------------------------------------------- /old/stable_diffusion_auto1111/HypernetworkLoader.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | 4 | import torch 5 | 6 | from core.cmdargs import cargs 7 | from core.printing import printerr 8 | from CheckpointLoader import CheckpointLoader 9 | from HypernetworkModule import HypernetworkModule 10 | from Hypernetwork import Hypernetwork 11 | 12 | 13 | class HypernetworkLoader(CheckpointLoader): 14 | def load_hypernetwork(self, info): 15 | if isinstance(info, str): 16 | info = self.get(info) 17 | if info is None: 18 | raise ValueError(f'Hypernetwork {info} not found') 19 | 20 | state_dict = torch.load(info.filepath, map_location='cpu') 21 | 22 | for size, sd in state_dict.items(): 23 | if type(size) == int: 24 | info.layers[size] = (HypernetworkModule(size, sd[0]), HypernetworkModule(size, sd[1])) 25 | 26 | info.name = state_dict.get('name', info.name) 27 | info.step = state_dict.get('step', 0) 28 | info.sd_checkpoint = state_dict.get('sd_checkpoint', None) 29 | info.sd_checkpoint_name = state_dict.get('sd_checkpoint_name', None) 30 | 31 | return info 32 | 33 | def attention_CrossAttention_forward(self, x, context=None, mask=None): 34 | h = self.heads 35 | 36 | q = self.to_q(x) 37 | context = np.default(context, x) 38 | 39 | context_k, context_v = self.apply_hypernetwork(hypernetworks_loaded, context, self) 40 | k = self.to_k(context_k) 41 | v = self.to_v(context_v) 42 | 43 | q, k, v = map(lambda t: np.rearrange(t, 'b n (h d) -> (b h) n d', h=h), (q, k, v)) 44 | 45 | sim = np.einsum('b i d, b j d -> b i j', q, k) * self.scale 46 | 47 | if mask is not None: 48 | mask = np.rearrange(mask, 'b ... -> b (...)') 49 | max_neg_value = -torch.finfo(sim.dtype).max 50 | mask = np.repeat(mask, 'b j -> (b h) () j', h=h) 51 | sim.masked_fill_(~mask, max_neg_value) 52 | 53 | # attention, what we cannot get enough of 54 | attn = sim.softmax(dim=-1) 55 | 56 | out = np.einsum('b i j, b j d -> b i d', attn, v) 57 | out = np.rearrange(out, '(b h) n d -> b n (h d)', h=h) 58 | return self.to_out(out) 59 | 60 | def save(self, hn, path): 61 | state_dict = {} 62 | 63 | for k, v in hn.layers.items(): 64 | state_dict[k] = (v[0].state_dict(), v[1].state_dict()) 65 | 66 | state_dict['step'] = hn.step 67 | state_dict['name'] = hn.name 68 | state_dict['sd_checkpoint'] = hn.sd_checkpoint 69 | state_dict['sd_checkpoint_name'] = hn.sd_checkpoint_name 70 | 71 | torch.save(state_dict, path) 72 | 73 | 74 | def list_paths(self, dirpath): 75 | res = [] 76 | for path in Path(dirpath).rglob('*.pt'): 77 | res.append(path) 78 | return res 79 | 80 | def instantiate_paths(self, paths): 81 | return [Hypernetwork(path) for path in paths] 82 | 83 | 84 | def apply_hypernetwork(self, hypernetwork, context, layer=None): 85 | hypernetwork_layers = (hypernetwork.layers if hypernetwork is not None else {}).get(context.shape[2], None) 86 | 87 | if hypernetwork_layers is None: 88 | return context, context 89 | 90 | if layer is not None: 91 | layer.hyper_k = hypernetwork_layers[0] 92 | layer.hyper_v = hypernetwork_layers[1] 93 | 94 | context_k = hypernetwork_layers[0](context) 95 | context_v = hypernetwork_layers[1](context) 96 | return context_k, context_v 97 | 98 | def find_closest_hypernetwork_name(self, search: str): 99 | if not search: 100 | return None 101 | search = search.lower() 102 | applicable = [name for name in self.hypernetworks_info if search in name.lower()] 103 | if not applicable: 104 | return None 105 | applicable = sorted(applicable, key=lambda name: len(name)) 106 | return applicable[0] -------------------------------------------------------------------------------- /old/stable_diffusion_auto1111/HypernetworkModule.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | from core import devicelib 4 | 5 | 6 | class HypernetworkModule(torch.nn.Module): 7 | def __init__(self, dim, state_dict=None): 8 | super().__init__() 9 | 10 | self.linear1 = torch.nn.Linear(dim, dim * 2) 11 | self.linear2 = torch.nn.Linear(dim * 2, dim) 12 | 13 | if state_dict is not None: 14 | self.load_state_dict(state_dict, strict=True) 15 | else: 16 | self.linear1.weight.data.normal_(mean=0.0, std=0.01) 17 | self.linear1.bias.data.zero_() 18 | self.linear2.weight.data.normal_(mean=0.0, std=0.01) 19 | self.linear2.bias.data.zero_() 20 | 21 | self.to(devicelib.device) 22 | 23 | def forward(self, x): 24 | return x + (self.linear2(self.linear1(x))) -------------------------------------------------------------------------------- /old/stable_diffusion_auto1111/SDAttention.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class SDAttention(Enum): 5 | LDM = 0 6 | SPLIT_BASUJINDAL = 1 7 | SPLIT_INVOKE = 2 8 | SPLIT_DOGGETT = 3 9 | XFORMERS = 4 -------------------------------------------------------------------------------- /old/stable_diffusion_auto1111/SDConstants.py: -------------------------------------------------------------------------------- 1 | 2 | model_dirname = "Stable-diffusion" 3 | opt_C = 4 4 | opt_f = 8 5 | -------------------------------------------------------------------------------- /old/stable_diffusion_auto1111/SDEmbeddingLoader.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | from pathlib import Path 3 | 4 | import torch 5 | from PIL import Image 6 | from torch.nn import Embedding 7 | 8 | from core import devicelib 9 | from core.printing import printerr 10 | from modules.stable_diffusion_auto1111.TextInv64 import embedding_from_b64, extract_image_data_embed 11 | 12 | 13 | class SDEmbeddingLoader: 14 | def __init__(self, directory:Path): 15 | self.id_lookup = {} 16 | self.embeddings = {} 17 | self.directory = directory 18 | self.directory_mtime = None 19 | 20 | def reload(self, model): 21 | mt = self.directory.stat().st_mtime 22 | if self.directory_mtime is not None and mt <= self.directory_mtime: 23 | return 24 | self.directory_mtime = mt 25 | 26 | self.id_lookup.clear() 27 | self.embeddings.clear() 28 | 29 | for file in self.directory.iterdir(): 30 | try: 31 | if file.stat().st_size == 0: 32 | continue 33 | self.load_file(file) 34 | except Exception: 35 | printerr(f"Error loading emedding {file}:") 36 | printerr(traceback.format_exc()) 37 | continue 38 | 39 | print(f"Loaded {len(self.embeddings)} textual inversion embeddings.") 40 | print("Embeddings:", ', '.join(self.embeddings.keys())) 41 | 42 | def load_file(self, path, model): 43 | name = path.stem 44 | 45 | if path.suffix.upper() in ['.PNG', '.WEBP', '.JXL', '.AVIF']: 46 | embed_image = Image.open(path) 47 | if hasattr(embed_image, 'text') and 'sd-ti-embedding' in embed_image.text: 48 | data = embedding_from_b64(embed_image.text['sd-ti-embedding']) 49 | name = data.get('name', name) 50 | else: 51 | data = extract_image_data_embed(embed_image) 52 | name = data.get('name', name) 53 | else: 54 | data = torch.load(path, map_location="cpu") 55 | 56 | # Textual Inversion embeddings 57 | if 'string_to_param' in data: 58 | param_dict = data['string_to_param'] 59 | if hasattr(param_dict, '_parameters'): 60 | param_dict = getattr(param_dict, '_parameters') # fix for torch 1.12.1 loading saved file from torch 1.11 61 | assert len(param_dict) == 1, 'embedding file has multiple terms in it' 62 | emb = next(iter(param_dict.items()))[1] 63 | 64 | # Diffuser concepts 65 | elif type(data) == dict and type(next(iter(data.values()))) == torch.Tensor: 66 | assert len(data.keys()) == 1, 'embedding file has multiple terms in it' 67 | 68 | emb = next(iter(data.values())) 69 | if len(emb.shape) == 1: 70 | emb = emb.unsqueeze(0) 71 | else: 72 | raise Exception(f"Couldn't identify {name} as neither textual inversion embedding nor diffuser concept.") 73 | 74 | vec = emb.detach().to(devicelib.device, dtype=torch.float32) 75 | embedding = Embedding(vec, name) 76 | embedding.step = data.get('step', None) 77 | embedding.sd_checkpoint = data.get('hash', None) 78 | embedding.sd_checkpoint_name = data.get('sd_checkpoint_name', None) 79 | 80 | self.add_embedding(embedding, model) 81 | 82 | def add_embedding(self, embedding, model): 83 | self.embeddings[embedding.name] = embedding 84 | 85 | ids = model.cond_stage_model.tokenizer([embedding.name], add_special_tokens=False)['input_ids'][0] 86 | 87 | first_id = ids[0] 88 | if first_id not in self.id_lookup: 89 | self.id_lookup[first_id] = [] 90 | 91 | self.id_lookup[first_id] = sorted(self.id_lookup[first_id] + [(ids, embedding)], key=lambda x: len(x[0]), reverse=True) 92 | 93 | return embedding 94 | 95 | def find_embedding_at_position(self, tokens, offset): 96 | token = tokens[offset] 97 | possible_matches = self.id_lookup.get(token, None) 98 | 99 | if possible_matches is None: 100 | return None, None 101 | 102 | for ids, embedding in possible_matches: 103 | if tokens[offset:offset + len(ids)] == ids: 104 | return embedding, len(ids) 105 | 106 | return None, None -------------------------------------------------------------------------------- /old/stable_diffusion_auto1111/SDJob_train_embedding.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from core.jobs import JobParams 4 | 5 | 6 | class SDJob_train_embedding(JobParams): 7 | def get_plugin_impl(self): 8 | return 'stable_diffusion', 'data2ckpt_embedding' 9 | 10 | def __init__(self, 11 | name:str, 12 | datadir:Path, 13 | model, 14 | lr:float=0.00001, # TODO idk what the default should be 15 | num_repeats:int=1, 16 | w:int=512, 17 | h:int=512, 18 | steps:int=500, # TODO idk what the default should be 19 | save_every:int=100, # TODO idk what the default should be 20 | template_file:Path=None): # TODO no clue what this is 21 | super().__init__() 22 | self.name = name 23 | self.datadir = datadir 24 | self.model = model 25 | self.learn_rate = lr 26 | self.training_width = w 27 | self.training_height = h 28 | self.steps = steps 29 | self.num_repeats = num_repeats 30 | self.save_embedding_every = save_every 31 | self.template_file = template_file -------------------------------------------------------------------------------- /old/stable_diffusion_auto1111/SDJob_txt2img.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | import numpy as np 4 | import torch 5 | from PIL.Image import Image 6 | 7 | from core import imagelib, devicelib 8 | from core.options import opts 9 | from modules.stable_diffusion_auto1111.SDConstants import opt_C, opt_f 10 | from modules.stable_diffusion_auto1111.SDJob import SDJob 11 | 12 | 13 | class SDJob_txt2img(SDJob): 14 | def get_plugin_impl(self): 15 | return 'stable_diffusion', 'txt2img' 16 | 17 | def __init__(self, enable_hr=False, scale_latent=True, denoising_strength=0.75, sampler='euler-a', **kwargs): 18 | # TODO take in input images 19 | super().__init__(**kwargs) 20 | self.sampler = sampler 21 | self.firstphase_width = 0 22 | self.firstphase_height = 0 23 | self.firstphase_width_truncated = 0 24 | self.firstphase_height_truncated = 0 25 | self.enable_hr = enable_hr # TODO what is hr 26 | self.scale_latent = scale_latent 27 | self.denoising_strength = denoising_strength 28 | 29 | def init(self, all_prompts, all_seeds, all_subseeds): 30 | # TODO what is enable_hr ... 31 | if self.enable_hr: 32 | # if state.job_count == -1: 33 | # state.job_count = self.n_iter * 2 34 | # else: 35 | # state.job_count = state.job_count * 2 36 | 37 | desired_pixel_count = 512 * 512 38 | actual_pixel_count = self.width * self.height 39 | scale = math.sqrt(desired_pixel_count / actual_pixel_count) 40 | 41 | self.firstphase_width = math.ceil(scale * self.width / 64) * 64 42 | self.firstphase_height = math.ceil(scale * self.height / 64) * 64 43 | self.firstphase_width_truncated = int(scale * self.width) 44 | self.firstphase_height_truncated = int(scale * self.height) 45 | 46 | def sample(self, sampler, conditioning, unconditional_conditioning, seeds, subseeds, subseed_strength): 47 | # TODO what is enable_hr ... 48 | if not self.enable_hr: 49 | x = self.create_random_tensors([opt_C, self.height // opt_f, self.width // opt_f], 50 | seeds=seeds, 51 | subseeds=subseeds, 52 | subseed_strength=self.subseed_strength, 53 | seed_resize_from_h=self.seed_resize_from_h, 54 | seed_resize_from_w=self.seed_resize_from_w, 55 | p=self) 56 | samples = sampler.sample(self, x, conditioning, unconditional_conditioning) 57 | return samples 58 | 59 | x = self.create_random_tensors([opt_C, self.firstphase_height // opt_f, self.firstphase_width // opt_f], 60 | seeds=seeds, 61 | subseeds=subseeds, 62 | subseed_strength=self.subseed_strength, 63 | seed_resize_from_h=self.seed_resize_from_h, 64 | seed_resize_from_w=self.seed_resize_from_w, 65 | p=self) 66 | samples = sampler.sample(self, x, conditioning, unconditional_conditioning) 67 | 68 | truncate_x = (self.firstphase_width - self.firstphase_width_truncated) // opt_f 69 | truncate_y = (self.firstphase_height - self.firstphase_height_truncated) // opt_f 70 | 71 | samples = samples[:, :, truncate_y // 2:samples.shape[2] - truncate_y // 2, truncate_x // 2:samples.shape[3] - truncate_x // 2] 72 | 73 | if self.scale_latent: 74 | samples = torch.nn.functional.interpolate(samples, size=(self.height // opt_f, self.width // opt_f), mode="bilinear") 75 | else: 76 | decoded_samples = self.plugin().decode_first_stage(self.plugin.model, samples) # TODO it's in the plugin 77 | 78 | if opts.upscaler_for_img2img is None or opts.upscaler_for_img2img == "None": 79 | decoded_samples = torch.nn.functional.interpolate(decoded_samples, size=(self.height, self.width), mode="bilinear") 80 | else: 81 | lowres_samples = torch.clamp((decoded_samples + 1.0) / 2.0, min=0.0, max=1.0) 82 | 83 | batch_images = [] 84 | for i, x_sample in enumerate(lowres_samples): 85 | x_sample = 255. * np.moveaxis(x_sample.cpu().numpy(), 0, 2) 86 | x_sample = x_sample.astype(np.uint8) 87 | image = Image.fromarray(x_sample) 88 | image = imagelib.resize_image(0, image, self.width, self.height) 89 | image = np.array(image).astype(np.float32) / 255.0 90 | image = np.moveaxis(image, 2, 0) 91 | batch_images.append(image) 92 | 93 | decoded_samples = torch.from_numpy(np.array(batch_images)) 94 | decoded_samples = decoded_samples.to(devicelib.device) 95 | decoded_samples = 2. * decoded_samples - 1. 96 | 97 | samples = self.plugin.model.get_first_stage_encoding(self.plugin.model.encode_first_stage(decoded_samples)) 98 | 99 | noise = self.create_random_tensors(samples.shape[1:], seeds=seeds, subseeds=subseeds, subseed_strength=subseed_strength, seed_resize_from_h=self.subseed.resize_from_h, seed_resize_from_w=self.subseed.resize_from_w, p=self) 100 | 101 | # GC now before running the next img2img to prevent running out of memory 102 | x = None 103 | devicelib.torch_gc() 104 | 105 | samples = sampler.sample_img2img(self, samples, noise, conditioning, unconditional_conditioning, steps=self.steps) 106 | 107 | return samples 108 | -------------------------------------------------------------------------------- /old/stable_diffusion_auto1111/SDSampler.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | def setup_img2img_steps(p, steps=None, img2img_fix_steps=False): 5 | if steps is not None: 6 | steps = int((steps or p.steps) / min(p.denoising_strength, 0.999)) if p.denoising_strength > 0 else 0 7 | t_enc = p.steps - 1 8 | else: 9 | steps = p.steps 10 | t_enc = int(min(p.denoising_strength, 0.999) * steps) 11 | 12 | return steps, t_enc 13 | 14 | class SDSampler(metaclass=ABCMeta): 15 | def __init__(self, plugin): 16 | self.plugin = plugin 17 | self.p = None # State parameters, set upon using the sampler 18 | -------------------------------------------------------------------------------- /old/stable_diffusion_auto1111/SDSampler_Vanilla.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | from core import promptlib, devicelib 4 | 5 | # from StableDiffusionPlugins_samplers import store_latent, setup_img2img_steps 6 | from SDSampler import SDSampler, setup_img2img_steps 7 | 8 | 9 | class SDSampler_Vanilla(SDSampler): 10 | def __init__(self, constructor, plugin): 11 | super().__init__(constructor, plugin) 12 | self.sampler = constructor(plugin) 13 | self.orig_p_sample_ddim = self.sampler.p_sample_ddim if hasattr(self.sampler, 'p_sample_ddim') else self.sampler.p_sample_plms 14 | self.mask = None 15 | self.nmask = None 16 | self.init_latent = None 17 | self.sampler_noises = None 18 | self.step = 0 19 | self.eta = 0.0 # 0 to 1, 0.01 step 20 | self.default_eta = 0.0 21 | self.config = None 22 | 23 | def number_of_needed_noises(self, p): 24 | return 0 25 | 26 | def p_sample_ddim_hook(self, x_dec, cond, ts, unconditional_conditioning, *args, **kwargs): 27 | conds_list, tensor = promptlib.reconstruct_multicond_batch(cond, self.step) 28 | unconditional_conditioning = promptlib.reconstruct_cond_batch(unconditional_conditioning, self.step) 29 | 30 | assert all([len(conds) == 1 for conds in conds_list]), 'composition via AND is not supported for DDIM/PLMS samplers' 31 | cond = tensor 32 | 33 | # for DDIM, shapes must match, we can't just process cond and uncond independently; 34 | # filling unconditional_conditioning with repeats of the last vector to match length is 35 | # not 100% correct but should work well enough 36 | if unconditional_conditioning.shape[1] < cond.shape[1]: 37 | last_vector = unconditional_conditioning[:, -1:] 38 | last_vector_repeated = last_vector.repeat([1, cond.shape[1] - unconditional_conditioning.shape[1], 1]) 39 | unconditional_conditioning = torch.hstack([unconditional_conditioning, last_vector_repeated]) 40 | elif unconditional_conditioning.shape[1] > cond.shape[1]: 41 | unconditional_conditioning = unconditional_conditioning[:, :cond.shape[1]] 42 | 43 | if self.mask is not None: 44 | img_orig = self.sampler.model.q_sample(self.init_latent, ts) 45 | x_dec = img_orig * self.mask + self.nmask * x_dec 46 | 47 | res = self.orig_p_sample_ddim(x_dec, cond, ts, unconditional_conditioning=unconditional_conditioning, *args, **kwargs) 48 | 49 | # TODO get store_latent from the plugin itself 50 | if self.mask is not None: 51 | self.p.store_latent(self.init_latent * self.mask + self.nmask * res[1]) 52 | else: 53 | self.p.store_latent(res[1]) 54 | 55 | self.step += 1 56 | return res 57 | 58 | def initialize(self, p): 59 | super(SDSampler_Vanilla, self).initialize(p) 60 | 61 | self.eta = p.eta 62 | 63 | for fieldname in ['p_sample_ddim', 'p_sample_plms']: 64 | if hasattr(self.sampler, fieldname): 65 | setattr(self.sampler, fieldname, self.p_sample_ddim_hook) 66 | 67 | self.mask = p.mask if hasattr(p, 'mask') else None 68 | self.nmask = p.nmask if hasattr(p, 'nmask') else None 69 | 70 | def sample_img2img(self, p, x, noise, conditioning, unconditional_conditioning, steps=None): 71 | steps, t_enc = setup_img2img_steps(p, steps) 72 | 73 | self.initialize(p) 74 | 75 | # existing code fails with certain step counts, like 9 76 | try: 77 | self.sampler.make_schedule(ddim_num_steps=steps, ddim_eta=self.p.eta, ddim_discretize=p.ddim_discretize, verbose=False) 78 | except Exception: 79 | self.sampler.make_schedule(ddim_num_steps=steps + 1, ddim_eta=self.p.eta, ddim_discretize=p.ddim_discretize, verbose=False) 80 | 81 | x1 = self.sampler.stochastic_encode(x, torch.tensor([t_enc] * int(x.shape[0])).to(devicelib.device), noise=noise) 82 | 83 | self.init_latent = x 84 | self.step = 0 85 | 86 | samples = self.sampler.decode(x1, conditioning, t_enc, unconditional_guidance_scale=p.cfg, unconditional_conditioning=unconditional_conditioning) 87 | 88 | return samples 89 | 90 | def sample(self, p, x, conditioning, unconditional_conditioning, steps=None): 91 | self.initialize(p) 92 | 93 | self.init_latent = None 94 | self.step = 0 95 | 96 | steps = steps or p.steps 97 | 98 | # existing code fails with certain step counts, like 9 99 | try: 100 | samples_ddim, _ = self.sampler.sample(S=steps, conditioning=conditioning, batch_size=int(x.shape[0]), shape=x[0].shape, verbose=False, unconditional_guidance_scale=p.cfg, unconditional_conditioning=unconditional_conditioning, x_T=x, eta=self.eta) 101 | except Exception: 102 | samples_ddim, _ = self.sampler.sample(S=steps + 1, conditioning=conditioning, batch_size=int(x.shape[0]), shape=x[0].shape, verbose=False, unconditional_guidance_scale=p.cfg, unconditional_conditioning=unconditional_conditioning, x_T=x, eta=self.eta) 103 | 104 | return samples_ddim -------------------------------------------------------------------------------- /old/stable_diffusion_auto1111/SDUtil.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import random 3 | 4 | import cv2 5 | import numpy as np 6 | import torch 7 | 8 | # from https://discuss.pytorch.org/t/help-regarding-slerp-function-for-generative-model-sampling/32475/3 9 | from PIL.Image import Image 10 | from skimage import exposure 11 | 12 | from core import devicelib 13 | import core.options 14 | # from core.options import opts 15 | 16 | 17 | def slerp(val, low, high): 18 | low_norm = low / torch.norm(low, dim=1, keepdim=True) 19 | high_norm = high / torch.norm(high, dim=1, keepdim=True) 20 | dot = (low_norm * high_norm).sum(1) 21 | 22 | if dot.mean() > 0.9995: 23 | return low * val + high * (1 - val) 24 | 25 | omega = torch.acos(dot) 26 | so = torch.sin(omega) 27 | res = (torch.sin((1.0 - val) * omega) / so).unsqueeze(1) * low + (torch.sin(val * omega) / so).unsqueeze(1) * high 28 | return res 29 | 30 | 31 | def setup_color_correction(image): 32 | logging.info("Calibrating color correction.") 33 | correction_target = cv2.cvtColor(np.asarray(image.copy()), cv2.COLOR_RGB2LAB) 34 | return correction_target 35 | 36 | 37 | def apply_color_correction(correction, image): 38 | logging.info("Applying color correction.") 39 | image = Image.fromarray(cv2.cvtColor(exposure.match_histograms( 40 | cv2.cvtColor( 41 | np.asarray(image), 42 | cv2.COLOR_RGB2LAB 43 | ), 44 | correction, 45 | channel_axis=2 46 | ), cv2.COLOR_LAB2RGB).astype("uint8")) 47 | 48 | return image 49 | 50 | 51 | def get_fixed_seed(seed): 52 | if seed is None or seed == '' or seed == -1: 53 | return int(random.randrange(4294967294)) 54 | 55 | return seed 56 | 57 | 58 | def fix_seed(p): 59 | p.seed = get_fixed_seed(p.seed) 60 | p.seed = get_fixed_seed(p.seed) 61 | 62 | 63 | 64 | 65 | from PIL import Image, ImageFilter, ImageOps 66 | 67 | 68 | def get_crop_region(mask, pad=0): 69 | """finds a rectangular region that contains all masked ares in an image. Returns (x1, y1, x2, y2) coordinates of the rectangle. 70 | For example, if a user has painted the top-right part of a 512x512 image", the result may be (256, 0, 512, 256)""" 71 | 72 | h, w = mask.shape 73 | 74 | crop_left = 0 75 | for i in range(w): 76 | if not (mask[:, i] == 0).all(): 77 | break 78 | crop_left += 1 79 | 80 | crop_right = 0 81 | for i in reversed(range(w)): 82 | if not (mask[:, i] == 0).all(): 83 | break 84 | crop_right += 1 85 | 86 | crop_top = 0 87 | for i in range(h): 88 | if not (mask[i] == 0).all(): 89 | break 90 | crop_top += 1 91 | 92 | crop_bottom = 0 93 | for i in reversed(range(h)): 94 | if not (mask[i] == 0).all(): 95 | break 96 | crop_bottom += 1 97 | 98 | return ( 99 | int(max(crop_left - pad, 0)), 100 | int(max(crop_top - pad, 0)), 101 | int(min(w - crop_right + pad, w)), 102 | int(min(h - crop_bottom + pad, h)) 103 | ) 104 | 105 | 106 | def expand_crop_region(crop_region, processing_width, processing_height, image_width, image_height): 107 | """expands crop region get_crop_region() to match the ratio of the image the region will processed in; returns expanded region 108 | for example, if user drew mask in a 128x32 region, and the dimensions for processing are 512x512, the region will be expanded to 128x128.""" 109 | 110 | x1, y1, x2, y2 = crop_region 111 | 112 | ratio_crop_region = (x2 - x1) / (y2 - y1) 113 | ratio_processing = processing_width / processing_height 114 | 115 | if ratio_crop_region > ratio_processing: 116 | desired_height = (x2 - x1) * ratio_processing 117 | desired_height_diff = int(desired_height - (y2 - y1)) 118 | y1 -= desired_height_diff // 2 119 | y2 += desired_height_diff - desired_height_diff // 2 120 | if y2 >= image_height: 121 | diff = y2 - image_height 122 | y2 -= diff 123 | y1 -= diff 124 | if y1 < 0: 125 | y2 -= y1 126 | y1 -= y1 127 | if y2 >= image_height: 128 | y2 = image_height 129 | else: 130 | desired_width = (y2 - y1) * ratio_processing 131 | desired_width_diff = int(desired_width - (x2 - x1)) 132 | x1 -= desired_width_diff // 2 133 | x2 += desired_width_diff - desired_width_diff // 2 134 | if x2 >= image_width: 135 | diff = x2 - image_width 136 | x2 -= diff 137 | x1 -= diff 138 | if x1 < 0: 139 | x2 -= x1 140 | x1 -= x1 141 | if x2 >= image_width: 142 | x2 = image_width 143 | 144 | return x1, y1, x2, y2 145 | 146 | 147 | def fill(image, mask): 148 | """fills masked regions with colors from image using blur. Not extremely effective.""" 149 | 150 | image_mod = Image.new('RGBA', (image.width, image.height)) 151 | 152 | image_masked = Image.new('RGBa', (image.width, image.height)) 153 | image_masked.paste(image.convert("RGBA").convert("RGBa"), mask=ImageOps.invert(mask.convert('L'))) 154 | 155 | image_masked = image_masked.convert('RGBa') 156 | 157 | for radius, repeats in [(256, 1), (64, 1), (16, 2), (4, 4), (2, 2), (0, 1)]: 158 | blurred = image_masked.filter(ImageFilter.GaussianBlur(radius)).convert('RGBA') 159 | for _ in range(repeats): 160 | image_mod.alpha_composite(blurred) 161 | 162 | return image_mod.convert("RGB") 163 | 164 | -------------------------------------------------------------------------------- /old/stable_diffusion_auto1111/TextInvDataset.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import PIL 4 | import torch 5 | from PIL import Image 6 | from torch.utils.data import Dataset 7 | from torchvision import transforms 8 | 9 | import random 10 | import tqdm 11 | from core import devicelib 12 | import re 13 | 14 | re_tag = re.compile(r"[a-zA-Z][_\w\d()]+") 15 | 16 | # TODO why is this a new class instead of just a function? can we generalize to BetterDataset or something? 17 | class TextInvDataset(Dataset): 18 | def __init__(self, data_root, width, height, repeats, flip_p=0.5, placeholder_token="*", model=None, device=None, template_file=None, include_cond=False): 19 | self.placeholder_token = placeholder_token 20 | 21 | self.width = width 22 | self.height = height 23 | self.flip = transforms.RandomHorizontalFlip(p=flip_p) 24 | 25 | self.dataset = [] 26 | 27 | with open(template_file, "r") as file: 28 | lines = [x.strip() for x in file.readlines()] 29 | 30 | self.lines = lines 31 | 32 | assert data_root, 'dataset directory not specified' 33 | 34 | cond_model = shared.sd_model.cond_stage_model 35 | 36 | self.image_paths = [os.path.join(data_root, file_path) for file_path in os.listdir(data_root)] 37 | 38 | print("Preparing dataset...") 39 | for path in tqdm.tqdm(self.image_paths): 40 | try: 41 | image = Image.open(path).convert('RGB').resize((self.width, self.height), PIL.Image.BICUBIC) 42 | except Exception: 43 | continue 44 | 45 | filename = os.path.basename(path) 46 | filename_tokens = os.path.splitext(filename)[0] 47 | filename_tokens = re_tag.findall(filename_tokens) 48 | 49 | npimage = np.array(image).astype(np.uint8) 50 | npimage = (npimage / 127.5 - 1.0).astype(np.float32) 51 | 52 | torchdata = torch.from_numpy(npimage).to(device=device, dtype=torch.float32) 53 | torchdata = torch.moveaxis(torchdata, 2, 0) 54 | 55 | init_latent = model.get_first_stage_encoding(model.encode_first_stage(torchdata.unsqueeze(dim=0))).squeeze() 56 | init_latent = init_latent.to(devicelib.cpu) 57 | 58 | if include_cond: 59 | text = self.create_text(filename_tokens) 60 | cond = cond_model([text]).to(devicelib.cpu) 61 | else: 62 | cond = None 63 | 64 | self.dataset.append((init_latent, filename_tokens, cond)) 65 | 66 | self.length = len(self.dataset) * repeats 67 | 68 | self.initial_indexes = np.arange(self.length) % len(self.dataset) 69 | self.indexes = None 70 | self.shuffle() 71 | 72 | def shuffle(self): 73 | self.indexes = self.initial_indexes[torch.randperm(self.initial_indexes.shape[0])] 74 | 75 | def create_text(self, filename_tokens): 76 | text = random.choice(self.lines) 77 | text = text.replace("[name]", self.placeholder_token) 78 | text = text.replace("[filewords]", ' '.join(filename_tokens)) 79 | return text 80 | 81 | def __len__(self): 82 | return self.length 83 | 84 | def __getitem__(self, i): 85 | if i % len(self.dataset) == 0: 86 | self.shuffle() 87 | 88 | index = self.indexes[i % len(self.indexes)] 89 | x, filename_tokens, cond = self.dataset[index] 90 | 91 | text = self.create_text(filename_tokens) 92 | return x, text, cond 93 | -------------------------------------------------------------------------------- /old/stable_diffusion_auto1111/TextinvLearnSchedule.py: -------------------------------------------------------------------------------- 1 | 2 | class TextinvLearnSchedule: 3 | def __init__(self, learn_rate, max_steps, cur_step=0): 4 | pairs = learn_rate.split(',') 5 | self.rates = [] 6 | self.it = 0 7 | self.maxit = 0 8 | for i, pair in enumerate(pairs): 9 | tmp = pair.split(':') 10 | if len(tmp) == 2: 11 | step = int(tmp[1]) 12 | if step > cur_step: 13 | self.rates.append((float(tmp[0]), min(step, max_steps))) 14 | self.maxit += 1 15 | if step > max_steps: 16 | return 17 | elif step == -1: 18 | self.rates.append((float(tmp[0]), max_steps)) 19 | self.maxit += 1 20 | return 21 | else: 22 | self.rates.append((float(tmp[0]), max_steps)) 23 | self.maxit += 1 24 | return 25 | 26 | def __iter__(self): 27 | return self 28 | 29 | def __next__(self): 30 | if self.it < self.maxit: 31 | self.it += 1 32 | return self.rates[self.it - 1] 33 | else: 34 | raise StopIteration 35 | -------------------------------------------------------------------------------- /old/stable_diffusion_auto1111/TextinvUI_old.py: -------------------------------------------------------------------------------- 1 | import os 2 | from PIL import Image, ImageOps 3 | import platform 4 | import sys 5 | import tqdm 6 | import time 7 | 8 | # if cmd_opts.deepdanbooru: 9 | # from core import plugins as deepbooru 10 | from core import jobs 11 | -------------------------------------------------------------------------------- /old/stable_diffusion_auto1111/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oxysoft/stable-diffusion-webui/14c1272b5e436bb3eaeb9713a07ea50443e9970e/old/stable_diffusion_auto1111/__init__.py -------------------------------------------------------------------------------- /old/styles.py: -------------------------------------------------------------------------------- 1 | # We need this so Python doesn't complain about the unknown StableDiffusionProcessing-typehint at runtime 2 | from __future__ import annotations 3 | 4 | import csv 5 | import os 6 | import os.path 7 | import typing 8 | import collections.abc as abc 9 | import tempfile 10 | import shutil 11 | 12 | if typing.TYPE_CHECKING: 13 | # Only import this when code is being type-checked, it doesn't have any effect at runtime 14 | from .processing import StableDiffusionProcessing 15 | 16 | 17 | class PromptStyle(typing.NamedTuple): 18 | name: str 19 | prompt: str 20 | negative_prompt: str 21 | 22 | 23 | def merge_prompts(style_prompt: str, prompt: str) -> str: 24 | if "{prompt}" in style_prompt: 25 | res = style_prompt.replace("{prompt}", prompt) 26 | else: 27 | parts = filter(None, (prompt.strip(), style_prompt.strip())) 28 | res = ", ".join(parts) 29 | 30 | return res 31 | 32 | 33 | def apply_styles_to_prompt(prompt, styles): 34 | for style in styles: 35 | prompt = merge_prompts(style, prompt) 36 | 37 | return prompt 38 | 39 | 40 | class StyleDatabase: 41 | def __init__(self, path: str): 42 | self.no_style = PromptStyle("None", "", "") 43 | self.styles = {"None": self.no_style} 44 | 45 | if not os.path.exists(path): 46 | return 47 | 48 | with open(path, "r", encoding="utf8", newline='') as file: 49 | reader = csv.DictReader(file) 50 | for row in reader: 51 | # Support loading old CSV format with "name, text"-columns 52 | prompt = row["prompt"] if "prompt" in row else row["text"] 53 | negative_prompt = row.get("negative_prompt", "") 54 | self.styles[row["name"]] = PromptStyle(row["name"], prompt, negative_prompt) 55 | 56 | def get_style_prompts(self, styles): 57 | return [self.styles.get(x, self.no_style).prompt for x in styles] 58 | 59 | def get_negative_style_prompts(self, styles): 60 | return [self.styles.get(x, self.no_style).negative_prompt for x in styles] 61 | 62 | def apply_styles_to_prompt(self, prompt, styles): 63 | return apply_styles_to_prompt(prompt, [self.styles.get(x, self.no_style).prompt for x in styles]) 64 | 65 | def apply_negative_styles_to_prompt(self, prompt, styles): 66 | return apply_styles_to_prompt(prompt, [self.styles.get(x, self.no_style).negative_prompt for x in styles]) 67 | 68 | def apply_styles(self, p: StableDiffusionProcessing) -> None: 69 | if isinstance(p.prompt, list): 70 | p.prompt = [self.apply_styles_to_prompt(prompt, p.styles) for prompt in p.prompt] 71 | else: 72 | p.prompt = self.apply_styles_to_prompt(p.prompt, p.styles) 73 | 74 | if isinstance(p.promptneg, list): 75 | p.promptneg = [self.apply_negative_styles_to_prompt(prompt, p.styles) for prompt in p.promptneg] 76 | else: 77 | p.promptneg = self.apply_negative_styles_to_prompt(p.promptneg, p.styles) 78 | 79 | def save_styles(self, path: str) -> None: 80 | # Write to temporary file first, so we don't nuke the file if something goes wrong 81 | fd, temp_path = tempfile.mkstemp(".csv") 82 | with os.fdopen(fd, "w", encoding="utf8", newline='') as file: 83 | # _fields is actually part of the public API: typing.NamedTuple is a replacement for collections.NamedTuple, 84 | # and collections.NamedTuple has explicit documentation for accessing _fields. Same goes for _asdict() 85 | writer = csv.DictWriter(file, fieldnames=PromptStyle._fields) 86 | writer.writeheader() 87 | writer.writerows(style._asdict() for k, style in self.styles.items()) 88 | 89 | # Always keep a backup file around 90 | if os.path.exists(path): 91 | shutil.move(path, path + ".bak") 92 | shutil.move(temp_path, path) 93 | -------------------------------------------------------------------------------- /old/upscaler.py: -------------------------------------------------------------------------------- 1 | import os 2 | from abc import abstractmethod 3 | 4 | import PIL 5 | from PIL import Image 6 | 7 | from modules import shared 8 | from core import modellib 9 | 10 | LANCZOS = (Image.Resampling.LANCZOS if hasattr(Image, 'Resampling') else Image.LANCZOS) 11 | from core.paths import modeldir 12 | 13 | 14 | class Upscaler: 15 | name = None 16 | model_path = None 17 | model_name = None 18 | model_url = None 19 | enable = True 20 | filter = None 21 | model = None 22 | user_path = None 23 | scalers: [] 24 | tile = True 25 | 26 | def __init__(self, create_dirs=False): 27 | self.mod_pad_h = None 28 | self.tile_size = shared.opts.ESRGAN_tile 29 | self.tile_pad = shared.opts.ESRGAN_tile_overlap 30 | self.device = shared.device 31 | self.img = None 32 | self.output = None 33 | self.scale = 1 34 | self.half = not shared.cmd_opts.no_half 35 | self.pre_pad = 0 36 | self.mod_scale = None 37 | 38 | if self.model_path is None and self.name: 39 | self.model_path = os.path.join(modeldir, self.name) 40 | if self.model_path and create_dirs: 41 | os.makedirs(self.model_path, exist_ok=True) 42 | 43 | try: 44 | import cv2 45 | self.can_tile = True 46 | except: 47 | pass 48 | 49 | @abstractmethod 50 | def do_upscale(self, img: PIL.Image, selected_model: str): 51 | return img 52 | 53 | def upscale(self, img: PIL.Image, scale: int, selected_model: str = None): 54 | self.scale = scale 55 | dest_w = img.width * scale 56 | dest_h = img.height * scale 57 | for i in range(3): 58 | if img.width >= dest_w and img.height >= dest_h: 59 | break 60 | img = self.do_upscale(img, selected_model) 61 | if img.width != dest_w or img.height != dest_h: 62 | img = img.resize((int(dest_w), int(dest_h)), resample=LANCZOS) 63 | 64 | return img 65 | 66 | @abstractmethod 67 | def load_model(self, path: str): 68 | pass 69 | 70 | def find_models(self, ext_filter=None) -> list: 71 | return modellib.load_models(model_path=self.model_path, model_url=self.model_url, command_path=self.user_path) 72 | 73 | def update_status(self, prompt): 74 | print(f"\nextras: {prompt}", file=shared.progress_print_out) 75 | 76 | 77 | class UpscalerData: 78 | name = None 79 | data_path = None 80 | scale: int = 4 81 | scaler: Upscaler = None 82 | model: None 83 | 84 | def __init__(self, name: str, path: str, upscaler: Upscaler = None, scale: int = 4, model=None): 85 | self.name = name 86 | self.data_path = path 87 | self.scaler = upscaler 88 | self.scale = scale 89 | self.model = model 90 | 91 | 92 | -------------------------------------------------------------------------------- /plug-models/deepbooru/Put your deepbooru release project folder here.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oxysoft/stable-diffusion-webui/14c1272b5e436bb3eaeb9713a07ea50443e9970e/plug-models/deepbooru/Put your deepbooru release project folder here.txt -------------------------------------------------------------------------------- /plugins/ArtistPlugin.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import csv 3 | from collections import namedtuple 4 | 5 | Artist = namedtuple("Artist", ['name', 'weight', 'category']) 6 | 7 | 8 | class ArtistsDatabase: 9 | def __init__(self, filename): 10 | self.cats = set() 11 | self.artists = [] 12 | 13 | if not os.path.exists(filename): 14 | return 15 | 16 | with open(filename, "r", newline='', encoding="utf8") as file: 17 | reader = csv.DictReader(file) 18 | 19 | for row in reader: 20 | artist = Artist(row["artist"], float(row["score"]), row["category"]) 21 | self.artists.append(artist) 22 | self.cats.add(artist.category) 23 | 24 | def categories(self): 25 | return sorted(self.cats) 26 | -------------------------------------------------------------------------------- /plugins/BSRGANPlugin.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import sys 3 | import traceback 4 | 5 | import PIL.Image 6 | import numpy as np 7 | import torch 8 | from basicsr.utils.download_util import load_file_from_url 9 | 10 | import old.upscaler 11 | from core import modellib, devicelib 12 | from BSRGANPlugin_arch import RRDBNet 13 | 14 | 15 | class UpscalerBSRGAN(old.upscaler.Upscaler): 16 | def __init__(self, dirname): 17 | self.name = "BSRGAN" 18 | self.model_name = "BSRGAN 4x" 19 | self.model_url = "https://github.com/cszn/KAIR/releases/download/v1.0/BSRGAN.pth" 20 | self.user_path = dirname 21 | super().__init__() 22 | model_paths = self.find_models(ext_filter=[".pt", ".pth"]) 23 | scalers = [] 24 | if len(model_paths) == 0: 25 | scaler_data = old.upscaler.UpscalerData(self.model_name, self.model_url, self, 4) 26 | scalers.append(scaler_data) 27 | for file in model_paths: 28 | if "http" in file: 29 | name = self.model_name 30 | else: 31 | name = modellib.friendly_name(file) 32 | try: 33 | scaler_data = old.upscaler.UpscalerData(name, file, self, 4) 34 | scalers.append(scaler_data) 35 | except Exception: 36 | print(f"Error loading BSRGAN model: {file}", file=sys.stderr) 37 | print(traceback.format_exc(), file=sys.stderr) 38 | self.scalers = scalers 39 | 40 | def do_upscale(self, img: PIL.Image, selected_file): 41 | torch.cuda.empty_cache() 42 | model = self.load_model(selected_file) 43 | if model is None: 44 | return img 45 | model.to(devicelib.device_bsrgan) 46 | torch.cuda.empty_cache() 47 | img = np.array(img) 48 | img = img[:, :, ::-1] 49 | img = np.moveaxis(img, 2, 0) / 255 50 | img = torch.from_numpy(img).float() 51 | img = img.unsqueeze(0).to(devicelib.device_bsrgan) 52 | with torch.no_grad(): 53 | output = model(img) 54 | output = output.squeeze().float().cpu().clamp_(0, 1).numpy() 55 | output = 255. * np.moveaxis(output, 0, 2) 56 | output = output.astype(np.uint8) 57 | output = output[:, :, ::-1] 58 | torch.cuda.empty_cache() 59 | return PIL.Image.fromarray(output, 'RGB') 60 | 61 | def load_model(self, path: str): 62 | if "http" in path: 63 | filename = load_file_from_url(url=self.model_url, model_dir=self.model_path, file_name="%s.pth" % self.name, 64 | progress=True) 65 | else: 66 | filename = path 67 | if not os.path.exists(filename) or filename is None: 68 | print(f"BSRGAN: Unable to load model from {filename}", file=sys.stderr) 69 | return None 70 | model = RRDBNet(in_nc=3, out_nc=3, nf=64, nb=23, gc=32, sf=4) # define network 71 | model.load_state_dict(torch.load(filename), strict=True) 72 | model.eval() 73 | for k, v in model.named_parameters(): 74 | v.requires_grad = False 75 | return model 76 | 77 | -------------------------------------------------------------------------------- /plugins/BSRGANPlugin_arch.py: -------------------------------------------------------------------------------- 1 | import functools 2 | import torch 3 | import torch.nn as nn 4 | import torch.nn.functional as F 5 | import torch.nn.init as init 6 | 7 | 8 | def initialize_weights(net_l, scale=1): 9 | if not isinstance(net_l, list): 10 | net_l = [net_l] 11 | for net in net_l: 12 | for m in net.modules(): 13 | if isinstance(m, nn.Conv2d): 14 | init.kaiming_normal_(m.weight, a=0, mode='fan_in') 15 | m.weight.data *= scale # for residual block 16 | if m.bias is not None: 17 | m.bias.data.zero_() 18 | elif isinstance(m, nn.Linear): 19 | init.kaiming_normal_(m.weight, a=0, mode='fan_in') 20 | m.weight.data *= scale 21 | if m.bias is not None: 22 | m.bias.data.zero_() 23 | elif isinstance(m, nn.BatchNorm2d): 24 | init.constant_(m.weight, 1) 25 | init.constant_(m.bias.data, 0.0) 26 | 27 | 28 | def make_layer(block, n_layers): 29 | layers = [] 30 | for _ in range(n_layers): 31 | layers.append(block()) 32 | return nn.Sequential(*layers) 33 | 34 | 35 | class ResidualDenseBlock_5C(nn.Module): 36 | def __init__(self, nf=64, gc=32, bias=True): 37 | super(ResidualDenseBlock_5C, self).__init__() 38 | # gc: growth channel, i.e. intermediate channels 39 | self.conv1 = nn.Conv2d(nf, gc, 3, 1, 1, bias=bias) 40 | self.conv2 = nn.Conv2d(nf + gc, gc, 3, 1, 1, bias=bias) 41 | self.conv3 = nn.Conv2d(nf + 2 * gc, gc, 3, 1, 1, bias=bias) 42 | self.conv4 = nn.Conv2d(nf + 3 * gc, gc, 3, 1, 1, bias=bias) 43 | self.conv5 = nn.Conv2d(nf + 4 * gc, nf, 3, 1, 1, bias=bias) 44 | self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True) 45 | 46 | # initialization 47 | initialize_weights([self.conv1, self.conv2, self.conv3, self.conv4, self.conv5], 0.1) 48 | 49 | def forward(self, x): 50 | x1 = self.lrelu(self.conv1(x)) 51 | x2 = self.lrelu(self.conv2(torch.cat((x, x1), 1))) 52 | x3 = self.lrelu(self.conv3(torch.cat((x, x1, x2), 1))) 53 | x4 = self.lrelu(self.conv4(torch.cat((x, x1, x2, x3), 1))) 54 | x5 = self.conv5(torch.cat((x, x1, x2, x3, x4), 1)) 55 | return x5 * 0.2 + x 56 | 57 | 58 | class RRDB(nn.Module): 59 | '''Residual in Residual Dense Block''' 60 | 61 | def __init__(self, nf, gc=32): 62 | super(RRDB, self).__init__() 63 | self.RDB1 = ResidualDenseBlock_5C(nf, gc) 64 | self.RDB2 = ResidualDenseBlock_5C(nf, gc) 65 | self.RDB3 = ResidualDenseBlock_5C(nf, gc) 66 | 67 | def forward(self, x): 68 | out = self.RDB1(x) 69 | out = self.RDB2(out) 70 | out = self.RDB3(out) 71 | return out * 0.2 + x 72 | 73 | 74 | class RRDBNet(nn.Module): 75 | def __init__(self, in_nc=3, out_nc=3, nf=64, nb=23, gc=32, sf=4): 76 | super(RRDBNet, self).__init__() 77 | RRDB_block_f = functools.partial(RRDB, nf=nf, gc=gc) 78 | self.sf = sf 79 | 80 | self.conv_first = nn.Conv2d(in_nc, nf, 3, 1, 1, bias=True) 81 | self.RRDB_trunk = make_layer(RRDB_block_f, nb) 82 | self.trunk_conv = nn.Conv2d(nf, nf, 3, 1, 1, bias=True) 83 | #### upsampling 84 | self.upconv1 = nn.Conv2d(nf, nf, 3, 1, 1, bias=True) 85 | if self.sf==4: 86 | self.upconv2 = nn.Conv2d(nf, nf, 3, 1, 1, bias=True) 87 | self.HRconv = nn.Conv2d(nf, nf, 3, 1, 1, bias=True) 88 | self.conv_last = nn.Conv2d(nf, out_nc, 3, 1, 1, bias=True) 89 | 90 | self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True) 91 | 92 | def forward(self, x): 93 | fea = self.conv_first(x) 94 | trunk = self.trunk_conv(self.RRDB_trunk(fea)) 95 | fea = fea + trunk 96 | 97 | fea = self.lrelu(self.upconv1(F.interpolate(fea, scale_factor=2, mode='nearest'))) 98 | if self.sf==4: 99 | fea = self.lrelu(self.upconv2(F.interpolate(fea, scale_factor=2, mode='nearest'))) 100 | out = self.conv_last(self.lrelu(self.HRconv(fea))) 101 | 102 | return out -------------------------------------------------------------------------------- /plugins/Codeforme_vqgan_arch/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oxysoft/stable-diffusion-webui/14c1272b5e436bb3eaeb9713a07ea50443e9970e/plugins/Codeforme_vqgan_arch/__init__.py -------------------------------------------------------------------------------- /plugins/DeepbooruPlugin.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import multiprocessing 3 | import time 4 | 5 | from core.installing import run_pip 6 | from launch import is_installed 7 | from core.plugins import Plugin 8 | 9 | 10 | 11 | class DeepDanbooruPlugin(Plugin): 12 | def title(self): 13 | return "Deep Danbooru" 14 | 15 | def args(self, parser): 16 | # TODO this is unnecessary 17 | parser.add_argument("--deepdanbooru", action='store_true', help="enable deepdanbooru interrogator") 18 | 19 | def install(self, args): 20 | if not is_installed("deepdanbooru"): 21 | run_pip("install git+https://github.com/KichangKim/DeepDanbooru.git@edf73df4cdaeea2cf00e9ac08bd8a9026b7a7b26#egg=deepdanbooru[tensorflow] tensorflow==2.10.0 tensorflow-io==0.27.0", "deepdanbooru") 22 | 23 | def img2txt(self, pil): 24 | """ 25 | This method is for running only one image at a time for simple use. Used to the img2img interrogate. 26 | """ 27 | self.create_deepbooru_process(self.opts.interrogate_deepbooru_score_threshold, self.opts.deepbooru_sort_alpha) 28 | self.deepbooru_process_return["value"] = -1 29 | self.deepbooru_process_queue.put(pil) 30 | 31 | while self.deepbooru_process_return["value"] == -1: 32 | time.sleep(0.2) 33 | tags = self.deepbooru_process_return["value"] 34 | 35 | self.release_process() 36 | return tags 37 | 38 | def deepbooru_process(queue, deepbooru_process_return, threshold, alpha_sort): 39 | model, tags = get_deepbooru_tags_model() 40 | while True: # while process is running, keep monitoring queue for new image 41 | pil_image = queue.get() 42 | if pil_image == "QUIT": 43 | break 44 | else: 45 | deepbooru_process_return["value"] = get_deepbooru_tags_from_model(model, tags, pil_image, threshold, alpha_sort) 46 | 47 | 48 | def create_deepbooru_process(self, threshold, alpha_sort): 49 | """ 50 | Creates deepbooru process. A queue is created to send images into the process. This enables multiple images 51 | to be processed in a row without reloading the model or creating a new process. To return the data, a self 52 | dictionary is created to hold the tags created. To wait for tags to be returned, a value of -1 is assigned 53 | to the dictionary and the method adding the image to the queue should wait for this value to be updated with 54 | the tags. 55 | """ 56 | self.deepbooru_process_manager = multiprocessing.Manager() 57 | self.deepbooru_process_queue = self.deepbooru_process_manager.Queue() 58 | self.deepbooru_process_return = self.deepbooru_process_manager.dict() 59 | self.deepbooru_process_return["value"] = -1 60 | self.deepbooru_process = multiprocessing.Process(target=deepbooru_process, args=(self.deepbooru_process_queue, self.deepbooru_process_return, threshold, alpha_sort)) 61 | self.deepbooru_process.start() 62 | 63 | 64 | def release_process(self): 65 | """ 66 | Stops the deepbooru process to return used memory 67 | """ 68 | import self 69 | self.deepbooru_process_queue.put("QUIT") 70 | self.deepbooru_process.join() 71 | self.deepbooru_process_queue = None 72 | self.deepbooru_process = None 73 | self.deepbooru_process_return = None 74 | self.deepbooru_process_manager = None 75 | 76 | 77 | def get_deepbooru_tags_model(self): 78 | import deepdanbooru as dd 79 | this_folder = os.path.dirname(__file__) 80 | model_path = os.path.abspath(os.path.join(this_folder, '..', 'models', 'deepbooru')) 81 | if not os.path.exists(os.path.join(model_path, 'project.json')): 82 | # there is no point importing these every time 83 | import zipfile 84 | from basicsr.utils.download_util import load_file_from_url 85 | load_file_from_url( 86 | r"https://github.com/KichangKim/DeepDanbooru/releases/download/v3-20211112-sgd-e28/deepdanbooru-v3-20211112-sgd-e28.zip", 87 | model_path) 88 | with zipfile.ZipFile(os.path.join(model_path, "deepdanbooru-v3-20211112-sgd-e28.zip"), "r") as zip_ref: 89 | zip_ref.extractall(model_path) 90 | os.remove(os.path.join(model_path, "deepdanbooru-v3-20211112-sgd-e28.zip")) 91 | 92 | tags = dd.project.load_tags_from_project(model_path) 93 | model = dd.project.load_model_from_project( 94 | model_path, compile_model=True 95 | ) 96 | return model, tags 97 | 98 | 99 | def get_deepbooru_tags_from_model(self, model, tags, pil_image, threshold, alpha_sort): 100 | import deepdanbooru as dd 101 | import tensorflow as tf 102 | import numpy as np 103 | width = model.input_shape[2] 104 | height = model.input_shape[1] 105 | image = np.array(pil_image) 106 | image = tf.image.resize( 107 | image, 108 | size=(height, width), 109 | method=tf.image.ResizeMethod.AREA, 110 | preserve_aspect_ratio=True, 111 | ) 112 | image = image.numpy() # EagerTensor to np.array 113 | image = dd.image.transform_and_pad_image(image, width, height) 114 | image = image / 255.0 115 | image_shape = image.shape 116 | image = image.reshape((1, image_shape[0], image_shape[1], image_shape[2])) 117 | 118 | y = model.predict(image)[0] 119 | 120 | result_dict = {} 121 | 122 | for i, tag in enumerate(tags): 123 | result_dict[tag] = y[i] 124 | 125 | unsorted_tags_in_theshold = [] 126 | result_tags_print = [] 127 | for tag in tags: 128 | if result_dict[tag] >= threshold: 129 | if tag.startswith("rating:"): 130 | continue 131 | unsorted_tags_in_theshold.append((result_dict[tag], tag)) 132 | result_tags_print.append(f'{result_dict[tag]} {tag}') 133 | 134 | # sort tags 135 | result_tags_out = [] 136 | sort_ndx = 0 137 | if alpha_sort: 138 | sort_ndx = 1 139 | 140 | # sort by reverse by likelihood and normal for alpha 141 | unsorted_tags_in_theshold.sort(key=lambda y: y[sort_ndx], reverse=(not alpha_sort)) 142 | for weight, tag in unsorted_tags_in_theshold: 143 | result_tags_out.append(tag) 144 | 145 | print('\n'.join(sorted(result_tags_print, reverse=True))) 146 | 147 | return ', '.join(result_tags_out).replace('_', ' ').replace(':', ' ') 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /plugins/ESRGANPlugin_arch.py: -------------------------------------------------------------------------------- 1 | # this file is taken from https://github.com/xinntao/ESRGAN 2 | 3 | import functools 4 | import torch 5 | import torch.nn as nn 6 | import torch.nn.functional as F 7 | 8 | 9 | def make_layer(block, n_layers): 10 | layers = [] 11 | for _ in range(n_layers): 12 | layers.append(block()) 13 | return nn.Sequential(*layers) 14 | 15 | 16 | class ResidualDenseBlock_5C(nn.Module): 17 | def __init__(self, nf=64, gc=32, bias=True): 18 | super(ResidualDenseBlock_5C, self).__init__() 19 | # gc: growth channel, i.e. intermediate channels 20 | self.conv1 = nn.Conv2d(nf, gc, 3, 1, 1, bias=bias) 21 | self.conv2 = nn.Conv2d(nf + gc, gc, 3, 1, 1, bias=bias) 22 | self.conv3 = nn.Conv2d(nf + 2 * gc, gc, 3, 1, 1, bias=bias) 23 | self.conv4 = nn.Conv2d(nf + 3 * gc, gc, 3, 1, 1, bias=bias) 24 | self.conv5 = nn.Conv2d(nf + 4 * gc, nf, 3, 1, 1, bias=bias) 25 | self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True) 26 | 27 | # initialization 28 | # mutil.initialize_weights([self.conv1, self.conv2, self.conv3, self.conv4, self.conv5], 0.1) 29 | 30 | def forward(self, x): 31 | x1 = self.lrelu(self.conv1(x)) 32 | x2 = self.lrelu(self.conv2(torch.cat((x, x1), 1))) 33 | x3 = self.lrelu(self.conv3(torch.cat((x, x1, x2), 1))) 34 | x4 = self.lrelu(self.conv4(torch.cat((x, x1, x2, x3), 1))) 35 | x5 = self.conv5(torch.cat((x, x1, x2, x3, x4), 1)) 36 | return x5 * 0.2 + x 37 | 38 | 39 | class RRDB(nn.Module): 40 | '''Residual in Residual Dense Block''' 41 | 42 | def __init__(self, nf, gc=32): 43 | super(RRDB, self).__init__() 44 | self.RDB1 = ResidualDenseBlock_5C(nf, gc) 45 | self.RDB2 = ResidualDenseBlock_5C(nf, gc) 46 | self.RDB3 = ResidualDenseBlock_5C(nf, gc) 47 | 48 | def forward(self, x): 49 | out = self.RDB1(x) 50 | out = self.RDB2(out) 51 | out = self.RDB3(out) 52 | return out * 0.2 + x 53 | 54 | 55 | class RRDBNet(nn.Module): 56 | def __init__(self, in_nc, out_nc, nf, nb, gc=32): 57 | super(RRDBNet, self).__init__() 58 | RRDB_block_f = functools.partial(RRDB, nf=nf, gc=gc) 59 | 60 | self.conv_first = nn.Conv2d(in_nc, nf, 3, 1, 1, bias=True) 61 | self.RRDB_trunk = make_layer(RRDB_block_f, nb) 62 | self.trunk_conv = nn.Conv2d(nf, nf, 3, 1, 1, bias=True) 63 | #### upsampling 64 | self.upconv1 = nn.Conv2d(nf, nf, 3, 1, 1, bias=True) 65 | self.upconv2 = nn.Conv2d(nf, nf, 3, 1, 1, bias=True) 66 | self.HRconv = nn.Conv2d(nf, nf, 3, 1, 1, bias=True) 67 | self.conv_last = nn.Conv2d(nf, out_nc, 3, 1, 1, bias=True) 68 | 69 | self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True) 70 | 71 | def forward(self, x): 72 | fea = self.conv_first(x) 73 | trunk = self.trunk_conv(self.RRDB_trunk(fea)) 74 | fea = fea + trunk 75 | 76 | fea = self.lrelu(self.upconv1(F.interpolate(fea, scale_factor=2, mode='nearest'))) 77 | fea = self.lrelu(self.upconv2(F.interpolate(fea, scale_factor=2, mode='nearest'))) 78 | out = self.conv_last(self.lrelu(self.HRconv(fea))) 79 | 80 | return out 81 | -------------------------------------------------------------------------------- /plugins/GFPGANPlugin.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | 3 | import facexlib 4 | import gfpgan 5 | 6 | import modules.stable_diffusion_auto1111.options 7 | from core import modellib, devicelib 8 | import shared 9 | from core.paths import modeldir, rootdir 10 | from core.installing import * 11 | from shared import cmd_opts 12 | 13 | model_dir = "GFPGAN" 14 | user_path = None 15 | model_path = os.path.join(modeldir, model_dir) 16 | model_url = "https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.4.pth" 17 | have_gfpgan = False 18 | loaded_gfpgan_model = None 19 | 20 | 21 | def gfpgann(): 22 | global loaded_gfpgan_model 23 | global model_path 24 | if loaded_gfpgan_model is not None: 25 | loaded_gfpgan_model.gfpgan.to(devicelib.device_gfpgan) 26 | return loaded_gfpgan_model 27 | 28 | if gfpgan_constructor is None: 29 | return None 30 | 31 | models = modellib.load_models(model_path, model_url, user_path, ext_filter="GFPGAN") 32 | if len(models) == 1 and "http" in models[0]: 33 | model_file = models[0] 34 | elif len(models) != 0: 35 | latest_file = max(models, key=os.path.getctime) 36 | model_file = latest_file 37 | else: 38 | print("Unable to load gfpgan model!") 39 | return None 40 | 41 | model = gfpgan_constructor(model_path=model_file, upscale=1, arch='clean', channel_multiplier=2, bg_upsampler=None) 42 | loaded_gfpgan_model = model 43 | 44 | return model 45 | 46 | 47 | def send_model_to(model, device): 48 | model.gfpgan.to(device) 49 | model.face_helper.face_det.to(device) 50 | model.face_helper.face_parse.to(device) 51 | 52 | 53 | 54 | 55 | gfpgan_constructor = None 56 | 57 | 58 | def setup_model(dirname): 59 | global model_path 60 | if not os.path.exists(model_path): 61 | os.makedirs(model_path) 62 | 63 | try: 64 | from gfpgan import GFPGANer 65 | from facexlib import detection, parsing 66 | global user_path 67 | global have_gfpgan 68 | global gfpgan_constructor 69 | 70 | load_file_from_url_orig = gfpgan.utils.load_file_from_url 71 | facex_load_file_from_url_orig = facexlib.detection.load_file_from_url 72 | facex_load_file_from_url_orig2 = facexlib.parsing.load_file_from_url 73 | 74 | def my_load_file_from_url(**kwargs): 75 | return load_file_from_url_orig(**dict(kwargs, model_dir=model_path)) 76 | 77 | def facex_load_file_from_url(**kwargs): 78 | return facex_load_file_from_url_orig(**dict(kwargs, save_dir=model_path, model_dir=None)) 79 | 80 | def facex_load_file_from_url2(**kwargs): 81 | return facex_load_file_from_url_orig2(**dict(kwargs, save_dir=model_path, model_dir=None)) 82 | 83 | gfpgan.utils.load_file_from_url = my_load_file_from_url 84 | facexlib.detection.load_file_from_url = facex_load_file_from_url 85 | facexlib.parsing.load_file_from_url = facex_load_file_from_url2 86 | user_path = dirname 87 | have_gfpgan = True 88 | gfpgan_constructor = GFPGANer 89 | 90 | except Exception: 91 | print("Error setting up GFPGAN:", file=sys.stderr) 92 | print(traceback.format_exc(), file=sys.stderr) 93 | 94 | 95 | class GFPGANPlugin: 96 | def install(self, args): 97 | gfpgan_package = os.environ.get('GFPGAN_PACKAGE', "git+https://github.com/TencentARC/GFPGAN.git@8d2447a2d918f8eba5a4a01463fd48e45126a379") 98 | if not is_installed("gfpgan"): 99 | run_pip(f"install {gfpgan_package}", "gfpgan") 100 | 101 | def setup(self): 102 | setup_model(cmd_opts.gfpgan_models_path) 103 | 104 | 105 | def postprocess(np_image): 106 | model = gfpgann() 107 | if model is None: 108 | return np_image 109 | 110 | send_model_to(model, devicelib.device_gfpgan) 111 | 112 | np_image_bgr = np_image[:, :, ::-1] 113 | cropped_faces, restored_faces, gfpgan_output_bgr = model.enhance(np_image_bgr, has_aligned=False, only_center_face=False, paste_back=True) 114 | np_image = gfpgan_output_bgr[:, :, ::-1] 115 | 116 | model.face_helper.clean_all() 117 | 118 | if modules.stable_diffusion_auto2222.options.opts.face_restoration_unload: 119 | send_model_to(model, devicelib.cpu) 120 | 121 | return np_image 122 | 123 | def copy_models(self): 124 | src_path = os.path.join(rootdir, "gfpgan") 125 | dest_path = os.path.join(modeldir, "GFPGAN") 126 | move_files(src_path, dest_path) 127 | 128 | -------------------------------------------------------------------------------- /plugins/LDSRPlugin.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import traceback 4 | 5 | from basicsr.utils.download_util import load_file_from_url 6 | 7 | import modules.stable_diffusion_auto1111.options 8 | from core.installing import move_files 9 | from old.upscaler import Upscaler, UpscalerData 10 | from core.plugins import LDSR 11 | import shared 12 | from core.paths import rootdir, modeldir 13 | 14 | 15 | class LDSRPlugin(Upscaler): 16 | def __init__(self, user_path): 17 | self.name = "LDSR" 18 | self.user_path = user_path 19 | self.model_url = "https://heibox.uni-heidelberg.de/f/578df07c8fc04ffbadf3/?dl=1" 20 | self.yaml_url = "https://heibox.uni-heidelberg.de/f/31a76b13ea27482981b4/?dl=1" 21 | super().__init__() 22 | scaler_data = UpscalerData("LDSR", None, self) 23 | self.scalers = [scaler_data] 24 | 25 | def load_model(self, path: str): 26 | # Remove incorrect project.yaml file if too big 27 | yaml_path = os.path.join(self.model_path, "project.yaml") 28 | old_model_path = os.path.join(self.model_path, "model.pth") 29 | new_model_path = os.path.join(self.model_path, "model.ckpt") 30 | if os.path.exists(yaml_path): 31 | statinfo = os.stat(yaml_path) 32 | if statinfo.st_size >= 10485760: 33 | print("Removing invalid LDSR YAML file.") 34 | os.remove(yaml_path) 35 | if os.path.exists(old_model_path): 36 | print("Renaming model from model.pth to model.ckpt") 37 | os.rename(old_model_path, new_model_path) 38 | model = load_file_from_url(url=self.model_url, model_dir=self.model_path, 39 | file_name="model.ckpt", progress=True) 40 | yaml = load_file_from_url(url=self.yaml_url, model_dir=self.model_path, 41 | file_name="project.yaml", progress=True) 42 | 43 | try: 44 | return LDSR(model, yaml) 45 | 46 | except Exception: 47 | print("Error importing LDSR:", file=sys.stderr) 48 | print(traceback.format_exc(), file=sys.stderr) 49 | return None 50 | 51 | def do_upscale(self, img, path): 52 | ldsr = self.load_model(path) 53 | if ldsr is None: 54 | print("NO LDSR!") 55 | return img 56 | ddim_steps = modules.stable_diffusion_auto2222.options.opts.ldsr_steps 57 | return ldsr.super_resolution(img, ddim_steps, self.scale) 58 | 59 | def move_models(self): 60 | src_path = os.path.join(rootdir, "LDSR") 61 | dest_path = os.path.join(modeldir, "LDSR") 62 | move_files(src_path, dest_path) 63 | -------------------------------------------------------------------------------- /plugins/LanczosUpscaler.py: -------------------------------------------------------------------------------- 1 | from old.upscaler import LANCZOS, UpscalerData 2 | from core.plugins import Plugin 3 | 4 | 5 | class LanczosUpscaler(Plugin): 6 | scalers = [] 7 | 8 | def load_model(self, _): 9 | pass 10 | 11 | def postprocess(self, img, selected_model=None): 12 | return img.resize((int(img.width * self.scale), int(img.height * self.scale)), resample=LANCZOS) 13 | 14 | def __init__(self, dirname=None): 15 | super().__init__(False) 16 | self.name = "Lanczos" 17 | self.scalers = [UpscalerData("Lanczos", None, self)] -------------------------------------------------------------------------------- /plugins/RealESRGANPlugin.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import traceback 4 | 5 | import numpy as np 6 | from PIL import Image 7 | from basicsr.utils.download_util import load_file_from_url 8 | from realesrgan import RealESRGANer 9 | 10 | from old.upscaler import Upscaler, UpscalerData 11 | from shared import cmd_opts, opts 12 | 13 | 14 | class UpscalerRealESRGAN(Upscaler): 15 | def __init__(self, path): 16 | self.name = "RealESRGAN" 17 | self.user_path = path 18 | super().__init__() 19 | try: 20 | from basicsr.archs.rrdbnet_arch import RRDBNet 21 | from realesrgan import RealESRGANer 22 | from realesrgan.archs.srvgg_arch import SRVGGNetCompact 23 | self.enable = True 24 | self.scalers = [] 25 | scalers = self.load_models(path) 26 | for scaler in scalers: 27 | if scaler.name in opts.realesrgan_enabled_models: 28 | self.scalers.append(scaler) 29 | 30 | except Exception: 31 | print("Error importing Real-ESRGAN:", file=sys.stderr) 32 | print(traceback.format_exc(), file=sys.stderr) 33 | self.enable = False 34 | self.scalers = [] 35 | 36 | def do_upscale(self, img, path): 37 | if not self.enable: 38 | return img 39 | 40 | info = self.load_model(path) 41 | if not os.path.exists(info.data_path): 42 | print("Unable to load RealESRGAN model: %s" % info.name) 43 | return img 44 | 45 | upsampler = RealESRGANer( 46 | scale=info.scale, 47 | model_path=info.data_path, 48 | model=info.model(), 49 | half=not cmd_opts.no_half, 50 | tile=opts.ESRGAN_tile, 51 | tile_pad=opts.ESRGAN_tile_overlap, 52 | ) 53 | 54 | upsampled = upsampler.enhance(np.array(img), outscale=info.scale)[0] 55 | 56 | image = Image.fromarray(upsampled) 57 | return image 58 | 59 | def load_model(self, path): 60 | try: 61 | info = None 62 | for scaler in self.scalers: 63 | if scaler.data_path == path: 64 | info = scaler 65 | 66 | if info is None: 67 | print(f"Unable to find model info: {path}") 68 | return None 69 | 70 | model_file = load_file_from_url(url=info.data_path, model_dir=self.model_path, progress=True) 71 | info.data_path = model_file 72 | return info 73 | except Exception as e: 74 | print(f"Error making Real-ESRGAN models list: {e}", file=sys.stderr) 75 | print(traceback.format_exc(), file=sys.stderr) 76 | return None 77 | 78 | def load_models(self, _): 79 | return get_realesrgan_models(self) 80 | 81 | 82 | # TODO create modules dynamically for each of these 83 | def get_realesrgan_models(scaler): 84 | try: 85 | from basicsr.archs.rrdbnet_arch import RRDBNet 86 | from realesrgan.archs.srvgg_arch import SRVGGNetCompact 87 | models = [ 88 | UpscalerData( 89 | name="R-ESRGAN General 4xV3", 90 | path="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.5.0/realesr-general-x4v3.pth", 91 | scale=4, 92 | upscaler=scaler, 93 | model=lambda: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=4, act_type='prelu') 94 | ), 95 | UpscalerData( 96 | name="R-ESRGAN General WDN 4xV3", 97 | path="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.5.0/realesr-general-wdn-x4v3.pth", 98 | scale=4, 99 | upscaler=scaler, 100 | model=lambda: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=4, act_type='prelu') 101 | ), 102 | UpscalerData( 103 | name="R-ESRGAN AnimeVideo", 104 | path="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.5.0/realesr-animevideov3.pth", 105 | scale=4, 106 | upscaler=scaler, 107 | model=lambda: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=16, upscale=4, act_type='prelu') 108 | ), 109 | UpscalerData( 110 | name="R-ESRGAN 4x+", 111 | path="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth", 112 | scale=4, 113 | upscaler=scaler, 114 | model=lambda: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=4) 115 | ), 116 | UpscalerData( 117 | name="R-ESRGAN 4x+ Anime6B", 118 | path="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth", 119 | scale=4, 120 | upscaler=scaler, 121 | model=lambda: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=6, num_grow_ch=32, scale=4) 122 | ), 123 | UpscalerData( 124 | name="R-ESRGAN 2x+", 125 | path="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.1/RealESRGAN_x2plus.pth", 126 | scale=2, 127 | upscaler=scaler, 128 | model=lambda: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=2) 129 | ), 130 | ] 131 | return models 132 | except Exception as e: 133 | print("Error making Real-ESRGAN models list:", file=sys.stderr) 134 | print(traceback.format_exc(), file=sys.stderr) 135 | -------------------------------------------------------------------------------- /plugins/SafetyPlugin.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from diffusers.pipelines.stable_diffusion.safety_checker import StableDiffusionSafetyChecker 3 | from transformers import AutoFeatureExtractor 4 | from PIL import Image 5 | 6 | safety_model_id = "CompVis/stable_diffusion-safety-checker" 7 | safety_feature_extractor = None 8 | safety_checker = None 9 | 10 | def numpy_to_pil(images): 11 | """ 12 | Convert a numpy image or a batch of images to a PIL image. 13 | """ 14 | if images.ndim == 3: 15 | images = images[None, ...] 16 | images = (images * 255).round().astype("uint8") 17 | pil_images = [Image.fromarray(image) for image in images] 18 | 19 | return pil_images 20 | 21 | # check and replace nsfw content 22 | def check_safety(x_image): 23 | global safety_feature_extractor, safety_checker 24 | 25 | if safety_feature_extractor is None: 26 | safety_feature_extractor = AutoFeatureExtractor.from_pretrained(safety_model_id) 27 | safety_checker = StableDiffusionSafetyChecker.from_pretrained(safety_model_id) 28 | 29 | safety_checker_input = safety_feature_extractor(numpy_to_pil(x_image), return_tensors="pt") 30 | x_checked_image, has_nsfw_concept = safety_checker(images=x_image, clip_input=safety_checker_input.pixel_values) 31 | 32 | return x_checked_image, has_nsfw_concept 33 | 34 | 35 | def censor_batch(x): 36 | x_samples_ddim_numpy = x.cpu().permute(0, 2, 3, 1).numpy() 37 | x_checked_image, has_nsfw_concept = check_safety(x_samples_ddim_numpy) 38 | x = torch.from_numpy(x_checked_image).permute(0, 3, 1, 2) 39 | 40 | return x 41 | -------------------------------------------------------------------------------- /plugins/ScunetPlugin.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import sys 3 | import traceback 4 | 5 | import PIL.Image 6 | import numpy as np 7 | import torch 8 | from basicsr.utils.download_util import load_file_from_url 9 | 10 | import old.upscaler 11 | from core import modellib, devicelib 12 | from core.plugins import SCUNet as net 13 | 14 | 15 | class UpscalerScuNET(old.upscaler.Upscaler): 16 | def __init__(self, dirname): 17 | self.name = "ScuNET" 18 | self.model_name = "ScuNET GAN" 19 | self.model_name2 = "ScuNET PSNR" 20 | self.model_url = "https://github.com/cszn/KAIR/releases/download/v1.0/scunet_color_real_gan.pth" 21 | self.model_url2 = "https://github.com/cszn/KAIR/releases/download/v1.0/scunet_color_real_psnr.pth" 22 | self.user_path = dirname 23 | super().__init__() 24 | model_paths = self.find_models(ext_filter=[".pth"]) 25 | scalers = [] 26 | add_model2 = True 27 | for file in model_paths: 28 | if "http" in file: 29 | name = self.model_name 30 | else: 31 | name = modellib.friendly_name(file) 32 | if name == self.model_name2 or file == self.model_url2: 33 | add_model2 = False 34 | try: 35 | scaler_data = old.upscaler.UpscalerData(name, file, self, 4) 36 | scalers.append(scaler_data) 37 | except Exception: 38 | print(f"Error loading ScuNET model: {file}", file=sys.stderr) 39 | print(traceback.format_exc(), file=sys.stderr) 40 | if add_model2: 41 | scaler_data2 = old.upscaler.UpscalerData(self.model_name2, self.model_url2, self) 42 | scalers.append(scaler_data2) 43 | self.scalers = scalers 44 | 45 | def do_upscale(self, img: PIL.Image, selected_file): 46 | torch.cuda.empty_cache() 47 | 48 | model = self.load_model(selected_file) 49 | if model is None: 50 | return img 51 | 52 | device = devicelib.device_scunet 53 | img = np.array(img) 54 | img = img[:, :, ::-1] 55 | img = np.moveaxis(img, 2, 0) / 255 56 | img = torch.from_numpy(img).float() 57 | img = img.unsqueeze(0).to(device) 58 | 59 | img = img.to(device) 60 | with torch.no_grad(): 61 | output = model(img) 62 | output = output.squeeze().float().cpu().clamp_(0, 1).numpy() 63 | output = 255. * np.moveaxis(output, 0, 2) 64 | output = output.astype(np.uint8) 65 | output = output[:, :, ::-1] 66 | torch.cuda.empty_cache() 67 | return PIL.Image.fromarray(output, 'RGB') 68 | 69 | def load_model(self, path: str): 70 | device = devicelib.device_scunet 71 | if "http" in path: 72 | filename = load_file_from_url(url=self.model_url, model_dir=self.model_path, file_name="%s.pth" % self.name, 73 | progress=True) 74 | else: 75 | filename = path 76 | if not os.path.exists(os.path.join(self.model_path, filename)) or filename is None: 77 | print(f"ScuNET: Unable to load model from {filename}", file=sys.stderr) 78 | return None 79 | 80 | model = net(in_nc=3, config=[4, 4, 4, 4, 4, 4, 4], dim=64) 81 | model.load_state_dict(torch.load(filename), strict=True) 82 | model.eval() 83 | for k, v in model.named_parameters(): 84 | v.requires_grad = False 85 | model = model.to(device) 86 | 87 | return model 88 | 89 | -------------------------------------------------------------------------------- /plugins/XFormersPlugin.py: -------------------------------------------------------------------------------- 1 | import platform 2 | 3 | from core.installing import run_pip 4 | from core.plugins import Plugin 5 | 6 | 7 | class XFormersPlugin(Plugin): 8 | def title(self): 9 | return "XFormers" 10 | 11 | def describe(self): 12 | return "Handle XFormers installation for other plugins." 13 | 14 | def install(self, args): 15 | if platform.python_version().startswith("3.10"): 16 | if platform.system() == "Windows": 17 | run_pip("install https://github.com/C43H66N12O12S2/stable-diffusion-webui/releases/download/c/xformers-0.0.14.dev0-cp310-cp310-win_amd64.whl", "xformers") 18 | elif platform.system() == "Linux": 19 | run_pip("install xformers", "xformers") -------------------------------------------------------------------------------- /plugins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oxysoft/stable-diffusion-webui/14c1272b5e436bb3eaeb9713a07ea50443e9970e/plugins/__init__.py -------------------------------------------------------------------------------- /plugins/params.txt: -------------------------------------------------------------------------------- 1 | hehe -------------------------------------------------------------------------------- /plugins/prompt_style_plugin/PromptStylePlugin.py: -------------------------------------------------------------------------------- 1 | # We need this so Python doesn't complain about the unknown StableDiffusionProcessing-typehint at runtime 2 | from __future__ import annotations 3 | 4 | import csv 5 | import os 6 | import os.path 7 | import typing 8 | import collections.abc as abc 9 | import tempfile 10 | import shutil 11 | from pathlib import Path 12 | 13 | from core import paths 14 | from core.plugins import Plugin 15 | 16 | if typing.TYPE_CHECKING: 17 | # Only import this when code is being type-checked, it doesn't have any effect at runtime 18 | from modules.stable_diffusion_auto1111.SDJob import SDJob 19 | 20 | class PromptStylePlugin(Plugin): 21 | def __init__(self, dirpath: Path, id: str = None): 22 | super().__init__(dirpath, id) 23 | self.styles = None 24 | 25 | def title(self): 26 | return "Prompt Styles" 27 | 28 | def load(self): 29 | self.styles = StyleDatabase(paths.rootdir / 'styles') 30 | 31 | def prompt2prompt(self, txt): 32 | return txt 33 | 34 | 35 | class PromptStyle(typing.NamedTuple): 36 | name: str 37 | prompt: str 38 | negative_prompt: str 39 | 40 | 41 | def merge_prompts(style_prompt: str, prompt: str) -> str: 42 | if "{prompt}" in style_prompt: 43 | res = style_prompt.replace("{prompt}", prompt) 44 | else: 45 | parts = filter(None, (prompt.strip(), style_prompt.strip())) 46 | res = ", ".join(parts) 47 | 48 | return res 49 | 50 | 51 | def apply_style(prompt, styles): 52 | for style in styles: 53 | prompt = merge_prompts(style, prompt) 54 | 55 | return prompt 56 | 57 | 58 | class StyleDatabase: 59 | def __init__(self, path: str): 60 | self.no_style = PromptStyle("None", "", "") 61 | self.styles = {"None": self.no_style} 62 | 63 | if not os.path.exists(path): 64 | return 65 | 66 | # Support CSV [name|prompt, text] columns 67 | # ---------------------------------------- 68 | with open(path, "r", encoding="utf-8-sig", newline='') as file: 69 | reader = csv.DictReader(file) 70 | for row in reader: 71 | prompt = row["prompt"] if "prompt" in row else row["text"] 72 | promptneg = row.get("promptneg", "") 73 | self.styles[row["name"]] = PromptStyle(row["name"], prompt, promptneg) 74 | 75 | def get_style_prompts(self, styles): 76 | return [self.styles.get(x, self.no_style).prompt for x in styles] 77 | 78 | def get_negative_style_prompts(self, styles): 79 | return [self.styles.get(x, self.no_style).negative_prompt for x in styles] 80 | 81 | def apply_style(self, prompt, styles): 82 | return apply_style(prompt, [self.styles.get(x, self.no_style).prompt for x in styles]) 83 | 84 | def apply_neg_styles(self, prompt, styles): 85 | return apply_style(prompt, [self.styles.get(x, self.no_style).negative_prompt for x in styles]) 86 | 87 | def apply(self, p: SDJob) -> None: 88 | if isinstance(p.prompt, list): 89 | p.prompt = [self.apply_style(prompt, p.styles) for prompt in p.prompt] 90 | else: 91 | p.prompt = self.apply_style(p.prompt, p.styles) 92 | 93 | if isinstance(p.promptneg, list): 94 | p.promptneg = [self.apply_neg_styles(prompt, p.styles) for prompt in p.promptneg] 95 | else: 96 | p.promptneg = self.apply_neg_styles(p.promptneg, p.styles) 97 | 98 | def save_styles(self, path: str) -> None: 99 | # Write to temporary file first, so we don't nuke the file if something goes wrong 100 | fd, temp_path = tempfile.mkstemp(".csv") 101 | with os.fdopen(fd, "w", encoding="utf-8-sig", newline='') as file: 102 | # _fields is actually part of the public API: typing.NamedTuple is a replacement for collections.NamedTuple, 103 | # and collections.NamedTuple has explicit documentation for accessing _fields. Same goes for _asdict() 104 | writer = csv.DictWriter(file, fieldnames=PromptStyle._fields) 105 | writer.writeheader() 106 | writer.writerows(style._asdict() for k, style in self.styles.items()) 107 | 108 | # Always keep a backup file around 109 | if os.path.exists(path): 110 | shutil.move(path, path + ".bak") 111 | shutil.move(temp_path, path) 112 | -------------------------------------------------------------------------------- /plugins/prompt_style_plugin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oxysoft/stable-diffusion-webui/14c1272b5e436bb3eaeb9713a07ea50443e9970e/plugins/prompt_style_plugin/__init__.py -------------------------------------------------------------------------------- /plugins/sd_1111_plugin/SDAttention.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class SDAttention(Enum): 5 | LDM = 0 6 | SPLIT_BASUJINDAL = 1 7 | SPLIT_INVOKE = 2 8 | SPLIT_DOGGETT = 3 9 | XFORMERS = 4 -------------------------------------------------------------------------------- /plugins/sd_1111_plugin/SDPlugin.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import sys 4 | 5 | import devices as devices 6 | # from paths import models_path, script_path, sd_path 7 | from core.jobs import JobData, JobParams 8 | from modules.stable_diffusion_auto1111 import sd_hypernetwork, safe 9 | from modules.stable_diffusion_auto1111.SDAttention import SDAttention 10 | from modules.stable_diffusion_auto1111.options import opts 11 | 12 | # Constants 13 | from modules.stable_diffusion_auto1111.sd_paths import ckpt_dir, hypernetwork_dir 14 | 15 | # Options 16 | attention = SDAttention.SPLIT_DOGGETT 17 | lowvram = False 18 | medvram = True 19 | lowram = False 20 | precision = "full" 21 | no_half = True 22 | opt_channelslast = False 23 | always_batch_cond_uncond = False # disables cond/uncond batching that is enabled to save memory with --medvram or --lowvram 24 | xformers = False 25 | force_enable_xformers = False 26 | use_cpu = False 27 | batch_cond_uncond = always_batch_cond_uncond or not (lowvram or medvram) 28 | 29 | # Arguments 30 | # ---------------------------------------- 31 | parser = argparse.ArgumentParser() 32 | parser.add_argument("--device-id", type=str, help="Select the default CUDA device to use (export CUDA_VISIBLE_DEVICES=0,1,etc might be needed before)", default=None) 33 | cmd_opts = parser.parse_args() 34 | 35 | # Hardware and optimizations 36 | # ---------------------------------------- 37 | weight_load_location = None if lowram else "cpu" 38 | 39 | parallel_processing_allowed = not lowvram and not medvram 40 | safe.run(devices.enable_tf32, "Enabling TF32") 41 | devices.set(devices.get_optimal_device(), 'half') 42 | 43 | # Hypernetworks 44 | # ---------------------------------------- 45 | os.makedirs(hypernetwork_dir, exist_ok=True) 46 | 47 | def reload_hypernetworks(): 48 | global hypernetworks 49 | 50 | hypernetworks = hypernetwork.discover_hypernetworks(cmd_opts.hypernetwork_dir) 51 | hypernetwork.load_hypernetwork(opts.sd_hypernetwork) 52 | 53 | 54 | # Interrogate 55 | # ---------------------------------------- 56 | # import interrogate 57 | # interrogator = interrogate.InterrogateModels("interrogate") 58 | 59 | sdmodel = None 60 | clip_model = None 61 | progress_print_out = sys.stdout 62 | import os 63 | import threading 64 | 65 | from core.plugins import Plugin 66 | from modules.stable_diffusion_auto1111 import sd_models, sd_samplers 67 | from modules.stable_diffusion_auto1111.SDJob import process_images, SDJob_txt 68 | from modules.stable_diffusion_auto1111.sd_models import model_path, discover_models 69 | 70 | queue_lock = threading.Lock() 71 | 72 | 73 | class SDPlugin(Plugin): 74 | def title(self): 75 | return "Stable Diffusion AUTO1111" 76 | 77 | def load(self): 78 | # modelloader.cleanup_models() 79 | if not os.path.exists(model_path): 80 | os.makedirs(model_path) 81 | 82 | discover_models() 83 | 84 | # codeformer.setup_model(cmd_opts.codeformer_models_path) 85 | # gfpgan.setup_model(cmd_opts.gfpgan_models_path) 86 | # SDPlugin.face_restorers.append(modules.face_restoration.FaceRestoration()) 87 | # modelloader.load_upscalers() 88 | 89 | # modules.scripts.load_scripts() 90 | 91 | sd_models.load_model() 92 | sd_models.discover_models() 93 | 94 | # SDPlugin.opts.onchange("sd_model_checkpoint", wrap_queued_call(lambda: sd_models.reload_model_weights(SDPlugin.sd_model))) 95 | # SDPlugin.opts.onchange("sd_hypernetwork", wrap_queued_call(lambda: hypernetworks.hypernetwork.load_hypernetwork(SDPlugin.opts.sd_hypernetwork))) 96 | # SDPlugin.opts.onchange("sd_hypernetwork_strength", hypernetworks.hypernetwork.apply_strength) 97 | 98 | print('Refreshing Model List') 99 | 100 | def txt2img(self, prompt: str): 101 | process_images(SDJob_txt(prompt=prompt)) -------------------------------------------------------------------------------- /plugins/sd_1111_plugin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oxysoft/stable-diffusion-webui/14c1272b5e436bb3eaeb9713a07ea50443e9970e/plugins/sd_1111_plugin/__init__.py -------------------------------------------------------------------------------- /plugins/sd_1111_plugin/devices.py: -------------------------------------------------------------------------------- 1 | import sys, os, shlex 2 | import contextlib 3 | import torch 4 | 5 | import errors 6 | 7 | 8 | # has_mps is only available in nightly pytorch (for now), `getattr` for compatibility 9 | 10 | def extract_device_id(args, name): 11 | for x in range(len(args)): 12 | if name in args[x]: return args[x + 1] 13 | return None 14 | 15 | 16 | def get_optimal_device(device_id=None): 17 | if torch.cuda.is_available(): 18 | if device_id is not None: 19 | cuda_device = f"cuda:{device_id}" 20 | return torch.device(cuda_device) 21 | else: 22 | return torch.device("cuda") 23 | 24 | if has_mps: 25 | return torch.device("mps") 26 | 27 | return cpu 28 | 29 | 30 | def torch_gc(): 31 | if torch.cuda.is_available(): 32 | torch.cuda.empty_cache() 33 | torch.cuda.ipc_collect() 34 | 35 | 36 | def enable_tf32(): 37 | if torch.cuda.is_available(): 38 | torch.backends.cuda.matmul.allow_tf32 = True 39 | torch.backends.cudnn.allow_tf32 = True 40 | 41 | 42 | def randn(seed, shape): 43 | # Pytorch currently doesn't handle setting randomness correctly when the metal backend is used. 44 | if device.type == 'mps': 45 | generator = torch.Generator(device=cpu) 46 | generator.manual_seed(seed) 47 | noise = torch.randn(shape, generator=generator, device=cpu).to(device) 48 | return noise 49 | 50 | torch.manual_seed(seed) 51 | return torch.randn(shape, device=device) 52 | 53 | 54 | def randn_without_seed(shape): 55 | # Pytorch currently doesn't handle setting randomness correctly when the metal backend is used. 56 | if device.type == 'mps': 57 | generator = torch.Generator(device=cpu) 58 | noise = torch.randn(shape, generator=generator, device=cpu).to(device) 59 | return noise 60 | 61 | return torch.randn(shape, device=device) 62 | 63 | 64 | def autocast(disable=False): 65 | if disable: 66 | return contextlib.nullcontext() 67 | 68 | if dtype == torch.float32: 69 | return contextlib.nullcontext() 70 | elif dtype == torch.float16: 71 | return dtype == torch.autocast("cuda") 72 | 73 | raise ValueError(f"Unknown precision {precision}") 74 | 75 | 76 | has_mps = getattr(torch, 'has_mps', False) 77 | cpu = torch.device("cpu") 78 | 79 | # State values 80 | device = get_optimal_device() 81 | precision = 'half' 82 | dtype = torch.float16 83 | dtype_vae = torch.float16 84 | 85 | 86 | def set_optimal(_precision='half', _precision_vae='half'): 87 | set(get_optimal_device(), _precision, _precision_vae) 88 | 89 | 90 | def set(_device=None, _precision='half', _precision_vae='half'): 91 | global device, precision, dtype, dtype_vae 92 | if _device is None: 93 | _device = get_optimal_device() 94 | 95 | _device = _device 96 | precision = _precision 97 | 98 | if precision == 'half': 99 | dtype = torch.float16 100 | elif precision == 'full': 101 | dtype = torch.float32 102 | 103 | if _precision_vae == 'half': 104 | dtype_vae = torch.float16 105 | elif _precision_vae == 'full': 106 | dtype_vae = torch.float32 107 | 108 | xformers_available = False 109 | 110 | try: 111 | import xformers.ops 112 | xformers_available = True 113 | except Exception: 114 | # print("Cannot import xformers", file=sys.stderr) 115 | # print(traceback.format_exc(), file=sys.stderr) 116 | pass 117 | 118 | -------------------------------------------------------------------------------- /plugins/sd_1111_plugin/img2img.py: -------------------------------------------------------------------------------- 1 | # import os 2 | # 3 | # from PIL import Image, ImageOps, ImageChops 4 | # 5 | # from processing import Processed, StableDiffusionProcessingImg2Img, process_images 6 | # from SDPlugin import opts 7 | # import SDPlugin as SDPlugin 8 | # import processing as processing 9 | # 10 | # 11 | # def img2img(mode: int, 12 | # prompt: str, 13 | # negative_prompt: str, 14 | # prompt_style: str, 15 | # prompt_style2: str, 16 | # init_img, 17 | # init_img_with_mask, 18 | # init_img_inpaint, 19 | # init_mask_inpaint, 20 | # mask_mode, 21 | # steps: int, 22 | # sampler_index: int, 23 | # mask_blur: int, 24 | # inpainting_fill: int, 25 | # restore_faces: bool, 26 | # tiling: bool, 27 | # n_iter: int, 28 | # batch_size: int, 29 | # cfg_scale: float, 30 | # denoising_strength: float, 31 | # seed: int, 32 | # subseed: int, 33 | # subseed_strength: float, 34 | # seed_resize_from_h: int, 35 | # seed_resize_from_w: int, 36 | # seed_enable_extras: bool, 37 | # height: int, 38 | # width: int, 39 | # resize_mode: int, 40 | # inpaint_full_res: bool, 41 | # inpaint_full_res_padding: int, 42 | # inpainting_mask_invert: int, 43 | # img2img_batch_input_dir: str, 44 | # img2img_batch_output_dir: str, 45 | # *args): 46 | # is_inpaint = mode == 1 47 | # is_batch = mode == 2 48 | # 49 | # if is_inpaint: 50 | # if mask_mode == 0: 51 | # image = init_img_with_mask['image'] 52 | # mask = init_img_with_mask['mask'] 53 | # alpha_mask = ImageOps.invert(image.split()[-1]).convert('L').point(lambda x: 255 if x > 0 else 0, mode='1') 54 | # mask = ImageChops.lighter(alpha_mask, mask.convert('L')).convert('L') 55 | # image = image.convert('RGB') 56 | # else: 57 | # image = init_img_inpaint 58 | # mask = init_mask_inpaint 59 | # else: 60 | # image = init_img 61 | # mask = None 62 | # 63 | # assert 0. <= denoising_strength <= 1., 'can only work with strength in [0.0, 1.0]' 64 | # 65 | # p = StableDiffusionProcessingImg2Img( 66 | # sd_model=SDPlugin.sd_model, 67 | # outpath_samples=opts.outdir_samples or opts.outdir_img2img_samples, 68 | # outpath_grids=opts.outdir_grids or opts.outdir_img2img_grids, 69 | # prompt=prompt, 70 | # negative_prompt=negative_prompt, 71 | # styles=[prompt_style, prompt_style2], 72 | # seed=seed, 73 | # subseed=subseed, 74 | # subseed_strength=subseed_strength, 75 | # seed_resize_from_h=seed_resize_from_h, 76 | # seed_resize_from_w=seed_resize_from_w, 77 | # seed_enable_extras=seed_enable_extras, 78 | # sampler_index=sampler_index, 79 | # batch_size=batch_size, 80 | # n_iter=n_iter, 81 | # steps=steps, 82 | # cfg_scale=cfg_scale, 83 | # width=width, 84 | # height=height, 85 | # restore_faces=restore_faces, 86 | # tiling=tiling, 87 | # init_images=[image], 88 | # mask=mask, 89 | # mask_blur=mask_blur, 90 | # inpainting_fill=inpainting_fill, 91 | # resize_mode=resize_mode, 92 | # denoising_strength=denoising_strength, 93 | # inpaint_full_res=inpaint_full_res, 94 | # inpaint_full_res_padding=inpaint_full_res_padding, 95 | # inpainting_mask_invert=inpainting_mask_invert, 96 | # ) 97 | # 98 | # p.script_args = args 99 | # 100 | # if SDPlugin.cmd_opts.enable_console_prompts: 101 | # print(f"\nimg2img: {prompt}", file=SDPlugin.progress_print_out) 102 | # 103 | # p.extra_generation_params["Mask blur"] = mask_blur 104 | # 105 | # if is_batch: 106 | # assert not SDPlugin.cmd_opts.hide_ui_dir_config, "Launched with --hide-ui-dir-config, batch img2img disabled" 107 | # 108 | # processing.fix_seed(p) 109 | # images1 = [file for file in [os.path.join(img2img_batch_input_dir, x1) for x1 in os.listdir(img2img_batch_input_dir)] if os.path.isfile(file)] 110 | # 111 | # print(f"Will process {len(images1)} images, creating {p.n_iter * p.batch_size} new images for each.") 112 | # save_normally = img2img_batch_output_dir == '' 113 | # p.do_not_save_grid = True 114 | # p.do_not_save_samples = not save_normally 115 | # for i, image1 in enumerate(images1): 116 | # # if state.skipped: 117 | # # state.skipped = False 118 | # 119 | # # if state.interrupted: 120 | # # break 121 | # 122 | # img = Image.open(image1) 123 | # p.init_images = [img] * p.batch_size 124 | # 125 | # proc = process_images(p) 126 | # for n, processed_image in enumerate(proc.images): 127 | # filename = os.path.basename(image1) 128 | # 129 | # if n > 0: 130 | # left, right = os.path.splitext(filename) 131 | # filename = f"{left}-{n}{right}" 132 | # 133 | # if not save_normally: 134 | # processed_image.save(os.path.join(img2img_batch_output_dir, filename)) 135 | # 136 | # processed = Processed(p, [], p.seed, "") 137 | # else: 138 | # processed = process_images(p) 139 | # 140 | # return processed.images 141 | # 142 | # 143 | -------------------------------------------------------------------------------- /plugins/sd_1111_plugin/masking.py: -------------------------------------------------------------------------------- 1 | from PIL import Image, ImageFilter, ImageOps 2 | 3 | 4 | def get_crop_region(mask, pad=0): 5 | """finds a rectangular region that contains all masked ares in an image. Returns (x1, y1, x2, y2) coordinates of the rectangle. 6 | For example, if a user has painted the top-right part of a 512x512 image", the result may be (256, 0, 512, 256)""" 7 | 8 | h, w = mask.shape 9 | 10 | crop_left = 0 11 | for i in range(w): 12 | if not (mask[:, i] == 0).all(): 13 | break 14 | crop_left += 1 15 | 16 | crop_right = 0 17 | for i in reversed(range(w)): 18 | if not (mask[:, i] == 0).all(): 19 | break 20 | crop_right += 1 21 | 22 | crop_top = 0 23 | for i in range(h): 24 | if not (mask[i] == 0).all(): 25 | break 26 | crop_top += 1 27 | 28 | crop_bottom = 0 29 | for i in reversed(range(h)): 30 | if not (mask[i] == 0).all(): 31 | break 32 | crop_bottom += 1 33 | 34 | return ( 35 | int(max(crop_left-pad, 0)), 36 | int(max(crop_top-pad, 0)), 37 | int(min(w - crop_right + pad, w)), 38 | int(min(h - crop_bottom + pad, h)) 39 | ) 40 | 41 | 42 | def expand_crop_region(crop_region, processing_width, processing_height, image_width, image_height): 43 | """expands crop region get_crop_region() to match the ratio of the image the region will processed in; returns expanded region 44 | for example, if user drew mask in a 128x32 region, and the dimensions for processing are 512x512, the region will be expanded to 128x128.""" 45 | 46 | x1, y1, x2, y2 = crop_region 47 | 48 | ratio_crop_region = (x2 - x1) / (y2 - y1) 49 | ratio_processing = processing_width / processing_height 50 | 51 | if ratio_crop_region > ratio_processing: 52 | desired_height = (x2 - x1) * ratio_processing 53 | desired_height_diff = int(desired_height - (y2-y1)) 54 | y1 -= desired_height_diff//2 55 | y2 += desired_height_diff - desired_height_diff//2 56 | if y2 >= image_height: 57 | diff = y2 - image_height 58 | y2 -= diff 59 | y1 -= diff 60 | if y1 < 0: 61 | y2 -= y1 62 | y1 -= y1 63 | if y2 >= image_height: 64 | y2 = image_height 65 | else: 66 | desired_width = (y2 - y1) * ratio_processing 67 | desired_width_diff = int(desired_width - (x2-x1)) 68 | x1 -= desired_width_diff//2 69 | x2 += desired_width_diff - desired_width_diff//2 70 | if x2 >= image_width: 71 | diff = x2 - image_width 72 | x2 -= diff 73 | x1 -= diff 74 | if x1 < 0: 75 | x2 -= x1 76 | x1 -= x1 77 | if x2 >= image_width: 78 | x2 = image_width 79 | 80 | return x1, y1, x2, y2 81 | 82 | 83 | def fill(image, mask): 84 | """fills masked regions with colors from image using blur. Not extremely effective.""" 85 | 86 | image_mod = Image.new('RGBA', (image.width, image.height)) 87 | 88 | image_masked = Image.new('RGBa', (image.width, image.height)) 89 | image_masked.paste(image.convert("RGBA").convert("RGBa"), mask=ImageOps.invert(mask.convert('L'))) 90 | 91 | image_masked = image_masked.convert('RGBa') 92 | 93 | for radius, repeats in [(256, 1), (64, 1), (16, 2), (4, 4), (2, 2), (0, 1)]: 94 | blurred = image_masked.filter(ImageFilter.GaussianBlur(radius)).convert('RGBA') 95 | for _ in range(repeats): 96 | image_mod.alpha_composite(blurred) 97 | 98 | return image_mod.convert("RGB") 99 | 100 | -------------------------------------------------------------------------------- /plugins/sd_1111_plugin/modelloader.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import os 3 | import shutil 4 | import importlib 5 | from urllib.parse import urlparse 6 | 7 | # from basicsr.utils.download_util import load_file_from_url 8 | import SDPlugin 9 | from core.modellib import load_file_from_url 10 | 11 | 12 | 13 | 14 | 15 | 16 | # def cleanup_models(): 17 | # # This code could probably be more efficient if we used a tuple list or something to store the src/destinations 18 | # # and then enumerate that, but this works for now. In the future, it'd be nice to just have every "model" scaler 19 | # # somehow auto-register and just do these things... 20 | # root_path = script_path 21 | # src_path = models_path 22 | # dest_path = os.path.join(models_path, "Stable-diffusion") 23 | # move_files(src_path, dest_path, ".ckpt") 24 | # src_path = os.path.join(root_path, "ESRGAN") 25 | # dest_path = os.path.join(models_path, "ESRGAN") 26 | # move_files(src_path, dest_path) 27 | # src_path = os.path.join(root_path, "gfpgan") 28 | # dest_path = os.path.join(models_path, "GFPGAN") 29 | # move_files(src_path, dest_path) 30 | # src_path = os.path.join(root_path, "SwinIR") 31 | # dest_path = os.path.join(models_path, "SwinIR") 32 | # move_files(src_path, dest_path) 33 | # src_path = os.path.join(root_path, "plugin-repos/latent-diffusion/experiments/pretrained_models/") 34 | # dest_path = os.path.join(models_path, "LDSR") 35 | # move_files(src_path, dest_path) 36 | 37 | 38 | def move_files(src_path: str, dest_path: str, ext_filter: str = None): 39 | try: 40 | if not os.path.exists(dest_path): 41 | os.makedirs(dest_path) 42 | if os.path.exists(src_path): 43 | for file in os.listdir(src_path): 44 | fullpath = os.path.join(src_path, file) 45 | if os.path.isfile(fullpath): 46 | if ext_filter is not None: 47 | if ext_filter not in file: 48 | continue 49 | print(f"Moving {file} from {src_path} to {dest_path}.") 50 | try: 51 | shutil.move(fullpath, dest_path) 52 | except: 53 | pass 54 | if len(os.listdir(src_path)) == 0: 55 | print(f"Removing empty folder: {src_path}") 56 | shutil.rmtree(src_path, True) 57 | except: 58 | pass -------------------------------------------------------------------------------- /plugins/sd_1111_plugin/modelsplit.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | import devices 4 | 5 | module_in_gpu = None 6 | cpu = torch.device("cpu") 7 | 8 | 9 | def send_everything_to_cpu(): 10 | global module_in_gpu 11 | 12 | if module_in_gpu is not None: 13 | module_in_gpu.to(cpu) 14 | 15 | module_in_gpu = None 16 | 17 | 18 | def setup_for_low_vram(sd_model, use_medvram): 19 | parents = {} 20 | 21 | def send_me_to_gpu(module, _): 22 | """send this module to GPU; send whatever tracked module was previous in GPU to CPU; 23 | we add this as forward_pre_hook to a lot of modules and this way all but one of them will 24 | be in CPU 25 | """ 26 | global module_in_gpu 27 | 28 | module = parents.get(module, module) 29 | 30 | if module_in_gpu == module: 31 | return 32 | 33 | if module_in_gpu is not None: 34 | module_in_gpu.to(cpu) 35 | 36 | module.to(devices.device) 37 | module_in_gpu = module 38 | 39 | # see below for register_forward_pre_hook; 40 | # first_stage_model does not use forward(), it uses encode/decode, so register_forward_pre_hook is 41 | # useless here, and we just replace those methods 42 | def first_stage_model_encode_wrap(self, encoder, x): 43 | send_me_to_gpu(self, None) 44 | return encoder(x) 45 | 46 | def first_stage_model_decode_wrap(self, decoder, z): 47 | send_me_to_gpu(self, None) 48 | return decoder(z) 49 | 50 | # remove three big modules, cond, first_stage, and unet from the model and then 51 | # send the model to GPU. Then put modules back. the modules will be in CPU. 52 | stored = sd_model.cond_stage_model.transformer, sd_model.first_stage_model, sd_model.model 53 | sd_model.cond_stage_model.transformer, sd_model.first_stage_model, sd_model.model = None, None, None 54 | sd_model.to(devices.device) 55 | sd_model.cond_stage_model.transformer, sd_model.first_stage_model, sd_model.model = stored 56 | 57 | # register hooks for those the first two models 58 | sd_model.cond_stage_model.transformer.register_forward_pre_hook(send_me_to_gpu) 59 | sd_model.first_stage_model.register_forward_pre_hook(send_me_to_gpu) 60 | sd_model.first_stage_model.encode = lambda x, en=sd_model.first_stage_model.encode: first_stage_model_encode_wrap(sd_model.first_stage_model, en, x) 61 | sd_model.first_stage_model.decode = lambda z, de=sd_model.first_stage_model.decode: first_stage_model_decode_wrap(sd_model.first_stage_model, de, z) 62 | parents[sd_model.cond_stage_model.transformer] = sd_model.cond_stage_model 63 | 64 | if use_medvram: 65 | sd_model.model.register_forward_pre_hook(send_me_to_gpu) 66 | else: 67 | diff_model = sd_model.model.diffusion_model 68 | 69 | # the third remaining model is still too big for 4 GB, so we also do the same for its submodules 70 | # so that only one of them is in GPU at a time 71 | stored = diff_model.input_blocks, diff_model.middle_block, diff_model.output_blocks, diff_model.time_embed 72 | diff_model.input_blocks, diff_model.middle_block, diff_model.output_blocks, diff_model.time_embed = None, None, None, None 73 | sd_model.model.to(devices.device) 74 | diff_model.input_blocks, diff_model.middle_block, diff_model.output_blocks, diff_model.time_embed = stored 75 | 76 | # install hooks for bits of third model 77 | diff_model.time_embed.register_forward_pre_hook(send_me_to_gpu) 78 | for block in diff_model.input_blocks: 79 | block.register_forward_pre_hook(send_me_to_gpu) 80 | diff_model.middle_block.register_forward_pre_hook(send_me_to_gpu) 81 | for block in diff_model.output_blocks: 82 | block.register_forward_pre_hook(send_me_to_gpu) 83 | -------------------------------------------------------------------------------- /plugins/sd_1111_plugin/safe.py: -------------------------------------------------------------------------------- 1 | # this code is adapted from the script contributed by anon from /h/ 2 | 3 | import _codecs 4 | import collections 5 | import pickle 6 | import re 7 | import sys 8 | import traceback 9 | import zipfile 10 | import numpy 11 | import torch 12 | 13 | # PyTorch 1.13 and later have _TypedStorage renamed to TypedStorage 14 | TypedStorage = torch.storage.TypedStorage if hasattr(torch.storage, 'TypedStorage') else torch.storage._TypedStorage 15 | 16 | disable_safe_unpickle = False 17 | 18 | def encode(*args): 19 | out = _codecs.encode(*args) 20 | return out 21 | 22 | 23 | class RestrictedUnpickler(pickle.Unpickler): 24 | def persistent_load(self, saved_id): 25 | assert saved_id[0] == 'storage' 26 | return TypedStorage() 27 | 28 | def find_class(self, module, name): 29 | if module == 'collections' and name == 'OrderedDict': 30 | return getattr(collections, name) 31 | if module == 'torch._utils' and name in ['_rebuild_tensor_v2', '_rebuild_parameter']: 32 | return getattr(torch._utils, name) 33 | if module == 'torch' and name in ['FloatStorage', 'HalfStorage', 'IntStorage', 'LongStorage', 'DoubleStorage']: 34 | return getattr(torch, name) 35 | if module == 'torch.nn.modules.container' and name in ['ParameterDict']: 36 | return getattr(torch.nn.modules.container, name) 37 | if module == 'numpy.core.multiarray' and name == 'scalar': 38 | return numpy.core.multiarray.scalar 39 | if module == 'numpy' and name == 'dtype': 40 | return numpy.dtype 41 | if module == '_codecs' and name == 'encode': 42 | return encode 43 | if module == "pytorch_lightning.callbacks" and name == 'model_checkpoint': 44 | import pytorch_lightning.callbacks 45 | return pytorch_lightning.callbacks.model_checkpoint 46 | if module == "pytorch_lightning.callbacks.model_checkpoint" and name == 'ModelCheckpoint': 47 | import pytorch_lightning.callbacks.model_checkpoint 48 | return pytorch_lightning.callbacks.model_checkpoint.ModelCheckpoint 49 | if module == "__builtin__" and name == 'set': 50 | return set 51 | 52 | # Forbid everything else. 53 | raise pickle.UnpicklingError(f"global '{module}/{name}' is forbidden") 54 | 55 | 56 | allowed_zip_names = ["archive/data.pkl", "archive/version"] 57 | allowed_zip_names_re = re.compile(r"^archive/data/\d+$") 58 | 59 | 60 | def check_zip_filenames(filename, names): 61 | for name in names: 62 | if name in allowed_zip_names: 63 | continue 64 | if allowed_zip_names_re.match(name): 65 | continue 66 | 67 | raise Exception(f"bad file inside {filename}: {name}") 68 | 69 | 70 | def check_pt(filename): 71 | try: 72 | # new pytorch format is a zip file 73 | with zipfile.ZipFile(filename) as z: 74 | check_zip_filenames(filename, z.namelist()) 75 | 76 | with z.open('archive/data.pkl') as file: 77 | unpickler = RestrictedUnpickler(file) 78 | unpickler.load() 79 | 80 | except zipfile.BadZipfile: 81 | # if it's not a zip file, it's an olf pytorch format, with five objects written to pickle 82 | with open(filename, "rb") as file: 83 | unpickler = RestrictedUnpickler(file) 84 | for i in range(5): 85 | unpickler.load() 86 | 87 | 88 | def load(filename, *args, **kwargs): 89 | import SDPlugin 90 | 91 | try: 92 | if not disable_safe_unpickle: 93 | check_pt(filename) 94 | 95 | except pickle.UnpicklingError: 96 | print(f"Error verifying pickled file from {filename}:", file=sys.stderr) 97 | print(traceback.format_exc(), file=sys.stderr) 98 | print(f"-----> !!!! The file is most likely corrupted !!!! <-----", file=sys.stderr) 99 | print(f"You can skip this check with --disable-safe-unpickle commandline argument, but that is not going to help you.\n\n", file=sys.stderr) 100 | return None 101 | 102 | except Exception: 103 | print(f"Error verifying pickled file from {filename}:", file=sys.stderr) 104 | print(traceback.format_exc(), file=sys.stderr) 105 | print(f"\nThe file may be malicious, so the program is not going to read it.", file=sys.stderr) 106 | print(f"You can skip this check with --disable-safe-unpickle commandline argument.\n\n", file=sys.stderr) 107 | return None 108 | 109 | return unsafe_torch_load(filename, *args, **kwargs) 110 | 111 | 112 | def run(code, task): 113 | try: 114 | code() 115 | except Exception as e: 116 | print(f"{task}: {type(e).__name__}", file=sys.stderr) 117 | print(traceback.format_exc(), file=sys.stderr) 118 | 119 | 120 | unsafe_torch_load = torch.load 121 | torch.load = load 122 | -------------------------------------------------------------------------------- /plugins/sd_1111_plugin/sd_paths.py: -------------------------------------------------------------------------------- 1 | from core import paths 2 | 3 | config = paths.repodir / 'stable_diffusion' / 'configs/stable-diffusion/v1-inference.yaml' 4 | ckpt = "models/Stable-diffusion/sd-v1-4.ckpt" 5 | ckpt_dir = paths.modeldir / 'Stable-diffusion' 6 | embeddings_dir = paths.rootdir / 'embeddings' 7 | hypernetwork_dir = ckpt_dir / 'hypernetworks' 8 | vae_path = None 9 | 10 | ckpt_dir = ckpt_dir.as_posix() # TODO we should refactor all the code to use pathlib instead, it's 100x more readable 11 | -------------------------------------------------------------------------------- /plugins/sd_1111_plugin/sd_textinv_dataset.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import PIL 4 | import torch 5 | from PIL import Image 6 | from torch.utils.data import Dataset 7 | from torchvision import transforms 8 | 9 | import random 10 | import tqdm 11 | 12 | import modules.stable_diffusion_auto1111.options 13 | from modules.stable_diffusion_auto1111 import devices 14 | import re 15 | 16 | re_numbers_at_start = re.compile(r"^[-\d]+\s*") 17 | 18 | 19 | class DatasetEntry: 20 | def __init__(self, filename=None, latent=None, filename_text=None): 21 | self.filename = filename 22 | self.latent = latent 23 | self.filename_text = filename_text 24 | self.cond = None 25 | self.cond_text = None 26 | 27 | 28 | class PersonalizedBase(Dataset): 29 | def __init__(self, data_root, width, height, repeats, flip_p=0.5, placeholder_token="*", model=None, device=None, template_file=None, include_cond=False, batch_size=1): 30 | re_word = re.compile(modules.stable_diffusion_auto2222.options.opts.dataset_filename_word_regex) if len(modules.stable_diffusion_auto2222.options.opts.dataset_filename_word_regex) > 0 else None 31 | 32 | self.placeholder_token = placeholder_token 33 | 34 | self.batch_size = batch_size 35 | self.width = width 36 | self.height = height 37 | self.flip = transforms.RandomHorizontalFlip(p=flip_p) 38 | 39 | self.dataset = [] 40 | 41 | with open(template_file, "r") as file: 42 | lines = [x.strip() for x in file.readlines()] 43 | 44 | self.lines = lines 45 | 46 | assert data_root, 'dataset directory not specified' 47 | 48 | from modules.stable_diffusion_auto1111 import SDPlugin 49 | cond_model = SDPlugin.sdmodel.cond_stage_model 50 | 51 | self.image_paths = [os.path.join(data_root, file_path) for file_path in os.listdir(data_root)] 52 | print("Preparing dataset...") 53 | for path in tqdm.tqdm(self.image_paths): 54 | try: 55 | image = Image.open(path).convert('RGB').resize((self.width, self.height), PIL.Image.BICUBIC) 56 | except Exception: 57 | continue 58 | 59 | text_filename = os.path.splitext(path)[0] + ".txt" 60 | filename = os.path.basename(path) 61 | 62 | if os.path.exists(text_filename): 63 | with open(text_filename, "r", encoding="utf8") as file: 64 | filename_text = file.read() 65 | else: 66 | filename_text = os.path.splitext(filename)[0] 67 | filename_text = re.sub(re_numbers_at_start, '', filename_text) 68 | if re_word: 69 | tokens = re_word.findall(filename_text) 70 | filename_text = (modules.stable_diffusion_auto2222.options.opts.dataset_filename_join_string or "").join(tokens) 71 | 72 | npimage = np.array(image).astype(np.uint8) 73 | npimage = (npimage / 127.5 - 1.0).astype(np.float32) 74 | 75 | torchdata = torch.from_numpy(npimage).to(device=device, dtype=torch.float32) 76 | torchdata = torch.moveaxis(torchdata, 2, 0) 77 | 78 | init_latent = model.get_first_stage_encoding(model.encode_first_stage(torchdata.unsqueeze(dim=0))).squeeze() 79 | init_latent = init_latent.to(devices.cpu) 80 | 81 | entry = DatasetEntry(filename=path, filename_text=filename_text, latent=init_latent) 82 | 83 | if include_cond: 84 | entry.cond_text = self.create_text(filename_text) 85 | entry.cond = cond_model([entry.cond_text]).to(devices.cpu).squeeze(0) 86 | 87 | self.dataset.append(entry) 88 | 89 | assert len(self.dataset) > 0, "No images have been found in the dataset." 90 | self.length = len(self.dataset) * repeats // batch_size 91 | 92 | self.initial_indexes = np.arange(len(self.dataset)) 93 | self.indexes = None 94 | self.shuffle() 95 | 96 | def shuffle(self): 97 | self.indexes = self.initial_indexes[torch.randperm(self.initial_indexes.shape[0]).numpy()] 98 | 99 | def create_text(self, filename_text): 100 | text = random.choice(self.lines) 101 | text = text.replace("[name]", self.placeholder_token) 102 | text = text.replace("[filewords]", filename_text) 103 | return text 104 | 105 | def __len__(self): 106 | return self.length 107 | 108 | def __getitem__(self, i): 109 | res = [] 110 | 111 | for j in range(self.batch_size): 112 | position = i * self.batch_size + j 113 | if position % len(self.indexes) == 0: 114 | self.shuffle() 115 | 116 | index = self.indexes[position % len(self.indexes)] 117 | entry = self.dataset[index] 118 | 119 | if entry.cond is None: 120 | entry.cond_text = self.create_text(entry.filename_text) 121 | 122 | res.append(entry) 123 | 124 | return res 125 | -------------------------------------------------------------------------------- /plugins/sd_1111_plugin/sd_textinv_learn_schedule.py: -------------------------------------------------------------------------------- 1 | import tqdm 2 | 3 | 4 | class LearnScheduleIterator: 5 | def __init__(self, learn_rate, max_steps, cur_step=0): 6 | """ 7 | specify learn_rate as "0.001:100, 0.00001:1000, 1e-5:10000" to have lr of 0.001 until step 100, 0.00001 until 1000, 1e-5:10000 until 10000 8 | """ 9 | 10 | pairs = learn_rate.split(',') 11 | self.rates = [] 12 | self.it = 0 13 | self.maxit = 0 14 | for i, pair in enumerate(pairs): 15 | tmp = pair.split(':') 16 | if len(tmp) == 2: 17 | step = int(tmp[1]) 18 | if step > cur_step: 19 | self.rates.append((float(tmp[0]), min(step, max_steps))) 20 | self.maxit += 1 21 | if step > max_steps: 22 | return 23 | elif step == -1: 24 | self.rates.append((float(tmp[0]), max_steps)) 25 | self.maxit += 1 26 | return 27 | else: 28 | self.rates.append((float(tmp[0]), max_steps)) 29 | self.maxit += 1 30 | return 31 | 32 | def __iter__(self): 33 | return self 34 | 35 | def __next__(self): 36 | if self.it < self.maxit: 37 | self.it += 1 38 | return self.rates[self.it - 1] 39 | else: 40 | raise StopIteration 41 | 42 | 43 | class LearnRateScheduler: 44 | def __init__(self, learn_rate, max_steps, cur_step=0, verbose=True): 45 | self.schedules = LearnScheduleIterator(learn_rate, max_steps, cur_step) 46 | (self.learn_rate, self.end_step) = next(self.schedules) 47 | self.verbose = verbose 48 | 49 | if self.verbose: 50 | print(f'Training at rate of {self.learn_rate} until step {self.end_step}') 51 | 52 | self.finished = False 53 | 54 | def apply(self, optimizer, step_number): 55 | if step_number <= self.end_step: 56 | return 57 | 58 | try: 59 | (self.learn_rate, self.end_step) = next(self.schedules) 60 | except Exception: 61 | self.finished = True 62 | return 63 | 64 | if self.verbose: 65 | tqdm.tqdm.write(f'Training at rate of {self.learn_rate} until step {self.end_step}') 66 | 67 | for pg in optimizer.param_groups: 68 | pg['lr'] = self.learn_rate 69 | 70 | -------------------------------------------------------------------------------- /plugins/sd_1111_plugin/sd_textinv_preprocess.py: -------------------------------------------------------------------------------- 1 | import os 2 | from PIL import Image, ImageOps 3 | import math 4 | import platform 5 | import sys 6 | import tqdm 7 | import time 8 | 9 | from modules.stable_diffusion_auto1111 import SDPlugin, images 10 | from modules.stable_diffusion_auto1111.SDPlugin import cmd_opts 11 | from modules.stable_diffusion_auto1111.options import opts 12 | 13 | 14 | # if cmd_opts.deepdanbooru: 15 | # import modules.stable_diffusion_auto2222.deepbooru as deepbooru 16 | 17 | 18 | def preprocess(process_src, process_dst, process_width, process_height, preprocess_txt_action, process_flip, process_split, process_caption, process_caption_deepbooru=False, split_threshold=0.5, overlap_ratio=0.2): 19 | try: 20 | if process_caption: 21 | SDPlugin.interrogator.load() 22 | 23 | # if process_caption_deepbooru: 24 | # db_opts = deepbooru.create_deepbooru_opts() 25 | # db_opts[deepbooru.OPT_INCLUDE_RANKS] = False 26 | # deepbooru.create_deepbooru_process(opts.interrogate_deepbooru_score_threshold, db_opts) 27 | 28 | preprocess_work(process_src, process_dst, process_width, process_height, preprocess_txt_action, process_flip, process_split, process_caption, process_caption_deepbooru, split_threshold, overlap_ratio) 29 | 30 | finally: 31 | 32 | if process_caption: 33 | SDPlugin.interrogator.send_blip_to_ram() 34 | 35 | # if process_caption_deepbooru: 36 | # deepbooru.release_process() 37 | 38 | 39 | 40 | def preprocess_work(process_src, process_dst, process_width, process_height, preprocess_txt_action, process_flip, process_split, process_caption, process_caption_deepbooru=False, split_threshold=0.5, overlap_ratio=0.2): 41 | width = process_width 42 | height = process_height 43 | src = os.path.abspath(process_src) 44 | dst = os.path.abspath(process_dst) 45 | split_threshold = max(0.0, min(1.0, split_threshold)) 46 | overlap_ratio = max(0.0, min(0.9, overlap_ratio)) 47 | 48 | assert src != dst, 'same directory specified as source and destination' 49 | 50 | os.makedirs(dst, exist_ok=True) 51 | 52 | files = os.listdir(src) 53 | 54 | SDPlugin.state.textinfo = "Preprocessing..." 55 | SDPlugin.state.job_count = len(files) 56 | 57 | def save_pic_with_caption(image, index, existing_caption=None): 58 | caption = "" 59 | 60 | if process_caption: 61 | caption += SDPlugin.interrogator.generate_caption(image) 62 | 63 | # if process_caption_deepbooru: 64 | # if len(caption) > 0: 65 | # caption += ", " 66 | # caption += deepbooru.get_tags_from_process(image) 67 | 68 | filename_part = filename 69 | filename_part = os.path.splitext(filename_part)[0] 70 | filename_part = os.path.basename(filename_part) 71 | 72 | basename = f"{index:05}-{subindex[0]}-{filename_part}" 73 | image.save(os.path.join(dst, f"{basename}.png")) 74 | 75 | if preprocess_txt_action == 'prepend' and existing_caption: 76 | caption = existing_caption + ' ' + caption 77 | elif preprocess_txt_action == 'append' and existing_caption: 78 | caption = caption + ' ' + existing_caption 79 | elif preprocess_txt_action == 'copy' and existing_caption: 80 | caption = existing_caption 81 | 82 | caption = caption.strip() 83 | 84 | if len(caption) > 0: 85 | with open(os.path.join(dst, f"{basename}.txt"), "w", encoding="utf8") as file: 86 | file.write(caption) 87 | 88 | subindex[0] += 1 89 | 90 | def save_pic(image, index, existing_caption=None): 91 | save_pic_with_caption(image, index, existing_caption=existing_caption) 92 | 93 | if process_flip: 94 | save_pic_with_caption(ImageOps.mirror(image), index, existing_caption=existing_caption) 95 | 96 | def split_pic(image, inverse_xy): 97 | if inverse_xy: 98 | from_w, from_h = image.height, image.width 99 | to_w, to_h = height, width 100 | else: 101 | from_w, from_h = image.width, image.height 102 | to_w, to_h = width, height 103 | h = from_h * to_w // from_w 104 | if inverse_xy: 105 | image = image.resize((h, to_w)) 106 | else: 107 | image = image.resize((to_w, h)) 108 | 109 | split_count = math.ceil((h - to_h * overlap_ratio) / (to_h * (1.0 - overlap_ratio))) 110 | y_step = (h - to_h) / (split_count - 1) 111 | for i in range(split_count): 112 | y = int(y_step * i) 113 | if inverse_xy: 114 | splitted = image.crop((y, 0, y + to_h, to_w)) 115 | else: 116 | splitted = image.crop((0, y, to_w, y + to_h)) 117 | yield splitted 118 | 119 | for index, imagefile in enumerate(tqdm.tqdm(files)): 120 | subindex = [0] 121 | filename = os.path.join(src, imagefile) 122 | try: 123 | img = Image.open(filename).convert("RGB") 124 | except Exception: 125 | continue 126 | 127 | existing_caption = None 128 | existing_caption_filename = os.path.splitext(filename)[0] + '.txt' 129 | if os.path.exists(existing_caption_filename): 130 | with open(existing_caption_filename, 'r', encoding="utf8") as file: 131 | existing_caption = file.read() 132 | 133 | if SDPlugin.state.interrupted: 134 | break 135 | 136 | if img.height > img.width: 137 | ratio = (img.width * height) / (img.height * width) 138 | inverse_xy = False 139 | else: 140 | ratio = (img.height * width) / (img.width * height) 141 | inverse_xy = True 142 | 143 | if process_split and ratio < 1.0 and ratio <= split_threshold: 144 | for splitted in split_pic(img, inverse_xy): 145 | save_pic(splitted, index, existing_caption=existing_caption) 146 | else: 147 | img = images.resize_image(1, img, width, height) 148 | save_pic(img, index, existing_caption=existing_caption) 149 | 150 | SDPlugin.state.nextjob() 151 | -------------------------------------------------------------------------------- /plugins/sd_1111_plugin/txt2img.py: -------------------------------------------------------------------------------- 1 | # import SDPlugin as SDPlugin 2 | # from processing import StableDiffusionProcessingTxt2Img, process_images 3 | # 4 | # 5 | # def txt2img(prompt: str, 6 | # negative_prompt: str = "", 7 | # prompt_style: str = "", 8 | # prompt_style2: str = "", 9 | # steps: int = 22, 10 | # sampler_index: int = 0, 11 | # tiling: bool = False, 12 | # n_iter: int = 1, 13 | # batch_size: int = 1, 14 | # cfg_scale: float = 7.0, 15 | # seed: int = -1, 16 | # subseed: int = -1, 17 | # subseed_strength: float = 1, 18 | # seed_resize_from_h: int = 512, 19 | # seed_resize_from_w: int = 512, 20 | # seed_enable_extras: bool = False, 21 | # height: int = 512, 22 | # width: int = 512, 23 | # enable_hr: bool = False, 24 | # denoising_strength: float = 0.5, 25 | # firstphase_width: int = 0, 26 | # firstphase_height: int = 0, 27 | # *args): 28 | # p = StableDiffusionProcessingTxt2Img( 29 | # sd_model=SDPlugin.sd_model, 30 | # # outpath_samples=opts.outdir_samples or opts.outdir_txt2img_samples, 31 | # # outpath_grids=opts.outdir_grids or opts.outdir_txt2img_grids, 32 | # prompt=prompt, 33 | # # styles=[prompt_style, prompt_style2], 34 | # negative_prompt=negative_prompt, 35 | # seed=seed, 36 | # subseed=subseed, 37 | # subseed_strength=subseed_strength, 38 | # seed_resize_from_h=seed_resize_from_h, 39 | # seed_resize_from_w=seed_resize_from_w, 40 | # seed_enable_extras=seed_enable_extras, 41 | # sampler_index=sampler_index, 42 | # batch_size=batch_size, 43 | # n_iter=n_iter, 44 | # steps=steps, 45 | # cfg=cfg_scale, 46 | # width=width, 47 | # height=height, 48 | # tiling=tiling, 49 | # enable_hr=enable_hr, 50 | # denoising_strength=denoising_strength if enable_hr else None, 51 | # firstphase_width=firstphase_width if enable_hr else None, 52 | # firstphase_height=firstphase_height if enable_hr else None, 53 | # ) 54 | # 55 | # p.script_args = args 56 | # 57 | # return process_images(p).images 58 | -------------------------------------------------------------------------------- /plugins/sd_diffusers_plugin/README.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | This plugin is up for grabs, anyone feel free to contribute 4 | Diffusers works differently and has different generation qualities to it, worth supporting along with auto1111 -------------------------------------------------------------------------------- /plugins/sd_diffusers_plugin/StableDiffusionPluginHF.py: -------------------------------------------------------------------------------- 1 | # import torch 2 | # 3 | # from core.plugins import Plugin 4 | # 5 | # from diffusers import StableDiffusionPipeline, LMSDiscreteScheduler 6 | # 7 | # 8 | # class StableDiffusionPluginHF(Plugin): 9 | # def txt2img(self): 10 | # from transformers import CLIPTextModel, CLIPTokenizer 11 | # from diffusers import AutoencoderKL, UNet2DConditionModel, PNDMScheduler 12 | # 13 | # p = "~/stable-diffusion-webui-dev/models/Stable-diffusion/sd-v1-4.ckpt" 14 | # pipe = StableDiffusionPipeline.from_pretrained(p, torch_type=torch.float32, revision="fp32") 15 | # # pipe = pipe.to("cuda") 16 | # 17 | # prompt = "a photo of an astronaut riding a horse on mars" 18 | # image = pipe(prompt).images[0] 19 | # 20 | # # 1. Load the autoencoder model which will be used to decode the latents into image space. 21 | # vae = AutoencoderKL.from_pretrained("CompVis/stable-diffusion-v1-4", subfolder="vae") 22 | # 23 | # # 2. Load the tokenizer and text encoder to tokenize and encode the text. 24 | # tokenizer = CLIPTokenizer.from_pretrained("openai/clip-vit-large-patch14") 25 | # text_encoder = CLIPTextModel.from_pretrained("openai/clip-vit-large-patch14") 26 | # 27 | # # 3. The UNet model for generating the latents. 28 | # unet = UNet2DConditionModel.from_pretrained("CompVis/stable-diffusion-v1-4", subfolder="unet") 29 | # 30 | # scheduler = LMSDiscreteScheduler(beta_start=0.00085, beta_end=0.012, beta_schedule="scaled_linear", num_train_timesteps=1000) 31 | # 32 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | basicsr 2 | diffusers 3 | fairscale==0.4.4 4 | fonts 5 | font-roboto 6 | gfpgan 7 | gradio==3.4.1 8 | invisible-watermark 9 | numpy 10 | omegaconf 11 | piexif 12 | Pillow 13 | pytorch_lightning 14 | realesrgan 15 | scikit-image>=0.19 16 | timm==0.4.12 17 | transformers==4.19.2 18 | torch 19 | einops 20 | jsonmerge 21 | clean-fid 22 | resize-right 23 | torchdiffeq 24 | kornia 25 | lark 26 | flask 27 | flask-socketio 28 | bunch 29 | -------------------------------------------------------------------------------- /requirements_versions.txt: -------------------------------------------------------------------------------- 1 | transformers==4.19.2 2 | diffusers==0.3.0 3 | basicsr==1.4.2 4 | gfpgan==1.3.8 5 | gradio==3.4.1 6 | numpy==1.23.3 7 | Pillow==9.2.0 8 | realesrgan==0.3.0 9 | torch 10 | omegaconf==2.2.3 11 | pytorch_lightning==1.7.6 12 | scikit-image==0.19.2 13 | fonts 14 | font-roboto 15 | timm==0.6.7 16 | fairscale==0.4.9 17 | piexif==1.1.3 18 | einops==0.4.1 19 | jsonmerge==1.8.0 20 | clean-fid==0.1.29 21 | resize-right==0.0.2 22 | torchdiffeq==0.2.3 23 | kornia==0.6.7 24 | lark==1.1.2 25 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oxysoft/stable-diffusion-webui/14c1272b5e436bb3eaeb9713a07ea50443e9970e/screenshot.png -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | function gradioApp(){ 2 | return document.getElementsByTagName('gradio-app')[0].shadowRoot; 3 | } 4 | 5 | function get_uiCurrentTab() { 6 | return gradioApp().querySelector('.tabs button:not(.border-transparent)') 7 | } 8 | 9 | function get_uiCurrentTabContent() { 10 | return gradioApp().querySelector('.tabitem[id^=tab_]:not([style*="display: none"])') 11 | } 12 | 13 | uiUpdateCallbacks = [] 14 | uiTabChangeCallbacks = [] 15 | let uiCurrentTab = null 16 | 17 | function onUiUpdate(callback){ 18 | uiUpdateCallbacks.push(callback) 19 | } 20 | function onUiTabChange(callback){ 21 | uiTabChangeCallbacks.push(callback) 22 | } 23 | 24 | function runCallback(x){ 25 | try { 26 | x() 27 | } catch (e) { 28 | (console.error || console.log).call(console, e.message, e); 29 | } 30 | } 31 | function executeCallbacks(queue) { 32 | queue.forEach(runCallback) 33 | } 34 | 35 | document.addEventListener("DOMContentLoaded", function() { 36 | var mutationObserver = new MutationObserver(function(m){ 37 | executeCallbacks(uiUpdateCallbacks); 38 | const newTab = get_uiCurrentTab(); 39 | if ( newTab && ( newTab !== uiCurrentTab ) ) { 40 | uiCurrentTab = newTab; 41 | executeCallbacks(uiTabChangeCallbacks); 42 | } 43 | }); 44 | mutationObserver.observe( gradioApp(), { childList:true, subtree:true }) 45 | }); 46 | 47 | /** 48 | * Add a ctrl+enter as a shortcut to start a generation 49 | */ 50 | document.addEventListener('keydown', function(e) { 51 | var handled = false; 52 | if (e.key !== undefined) { 53 | if((e.key == "Enter" && (e.metaKey || e.ctrlKey))) handled = true; 54 | } else if (e.keyCode !== undefined) { 55 | if((e.keyCode == 13 && (e.metaKey || e.ctrlKey))) handled = true; 56 | } 57 | if (handled) { 58 | button = get_uiCurrentTabContent().querySelector('button[id$=_generate]'); 59 | if (button) { 60 | button.click(); 61 | } 62 | e.preventDefault(); 63 | } 64 | }) 65 | 66 | /** 67 | * checks that a UI element is not in another hidden element or tab content 68 | */ 69 | function uiElementIsVisible(el) { 70 | let isVisible = !el.closest('.\\!hidden'); 71 | if ( ! isVisible ) { 72 | return false; 73 | } 74 | 75 | while( isVisible = el.closest('.tabitem')?.style.display !== 'none' ) { 76 | if ( ! isVisible ) { 77 | return false; 78 | } else if ( el.parentElement ) { 79 | el = el.parentElement 80 | } else { 81 | break; 82 | } 83 | } 84 | return isVisible; 85 | } -------------------------------------------------------------------------------- /scripts/custom_code.py: -------------------------------------------------------------------------------- 1 | from core import plugins as scripts 2 | import gradio as gr 3 | 4 | from modules.processing import Processed 5 | from shared import cmd_opts 6 | 7 | 8 | class Script(scripts.Plugin): 9 | 10 | def title(self): 11 | return "Custom code" 12 | 13 | 14 | def show(self, is_img2img): 15 | return cmd_opts.allow_code 16 | 17 | def ui(self, is_img2img): 18 | code = gr.Textbox(label="Python code", visible=False, lines=1) 19 | 20 | return [code] 21 | 22 | 23 | def run(self, p, code): 24 | assert cmd_opts.allow_code, '--allow-code option must be enabled' 25 | 26 | display_result_data = [[], -1, ""] 27 | 28 | def display(imgs, s=display_result_data[1], i=display_result_data[2]): 29 | display_result_data[0] = imgs 30 | display_result_data[1] = s 31 | display_result_data[2] = i 32 | 33 | from types import ModuleType 34 | compiled = compile(code, '', 'exec') 35 | module = ModuleType("testmodule") 36 | module.__dict__.update(globals()) 37 | module.p = p 38 | module.display = display 39 | exec(compiled, module.__dict__) 40 | 41 | return Processed(p, *display_result_data) 42 | 43 | -------------------------------------------------------------------------------- /scripts/loopback.py: -------------------------------------------------------------------------------- 1 | from core import plugins as scripts 2 | import gradio as gr 3 | 4 | from modules import processing 5 | from modules.processing import Processed 6 | from shared import opts, state 7 | 8 | class Script(scripts.Plugin): 9 | def title(self): 10 | return "Loopback" 11 | 12 | def show(self, is_img2img): 13 | return is_img2img 14 | 15 | def ui(self, is_img2img): 16 | loops = gr.Slider(minimum=1, maximum=32, step=1, label='Loops', value=4) 17 | denoising_strength_change_factor = gr.Slider(minimum=0.9, maximum=1.1, step=0.01, label='Denoising strength change factor', value=1) 18 | 19 | return [loops, denoising_strength_change_factor] 20 | 21 | def run(self, p, loops, denoising_strength_change_factor): 22 | processing.fix_seed(p) 23 | batch_count = p.n_iter 24 | p.extra_generation_params = { 25 | "Denoising strength change factor": denoising_strength_change_factor, 26 | } 27 | 28 | p.batch_size = 1 29 | p.n_iter = 1 30 | 31 | output_images, info = None, None 32 | initial_seed = None 33 | initial_info = None 34 | 35 | grids = [] 36 | all_images = [] 37 | original_init_image = p.init_images 38 | state.job_count = loops * batch_count 39 | 40 | initial_color_corrections = [processing.setup_color_correction(p.init_images[0])] 41 | 42 | for n in range(batch_count): 43 | history = [] 44 | 45 | # Reset to original init image at the start of each batch 46 | p.init_images = original_init_image 47 | 48 | for i in range(loops): 49 | p.n_iter = 1 50 | p.batch_size = 1 51 | p.do_not_save_grid = True 52 | 53 | if opts.img2img_color_correction: 54 | p.color_corrections = initial_color_corrections 55 | 56 | state.job = f"Iteration {i + 1}/{loops}, batch {n + 1}/{batch_count}" 57 | 58 | processed = processing.process_job(p) 59 | 60 | if initial_seed is None: 61 | initial_seed = processed.seed 62 | initial_info = processed.info 63 | 64 | init_img = processed.images[0] 65 | 66 | p.init_images = [init_img] 67 | p.seed = processed.seed + 1 68 | p.denoising_strength = min(max(p.denoising_strength * denoising_strength_change_factor, 0.1), 1) 69 | history.append(processed.images[0]) 70 | 71 | grid = images.image_grid(history, rows=1) 72 | if opts.grid_save: 73 | images.save_image(grid, p.outpath_grids, "grid", initial_seed, p.prompt, opts.grid_format, metadata=info, short_filename=not opts.grid_extended_filename, grid=True, p=p) 74 | 75 | grids.append(grid) 76 | all_images += history 77 | 78 | if opts.return_grid: 79 | all_images = grids + all_images 80 | 81 | processed = Processed(p, all_images, initial_seed, initial_info) 82 | 83 | return processed 84 | -------------------------------------------------------------------------------- /scripts/poor_mans_outpainting.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | from core import plugins as scripts, devicelib 4 | import gradio as gr 5 | from PIL import Image, ImageDraw 6 | 7 | from modules.processing import Processed, process_images 8 | from shared import opts, state 9 | 10 | 11 | 12 | class Script(scripts.Plugin): 13 | def title(self): 14 | return "Poor man's outpainting" 15 | 16 | def show(self, is_img2img): 17 | return is_img2img 18 | 19 | def ui(self, is_img2img): 20 | if not is_img2img: 21 | return None 22 | 23 | pixels = gr.Slider(label="Pixels to expand", minimum=8, maximum=256, step=8, value=128) 24 | mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=4, visible=False) 25 | inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='fill', type="index", visible=False) 26 | direction = gr.CheckboxGroup(label="Outpainting direction", choices=['left', 'right', 'up', 'down'], value=['left', 'right', 'up', 'down']) 27 | 28 | return [pixels, mask_blur, inpainting_fill, direction] 29 | 30 | def run(self, p, pixels, mask_blur, inpainting_fill, direction): 31 | initial_seed = None 32 | initial_info = None 33 | 34 | p.mask_blur = mask_blur * 2 35 | p.inpainting_fill = inpainting_fill 36 | p.inpaint_full_res = False 37 | 38 | left = pixels if "left" in direction else 0 39 | right = pixels if "right" in direction else 0 40 | up = pixels if "up" in direction else 0 41 | down = pixels if "down" in direction else 0 42 | 43 | init_img = p.init_images[0] 44 | target_w = math.ceil((init_img.width + left + right) / 64) * 64 45 | target_h = math.ceil((init_img.height + up + down) / 64) * 64 46 | 47 | if left > 0: 48 | left = left * (target_w - init_img.width) // (left + right) 49 | if right > 0: 50 | right = target_w - init_img.width - left 51 | 52 | if up > 0: 53 | up = up * (target_h - init_img.height) // (up + down) 54 | 55 | if down > 0: 56 | down = target_h - init_img.height - up 57 | 58 | img = Image.new("RGB", (target_w, target_h)) 59 | img.paste(init_img, (left, up)) 60 | 61 | mask = Image.new("L", (img.width, img.height), "white") 62 | draw = ImageDraw.Draw(mask) 63 | draw.rectangle(( 64 | left + (mask_blur * 2 if left > 0 else 0), 65 | up + (mask_blur * 2 if up > 0 else 0), 66 | mask.width - right - (mask_blur * 2 if right > 0 else 0), 67 | mask.height - down - (mask_blur * 2 if down > 0 else 0) 68 | ), fill="black") 69 | 70 | latent_mask = Image.new("L", (img.width, img.height), "white") 71 | latent_draw = ImageDraw.Draw(latent_mask) 72 | latent_draw.rectangle(( 73 | left + (mask_blur//2 if left > 0 else 0), 74 | up + (mask_blur//2 if up > 0 else 0), 75 | mask.width - right - (mask_blur//2 if right > 0 else 0), 76 | mask.height - down - (mask_blur//2 if down > 0 else 0) 77 | ), fill="black") 78 | 79 | devicelib.torch_gc() 80 | 81 | grid = images.split_grid(img, tile_w=p.width, tile_h=p.height, overlap=pixels) 82 | grid_mask = images.split_grid(mask, tile_w=p.width, tile_h=p.height, overlap=pixels) 83 | grid_latent_mask = images.split_grid(latent_mask, tile_w=p.width, tile_h=p.height, overlap=pixels) 84 | 85 | p.n_iter = 1 86 | p.batch_size = 1 87 | p.do_not_save_grid = True 88 | p.do_not_save_samples = True 89 | 90 | work = [] 91 | work_mask = [] 92 | work_latent_mask = [] 93 | work_results = [] 94 | 95 | for (y, h, row), (_, _, row_mask), (_, _, row_latent_mask) in zip(grid.tiles, grid_mask.tiles, grid_latent_mask.tiles): 96 | for tiledata, tiledata_mask, tiledata_latent_mask in zip(row, row_mask, row_latent_mask): 97 | x, w = tiledata[0:2] 98 | 99 | if x >= left and x+w <= img.width - right and y >= up and y+h <= img.height - down: 100 | continue 101 | 102 | work.append(tiledata[2]) 103 | work_mask.append(tiledata_mask[2]) 104 | work_latent_mask.append(tiledata_latent_mask[2]) 105 | 106 | batch_count = len(work) 107 | print(f"Poor man's outpainting will process a total of {len(work)} images tiled as {len(grid.tiles[0][2])}x{len(grid.tiles)}.") 108 | 109 | state.job_count = batch_count 110 | 111 | for i in range(batch_count): 112 | p.init_images = [work[i]] 113 | p.image_mask = work_mask[i] 114 | p.latent_mask = work_latent_mask[i] 115 | 116 | state.job = f"Batch {i + 1} out of {batch_count}" 117 | processed = process_images(p) 118 | 119 | if initial_seed is None: 120 | initial_seed = processed.seed 121 | initial_info = processed.info 122 | 123 | p.seed = processed.seed + 1 124 | work_results += processed.images 125 | 126 | 127 | image_index = 0 128 | for y, h, row in grid.tiles: 129 | for tiledata in row: 130 | x, w = tiledata[0:2] 131 | 132 | if x >= left and x+w <= img.width - right and y >= up and y+h <= img.height - down: 133 | continue 134 | 135 | tiledata[2] = work_results[image_index] if image_index < len(work_results) else Image.new("RGB", (p.width, p.height)) 136 | image_index += 1 137 | 138 | combined_image = images.combine_grid(grid) 139 | 140 | if opts.samples_save: 141 | images.save_image(combined_image, p.outpath_samples, "", initial_seed, p.prompt, opts.grid_format, metadata=initial_info, p=p) 142 | 143 | processed = Processed(p, [combined_image], initial_seed, initial_info) 144 | 145 | return processed 146 | 147 | -------------------------------------------------------------------------------- /scripts/prompt_matrix.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | from core import plugins as scripts 4 | import gradio as gr 5 | 6 | from modules.processing import process_images 7 | from shared import opts, state 8 | 9 | 10 | def draw_xy_grid(xs, ys, x_label, y_label, cell): 11 | res = [] 12 | 13 | ver_texts = [[images.GridAnnotation(y_label(y))] for y in ys] 14 | hor_texts = [[images.GridAnnotation(x_label(x))] for x in xs] 15 | 16 | first_pocessed = None 17 | 18 | state.job_count = len(xs) * len(ys) 19 | 20 | for iy, y in enumerate(ys): 21 | for ix, x in enumerate(xs): 22 | state.job = f"{ix + iy * len(xs) + 1} out of {len(xs) * len(ys)}" 23 | 24 | processed = cell(x, y) 25 | if first_pocessed is None: 26 | first_pocessed = processed 27 | 28 | res.append(processed.images[0]) 29 | 30 | grid = images.image_grid(res, rows=len(ys)) 31 | grid = images.draw_grid_annotations(grid, res[0].width, res[0].height, hor_texts, ver_texts) 32 | 33 | first_pocessed.images = [grid] 34 | 35 | return first_pocessed 36 | 37 | 38 | class Script(scripts.Plugin): 39 | def title(self): 40 | return "Prompt matrix" 41 | 42 | def ui(self, is_img2img): 43 | put_at_start = gr.Checkbox(label='Put variable parts at start of prompt', value=False) 44 | 45 | return [put_at_start] 46 | 47 | def run(self, p, put_at_start): 48 | modules.processing.fix_seed(p) 49 | 50 | original_prompt = p.prompt[0] if type(p.prompt) == list else p.prompt 51 | 52 | all_prompts = [] 53 | prompt_matrix_parts = original_prompt.split("|") 54 | combination_count = 2 ** (len(prompt_matrix_parts) - 1) 55 | for combination_num in range(combination_count): 56 | selected_prompts = [text.strip().strip(',') for n, text in enumerate(prompt_matrix_parts[1:]) if combination_num & (1 << n)] 57 | 58 | if put_at_start: 59 | selected_prompts = selected_prompts + [prompt_matrix_parts[0]] 60 | else: 61 | selected_prompts = [prompt_matrix_parts[0]] + selected_prompts 62 | 63 | all_prompts.append(", ".join(selected_prompts)) 64 | 65 | p.n_iter = math.ceil(len(all_prompts) / p.batch_size) 66 | p.do_not_save_grid = True 67 | 68 | print(f"Prompt matrix will create {len(all_prompts)} images using a total of {p.n_iter} batches.") 69 | 70 | p.prompt = all_prompts 71 | p.seed = [p.seed for _ in all_prompts] 72 | p.prompt_for_display = original_prompt 73 | processed = process_images(p) 74 | 75 | grid = images.image_grid(processed.images, p.batch_size, rows=1 << ((len(prompt_matrix_parts) - 1) // 2)) 76 | grid = images.draw_prompt_matrix(grid, p.width, p.height, prompt_matrix_parts) 77 | processed.images.insert(0, grid) 78 | 79 | if opts.grid_save: 80 | images.save_image(processed.images[0], p.outpath_grids, "prompt_matrix", prompt=original_prompt, seed=processed.seed, grid=True, p=p) 81 | 82 | return processed 83 | -------------------------------------------------------------------------------- /scripts/prompts_from_file.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | from core import plugins as scripts 4 | import gradio as gr 5 | 6 | from modules.processing import Processed, process_images 7 | from shared import state 8 | 9 | class Script(scripts.Plugin): 10 | def title(self): 11 | return "Prompts from file or textbox" 12 | 13 | def ui(self, is_img2img): 14 | # This checkbox would look nicer as two tabs, but there are two problems: 15 | # 1) There is a bug in Gradio 3.3 that prevents visibility from working on Tabs 16 | # 2) Even with Gradio 3.3.1, returning a control (like Tabs) that can't be used as input 17 | # causes a AttributeError: 'Tabs' object has no attribute 'preprocess' assert, 18 | # due to the way Script assumes all controls returned can be used as inputs. 19 | # Therefore, there's no good way to use grouping components right now, 20 | # so we will use a checkbox! :) 21 | checkbox_txt = gr.Checkbox(label="Show Textbox", value=False) 22 | file = gr.File(label="File with inputs", type='bytes') 23 | prompt_txt = gr.TextArea(label="Prompts") 24 | checkbox_txt.change(fn=lambda x: [gr.File.update(visible = not x), gr.TextArea.update(visible = x)], inputs=[checkbox_txt], outputs=[file, prompt_txt]) 25 | return [checkbox_txt, file, prompt_txt] 26 | 27 | def on_show(self, checkbox_txt, file, prompt_txt): 28 | return [ gr.Checkbox.update(visible = True), gr.File.update(visible = not checkbox_txt), gr.TextArea.update(visible = checkbox_txt) ] 29 | 30 | def run(self, p, checkbox_txt, data: bytes, prompt_txt: str): 31 | if (checkbox_txt): 32 | lines = [x.strip() for x in prompt_txt.splitlines()] 33 | else: 34 | lines = [x.strip() for x in data.decode('utf8', errors='ignore').split("\n")] 35 | lines = [x for x in lines if len(x) > 0] 36 | 37 | img_count = len(lines) * p.n_iter 38 | batch_count = math.ceil(img_count / p.batch_size) 39 | loop_count = math.ceil(batch_count / p.n_iter) 40 | print(f"Will process {img_count} images in {batch_count} batches.") 41 | 42 | p.do_not_save_grid = True 43 | 44 | state.job_count = batch_count 45 | 46 | images = [] 47 | for loop_no in range(loop_count): 48 | state.job = f"{loop_no + 1} out of {loop_count}" 49 | p.prompt = lines[loop_no*p.batch_size:(loop_no+1)*p.batch_size] * p.n_iter 50 | proc = process_images(p) 51 | images += proc.images 52 | 53 | return Processed(p, images, p.seed, "") 54 | -------------------------------------------------------------------------------- /scripts/sd_upscale.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | from core import plugins as scripts, devicelib 4 | import gradio as gr 5 | from PIL import Image 6 | 7 | from modules import processing 8 | import shared 9 | from modules.processing import Processed 10 | from shared import opts, state 11 | 12 | 13 | class Script(scripts.Plugin): 14 | def title(self): 15 | return "stable_diffusion upscale" 16 | 17 | def show(self, is_img2img): 18 | return is_img2img 19 | 20 | def ui(self, is_img2img): 21 | info = gr.HTML("

Will upscale the image to twice the dimensions; use width and height sliders to set tile size

") 22 | overlap = gr.Slider(minimum=0, maximum=256, step=16, label='Tile overlap', value=64, visible=False) 23 | upscaler_index = gr.Radio(label='Upscaler', choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name, type="index", visible=False) 24 | 25 | return [info, overlap, upscaler_index] 26 | 27 | def run(self, p, _, overlap, upscaler_index): 28 | processing.fix_seed(p) 29 | upscaler = shared.sd_upscalers[upscaler_index] 30 | 31 | p.extra_generation_params["stable_diffusion upscale overlap"] = overlap 32 | p.extra_generation_params["stable_diffusion upscale upscaler"] = upscaler.name 33 | 34 | initial_info = None 35 | seed = p.seed 36 | 37 | init_img = p.init_images[0] 38 | 39 | if(upscaler.name != "None"): 40 | img = upscaler.scaler.upscale(init_img, 2, upscaler.data_path) 41 | else: 42 | img = init_img 43 | 44 | devicelib.torch_gc() 45 | 46 | grid = images.split_grid(img, tile_w=p.width, tile_h=p.height, overlap=overlap) 47 | 48 | batch_size = p.batch_size 49 | upscale_count = p.n_iter 50 | p.n_iter = 1 51 | p.do_not_save_grid = True 52 | p.do_not_save_samples = True 53 | 54 | work = [] 55 | 56 | for y, h, row in grid.tiles: 57 | for tiledata in row: 58 | work.append(tiledata[2]) 59 | 60 | batch_count = math.ceil(len(work) / batch_size) 61 | state.job_count = batch_count * upscale_count 62 | 63 | print(f"stable_diffusion upscaling will process {len(work)} images tiled as {len(grid.tiles[0][2])}x{len(grid.tiles)} per upscale in a total of {state.job_count} batches.") 64 | 65 | result_images = [] 66 | for n in range(upscale_count): 67 | start_seed = seed + n 68 | p.seed = start_seed 69 | 70 | work_results = [] 71 | for i in range(batch_count): 72 | p.batch_size = batch_size 73 | p.init_images = work[i*batch_size:(i+1)*batch_size] 74 | 75 | state.job = f"Batch {i + 1 + n * batch_count} out of {state.job_count}" 76 | processed = processing.process_job(p) 77 | 78 | if initial_info is None: 79 | initial_info = processed.info 80 | 81 | p.seed = processed.seed + 1 82 | work_results += processed.images 83 | 84 | image_index = 0 85 | for y, h, row in grid.tiles: 86 | for tiledata in row: 87 | tiledata[2] = work_results[image_index] if image_index < len(work_results) else Image.new("RGB", (p.width, p.height)) 88 | image_index += 1 89 | 90 | combined_image = images.combine_grid(grid) 91 | result_images.append(combined_image) 92 | 93 | if opts.samples_save: 94 | images.save_image(combined_image, p.outpath_samples, "", start_seed, p.prompt, opts.samples_format, metadata=initial_info, p=p) 95 | 96 | processed = Processed(p, result_images, seed, initial_info) 97 | 98 | return processed 99 | -------------------------------------------------------------------------------- /textual_inversion_templates/hypernetwork.txt: -------------------------------------------------------------------------------- 1 | a photo of a [filewords] 2 | a rendering of a [filewords] 3 | a cropped photo of the [filewords] 4 | the photo of a [filewords] 5 | a photo of a clean [filewords] 6 | a photo of a dirty [filewords] 7 | a dark photo of the [filewords] 8 | a photo of my [filewords] 9 | a photo of the cool [filewords] 10 | a close-up photo of a [filewords] 11 | a bright photo of the [filewords] 12 | a cropped photo of a [filewords] 13 | a photo of the [filewords] 14 | a good photo of the [filewords] 15 | a photo of one [filewords] 16 | a close-up photo of the [filewords] 17 | a rendition of the [filewords] 18 | a photo of the clean [filewords] 19 | a rendition of a [filewords] 20 | a photo of a nice [filewords] 21 | a good photo of a [filewords] 22 | a photo of the nice [filewords] 23 | a photo of the small [filewords] 24 | a photo of the weird [filewords] 25 | a photo of the large [filewords] 26 | a photo of a cool [filewords] 27 | a photo of a small [filewords] 28 | -------------------------------------------------------------------------------- /textual_inversion_templates/none.txt: -------------------------------------------------------------------------------- 1 | picture 2 | -------------------------------------------------------------------------------- /textual_inversion_templates/style.txt: -------------------------------------------------------------------------------- 1 | a painting, art by [name] 2 | a rendering, art by [name] 3 | a cropped painting, art by [name] 4 | the painting, art by [name] 5 | a clean painting, art by [name] 6 | a dirty painting, art by [name] 7 | a dark painting, art by [name] 8 | a picture, art by [name] 9 | a cool painting, art by [name] 10 | a close-up painting, art by [name] 11 | a bright painting, art by [name] 12 | a cropped painting, art by [name] 13 | a good painting, art by [name] 14 | a close-up painting, art by [name] 15 | a rendition, art by [name] 16 | a nice painting, art by [name] 17 | a small painting, art by [name] 18 | a weird painting, art by [name] 19 | a large painting, art by [name] 20 | -------------------------------------------------------------------------------- /textual_inversion_templates/style_filewords.txt: -------------------------------------------------------------------------------- 1 | a painting of [filewords], art by [name] 2 | a rendering of [filewords], art by [name] 3 | a cropped painting of [filewords], art by [name] 4 | the painting of [filewords], art by [name] 5 | a clean painting of [filewords], art by [name] 6 | a dirty painting of [filewords], art by [name] 7 | a dark painting of [filewords], art by [name] 8 | a picture of [filewords], art by [name] 9 | a cool painting of [filewords], art by [name] 10 | a close-up painting of [filewords], art by [name] 11 | a bright painting of [filewords], art by [name] 12 | a cropped painting of [filewords], art by [name] 13 | a good painting of [filewords], art by [name] 14 | a close-up painting of [filewords], art by [name] 15 | a rendition of [filewords], art by [name] 16 | a nice painting of [filewords], art by [name] 17 | a small painting of [filewords], art by [name] 18 | a weird painting of [filewords], art by [name] 19 | a large painting of [filewords], art by [name] 20 | -------------------------------------------------------------------------------- /textual_inversion_templates/subject.txt: -------------------------------------------------------------------------------- 1 | a photo of a [name] 2 | a rendering of a [name] 3 | a cropped photo of the [name] 4 | the photo of a [name] 5 | a photo of a clean [name] 6 | a photo of a dirty [name] 7 | a dark photo of the [name] 8 | a photo of my [name] 9 | a photo of the cool [name] 10 | a close-up photo of a [name] 11 | a bright photo of the [name] 12 | a cropped photo of a [name] 13 | a photo of the [name] 14 | a good photo of the [name] 15 | a photo of one [name] 16 | a close-up photo of the [name] 17 | a rendition of the [name] 18 | a photo of the clean [name] 19 | a rendition of a [name] 20 | a photo of a nice [name] 21 | a good photo of a [name] 22 | a photo of the nice [name] 23 | a photo of the small [name] 24 | a photo of the weird [name] 25 | a photo of the large [name] 26 | a photo of a cool [name] 27 | a photo of a small [name] 28 | -------------------------------------------------------------------------------- /textual_inversion_templates/subject_filewords.txt: -------------------------------------------------------------------------------- 1 | a photo of a [name], [filewords] 2 | a rendering of a [name], [filewords] 3 | a cropped photo of the [name], [filewords] 4 | the photo of a [name], [filewords] 5 | a photo of a clean [name], [filewords] 6 | a photo of a dirty [name], [filewords] 7 | a dark photo of the [name], [filewords] 8 | a photo of my [name], [filewords] 9 | a photo of the cool [name], [filewords] 10 | a close-up photo of a [name], [filewords] 11 | a bright photo of the [name], [filewords] 12 | a cropped photo of a [name], [filewords] 13 | a photo of the [name], [filewords] 14 | a good photo of the [name], [filewords] 15 | a photo of one [name], [filewords] 16 | a close-up photo of the [name], [filewords] 17 | a rendition of the [name], [filewords] 18 | a photo of the clean [name], [filewords] 19 | a rendition of a [name], [filewords] 20 | a photo of a nice [name], [filewords] 21 | a good photo of a [name], [filewords] 22 | a photo of the nice [name], [filewords] 23 | a photo of the small [name], [filewords] 24 | a photo of the weird [name], [filewords] 25 | a photo of the large [name], [filewords] 26 | a photo of a cool [name], [filewords] 27 | a photo of a small [name], [filewords] 28 | -------------------------------------------------------------------------------- /webui.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | if not defined PYTHON (set PYTHON=python) 4 | if not defined VENV_DIR (set VENV_DIR=venv) 5 | 6 | set ERROR_REPORTING=FALSE 7 | 8 | mkdir tmp 2>NUL 9 | 10 | %PYTHON% -c "" >tmp/stdout.txt 2>tmp/stderr.txt 11 | if %ERRORLEVEL% == 0 goto :start_venv 12 | echo Couldn't launch python 13 | goto :show_stdout_stderr 14 | 15 | :start_venv 16 | if [%VENV_DIR%] == [-] goto :skip_venv 17 | 18 | dir %VENV_DIR%\Scripts\Python.exe >tmp/stdout.txt 2>tmp/stderr.txt 19 | if %ERRORLEVEL% == 0 goto :activate_venv 20 | 21 | for /f "delims=" %%i in ('CALL %PYTHON% -c "import sys; print(sys.executable)"') do set PYTHON_FULLNAME="%%i" 22 | echo Creating venv in directory %VENV_DIR% using python %PYTHON_FULLNAME% 23 | %PYTHON_FULLNAME% -m venv %VENV_DIR% >tmp/stdout.txt 2>tmp/stderr.txt 24 | if %ERRORLEVEL% == 0 goto :activate_venv 25 | echo Unable to create venv in directory %VENV_DIR% 26 | goto :show_stdout_stderr 27 | 28 | :activate_venv 29 | set PYTHON="%~dp0%VENV_DIR%\Scripts\Python.exe" 30 | echo venv %PYTHON% 31 | goto :launch 32 | 33 | :skip_venv 34 | 35 | :launch 36 | %PYTHON% launch.py 37 | pause 38 | exit /b 39 | 40 | :show_stdout_stderr 41 | 42 | echo. 43 | echo exit code: %errorlevel% 44 | 45 | for /f %%i in ("tmp\stdout.txt") do set size=%%~zi 46 | if %size% equ 0 goto :show_stderr 47 | echo. 48 | echo stdout: 49 | type tmp\stdout.txt 50 | 51 | :show_stderr 52 | for /f %%i in ("tmp\stderr.txt") do set size=%%~zi 53 | if %size% equ 0 goto :show_stderr 54 | echo. 55 | echo stderr: 56 | type tmp\stderr.txt 57 | 58 | :endofscript 59 | 60 | echo. 61 | echo Launch unsuccessful. Exiting. 62 | pause 63 | -------------------------------------------------------------------------------- /webui.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ################################################# 3 | # Please do not make any changes to this file, # 4 | # change the variables in webui-user.sh instead # 5 | ################################################# 6 | 7 | # Set defaults 8 | # Install directory without trailing slash 9 | if [[ -z "${install_dir}" ]] 10 | then 11 | install_dir="/home/$(whoami)" 12 | fi 13 | 14 | # Name of the subdirectory (defaults to stable-core) 15 | if [[ -z "${clone_dir}" ]] 16 | then 17 | clone_dir="stable-core" 18 | fi 19 | 20 | # python3 executable 21 | if [[ -z "${python_cmd}" ]] 22 | then 23 | python_cmd="python3" 24 | fi 25 | 26 | # git executable 27 | if [[ -z "${GIT}" ]] 28 | then 29 | export GIT="git" 30 | fi 31 | 32 | # python3 venv without trailing slash (defaults to ${install_dir}/${clone_dir}/venv) 33 | if [[ -z "${venv_dir}" ]] 34 | then 35 | venv_dir="venv" 36 | fi 37 | 38 | if [[ -z "${LAUNCH_SCRIPT}" ]] 39 | then 40 | LAUNCH_SCRIPT="launch.py" 41 | fi 42 | 43 | # Disable sentry logging 44 | export ERROR_REPORTING=FALSE 45 | 46 | # Do not reinstall existing pip packages on Debian/Ubuntu 47 | export PIP_IGNORE_INSTALLED=0 48 | 49 | # Pretty print 50 | delimiter="################################################################" 51 | 52 | printf "\n%s\n" "${delimiter}" 53 | printf "\e[1m\e[32mInstall script for stable-diffusion + Web UI\n" 54 | printf "\e[1m\e[34mTested on Debian 11 (Bullseye)\e[0m" 55 | printf "\n%s\n" "${delimiter}" 56 | 57 | # Do not run as root 58 | if [[ $(id -u) -eq 0 ]] 59 | then 60 | printf "\n%s\n" "${delimiter}" 61 | printf "\e[1m\e[31mERROR: This script must not be launched as root, aborting...\e[0m" 62 | printf "\n%s\n" "${delimiter}" 63 | exit 1 64 | else 65 | printf "\n%s\n" "${delimiter}" 66 | printf "Running on \e[1m\e[32m%s\e[0m user" "$(whoami)" 67 | printf "\n%s\n" "${delimiter}" 68 | fi 69 | 70 | if [[ -d .git ]] 71 | then 72 | printf "\n%s\n" "${delimiter}" 73 | printf "Repo already cloned, using it as install directory" 74 | printf "\n%s\n" "${delimiter}" 75 | install_dir="${PWD}/../" 76 | clone_dir="${PWD##*/}" 77 | fi 78 | 79 | # Check prequisites 80 | for preq in git python3 81 | do 82 | if ! hash "${preq}" &>/dev/null 83 | then 84 | printf "\n%s\n" "${delimiter}" 85 | printf "\e[1m\e[31mERROR: %s is not installed, aborting...\e[0m" "${preq}" 86 | printf "\n%s\n" "${delimiter}" 87 | exit 1 88 | fi 89 | done 90 | 91 | if ! "${python_cmd}" -c "import venv" &>/dev/null 92 | then 93 | printf "\n%s\n" "${delimiter}" 94 | printf "\e[1m\e[31mERROR: python3-venv is not installed, aborting...\e[0m" 95 | printf "\n%s\n" "${delimiter}" 96 | exit 1 97 | fi 98 | 99 | printf "\n%s\n" "${delimiter}" 100 | printf "Clone or update stable-core" 101 | printf "\n%s\n" "${delimiter}" 102 | cd "${install_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/, aborting...\e[0m" "${install_dir}"; exit 1; } 103 | if [[ -d "${clone_dir}" ]] 104 | then 105 | cd "${clone_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/%s/, aborting...\e[0m" "${install_dir}" "${clone_dir}"; exit 1; } 106 | # "${GIT}" pull 107 | else 108 | "${GIT}" clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git "${clone_dir}" 109 | cd "${clone_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/%s/, aborting...\e[0m" "${install_dir}" "${clone_dir}"; exit 1; } 110 | fi 111 | 112 | printf "\n%s\n" "${delimiter}" 113 | printf "Create and activate python venv" 114 | printf "\n%s\n" "${delimiter}" 115 | cd "${install_dir}"/"${clone_dir}"/ || { printf "\e[1m\e[31mERROR: Can't cd to %s/%s/, aborting...\e[0m" "${install_dir}" "${clone_dir}"; exit 1; } 116 | if [[ ! -d "${venv_dir}" ]] 117 | then 118 | "${python_cmd}" -m venv "${venv_dir}" 119 | first_launch=1 120 | fi 121 | # shellcheck source=/dev/null 122 | if [[ -f "${venv_dir}"/bin/activate ]] 123 | then 124 | source "${venv_dir}"/bin/activate 125 | else 126 | printf "\n%s\n" "${delimiter}" 127 | printf "\e[1m\e[31mERROR: Cannot activate python venv, aborting...\e[0m" 128 | printf "\n%s\n" "${delimiter}" 129 | exit 1 130 | fi 131 | 132 | printf "\n%s\n" "${delimiter}" 133 | printf "Launching launch.py..." 134 | printf "\n%s\n" "${delimiter}" 135 | "${python_cmd}" "${LAUNCH_SCRIPT}" 136 | --------------------------------------------------------------------------------