Compare commits

..
66 Commits
Author SHA1 Message Date
AUTOMATIC d714ea4c41 ability to upload mask for inpainting 2022-09-09 19:43:16 +03:00
AUTOMATIC 5b6a585ae5 Merge remote-tracking branch 'origin/master' into seeds 2022-09-09 19:13:40 +03:00
David YatesandAUTOMATIC1111 17a7477c72 Include negative prompt in parameters text file 2022-09-09 18:19:37 +03:00
AUTOMATIC efde17e839 i will also fix floating point significant digits 2022-09-09 18:05:43 +03:00
AUTOMATIC b1707553cf added resize seeds and variation seeds features 2022-09-09 17:54:04 +03:00
AUTOMATIC1111andGitHub ab623db52c Merge pull request #175 from Thielak/master
Removed mention of CUDA in the README
2022-09-09 12:48:35 +03:00
KaleithandGitHub 16792691c7 Removed mention of CUDA in the README
The requirement to install CUDA was removed with https://github.com/AUTOMATIC1111/stable-diffusion-webui/commit/e92d4cf7476f1897fce376916dfb40755ea7920f#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5L63 so that mention in README should be superfluous
2022-09-09 10:39:41 +02:00
AUTOMATIC 003b60b94e add an option to show negative prompt 2022-09-09 09:15:36 +03:00
AUTOMATIC 41434ba3cd make X/Y plot's S/R apply to negative prompt as well. 2022-09-09 08:58:31 +03:00
AUTOMATIC bcb8a5eb0a change default font capitalization to possibly help linux users #157 2022-09-09 08:45:39 +03:00
AUTOMATIC 1fd2c22919 brought manual instructions up to date
reworked launching with different parameters
2022-09-09 08:37:19 +03:00
AUTOMATIC 0c63aa95e1 Merge remote-tracking branch 'origin/master' 2022-09-09 07:22:46 +03:00
AUTOMATIC1111andGitHub 116a2b89c0 Merge pull request #167 from orionaskatu/patch-1
Some typos
2022-09-09 07:21:34 +03:00
AUTOMATIC1111andGitHub 93524bfb73 Merge pull request #153 from SafentisFox/fix_output_display
Fix webui.bat ignoring cmd line arguments, fix output img overflowing
2022-09-09 07:19:46 +03:00
orionaskatuandGitHub 764a64b02e Some typos 2022-09-09 01:17:38 +02:00
safentisAuth 5d49003e0d Update README.md 2022-09-09 01:45:18 +03:00
AUTOMATIC 02bcd51a5a fix aggressive caching for extras tab 2022-09-08 23:29:36 +03:00
AUTOMATIC ec33d6e842 fix inconsistency in readme (thx #153) 2022-09-08 20:13:54 +03:00
AUTOMATIC1111andGitHub 7c8b6b2abb Merge pull request #161 from JohannesGaessler/typo-fix
Fixed typos in JavaScript descriptions
2022-09-08 20:08:12 +03:00
AUTOMATIC fe4e3c2673 fix for PLMS live previews in txt2img 2022-09-08 19:34:20 +03:00
AUTOMATIC ca3861e05f fix for DDIM live previews in txt2img 2022-09-08 19:20:41 +03:00
JohannesGaessler bb46ad9504 Fixed typos in JavaScript descriptions 2022-09-08 18:19:53 +02:00
AUTOMATIC1111andGitHub 701f76b29a Merge pull request #158 from JohannesGaessler/progress-printing
More informative progress printing
2022-09-08 18:34:45 +03:00
JohannesGaessler f211c498b9 More informative progress printing 2022-09-08 17:05:17 +02:00
AUTOMATIC1111andGitHub 20b86e81c3 Merge pull request #154 from rewbs/img2img2-loopback-denoise-strength-change-factor
Turn the loopback denoising strength change factor into a parameter rather than hardcoding to 0.95. Set the default to 1.
2022-09-08 17:02:15 +03:00
AUTOMATIC ad02b249f5 add a helpful message when user puts RealESRGAN model into ESRGAN directory. 2022-09-08 15:49:47 +03:00
AUTOMATIC 62ce77e245 support for sd-concepts as alternatives for textual inversion #151 2022-09-08 15:36:50 +03:00
AUTOMATIC f5001246e2 honor tiling settings for RealESRGAN also
load scripts earlier to get errors before model loads
2022-09-08 15:19:36 +03:00
safentisAuth 6dc5cf558d Fix webui.bat ignoring cmd line arguments, fix output img overflowing 2022-09-08 15:08:23 +03:00
rewbs ed01f69542 Turn the loopback denoise strength change factor into a parameter rather than hardcoding to 0.95. Set the default to 1. 2022-09-08 12:02:06 +00:00
AUTOMATIC1111andGitHub 3eea3c4dab Merge pull request #148 from dgrenner/add-settings-file
Add settings file
2022-09-08 12:27:45 +03:00
Daniel Grenner e817a28b8e Add settings file
Signed-off-by: Daniel Grenner <[email protected]>
2022-09-08 11:18:50 +02:00
AUTOMATIC 3a4c6d9ef5 add webui() function for more simple cell in the notebook 2022-09-08 12:17:26 +03:00
AUTOMATIC1111andGitHub a196c45f15 Merge pull request #146 from orionaskatu/orionaskatu-port-option
--port option for #131
2022-09-08 11:46:51 +03:00
orionaskatuandGitHub cce6f1df41 fix default 2022-09-08 10:46:23 +02:00
AUTOMATIC 27dfcf69da readme update 2022-09-08 11:43:59 +03:00
orionaskatuandGitHub 50178b7f5b Port defaults to 7860 2022-09-08 10:43:12 +02:00
orionaskatuandGitHub 567c1fbc1c Port defaults to none 2022-09-08 10:42:21 +02:00
orionaskatuandGitHub 48317a5176 Port defaults to none 2022-09-08 10:40:56 +02:00
orionaskatuandGitHub 9c510011ac update readme for --port option 2022-09-08 09:51:33 +02:00
orionaskatuandGitHub db92896e30 help message for ports < 1024 2022-09-08 09:47:56 +02:00
orionaskatuandGitHub 4f3cebd51d Add server_port param to webui.py 2022-09-08 09:46:28 +02:00
orionaskatuandGitHub 5d087731a5 add --port argument to shared.py
defaults to 7860
2022-09-08 09:44:14 +02:00
AUTOMATIC 61785cef65 Merge remote-tracking branch 'origin/master' 2022-09-08 10:31:20 +03:00
AUTOMATIC 0fedd50886 another change for inpainting at full resolution 2022-09-08 10:03:21 +03:00
AUTOMATIC1111andGitHub 9ddaf8269e Merge pull request #135 from rewbs/img2img2-color-correction
Add color correction to img2img loopback to avoid a progressive skew to magenta. Based on codedealer's PR to hlky's repo here: https://github.com/sd-webui/stable-diffusion-webui/pull/698/files.
2022-09-08 09:45:55 +03:00
Robin FernandesandGitHub 21a375e6b2 Merge branch 'master' into img2img2-color-correction 2022-09-08 15:59:42 +10:00
rewbs bc12eddb40 Add scikit-image dependency to requirements_versions.txt for windows users. 2022-09-08 05:57:22 +00:00
AUTOMATIC1111andGitHub 0959fa2d02 Merge pull request #124 from fuzzytent/alpha-mask
Also use alpha channel from img2img input image as mask
2022-09-08 08:09:28 +03:00
AUTOMATIC1111andGitHub 782b819a55 Merge pull request #123 from fuzzytent/paste-images
Allow copy-pasting images into file inputs
2022-09-08 07:49:12 +03:00
AUTOMATIC1111andGitHub 02fecac1c7 Merge pull request #129 from Cikmo/master
Fix not being able to have spaces directory
2022-09-08 07:48:21 +03:00
rewbs 1e7a36fd79 Remove debug print. 2022-09-08 02:53:13 +00:00
rewbs 52e071da2a Add color correction to img2img loopback to avoid a progressive skew to magenta. Based on codedealer's PR to hlky's repo here: https://github.com/sd-webui/stable-diffusion-webui/pull/698/files. 2022-09-08 02:35:26 +00:00
ChristianandGitHub d03e9502b1 Fix not being able to have spaces directory
Adds quotes around the PYTHON path so that there can be spaces in parent folders.
2022-09-08 00:31:25 +02:00
fuzzytent 7045c84643 Also use alpha channel from img2img input image as mask 2022-09-07 22:37:54 +02:00
fuzzytent 4d5a366f00 Allow copy-pasting images into file inputs 2022-09-07 21:58:11 +02:00
AUTOMATIC1111andGitHub 296d012423 Merge pull request #108 from xeonvs/mps-support
Added support for launching on Apple Silicon M1/M2
2022-09-07 22:29:44 +03:00
xeonvs ba1124b326 directly convert list to tensor 2022-09-07 20:40:32 +02:00
AUTOMATIC ee29bb77bf FIX GRADIO CRASHING WHEN SWITCHING FROM TAB WITH MASK THANK YOU 2022-09-07 21:26:19 +03:00
AUTOMATIC 795d49aa24 MAde poor man's outpainting do less extra useless work. 2022-09-07 19:22:45 +03:00
xeonvs b681a8f4f2 rollback requirements 2022-09-07 18:22:36 +02:00
AUTOMATIC e92d4cf747 Remove requirement for CUDA in readme. 2022-09-07 19:13:37 +03:00
xeonvs aaeeef82fa Miss device type for option --medvram 2022-09-07 18:09:30 +02:00
AUTOMATIC 700c47a674 big improvements to inpainting and outpainting 2022-09-07 17:00:51 +03:00
xeonvs 65fbefd033 Added support for launching on Apple Silicon 2022-09-07 15:58:25 +02:00
AUTOMATIC 2cbda50cdd clarification about not running as root 2022-09-07 15:30:25 +03:00
24 changed files with 587 additions and 169 deletions
+3 -1
View File
@@ -8,4 +8,6 @@ __pycache__
/ui-config.json
/outputs
/config.json
/log
/log
/webui.settings.bat
/embeddings
+91 -47
View File
@@ -8,7 +8,7 @@ A browser interface based on Gradio library for Stable Diffusion.
[Detailed feature showcase with images, art by Greg Rutkowski](https://github.com/AUTOMATIC1111/stable-diffusion-webui-feature-showcase)
- Original txt2img and img2img modes
- One click install and run script (but you still must install python, git and CUDA)
- One click install and run script (but you still must install python and git)
- Outpainting
- Inpainting
- Prompt matrix
@@ -42,9 +42,6 @@ A browser interface based on Gradio library for Stable Diffusion.
You need [python](https://www.python.org/downloads/windows/) and [git](https://git-scm.com/download/win)
installed to run this, and an NVidia videocard.
I tested the installation to work Windows with Python 3.8.10, and with Python 3.10.6. You may be able
to have success with different versions.
You need `model.ckpt`, Stable Diffusion model checkpoint, a big file containing the neural network weights. You
can obtain it from the following places:
- [official download](https://huggingface.co/CompVis/stable-diffusion-v-1-4-original)
@@ -60,40 +57,75 @@ as model if it has .pth extension. Grab models from the [Model Database](https:/
- install [Python 3.10.6](https://www.python.org/downloads/windows/) and check "Add Python to PATH" during installation. You must install this exact version.
- install [git](https://git-scm.com/download/win)
- install [CUDA 11.3](https://developer.nvidia.com/cuda-11.3.0-download-archive?target_os=Windows&target_arch=x86_64)
- place `model.ckpt` into webui directory, next to `webui.bat`.
- _*(optional)*_ place `GFPGANv1.3.pth` into webui directory, next to `webui.bat`.
- run `webui.bat` from Windows Explorer.
- run `webui-user.bat` from Windows Explorer. Run it as normal user, ***not*** as administrator.
#### Troublehooting:
#### Troubleshooting
- According to reports, intallation currently does not work in a directory with spaces in filenames.
- if your version of Python is not in PATH (or if another version is), edit `webui.bat`, change the line `set PYTHON=python` to say the full path to your python executable: `set PYTHON=B:\soft\Python310\python.exe`. You can do this for python, but not for git.
- if you get out of memory errors and your videocard has low amount of VRAM (4GB), edit `webui.bat`, change line 5 to from `set COMMANDLINE_ARGS=` to `set COMMANDLINE_ARGS=--medvram` (see below for other possible options)
- installer creates python virtual environment, so none of installed modules will affect your system installation of python if you had one prior to installing this.
- to prevent the creation of virtual environment and use your system python, edit `webui.bat` replacing `set VENV_DIR=venv` with `set VENV_DIR=`.
- webui.bat installs requirements from files `requirements_versions.txt`, which lists versions for modules specifically compatible with Python 3.10.6. If you choose to install for a different version of python, editing `webui.bat` to have `set REQS_FILE=requirements.txt` instead of `set REQS_FILE=requirements_versions.txt` may help (but I still reccomend you to just use the recommended version of python).
- if your version of Python is not in PATH (or if another version is), edit `webui-user.bat`, and modify the
line `set PYTHON=python` to say the full path to your python executable, for example: `set PYTHON=B:\soft\Python310\python.exe`.
You can do this for python, but not for git.
- if you get out of memory errors and your video-card has a low amount of VRAM (4GB), use custom parameter `set COMMANDLINE_ARGS` (see section below)
to enable appropriate optimization according to low VRAM guide below (for example, `set COMMANDLINE_ARGS=--medvram --opt-split-attention`).
- to prevent the creation of virtual environment and use your system python, use custom parameter replacing `set VENV_DIR=-` (see below).
- webui.bat installs requirements from files `requirements_versions.txt`, which lists versions for modules specifically compatible with
Python 3.10.6. If you choose to install for a different version of python, using custom parameter `set REQS_FILE=requirements.txt`
may help (but I still recommend you to just use the recommended version of python).
- if you feel you broke something and want to reinstall from scratch, delete directories: `venv`, `repositories`.
- if you get a green or black screen instead of generated pictures, you have a card that doesn't support half precision
floating point numbers (Known issue with 16xx cards). You must use `--precision full --no-half` in addition to command line
arguments (set them using `set COMMANDLINE_ARGS`, see below), and the model will take much more space in VRAM (you will likely
have to also use at least `--medvram`).
- installer creates python virtual environment, so none of installed modules will affect your system installation of python if
you had one prior to installing this.
- About _"You must install this exact version"_ from the instructions above: you can use any version of python you like,
and it will likely work, but if you want to seek help about things not working, I will not offer help unless you this
exact version for my sanity.
### Google collab
#### How to run with custom parameters
If you don't want or can't run locally, here is google collab that allows you to run the webui:
It's possible to edit `set COMMANDLINE_ARGS=` line in `webui.bat` to run the program with different command line arguments, but that may lead
to inconveniences when the file is updated in the repository.
https://colab.research.google.com/drive/1Iy-xW9t1-OQWhb0hNxueGij8phCyluOh
The recommndended way is to use another .bat file named anything you like, set the parameters you want in it, and run webui.bat from it.
A `webui-user.bat` file included into the repository does exactly this.
### What options to use for low VRAM videocards?
Here is an example that runs the prgoram with `--opt-split-attention` argument:
```commandline
@echo off
set COMMANDLINE_ARGS=--opt-split-attention
call webui.bat
```
Another example, this file will run the program with custom python path, a different model named `a.ckpt` and without virtual environment:
```commandline
@echo off
set PYTHON=b:/soft/Python310/Python.exe
set VENV_DIR=-
set COMMANDLINE_ARGS=--ckpt a.ckpt
call webui.bat
```
### What options to use for low VRAM video-cards?
You can, through command line arguments, enable the various optimizations which sacrifice some/a lot of speed in favor of
using less VRAM. Those arguments are added to the `COMMANDLINE_ARGS` parameter, see section above.
Here's a list of optimization arguments:
- If you have 4GB VRAM and want to make 512x512 (or maybe up to 640x640) images, use `--medvram`.
- If you have 4GB VRAM and want to make 512x512 images, but you get an out of memory error with `--medvram`, use `--medvram --opt-split-attention` instead.
- If you have 4GB VRAM and want to make 512x512 images, and you still get an out of memory error, use `--lowvram --always-batch-cond-uncond --opt-split-attention` instead.
- If you have 4GB VRAM and want to make images larger than you can with `--medvram`, use `--lowvram --opt-split-attention`.
- If you have more VRAM and want to make larger images than you can usually make, use `--medvram --opt-split-attention`. You can use `--lowvram`
- If you have more VRAM and want to make larger images than you can usually make (for example 1024x1024 instead of 512x512), use `--medvram --opt-split-attention`. You can use `--lowvram`
also but the effect will likely be barely noticeable.
- Otherwise, do not use any of those.
Extra: if you get a green screen instead of generated pictures, you have a card that doesn't support half
precision floating point numbers. You must use `--precision full --no-half` in addition to other flags,
and the model will take much more space in VRAM.
### Running online
Use `--share` option to run online. You will get a xxx.app.gradio link. This is the intended way to use the
@@ -102,6 +134,16 @@ program in collabs.
Use `--listen` to make the server listen to network connections. This will allow computers on local newtork
to access the UI, and if you configure port forwarding, also computers on the internet.
Use `--port xxxx` to make the server listen on a specific port, xxxx being the wanted port. Remember that
all ports below 1024 needs root/admin rights, for this reason it is advised to use a port above 1024.
Defaults to port 7860 if available.
### Google collab
If you don't want or can't run locally, here is google collab that allows you to run the webui:
https://colab.research.google.com/drive/1Iy-xW9t1-OQWhb0hNxueGij8phCyluOh
### Textual Inversion
To make use of pretrained embeddings, create `embeddings` directory (in the same palce as `webui.py`)
and put your embeddings into it. They must be .pt files, each with only one trained embedding,
@@ -137,10 +179,6 @@ Alternatively, if you don't want to run webui.bat, here are instructions for ins
everything by hand:
```commandline
:: crate a directory somewhere for stable diffusion and open cmd in it;
:: make sure you are in the right directory; the command must output the directory you chose
echo %cd%
:: install torch with CUDA support. See https://pytorch.org/get-started/locally/ for more instructions if this fails.
pip install torch --extra-index-url https://download.pytorch.org/whl/cu113
@@ -148,53 +186,59 @@ pip install torch --extra-index-url https://download.pytorch.org/whl/cu113
:: a different version, but this is what I tested.
python -c "import torch; print(torch.cuda.is_available())"
:: clone Stable Diffusion repositories
git clone https://github.com/CompVis/stable-diffusion.git
git clone https://github.com/CompVis/taming-transformers
:: clone web ui and go into its directory
git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git
cd stable-diffusion-webui
:: clone repositories for Stable Diffusion and (optionally) CodeFormer
mkdir repositories
git clone https://github.com/CompVis/stable-diffusion.git repositories/stable-diffusion
git clone https://github.com/CompVis/taming-transformers.git repositories/taming-transformers
git clone https://github.com/sczhou/CodeFormer.git repositories/CodeFormer
:: install requirements of Stable Diffusion
pip install transformers==4.19.2 diffusers invisible-watermark
pip install transformers==4.19.2 diffusers invisible-watermark --prefer-binary
:: install k-diffusion
pip install git+https://github.com/crowsonkb/k-diffusion.git
pip install git+https://github.com/crowsonkb/k-diffusion.git --prefer-binary
:: (optional) install GFPGAN to fix faces
pip install git+https://github.com/TencentARC/GFPGAN.git
:: (optional) install GFPGAN (face resoration)
pip install git+https://github.com/TencentARC/GFPGAN.git --prefer-binary
:: go into stable diffusion's repo directory
cd stable-diffusion
:: clone web ui
git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git
:: (optional) install requirements for CodeFormer (face resoration)
pip install -r repositories/CodeFormer/requirements.txt --prefer-binary
:: install requirements of web ui
pip install -r stable-diffusion-webui/requirements.txt
pip install -r stable-diffusion-webui/requirements.txt --prefer-binary
:: update numpy to latest version
pip install -U numpy
pip install -U numpy --prefer-binary
:: (outside of command line) put stable diffusion model into models/ldm/stable-diffusion-v1/model.ckpt; you'll have
:: to create one missing directory;
:: (outside of command line) put stable diffusion model into web ui directory
:: the command below must output something like: 1 File(s) 4,265,380,512 bytes
dir models\ldm\stable-diffusion-v1\model.ckpt
dir model.ckpt
:: (outside of command line) put the GFPGAN model into same directory as webui script
:: (outside of command line) put the GFPGAN model into web ui directory
:: the command below must output something like: 1 File(s) 348,632,874 bytes
dir stable-diffusion-webui\GFPGANv1.3.pth
dir GFPGANv1.3.pth
```
> Note: the directory structure for manual instruction has been changed on 2022-09-09 to match automatic installation: previosuly
> webui was in a subdirectory of stable diffusion, now it's the reverse. If you followed manual installation before the
> chage, you can still use the program with you existing directory sctructure.
After that the installation is finished.
Run the command to start web ui:
```
python stable-diffusion-webui/webui.py
python webui.py
```
If you have a 4GB video card, run the command with either `--lowvram` or `--medvram` argument:
```
python stable-diffusion-webui/webui.py --medvram
python webui.py --medvram
```
After a while, you will get a message like this:
+8 -2
View File
@@ -14,14 +14,20 @@ import modules.images
def load_model(filename):
# this code is adapted from https://github.com/xinntao/ESRGAN
pretrained_net = torch.load(filename)
pretrained_net = torch.load(filename, map_location='cpu' if torch.has_mps else None)
crt_model = arch.RRDBNet(3, 3, 64, 23, gc=32)
if 'conv_first.weight' in pretrained_net:
crt_model.load_state_dict(pretrained_net)
return crt_model
if 'model.0.weight' not in pretrained_net:
is_realesrgan = "params_ema" in pretrained_net and 'body.0.rdb1.conv1.weight' in pretrained_net["params_ema"]
if is_realesrgan:
raise Exception("The file is a RealESRGAN model, it can't be used as a ESRGAN model.")
else:
raise Exception("The file is not a ESRGAN model.")
crt_net = crt_model.state_dict()
load_net_clean = {}
for k, v in pretrained_net.items():
+1 -1
View File
@@ -136,7 +136,7 @@ def draw_grid_annotations(im, width, height, hor_texts, ver_texts):
color_active = (0, 0, 0)
color_inactive = (153, 153, 153)
pad_left = width * 3 // 4 if len(ver_texts) > 0 else 0
pad_left = 0 if sum([sum([len(line.text) for line in lines]) for lines in ver_texts]) == 0 else width * 3 // 4
cols = im.width // width
rows = im.height // height
+49 -7
View File
@@ -1,5 +1,7 @@
import math
from PIL import Image
import cv2
import numpy as np
from PIL import Image, ImageOps, ImageChops
from modules.processing import Processed, StableDiffusionProcessingImg2Img, process_images
from modules.shared import opts, state
@@ -9,14 +11,21 @@ from modules.ui import plaintext_to_html
import modules.images as images
import modules.scripts
def img2img(prompt: str, init_img, init_img_with_mask, steps: int, sampler_index: int, mask_blur: int, inpainting_fill: int, restore_faces: bool, tiling: bool, mode: int, n_iter: int, batch_size: int, cfg_scale: float, denoising_strength: float, seed: int, height: int, width: int, resize_mode: int, upscaler_index: str, upscale_overlap: int, inpaint_full_res: bool, inpainting_mask_invert: int, *args):
def img2img(prompt: str, negative_prompt: str, init_img, init_img_with_mask, init_mask, mask_mode, steps: int, sampler_index: int, mask_blur: int, inpainting_fill: int, restore_faces: bool, tiling: bool, mode: int, n_iter: int, batch_size: int, cfg_scale: float, denoising_strength: float, denoising_strength_change_factor: float, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, height: int, width: int, resize_mode: int, upscaler_index: str, upscale_overlap: int, inpaint_full_res: bool, inpainting_mask_invert: int, *args):
is_inpaint = mode == 1
is_loopback = mode == 2
is_upscale = mode == 3
if is_inpaint:
image = init_img_with_mask['image']
mask = init_img_with_mask['mask']
if mask_mode == 0:
image = init_img_with_mask['image']
mask = init_img_with_mask['mask']
alpha_mask = ImageOps.invert(image.split()[-1]).convert('L').point(lambda x: 255 if x > 0 else 0, mode='1')
mask = ImageChops.lighter(alpha_mask, mask.convert('L')).convert('L')
image = image.convert('RGB')
else:
image = init_img
mask = init_mask
else:
image = init_img
mask = None
@@ -28,7 +37,12 @@ def img2img(prompt: str, init_img, init_img_with_mask, steps: int, sampler_index
outpath_samples=opts.outdir_samples or opts.outdir_img2img_samples,
outpath_grids=opts.outdir_grids or opts.outdir_img2img_grids,
prompt=prompt,
negative_prompt=negative_prompt,
seed=seed,
subseed=subseed,
subseed_strength=subseed_strength,
seed_resize_from_h=seed_resize_from_h,
seed_resize_from_w=seed_resize_from_w,
sampler_index=sampler_index,
batch_size=batch_size,
n_iter=n_iter,
@@ -46,8 +60,12 @@ def img2img(prompt: str, init_img, init_img_with_mask, steps: int, sampler_index
denoising_strength=denoising_strength,
inpaint_full_res=inpaint_full_res,
inpainting_mask_invert=inpainting_mask_invert,
extra_generation_params={"Denoising Strength": denoising_strength}
extra_generation_params={
"Denoising strength": denoising_strength,
"Denoising strength change factor": denoising_strength_change_factor
}
)
print(f"\nimg2img: {prompt}", file=shared.progress_print_out)
if is_loopback:
output_images, info = None, None
@@ -57,8 +75,19 @@ def img2img(prompt: str, init_img, init_img_with_mask, steps: int, sampler_index
state.job_count = n_iter
do_color_correction = False
try:
from skimage import exposure
do_color_correction = True
except:
print("Install scikit-image to perform color correction on loopback")
for i in range(n_iter):
if do_color_correction and i == 0:
correction_target = cv2.cvtColor(np.asarray(init_img.copy()), cv2.COLOR_RGB2LAB)
p.n_iter = 1
p.batch_size = 1
p.do_not_save_grid = True
@@ -69,10 +98,22 @@ def img2img(prompt: str, init_img, init_img_with_mask, steps: int, sampler_index
if initial_seed is None:
initial_seed = processed.seed
initial_info = processed.info
init_img = processed.images[0]
p.init_images = [processed.images[0]]
if do_color_correction and correction_target is not None:
init_img = Image.fromarray(cv2.cvtColor(exposure.match_histograms(
cv2.cvtColor(
np.asarray(init_img),
cv2.COLOR_RGB2LAB
),
correction_target,
channel_axis=2
), cv2.COLOR_LAB2RGB).astype("uint8"))
p.init_images = [init_img]
p.seed = processed.seed + 1
p.denoising_strength = max(p.denoising_strength * 0.95, 0.1)
p.denoising_strength = min(max(p.denoising_strength * denoising_strength_change_factor, 0.1), 1)
history.append(processed.images[0])
grid = images.image_grid(history, batch_size, rows=1)
@@ -141,5 +182,6 @@ def img2img(prompt: str, init_img, init_img_with_mask, steps: int, sampler_index
if processed is None:
processed = process_images(p)
shared.total_tqdm.clear()
return processed.images, processed.js(), plaintext_to_html(processed.info)
+6 -3
View File
@@ -2,9 +2,12 @@ import torch
module_in_gpu = None
cpu = torch.device("cpu")
gpu = torch.device("cuda")
device = gpu if torch.cuda.is_available() else cpu
if torch.has_cuda:
device = gpu = torch.device("cuda")
elif torch.has_mps:
device = gpu = torch.device("mps")
else:
device = gpu = torch.device("cpu")
def setup_for_low_vram(sd_model, use_medvram):
parents = {}
+93 -28
View File
@@ -29,7 +29,7 @@ def torch_gc():
class StableDiffusionProcessing:
def __init__(self, sd_model=None, outpath_samples=None, outpath_grids=None, prompt="", seed=-1, sampler_index=0, batch_size=1, n_iter=1, steps=50, cfg_scale=7.0, width=512, height=512, restore_faces=False, tiling=False, do_not_save_samples=False, do_not_save_grid=False, extra_generation_params=None, overlay_images=None, negative_prompt=None):
def __init__(self, sd_model=None, outpath_samples=None, outpath_grids=None, prompt="", seed=-1, subseed=-1, subseed_strength=0, seed_resize_from_h=-1, seed_resize_from_w=-1, sampler_index=0, batch_size=1, n_iter=1, steps=50, cfg_scale=7.0, width=512, height=512, restore_faces=False, tiling=False, do_not_save_samples=False, do_not_save_grid=False, extra_generation_params=None, overlay_images=None, negative_prompt=None):
self.sd_model = sd_model
self.outpath_samples: str = outpath_samples
self.outpath_grids: str = outpath_grids
@@ -37,6 +37,10 @@ class StableDiffusionProcessing:
self.prompt_for_display: str = None
self.negative_prompt: str = (negative_prompt or "")
self.seed: int = seed
self.subseed: int = subseed
self.subseed_strength: float = subseed_strength
self.seed_resize_from_h: int = seed_resize_from_h
self.seed_resize_from_w: int = seed_resize_from_w
self.sampler_index: int = sampler_index
self.batch_size: int = batch_size
self.n_iter: int = n_iter
@@ -52,7 +56,7 @@ class StableDiffusionProcessing:
self.overlay_images = overlay_images
self.paste_to = None
def init(self):
def init(self, seed):
pass
def sample(self, x, conditioning, unconditional_conditioning):
@@ -84,23 +88,67 @@ class Processed:
return json.dumps(obj)
# from https://discuss.pytorch.org/t/help-regarding-slerp-function-for-generative-model-sampling/32475/3
def slerp(val, low, high):
low_norm = low/torch.norm(low, dim=1, keepdim=True)
high_norm = high/torch.norm(high, dim=1, keepdim=True)
omega = torch.acos((low_norm*high_norm).sum(1))
so = torch.sin(omega)
res = (torch.sin((1.0-val)*omega)/so).unsqueeze(1)*low + (torch.sin(val*omega)/so).unsqueeze(1) * high
return res
def create_random_tensors(shape, seeds):
def create_random_tensors(shape, seeds, subseeds=None, subseed_strength=0.0, seed_resize_from_h=0, seed_resize_from_w=0):
xs = []
for seed in seeds:
torch.manual_seed(seed)
for i, seed in enumerate(seeds):
noise_shape = shape if seed_resize_from_h <= 0 or seed_resize_from_w <= 0 else (shape[0], seed_resize_from_h//8, seed_resize_from_w//8)
subnoise = None
if subseeds is not None:
subseed = 0 if i >= len(subseeds) else subseeds[i]
torch.manual_seed(subseed)
subnoise = torch.randn(noise_shape, device=shared.device)
# randn results depend on device; gpu and cpu get different results for same seed;
# the way I see it, it's better to do this on CPU, so that everyone gets same result;
# but the original script had it like this so I do not dare change it for now because
# but the original script had it like this, so I do not dare change it for now because
# it will break everyone's seeds.
xs.append(torch.randn(shape, device=shared.device))
x = torch.stack(xs)
torch.manual_seed(seed)
noise = torch.randn(noise_shape, device=shared.device)
if subnoise is not None:
#noise = subnoise * subseed_strength + noise * (1 - subseed_strength)
noise = slerp(subseed_strength, noise, subnoise)
if noise_shape != shape:
#noise = torch.nn.functional.interpolate(noise.unsqueeze(1), size=shape[1:], mode="bilinear").squeeze()
# noise_shape = (64, 80)
# shape = (64, 72)
torch.manual_seed(seed)
x = torch.randn(shape, device=shared.device)
dx = (shape[2] - noise_shape[2]) // 2 # -4
dy = (shape[1] - noise_shape[1]) // 2
w = noise_shape[2] if dx >= 0 else noise_shape[2] + 2 * dx
h = noise_shape[1] if dy >= 0 else noise_shape[1] + 2 * dy
tx = 0 if dx < 0 else dx
ty = 0 if dy < 0 else dy
dx = max(-dx, 0)
dy = max(-dy, 0)
x[:, ty:ty+h, tx:tx+w] = noise[:, dy:dy+h, dx:dx+w]
noise = x
xs.append(noise)
x = torch.stack(xs).to(shared.device)
return x
def set_seed(seed):
return int(random.randrange(4294967294)) if seed is None or seed == -1 else seed
def fix_seed(p):
p.seed = int(random.randrange(4294967294)) if p.seed is None or p.seed == -1 else p.seed
p.subseed = int(random.randrange(4294967294)) if p.subseed is None or p.subseed == -1 else p.subseed
def process_images(p: StableDiffusionProcessing) -> Processed:
@@ -111,7 +159,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
assert p.prompt is not None
torch_gc()
seed = set_seed(p.seed)
fix_seed(p)
os.makedirs(p.outpath_samples, exist_ok=True)
os.makedirs(p.outpath_grids, exist_ok=True)
@@ -125,28 +173,41 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
else:
all_prompts = p.batch_size * p.n_iter * [prompt]
if type(seed) == list:
all_seeds = seed
if type(p.seed) == list:
all_seeds = int(p.seed)
else:
all_seeds = [int(seed + x) for x in range(len(all_prompts))]
all_seeds = [int(p.seed + x) for x in range(len(all_prompts))]
if type(p.subseed) == list:
all_subseeds = p.subseed
else:
all_subseeds = [int(p.subseed + x) for x in range(len(all_prompts))]
def infotext(iteration=0, position_in_batch=0):
index = position_in_batch + iteration * p.batch_size
generation_params = {
"Steps": p.steps,
"Sampler": samplers[p.sampler_index].name,
"CFG scale": p.cfg_scale,
"Seed": all_seeds[position_in_batch + iteration * p.batch_size],
"Seed": all_seeds[index],
"Face restoration": (opts.face_restoration_model if p.restore_faces else None),
"Size": f"{p.width}x{p.height}",
"Batch size": (None if p.batch_size < 2 else p.batch_size),
"Batch pos": (None if p.batch_size < 2 else position_in_batch),
"Variation seed": (None if p.subseed_strength == 0 else all_subseeds[index]),
"Variation seed strength": (None if p.subseed_strength == 0 else p.subseed_strength),
"Seed resize from": (None if p.seed_resize_from_w == 0 or p.seed_resize_from_h == 0 else f"{p.seed_resize_from_w}x{p.seed_resize_from_h}"),
}
if p.extra_generation_params is not None:
generation_params.update(p.extra_generation_params)
generation_params_text = ", ".join([k if k == v else f'{k}: {v}' for k, v in generation_params.items() if v is not None])
negative_prompt_text = "\nNegative prompt: " + p.negative_prompt if p.negative_prompt else ""
return f"{p.prompt_for_display or prompt}\n{generation_params_text}".strip() + "".join(["\n\n" + x for x in comments])
return f"{p.prompt_for_display or prompt}{negative_prompt_text}\n{generation_params_text}".strip() + "".join(["\n\n" + x for x in comments])
if os.path.exists(cmd_opts.embeddings_dir):
model_hijack.load_textual_inversion_embeddings(cmd_opts.embeddings_dir, p.sd_model)
@@ -155,7 +216,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
precision_scope = torch.autocast if cmd_opts.precision == "autocast" else contextlib.nullcontext
ema_scope = (contextlib.nullcontext if cmd_opts.lowvram else p.sd_model.ema_scope)
with torch.no_grad(), precision_scope("cuda"), ema_scope():
p.init()
p.init(seed=all_seeds[0])
if state.job_count == -1:
state.job_count = p.n_iter
@@ -174,7 +235,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
comments += model_hijack.comments
# we manually generate all input noises because each one should have a specific seed
x = create_random_tensors([opt_C, p.height // opt_f, p.width // opt_f], seeds=seeds)
x = create_random_tensors([opt_C, p.height // opt_f, p.width // opt_f], seeds=seeds, subseeds=all_subseeds, subseed_strength=p.subseed_strength, seed_resize_from_h=p.seed_resize_from_h, seed_resize_from_w=p.seed_resize_from_w)
if p.n_iter > 1:
shared.state.job = f"Batch {n+1} out of {p.n_iter}"
@@ -231,16 +292,16 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
output_images.insert(0, grid)
if opts.grid_save:
images.save_image(grid, p.outpath_grids, "grid", seed, all_prompts[0], opts.grid_format, info=infotext(), short_filename=not opts.grid_extended_filename)
images.save_image(grid, p.outpath_grids, "grid", all_seeds[0], all_prompts[0], opts.grid_format, info=infotext(), short_filename=not opts.grid_extended_filename)
torch_gc()
return Processed(p, output_images, seed, infotext())
return Processed(p, output_images, all_seeds[0], infotext())
class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
sampler = None
def init(self):
def init(self, seed):
self.sampler = samplers[self.sampler_index].constructor(self.sd_model)
def sample(self, x, conditioning, unconditional_conditioning):
@@ -320,7 +381,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
self.mask = None
self.nmask = None
def init(self):
def init(self, seed):
self.sampler = samplers_for_img2img[self.sampler_index].constructor(self.sd_model)
crop_region = None
@@ -347,11 +408,13 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
else:
self.image_mask = images.resize_image(self.resize_mode, self.image_mask, self.width, self.height)
np_mask = np.array(self.image_mask)
np_mask = 255 - np.clip((255 - np_mask.astype(np.float)) * 2, 0, 255).astype(np.uint8)
np_mask = np.clip((np_mask.astype(np.float)) * 2, 0, 255).astype(np.uint8)
self.mask_for_overlay = Image.fromarray(np_mask)
self.overlay_images = []
latent_mask = self.latent_mask if self.latent_mask is not None else self.image_mask
imgs = []
for img in self.init_images:
image = img.convert("RGB")
@@ -360,9 +423,6 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
image = images.resize_image(self.resize_mode, image, self.width, self.height)
if self.image_mask is not None:
if self.inpainting_fill != 1:
image = fill(image, self.mask_for_overlay)
image_masked = Image.new('RGBa', (image.width, image.height))
image_masked.paste(image.convert("RGBA").convert("RGBa"), mask=ImageOps.invert(self.mask_for_overlay.convert('L')))
@@ -372,6 +432,10 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
image = image.crop(crop_region)
image = images.resize_image(2, image, self.width, self.height)
if self.image_mask is not None:
if self.inpainting_fill != 1:
image = fill(image, latent_mask)
image = np.array(image).astype(np.float32) / 255.0
image = np.moveaxis(image, 2, 0)
@@ -394,17 +458,18 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
self.init_latent = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(image))
if self.image_mask is not None:
init_mask = self.latent_mask if self.latent_mask is not None else self.image_mask
init_mask = latent_mask
latmask = init_mask.convert('RGB').resize((self.init_latent.shape[3], self.init_latent.shape[2]))
latmask = np.moveaxis(np.array(latmask, dtype=np.float64), 2, 0) / 255
latmask = latmask[0]
latmask = np.around(latmask)
latmask = np.tile(latmask[None], (4, 1, 1))
self.mask = torch.asarray(1.0 - latmask).to(shared.device).type(self.sd_model.dtype)
self.nmask = torch.asarray(latmask).to(shared.device).type(self.sd_model.dtype)
if self.inpainting_fill == 2:
self.init_latent = self.init_latent * self.mask + create_random_tensors(self.init_latent.shape[1:], [self.seed + x + 1 for x in range(self.init_latent.shape[0])]) * self.nmask
self.init_latent = self.init_latent * self.mask + create_random_tensors(self.init_latent.shape[1:], [seed + x + 1 for x in range(self.init_latent.shape[0])]) * self.nmask
elif self.inpainting_fill == 3:
self.init_latent = self.init_latent * self.mask
+4 -2
View File
@@ -5,7 +5,7 @@ import numpy as np
from PIL import Image
import modules.images
from modules.shared import cmd_opts
from modules.shared import cmd_opts, opts
RealesrganModelInfo = namedtuple("RealesrganModelInfo", ["name", "location", "model", "netscale"])
@@ -76,7 +76,9 @@ def upscale_with_realesrgan(image, RealESRGAN_upscaling, RealESRGAN_model_index)
scale=info.netscale,
model_path=info.location,
model=model,
half=not cmd_opts.no_half
half=not cmd_opts.no_half,
tile=opts.ESRGAN_tile,
tile_pad=opts.ESRGAN_tile_overlap,
)
upsampled = upsampler.enhance(np.array(image), outscale=RealESRGAN_upscaling)[0]
+3
View File
@@ -6,6 +6,7 @@ import modules.ui as ui
import gradio as gr
from modules.processing import StableDiffusionProcessing
from modules import shared
class Script:
filename = None
@@ -137,6 +138,8 @@ class ScriptRunner:
script_args = args[script.args_from:script.args_to]
processed = script.run(p, *script_args)
shared.total_tqdm.clear()
return processed
+16 -6
View File
@@ -73,11 +73,21 @@ class StableDiffusionModelHijack:
name = os.path.splitext(filename)[0]
data = torch.load(path)
param_dict = data['string_to_param']
if hasattr(param_dict, '_parameters'):
param_dict = getattr(param_dict, '_parameters') # fix for torch 1.12.1 loading saved file from torch 1.11
assert len(param_dict) == 1, 'embedding file has multiple terms in it'
emb = next(iter(param_dict.items()))[1]
# textual inversion embeddings
if 'string_to_param' in data:
param_dict = data['string_to_param']
if hasattr(param_dict, '_parameters'):
param_dict = getattr(param_dict, '_parameters') # fix for torch 1.12.1 loading saved file from torch 1.11
assert len(param_dict) == 1, 'embedding file has multiple terms in it'
emb = next(iter(param_dict.items()))[1]
elif type(data) == dict and type(next(iter(data.values()))) == torch.Tensor:
assert len(data.keys()) == 1, 'embedding file has multiple terms in it'
emb = next(iter(data.values()))
if len(emb.shape) == 1:
emb = emb.unsqueeze(0)
self.word_embeddings[name] = emb.detach()
self.word_embeddings_checksums[name] = f'{const_hash(emb.reshape(-1))&0xffff:04x}'
@@ -232,7 +242,7 @@ class FrozenCLIPEmbedderWithCustomWords(torch.nn.Module):
z = outputs.last_hidden_state
# restoring original mean is likely not correct, but it seems to work well to prevent artifacts that happen otherwise
batch_multipliers = torch.asarray(np.array(batch_multipliers)).to(device)
batch_multipliers = torch.asarray(batch_multipliers).to(device)
original_mean = z.mean()
z *= batch_multipliers.reshape(batch_multipliers.shape + (1,)).expand(z.shape)
new_mean = z.mean()
+17 -4
View File
@@ -58,7 +58,10 @@ def p_sample_ddim_hook(sampler_wrapper, x_dec, cond, ts, *args, **kwargs):
img_orig = sampler_wrapper.sampler.model.q_sample(sampler_wrapper.init_latent, ts)
x_dec = img_orig * sampler_wrapper.mask + sampler_wrapper.nmask * x_dec
store_latent(x_dec)
store_latent(sampler_wrapper.init_latent * sampler_wrapper.mask + sampler_wrapper.nmask * x_dec)
else:
store_latent(x_dec)
return sampler_wrapper.orig_p_sample_ddim(x_dec, cond, ts, *args, **kwargs)
@@ -67,13 +70,14 @@ def extended_tdqm(sequence, *args, desc=None, **kwargs):
state.sampling_steps = len(sequence)
state.sampling_step = 0
for x in tqdm.tqdm(sequence, *args, desc=state.job, **kwargs):
for x in tqdm.tqdm(sequence, *args, desc=state.job, file=shared.progress_print_out, **kwargs):
if state.interrupted:
break
yield x
state.sampling_step += 1
shared.total_tqdm.update()
ldm.models.diffusion.ddim.tqdm = lambda *args, desc=None, **kwargs: extended_tdqm(*args, desc=desc, **kwargs)
@@ -83,7 +87,7 @@ ldm.models.diffusion.plms.tqdm = lambda *args, desc=None, **kwargs: extended_tdq
class VanillaStableDiffusionSampler:
def __init__(self, constructor, sd_model):
self.sampler = constructor(sd_model)
self.orig_p_sample_ddim = self.sampler.p_sample_ddim if hasattr(self.sampler, 'p_sample_ddim') else None
self.orig_p_sample_ddim = self.sampler.p_sample_ddim if hasattr(self.sampler, 'p_sample_ddim') else self.sampler.p_sample_plms
self.mask = None
self.nmask = None
self.init_latent = None
@@ -109,6 +113,13 @@ class VanillaStableDiffusionSampler:
return samples
def sample(self, p, x, conditioning, unconditional_conditioning):
for fieldname in ['p_sample_ddim', 'p_sample_plms']:
if hasattr(self.sampler, fieldname):
setattr(self.sampler, fieldname, lambda x_dec, cond, ts, *args, **kwargs: p_sample_ddim_hook(self, x_dec, cond, ts, *args, **kwargs))
self.mask = None
self.nmask = None
self.init_latent = None
samples_ddim, _ = self.sampler.sample(S=p.steps, conditioning=conditioning, batch_size=int(x.shape[0]), shape=x[0].shape, verbose=False, unconditional_guidance_scale=p.cfg_scale, unconditional_conditioning=unconditional_conditioning, x_T=x)
return samples_ddim
@@ -143,13 +154,14 @@ def extended_trange(count, *args, **kwargs):
state.sampling_steps = count
state.sampling_step = 0
for x in tqdm.trange(count, *args, desc=state.job, **kwargs):
for x in tqdm.trange(count, *args, desc=state.job, file=shared.progress_print_out, **kwargs):
if state.interrupted:
break
yield x
state.sampling_step += 1
shared.total_tqdm.update()
class KDiffusionSampler:
@@ -165,6 +177,7 @@ class KDiffusionSampler:
def sample_img2img(self, p, x, noise, conditioning, unconditional_conditioning):
t_enc = int(min(p.denoising_strength, 0.999) * p.steps)
sigmas = self.model_wrap.get_sigmas(p.steps)
noise = noise * sigmas[p.steps - t_enc - 1]
xi = x + noise
+56 -19
View File
@@ -1,9 +1,11 @@
import sys
import argparse
import json
import os
import gradio as gr
import torch
import tqdm
import modules.artists
from modules.paths import script_path, sd_path
@@ -23,22 +25,28 @@ parser.add_argument("--gfpgan-model", type=str, help="GFPGAN model file name", d
parser.add_argument("--no-half", action='store_true', help="do not switch the model to 16-bit floats")
parser.add_argument("--no-progressbar-hiding", action='store_true', help="do not hide progressbar in gradio UI (we hide it because it slows down ML if you have hardware accleration in browser)")
parser.add_argument("--max-batch-count", type=int, default=16, help="maximum batch count value for the UI")
parser.add_argument("--embeddings-dir", type=str, default='embeddings', help="embeddings dirtectory for textual inversion (default: embeddings)")
parser.add_argument("--embeddings-dir", type=str, default='embeddings', help="embeddings directory for textual inversion (default: embeddings)")
parser.add_argument("--allow-code", action='store_true', help="allow custom script execution from webui")
parser.add_argument("--medvram", action='store_true', help="enable stable diffusion model optimizations for sacrficing a little speed for low VRM usage")
parser.add_argument("--lowvram", action='store_true', help="enable stable diffusion model optimizations for sacrficing a lot of speed for very low VRM usage")
parser.add_argument("--always-batch-cond-uncond", action='store_true', help="a workaround test; may help with speed in you use --lowvram")
parser.add_argument("--medvram", action='store_true', help="enable stable diffusion model optimizations for sacrificing a little speed for low VRM usage")
parser.add_argument("--lowvram", action='store_true', help="enable stable diffusion model optimizations for sacrificing a lot of speed for very low VRM usage")
parser.add_argument("--always-batch-cond-uncond", action='store_true', help="a workaround test; may help with speed if you use --lowvram")
parser.add_argument("--unload-gfpgan", action='store_true', help="unload GFPGAN every time after processing images. Warning: seems to cause memory leaks")
parser.add_argument("--precision", type=str, help="evaluate at this precision", choices=["full", "autocast"], default="autocast")
parser.add_argument("--share", action='store_true', help="use share=True for gradio and make the UI accessible through their site (doesn't work for me but you might have better luck)")
parser.add_argument("--esrgan-models-path", type=str, help="path to directory with ESRGAN models", default=os.path.join(script_path, 'ESRGAN'))
parser.add_argument("--opt-split-attention", action='store_true', help="enable optimization that reduced vram usage by a lot for about 10%% decrease in performance")
parser.add_argument("--opt-split-attention", action='store_true', help="enable optimization that reduce vram usage by a lot for about 10%% decrease in performance")
parser.add_argument("--listen", action='store_true', help="launch gradio with 0.0.0.0 as server name, allowing to respond to network requests")
parser.add_argument("--port", type=int, help="launch gradio with given server port, you need root/admin rights for ports < 1024, defaults to 7860 if available", default=None)
parser.add_argument("--show-negative-prompt", action='store_true', help="enable the field that lets you input negative prompt", default=False)
cmd_opts = parser.parse_args()
cpu = torch.device("cpu")
gpu = torch.device("cuda")
device = gpu if torch.cuda.is_available() else cpu
if torch.has_cuda:
device = torch.device("cuda")
elif torch.has_mps:
device = torch.device("mps")
else:
device = torch.device("cpu")
batch_cond_uncond = cmd_opts.always_batch_cond_uncond or not (cmd_opts.lowvram or cmd_opts.medvram)
parallel_processing_allowed = not cmd_opts.lowvram and not cmd_opts.medvram
@@ -54,7 +62,6 @@ class State:
current_image = None
current_image_sampling_step = 0
def interrupt(self):
self.interrupted = True
@@ -70,6 +77,7 @@ artist_db = modules.artists.ArtistsDatabase(os.path.join(script_path, 'artists.c
face_restorers = []
def find_any_font():
fonts = ['/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf']
@@ -77,7 +85,7 @@ def find_any_font():
if os.path.exists(font):
return font
return "arial.ttf"
return "Arial.TTF"
class Options:
@@ -90,13 +98,13 @@ class Options:
data = None
data_labels = {
"outdir_samples": OptionInfo("", "Output dictectory for images; if empty, defaults to two directories below"),
"outdir_txt2img_samples": OptionInfo("outputs/txt2img-images", 'Output dictectory for txt2img images'),
"outdir_img2img_samples": OptionInfo("outputs/img2img-images", 'Output dictectory for img2img images'),
"outdir_extras_samples": OptionInfo("outputs/extras-images", 'Output dictectory for images from extras tab'),
"outdir_grids": OptionInfo("", "Output dictectory for grids; if empty, defaults to two directories below"),
"outdir_txt2img_grids": OptionInfo("outputs/txt2img-grids", 'Output dictectory for txt2img grids'),
"outdir_img2img_grids": OptionInfo("outputs/img2img-grids", 'Output dictectory for img2img grids'),
"outdir_samples": OptionInfo("", "Output directory for images; if empty, defaults to two directories below"),
"outdir_txt2img_samples": OptionInfo("outputs/txt2img-images", 'Output directory for txt2img images'),
"outdir_img2img_samples": OptionInfo("outputs/img2img-images", 'Output directory for img2img images'),
"outdir_extras_samples": OptionInfo("outputs/extras-images", 'Output directory for images from extras tab'),
"outdir_grids": OptionInfo("", "Output directory for grids; if empty, defaults to two directories below"),
"outdir_txt2img_grids": OptionInfo("outputs/txt2img-grids", 'Output directory for txt2img grids'),
"outdir_img2img_grids": OptionInfo("outputs/img2img-grids", 'Output directory for img2img grids'),
"save_to_dirs": OptionInfo(False, "When writing images/grids, create a directory with name derived from the prompt"),
"save_to_dirs_prompt_len": OptionInfo(10, "When using above, how many words from prompt to put into directory name", gr.Slider, {"minimum": 1, "maximum": 32, "step": 1}),
"outdir_save": OptionInfo("log/images", "Directory for saving images using the Save button"),
@@ -114,12 +122,13 @@ class Options:
"font": OptionInfo(find_any_font(), "Font for image grids that have text"),
"enable_emphasis": OptionInfo(True, "Use (text) to make model pay more attention to text text and [text] to make it pay less attention"),
"save_txt": OptionInfo(False, "Create a text file next to every image with generation parameters."),
"ESRGAN_tile": OptionInfo(192, "Tile size for ESRGAN upscaling. 0 = no tiling.", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}),
"ESRGAN_tile_overlap": OptionInfo(8, "Tile overlap, in pixels for ESRGAN upscaling. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}),
"ESRGAN_tile": OptionInfo(192, "Tile size for upscaling. 0 = no tiling.", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}),
"ESRGAN_tile_overlap": OptionInfo(8, "Tile overlap, in pixels for upscaling. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}),
"random_artist_categories": OptionInfo([], "Allowed categories for random artists selection when using the Roll button", gr.CheckboxGroup, {"choices": artist_db.categories()}),
"upscale_at_full_resolution_padding": OptionInfo(16, "Inpainting at full resolution: padding, in pixels, for the masked region.", gr.Slider, {"minimum": 0, "maximum": 128, "step": 4}),
"show_progressbar": OptionInfo(True, "Show progressbar"),
"show_progress_every_n_steps": OptionInfo(0, "Show show image creation progress every N sampling steps. Set 0 to disable.", gr.Slider, {"minimum": 0, "maximum": 32, "step": 1}),
"multiple_tqdm": OptionInfo(True, "Add a second progress bar to the console that shows progress for an entire job. Broken in PyCharm console."),
"face_restoration_model": OptionInfo(None, "Face restoration model", gr.Radio, lambda: {"choices": [x.name() for x in face_restorers]}),
"code_former_weight": OptionInfo(0.5, "CodeFormer weight parameter; 0 = maximum effect; 1 = minimum effect", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01}),
}
@@ -161,4 +170,32 @@ sd_upscalers = []
sd_model = None
progress_print_out = sys.stdout
class TotalTQDM:
def __init__(self):
self._tqdm = None
def reset(self):
self._tqdm = tqdm.tqdm(
desc="Total progress",
total=state.job_count * state.sampling_steps,
position=1,
file=progress_print_out
)
def update(self):
if not opts.multiple_tqdm:
return
if self._tqdm is None:
self.reset()
self._tqdm.update()
def clear(self):
if self._tqdm is not None:
self._tqdm.close()
self._tqdm = None
total_tqdm = TotalTQDM()
+8 -1
View File
@@ -6,7 +6,7 @@ import modules.processing as processing
from modules.ui import plaintext_to_html
def txt2img(prompt: str, negative_prompt: str, steps: int, sampler_index: int, restore_faces: bool, tiling: bool, n_iter: int, batch_size: int, cfg_scale: float, seed: int, height: int, width: int, *args):
def txt2img(prompt: str, negative_prompt: str, steps: int, sampler_index: int, restore_faces: bool, tiling: bool, n_iter: int, batch_size: int, cfg_scale: float, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, height: int, width: int, *args):
p = StableDiffusionProcessingTxt2Img(
sd_model=shared.sd_model,
outpath_samples=opts.outdir_samples or opts.outdir_txt2img_samples,
@@ -14,6 +14,10 @@ def txt2img(prompt: str, negative_prompt: str, steps: int, sampler_index: int, r
prompt=prompt,
negative_prompt=negative_prompt,
seed=seed,
subseed=subseed,
subseed_strength=subseed_strength,
seed_resize_from_h=seed_resize_from_h,
seed_resize_from_w=seed_resize_from_w,
sampler_index=sampler_index,
batch_size=batch_size,
n_iter=n_iter,
@@ -25,6 +29,7 @@ def txt2img(prompt: str, negative_prompt: str, steps: int, sampler_index: int, r
tiling=tiling,
)
print(f"\ntxt2img: {prompt}", file=shared.progress_print_out)
processed = modules.scripts.scripts_txt2img.run(p, *args)
if processed is not None:
@@ -32,5 +37,7 @@ def txt2img(prompt: str, negative_prompt: str, steps: int, sampler_index: int, r
else:
processed = process_images(p)
shared.total_tqdm.clear()
return processed.images, processed.js(), plaintext_to_html(processed.info)
+78 -12
View File
@@ -192,11 +192,45 @@ def visit(x, func, path=""):
func(path + "/" + str(x.label), x)
def create_seed_inputs():
with gr.Row():
seed = gr.Number(label='Seed', value=-1)
subseed = gr.Number(label='Variation seed', value=-1, visible=False)
seed_checkbox = gr.Checkbox(label="Extra", elem_id="subseed_show", value=False)
with gr.Row():
subseed_strength = gr.Slider(label='Variation strength', value=0.0, minimum=0, maximum=1, step=0.01, visible=False)
seed_resize_from_h = gr.Slider(minimum=0, maximum=2048, step=64, label="Resize seed from height", value=0, visible=False)
seed_resize_from_w = gr.Slider(minimum=0, maximum=2048, step=64, label="Resize seed from width", value=0, visible=False)
def change_visiblity(show):
return {
subseed: gr_show(show),
subseed_strength: gr_show(show),
seed_resize_from_h: gr_show(show),
seed_resize_from_w: gr_show(show),
}
seed_checkbox.change(
change_visiblity,
inputs=[seed_checkbox],
outputs=[
subseed,
subseed_strength,
seed_resize_from_h,
seed_resize_from_w
]
)
return seed, subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w
def create_ui(txt2img, img2img, run_extras, run_pnginfo):
with gr.Blocks(analytics_enabled=False) as txt2img_interface:
with gr.Row():
prompt = gr.Textbox(label="Prompt", elem_id="txt2img_prompt", show_label=False, placeholder="Prompt", lines=1)
negative_prompt = gr.Textbox(label="Negative prompt", elem_id="txt2img_negative_prompt", show_label=False, placeholder="Negative prompt", lines=1, visible=False)
negative_prompt = gr.Textbox(label="Negative prompt", elem_id="txt2img_negative_prompt", show_label=False, placeholder="Negative prompt", lines=1, visible=cmd_opts.show_negative_prompt)
roll = gr.Button('Roll', elem_id="txt2img_roll", visible=len(shared.artist_db.artists) > 0)
submit = gr.Button('Generate', elem_id="txt2img_generate", variant='primary')
check_progress = gr.Button('Check progress', elem_id="check_progress", visible=False)
@@ -220,7 +254,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height", value=512)
width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width", value=512)
seed = gr.Number(label='Seed', value=-1)
seed, subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = create_seed_inputs()
with gr.Group():
custom_inputs = modules.scripts.scripts_txt2img.setup_ui(is_img2img=False)
@@ -228,7 +262,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
with gr.Column(variant='panel'):
with gr.Group():
txt2img_preview = gr.Image(elem_id='txt2img_preview', visible=False)
txt2img_gallery = gr.Gallery(label='Output', elem_id='txt2img_gallery')
txt2img_gallery = gr.Gallery(label='Output', elem_id='txt2img_gallery').style(grid=4)
with gr.Group():
@@ -260,6 +294,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
batch_size,
cfg_scale,
seed,
subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w,
height,
width,
] + custom_inputs,
@@ -314,6 +349,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
with gr.Blocks(analytics_enabled=False) as img2img_interface:
with gr.Row():
prompt = gr.Textbox(label="Prompt", elem_id="img2img_prompt", show_label=False, placeholder="Prompt", lines=1)
negative_prompt = gr.Textbox(label="Negative prompt", elem_id="img2img_negative_prompt", show_label=False, placeholder="Negative prompt", lines=1, visible=cmd_opts.show_negative_prompt)
submit = gr.Button('Generate', elem_id="img2img_generate", variant='primary')
check_progress = gr.Button('Check progress', elem_id="check_progress", visible=False)
@@ -323,8 +359,12 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
with gr.Group():
switch_mode = gr.Radio(label='Mode', elem_id="img2img_mode", choices=['Redraw whole image', 'Inpaint a part of image', 'Loopback', 'SD upscale'], value='Redraw whole image', type="index", show_label=False)
init_img = gr.Image(label="Image for img2img", source="upload", interactive=True, type="pil")
init_img_with_mask = gr.Image(label="Image for inpainting with mask", elem_id="img2maskimg", source="upload", interactive=True, type="pil", tool="sketch", visible=False)
resize_mode = gr.Radio(label="Resize mode", show_label=False, choices=["Just resize", "Crop and resize", "Resize and fill"], type="index", value="Just resize")
init_img_with_mask = gr.Image(label="Image for inpainting with mask", elem_id="img2maskimg", source="upload", interactive=True, type="pil", tool="sketch", visible=False, image_mode="RGBA")
init_mask = gr.Image(label="Mask", source="upload", interactive=True, type="pil", visible=False)
with gr.Row():
resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", show_label=False, choices=["Just resize", "Crop and resize", "Resize and fill"], type="index", value="Just resize")
mask_mode = gr.Radio(label="Mask mode", show_label=False, choices=["Draw mask", "Upload mask"], type="index", value="Draw mask")
steps = gr.Slider(minimum=1, maximum=150, step=1, label="Sampling Steps", value=20)
sampler_index = gr.Radio(label='Sampling method', choices=[x.name for x in samplers_for_img2img], value=samplers_for_img2img[0].name, type="index")
@@ -349,13 +389,14 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
with gr.Group():
cfg_scale = gr.Slider(minimum=1.0, maximum=15.0, step=0.5, label='CFG Scale', value=7.0)
denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising Strength', value=0.75)
denoising_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Denoising strength', value=0.75)
denoising_strength_change_factor = gr.Slider(minimum=0.9, maximum=1.1, step=0.01, label='Denoising strength change factor', value=1, visible=False)
with gr.Group():
height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height", value=512)
width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width", value=512)
seed = gr.Number(label='Seed', value=-1)
seed, subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = create_seed_inputs()
with gr.Group():
custom_inputs = modules.scripts.scripts_img2img.setup_ui(is_img2img=True)
@@ -363,7 +404,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
with gr.Column(variant='panel'):
with gr.Group():
img2img_preview = gr.Image(elem_id='img2img_preview', visible=False)
img2img_gallery = gr.Gallery(label='Output', elem_id='img2img_gallery')
img2img_gallery = gr.Gallery(label='Output', elem_id='img2img_gallery').style(grid=4)
with gr.Group():
with gr.Row():
@@ -379,15 +420,17 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
html_info = gr.HTML()
generation_info = gr.Textbox(visible=False)
def apply_mode(mode):
def apply_mode(mode, uploadmask):
is_classic = mode == 0
is_inpaint = mode == 1
is_loopback = mode == 2
is_upscale = mode == 3
return {
init_img: gr_show(not is_inpaint),
init_img_with_mask: gr_show(is_inpaint),
init_img: gr_show(not is_inpaint or (is_inpaint and uploadmask == 1)),
init_img_with_mask: gr_show(is_inpaint and uploadmask == 0),
init_mask: gr_show(is_inpaint and uploadmask == 1),
mask_mode: gr_show(is_inpaint),
mask_blur: gr_show(is_inpaint),
inpainting_fill: gr_show(is_inpaint),
batch_count: gr_show(not is_upscale),
@@ -396,14 +439,17 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
sd_upscale_overlap: gr_show(is_upscale),
inpaint_full_res: gr_show(is_inpaint),
inpainting_mask_invert: gr_show(is_inpaint),
denoising_strength_change_factor: gr_show(is_loopback),
}
switch_mode.change(
apply_mode,
inputs=[switch_mode],
inputs=[switch_mode, mask_mode],
outputs=[
init_img,
init_img_with_mask,
init_mask,
mask_mode,
mask_blur,
inpainting_fill,
batch_count,
@@ -412,16 +458,34 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
sd_upscale_overlap,
inpaint_full_res,
inpainting_mask_invert,
denoising_strength_change_factor,
]
)
mask_mode.change(
lambda mode: {
init_img: gr_show(mode == 1),
init_img_with_mask: gr_show(mode == 0),
init_mask: gr_show(mode == 1),
},
inputs=[mask_mode],
outputs=[
init_img,
init_img_with_mask,
init_mask,
],
)
img2img_args = dict(
fn=img2img,
_js="submit",
inputs=[
prompt,
negative_prompt,
init_img,
init_img_with_mask,
init_mask,
mask_mode,
steps,
sampler_index,
mask_blur,
@@ -433,7 +497,9 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
batch_size,
cfg_scale,
denoising_strength,
denoising_strength_change_factor,
seed,
subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w,
height,
width,
resize_mode,
+1
View File
@@ -10,5 +10,6 @@ omegaconf
pytorch_lightning
diffusers
invisible-watermark
scikit-image
git+https://github.com/crowsonkb/k-diffusion.git
git+https://github.com/TencentARC/GFPGAN.git
+1
View File
@@ -8,3 +8,4 @@ torch
transformers==4.19.2
omegaconf==2.1.1
pytorch_lightning==1.7.2
scikit-image==0.19.2
+50 -9
View File
@@ -1,8 +1,8 @@
titles = {
"Sampling steps": "How many times to imptove the generated image itratively; higher values take longer; very low values can produce bad results",
"Sampling steps": "How many times to improve the generated image iteratively; higher values take longer; very low values can produce bad results",
"Sampling method": "Which algorithm to use to produce the image",
"GFPGAN": "Restore low quality faces using GFPGAN neural network",
"Euler a": "Euler Ancestral - very creative, each can get acompletely different pictures depending on step count, setting seps tohigher than 30-40 does not help",
"Euler a": "Euler Ancestral - very creative, each can get a completely different picture depending on step count, setting steps to higher than 30-40 does not help",
"DDIM": "Denoising Diffusion Implicit Models - best at inpainting",
"Batch count": "How many batches of images to create",
@@ -11,7 +11,7 @@ titles = {
"Seed": "A value that determines the output of random number generator - if you create an image with same parameters and seed as another image, you'll get the same result",
"Inpaint a part of image": "Draw a mask over an image, and the script will regenerate the masked area with content according to prompt",
"Loopback": "Process an image, use it as an input, repeat. Batch count determings number of iterations.",
"Loopback": "Process an image, use it as an input, repeat. Batch count determins number of iterations.",
"SD upscale": "Upscale image normally, split result into tiles, improve each tile using img2img, merge whole image back",
"Just resize": "Resize image to target resolution. Unless height and width match, you will get incorrect aspect ratio.",
@@ -26,7 +26,8 @@ titles = {
"latent nothing": "fill it with latent space zeroes",
"Inpaint at full resolution": "Upscale masked region to target resolution, do inpainting, downscale back and paste into original image",
"Denoising Strength": "Determines how little respect the algorithm should have for image's content. At 0, nothing will change, and at 1 you'll get an unrelated image.",
"Denoising strength": "Determines how little respect the algorithm should have for image's content. At 0, nothing will change, and at 1 you'll get an unrelated image.",
"Denoising strength change factor": "In loopback mode, on each loop the denoising strength is multiplied by this value. <1 means decreasing variety so your sequence will converge on a fixed picture. >1 means increasing variety so your sequence will become more and more chaotic.",
"Interrupt": "Stop processing images and return any results accumulated so far.",
"Save": "Write image to a directory (default - log/images) and generation parameters into csv file.",
@@ -36,15 +37,20 @@ titles = {
"None": "Do not do anything special",
"Prompt matrix": "Separate prompts into parts using vertical pipe character (|) and the script will create a picture for every combination of them (except for the first part, which will be present in all combinations)",
"X/Y plot": "Create a grid where images will have different parameters. Use inputs below to specify which parameterswill be shared by columns and rows",
"Custom code": "Run python code. Advanced user only. Must run program with --allow-code for this to work",
"X/Y plot": "Create a grid where images will have different parameters. Use inputs below to specify which parameters will be shared by columns and rows",
"Custom code": "Run Python code. Advanced user only. Must run program with --allow-code for this to work",
"Prompt S/R": "Separate a list of words with commas, and the first word will be used as a keyword: script will search for this word in the prompt, and replace it with others",
"Tiling": "Produce an image that can be tiled.",
"Tile overlap": "For SD upscale, how much overlap in pixels should there be between tiles. Tils overlap so that when they are merged back into one oicture, there is no clearly visible seam.",
"Tile overlap": "For SD upscale, how much overlap in pixels should there be between tiles. Tiles overlap so that when they are merged back into one picture, there is no clearly visible seam.",
"Roll": "Add a random artist to the prompt.",
"Variation seed": "Seed of a different picture to be mixed into the generation.",
"Variation strength": "How strong of a variation to produce. At 0, there will be no effect. At 1, you will get the complete picture with variation seed (except for ancestral samplers, where you will just get something).",
"Resize seed from height": "Make an attempt to produce a picture similar to what would have been produced with same seed at specified resolution",
"Resize seed from width": "Make an attempt to produce a picture similar to what would have been produced with same seed at specified resolution",
}
function gradioApp(){
@@ -103,11 +109,31 @@ function addTitles(root){
}
tabNames = {"txt2img": 1, "img2img": 1, "Extras": 1, "PNG Info": 1, "Settings": 1}
processedTabs = {}
document.addEventListener("DOMContentLoaded", function() {
var mutationObserver = new MutationObserver(function(m){
addTitles(gradioApp());
// fix for gradio breaking when you switch away from tab with mask
gradioApp().querySelectorAll('button').forEach(function(button){
title = button.textContent.trim()
if(processedTabs[title]) return
if(tabNames[button.textContent.trim()]==null) return;
processedTabs[title]=1
button.onclick = function(){
mask_buttons = gradioApp().querySelectorAll('#img2maskimg button');
if(mask_buttons.length == 2){
mask_buttons[1].click();
}
}
})
});
mutationObserver.observe( gradioApp(), { childList:true, subtree:true })
});
function selected_gallery_index(){
@@ -150,6 +176,21 @@ function submit(){
for(var i=0;i<arguments.length;i++){
res.push(arguments[i])
}
console.log(res)
return res
}
}
window.addEventListener('paste', e => {
const files = e.clipboardData.files;
if (!files || files.length !== 1) {
return;
}
if (!['image/png', 'image/gif', 'image/jpeg'].includes(files[0].type)) {
return;
}
[...gradioApp().querySelectorAll('input[type=file][accept="image/x-png,image/gif,image/jpeg"]')]
.filter(input => !input.matches('.\\!hidden input[type=file]'))
.forEach(input => {
input.files = files;
input.dispatchEvent(new Event('change'))
});
});
+25 -7
View File
@@ -21,7 +21,7 @@ class Script(scripts.Script):
if not is_img2img:
return None
pixels = gr.Slider(label="Pixels to expand", minimum=8, maximum=128, step=8)
pixels = gr.Slider(label="Pixels to expand", minimum=8, maximum=256, step=8, value=128)
mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=4, visible=False)
inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='fill', type="index", visible=False)
direction = gr.CheckboxGroup(label="Outpainting direction", choices=['left', 'right', 'up', 'down'], value=['left', 'right', 'up', 'down'])
@@ -32,7 +32,7 @@ class Script(scripts.Script):
initial_seed = None
initial_info = None
p.mask_blur = mask_blur
p.mask_blur = mask_blur * 2
p.inpainting_fill = inpainting_fill
p.inpaint_full_res = False
@@ -47,11 +47,14 @@ class Script(scripts.Script):
if left > 0:
left = left * (target_w - init_img.width) // (left + right)
right = target_w - init_img.width - left
if right > 0:
right = target_w - init_img.width - left
if up > 0:
up = up * (target_h - init_img.height) // (up + down)
down = target_h - init_img.height - up
if down > 0:
down = target_h - init_img.height - up
img = Image.new("RGB", (target_w, target_h))
img.paste(init_img, (left, up))
@@ -67,13 +70,18 @@ class Script(scripts.Script):
latent_mask = Image.new("L", (img.width, img.height), "white")
latent_draw = ImageDraw.Draw(latent_mask)
latent_draw.rectangle((left + left//2, up + up//2, mask.width - right - right//2, mask.height - down - down//2), fill="black")
latent_draw.rectangle((
left + (mask_blur//2 if left > 0 else 0),
up + (mask_blur//2 if up > 0 else 0),
mask.width - right - (mask_blur//2 if right > 0 else 0),
mask.height - down - (mask_blur//2 if down > 0 else 0)
), fill="black")
processing.torch_gc()
grid = images.split_grid(img, tile_w=p.width, tile_h=p.height, overlap=pixels)
grid_mask = images.split_grid(mask, tile_w=p.width, tile_h=p.height, overlap=pixels)
grid_latent_mask = images.split_grid(mask, tile_w=p.width, tile_h=p.height, overlap=pixels)
grid_latent_mask = images.split_grid(latent_mask, tile_w=p.width, tile_h=p.height, overlap=pixels)
p.n_iter = 1
p.batch_size = 1
@@ -85,8 +93,13 @@ class Script(scripts.Script):
work_latent_mask = []
work_results = []
for (_, _, row), (_, _, row_mask), (_, _, row_latent_mask) in zip(grid.tiles, grid_mask.tiles, grid_latent_mask.tiles):
for (y, h, row), (_, _, row_mask), (_, _, row_latent_mask) in zip(grid.tiles, grid_mask.tiles, grid_latent_mask.tiles):
for tiledata, tiledata_mask, tiledata_latent_mask in zip(row, row_mask, row_latent_mask):
x, w = tiledata[0:2]
if x >= left and x+w <= img.width - right and y >= up and y+h <= img.height - down:
continue
work.append(tiledata[2])
work_mask.append(tiledata_mask[2])
work_latent_mask.append(tiledata_latent_mask[2])
@@ -115,6 +128,11 @@ class Script(scripts.Script):
image_index = 0
for y, h, row in grid.tiles:
for tiledata in row:
x, w = tiledata[0:2]
if x >= left and x+w <= img.width - right and y >= up and y+h <= img.height - down:
continue
tiledata[2] = work_results[image_index] if image_index < len(work_results) else Image.new("RGB", (p.width, p.height))
image_index += 1
+1 -1
View File
@@ -50,7 +50,7 @@ class Script(scripts.Script):
return [put_at_start]
def run(self, p, put_at_start):
seed = modules.processing.set_seed(p.seed)
modules.processing.fix_seed(p)
original_prompt = p.prompt[0] if type(p.prompt) == list else p.prompt
+38 -4
View File
@@ -2,6 +2,8 @@ from collections import namedtuple
from copy import copy
import random
import numpy as np
import modules.scripts as scripts
import gradio as gr
@@ -21,6 +23,7 @@ def apply_field(field):
def apply_prompt(p, x, xs):
p.prompt = p.prompt.replace(xs[0], x)
p.negative_prompt = p.negative_prompt.replace(xs[0], x)
samplers_dict = {}
@@ -39,24 +42,39 @@ def apply_sampler(p, x, xs):
def format_value_add_label(p, opt, x):
if type(x) == float:
x = round(x, 8)
return f"{opt.label}: {x}"
def format_value(p, opt, x):
if type(x) == float:
x = round(x, 8)
return x
def do_nothing(p, x, xs):
pass
def format_nothing(p, opt, x):
return ""
AxisOption = namedtuple("AxisOption", ["label", "type", "apply", "format_value"])
AxisOptionImg2Img = namedtuple("AxisOptionImg2Img", ["label", "type", "apply", "format_value"])
axis_options = [
AxisOption("Nothing", str, do_nothing, format_nothing),
AxisOption("Seed", int, apply_field("seed"), format_value_add_label),
AxisOption("Var. seed", int, apply_field("subseed"), format_value_add_label),
AxisOption("Var. strength", float, apply_field("subseed_strength"), format_value_add_label),
AxisOption("Steps", int, apply_field("steps"), format_value_add_label),
AxisOption("CFG Scale", float, apply_field("cfg_scale"), format_value_add_label),
AxisOption("Prompt S/R", str, apply_prompt, format_value),
AxisOption("Sampler", str, apply_sampler, format_value),
AxisOptionImg2Img("Denoising", float, apply_field("denoising_strength"), format_value_add_label) # as it is now all AxisOptionImg2Img items must go after AxisOption ones
AxisOptionImg2Img("Denoising", float, apply_field("denoising_strength"), format_value_add_label), # as it is now all AxisOptionImg2Img items must go after AxisOption ones
]
@@ -89,6 +107,7 @@ def draw_xy_grid(xs, ys, x_label, y_label, cell):
re_range = re.compile(r"\s*([+-]?\s*\d+)\s*-\s*([+-]?\s*\d+)(?:\s*\(([+-]\d+)\s*\))?\s*")
re_range_float = re.compile(r"\s*([+-]?\s*\d+(?:.\d*)?)\s*-\s*([+-]?\s*\d+(?:.\d*)?)(?:\s*\(([+-]\d+(?:.\d*)?)\s*\))?\s*")
class Script(scripts.Script):
def title(self):
@@ -98,17 +117,17 @@ class Script(scripts.Script):
current_axis_options = [x for x in axis_options if type(x) == AxisOption or type(x) == AxisOptionImg2Img and is_img2img]
with gr.Row():
x_type = gr.Dropdown(label="X type", choices=[x.label for x in current_axis_options], value=current_axis_options[0].label, visible=False, type="index", elem_id="x_type")
x_type = gr.Dropdown(label="X type", choices=[x.label for x in current_axis_options], value=current_axis_options[1].label, visible=False, type="index", elem_id="x_type")
x_values = gr.Textbox(label="X values", visible=False, lines=1)
with gr.Row():
y_type = gr.Dropdown(label="Y type", choices=[x.label for x in current_axis_options], value=current_axis_options[1].label, visible=False, type="index", elem_id="y_type")
y_type = gr.Dropdown(label="Y type", choices=[x.label for x in current_axis_options], value=current_axis_options[4].label, visible=False, type="index", elem_id="y_type")
y_values = gr.Textbox(label="Y values", visible=False, lines=1)
return [x_type, x_values, y_type, y_values]
def run(self, p, x_type, x_values, y_type, y_values):
p.seed = modules.processing.set_seed(p.seed)
modules.processing.fix_seed(p)
p.batch_size = 1
p.batch_count = 1
@@ -131,6 +150,21 @@ class Script(scripts.Script):
valslist_ext.append(val)
valslist = valslist_ext
elif opt.type == float:
valslist_ext = []
for val in valslist:
m = re_range_float.fullmatch(val)
if m is not None:
start = float(m.group(1))
end = float(m.group(2))
step = float(m.group(3)) if m.group(3) is not None else 1
valslist_ext += np.arange(start, end + step, step).tolist()
else:
valslist_ext.append(val)
valslist = valslist_ext
valslist = [opt.type(x) for x in valslist]
+14 -1
View File
@@ -5,16 +5,29 @@
max-width: 13em;
}
#subseed_show{
min-width: 6em;
max-width: 6em;
}
#subseed_show label{
height: 100%;
}
#txt2img_roll{
min-width: 1em;
max-width: 4em;
}
#resize_mode{
flex: 1.5;
}
button{
align-self: stretch !important;
}
#img2img_prompt, #txt2img_prompt{
#img2img_prompt, #txt2img_prompt, #img2img_negative_prompt, #txt2img_negative_prompt{
padding: 0;
border: none !important;
}
+8
View File
@@ -0,0 +1,8 @@
@echo off
set PYTHON=
set GIT=
set VENV_DIR=
set COMMANDLINE_ARGS=
call webui.bat
+8 -9
View File
@@ -1,15 +1,14 @@
@echo off
set PYTHON=python
set GIT=git
set COMMANDLINE_ARGS=
set VENV_DIR=venv
if not defined PYTHON (set PYTHON=python)
if not defined GIT (set GIT=git)
if not defined COMMANDLINE_ARGS (set COMMANDLINE_ARGS=%*)
if not defined VENV_DIR (set VENV_DIR=venv)
if not defined TORCH_COMMAND (set TORCH_COMMAND=pip install torch==1.12.1+cu113 --extra-index-url https://download.pytorch.org/whl/cu113)
if not defined REQS_FILE (set REQS_FILE=requirements_versions.txt)
mkdir tmp 2>NUL
set TORCH_COMMAND=pip install torch==1.12.1+cu113 --extra-index-url https://download.pytorch.org/whl/cu113
set REQS_FILE=requirements_versions.txt
%PYTHON% -c "" >tmp/stdout.txt 2>tmp/stderr.txt
if %ERRORLEVEL% == 0 goto :check_git
echo Couldn't launch python
@@ -22,7 +21,7 @@ echo Couldn't launch git
goto :show_stdout_stderr
:setup_venv
if [%VENV_DIR%] == [] goto :skip_venv
if [%VENV_DIR%] == [-] goto :skip_venv
dir %VENV_DIR%\Scripts\Python.exe >tmp/stdout.txt 2>tmp/stderr.txt
if %ERRORLEVEL% == 0 goto :activate_venv
@@ -35,7 +34,7 @@ echo Unable to create venv in directory %VENV_DIR%
goto :show_stdout_stderr
:activate_venv
set PYTHON=%~dp0%VENV_DIR%\Scripts\Python.exe
set PYTHON="%~dp0%VENV_DIR%\Scripts\Python.exe"
%PYTHON% --version
echo venv %PYTHON%
goto :install_torch
+8 -5
View File
@@ -43,6 +43,7 @@ def load_model_from_config(config, ckpt, verbose=False):
if "global_step" in pl_sd:
print(f"Global Step: {pl_sd['global_step']}")
sd = pl_sd["state_dict"]
model = instantiate_from_config(config.model)
m, u = model.load_state_dict(sd, strict=False)
if len(m) > 0 and verbose:
@@ -87,7 +88,7 @@ def run_extras(image, gfpgan_visibility, codeformer_visibility, codeformer_weigh
def upscale(image, scaler_index, resize):
small = image.crop((image.width // 2, image.height // 2, image.width // 2 + 10, image.height // 2 + 10))
pixels = tuple(np.array(small).flatten().tolist())
key = (resize, scaler_index, image.width, image.height) + pixels
key = (resize, scaler_index, image.width, image.height, gfpgan_visibility, codeformer_visibility, codeformer_weight) + pixels
c = cached_images.get(key)
if c is None:
@@ -152,6 +153,7 @@ def wrap_gradio_gpu_call(func):
return modules.ui.wrap_gradio_call(f)
modules.scripts.load_scripts(os.path.join(script_path, "scripts"))
try:
# this silences the annoying "Some weights of the model checkpoint were not used when initializing..." message at start.
@@ -173,15 +175,13 @@ else:
modules.sd_hijack.model_hijack.hijack(shared.sd_model)
modules.scripts.load_scripts(os.path.join(script_path, "scripts"))
if __name__ == "__main__":
def webui():
# make the program just exit at ctrl+c without waiting for anything
def sigint_handler(sig, frame):
print(f'Interrupted with signal {sig} in {frame}')
os._exit(0)
signal.signal(signal.SIGINT, sigint_handler)
demo = modules.ui.create_ui(
@@ -191,4 +191,7 @@ if __name__ == "__main__":
run_pnginfo=run_pnginfo
)
demo.launch(share=cmd_opts.share, server_name="0.0.0.0" if cmd_opts.listen else None)
demo.launch(share=cmd_opts.share, server_name="0.0.0.0" if cmd_opts.listen else None, server_port=cmd_opts.port)
if __name__ == "__main__":
webui()