Compare commits

..
Author SHA1 Message Date
Connum d5ecf56f8e make callback queue handling reusable and implement onUiTabChange() 2022-09-24 01:12:13 +02:00
AUTOMATIC 74f940e818 Merge remote-tracking branch 'origin/master' 2022-09-24 00:13:47 +03:00
AUTOMATIC 71cfb9ebac fix for settings sometimes not applying on javascript side 2022-09-24 00:13:32 +03:00
innovacionesandAUTOMATIC1111 b04a36be37 Tweak margin 2022-09-23 23:41:10 +03:00
innovacionesandAUTOMATIC1111 e1d49c5262 Fix border 2022-09-23 23:41:10 +03:00
innovacionesandAUTOMATIC1111 6cd613811b Fix preview images position 2022-09-23 23:41:10 +03:00
DepFAandAUTOMATIC1111 e560eb8b60 Only switch between visible gallery entries
Since the other tabs are kept in the dom now filtering is needed.
2022-09-23 23:38:45 +03:00
AUTOMATIC e12793e937 delete unwanted console.log 2022-09-23 23:34:33 +03:00
AUTOMATIC c8c662901b fix for inpaint at full resolution breaking if you have an NN upscaler. 2022-09-23 23:29:53 +03:00
AUTOMATIC 39ce23f42d add the bitton to paste parameters into UI for txt2img, img2img, and pnginfo tabs
fixed some [send to..] buttons to work properly with all tabs
2022-09-23 22:49:21 +03:00
AUTOMATIC 9c92a1a9aa removed some information that its owner decided he did not want to share 2022-09-23 20:55:54 +03:00
AUTOMATIC 8ffc07b7b1 one change didn't make it into the previous commit 2022-09-23 20:54:17 +03:00
AUTOMATIC 0065327726 upgrade to gradio==3.4b3 t fixthe inpain bugs
rework progressbar/preview to work with new gradio
remove unnecessary create style button
added link to alternative colab
2022-09-23 20:46:02 +03:00
AUTOMATIC 1a0353675d Option to use advanced upscalers with normal img2img 2022-09-23 17:37:47 +03:00
AUTOMATIC 6fa20d51dc prevent saving settings with bogus values 2022-09-23 17:27:30 +03:00
AUTOMATIC1111andGitHub 79e7c39298 Merge pull request #847 from rewbs/rewbs-optionally-save-before-color-correction
Add option to save before color correction. This helps with some posterisation issues in img2img loopback.
2022-09-23 14:41:06 +03:00
SekiandAUTOMATIC1111 03faf20251 add extrastab send to inpaint 2022-09-23 14:19:33 +03:00
SekiandAUTOMATIC1111 8708386609 add extrastab send to img2img 2022-09-23 14:19:33 +03:00
AUTOMATIC d4205e66fa gfpgan: just download the damn model 2022-09-23 10:26:00 +03:00
William MoorehouseandAUTOMATIC1111 d6fd71f36f Update .gitignore to ignore downloaded GFPGAN models 2022-09-23 09:23:16 +03:00
AUTOMATIC 7ef361dfc3 add warning for when user's settings are broken 2022-09-23 09:15:00 +03:00
AUTOMATIC c82e32652e prevent weird behavior when pressing interrupt just as image finishes 2022-09-23 08:48:19 +03:00
AUTOMATIC 02f58cde8d Merge remote-tracking branch 'origin/dfaker-patch-1' 2022-09-23 08:37:40 +03:00
AUTOMATIC 8e8d64199a Merge remote-tracking branch 'origin/dfaker-patch-2' 2022-09-23 08:37:32 +03:00
innovacionesandAUTOMATIC1111 0ce9e05a42 Fix typo 2022-09-23 08:33:08 +03:00
innovacionesandAUTOMATIC1111 6e86fc9fd0 Show interrupt button without progress bar 2022-09-23 08:33:08 +03:00
innovacionesandAUTOMATIC1111 ebf19c1145 Show interrupt button without progress bar 2022-09-23 08:33:08 +03:00
DepFAandAUTOMATIC1111 a2d084a07c Change default bug report template label to bug-report
for later confirmation or denial as an actual bug
2022-09-23 08:30:54 +03:00
DepFAandAUTOMATIC1111 42b7902922 sort JavaScript includes to assure script sequence
Who knows what filesystem they're on
2022-09-23 08:30:32 +03:00
DepFAandGitHub 08e27c3252 sort JavaScript includes to assure script sequence
Who knows what filesystem they're on
2022-09-23 03:05:42 +01:00
DepFAandGitHub ea2426fa61 Change default bug report template label to bug-report
for later confirmation or denial as an actual bug
2022-09-23 02:19:00 +01:00
Robin Fernandes d26d89377b Remove unnecessary duplication 2022-09-23 00:57:42 +00:00
Robin Fernandes 03738668c0 Merge from master 2022-09-23 00:54:32 +00:00
C43H66N12O12S2andAUTOMATIC1111 19fc3e8279 credit parlance-zz as they requested it 2022-09-23 01:42:55 +03:00
ConnumandAUTOMATIC1111 e16b9dc819 resize mask canvases to fit underlying image (fixes #668) 2022-09-22 22:36:47 +03:00
AUTOMATIC 77cf346d93 settings categories split to columns, remade categories 2022-09-22 21:32:44 +03:00
AUTOMATIC 75b90a5e40 emergency fix for the settings screen breaking the program 2022-09-22 20:41:22 +03:00
DepFAandAUTOMATIC1111 6d1c01c955 Add section splitting to settings ui 2022-09-22 20:26:21 +03:00
DepFAandAUTOMATIC1111 dec7584903 Add Section keys and headers to settings 2022-09-22 20:26:21 +03:00
Prof-CheeseandAUTOMATIC1111 ae32b8a53f Fixed directory name generation process.
'Max prompt words' has been added to config and modified to be used in the directory name generation process.
2022-09-22 20:15:37 +03:00
DepFAandAUTOMATIC1111 cae09e1651 Update selectors after ui and core overhaul 2022-09-22 19:29:20 +03:00
Johan Aires RasténandAUTOMATIC1111 a213d3a21c Add option to always log generation info 2022-09-22 16:34:54 +03:00
Robin Fernandes 25eb1e3d90 Add option to save before color correction. Add suffix param when saving files, used for special saves without color correction and face restoration. 2022-09-22 20:54:50 +10:00
AUTOMATIC1111andGitHub 34d5a31ea2 Merge pull request #834 from AUTOMATIC1111/dylancl-patch-2
Update README to link to wiki page for Apple Silicon installs
2022-09-22 13:45:25 +03:00
AUTOMATIC 3582befdcd move Notification.requestPermission() entirely to javascript to possibly fix problem with secure context people are having on non-localhost 2022-09-22 13:15:33 +03:00
AUTOMATIC b0765a6788 remove labels from output gallery 2022-09-22 12:30:11 +03:00
24 changed files with 674 additions and 268 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
labels: bug-report
assignees: ''
---
+1
View File
@@ -6,6 +6,7 @@ __pycache__
/model.ckpt
/models/*.ckpt
/GFPGANv1.3.pth
/gfpgan/weights/*.pth
/ui-config.json
/outputs
/config.json
+4 -1
View File
@@ -51,7 +51,10 @@ A browser interface based on Gradio library for Stable Diffusion.
## Installation and Running
Make sure the required [dependencies](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Dependencies) are met and follow the instructions available for both [NVidia](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Install-and-Run-on-NVidia-GPUs) (recommended) and [AMD](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Install-and-Run-on-AMD-GPUs) GPUs.
Alternatively, use [Google Colab](https://colab.research.google.com/drive/1Iy-xW9t1-OQWhb0hNxueGij8phCyluOh).
Alternatively, use Google Colab:
- [Colab, maintained by Akaibu](https://colab.research.google.com/drive/1kw3egmSn-KgWsikYvOMjJkVDsPLjEMzl)
- [Colab, original by me, outdated](https://colab.research.google.com/drive/1Iy-xW9t1-OQWhb0hNxueGij8phCyluOh).
### Automatic Installation on Windows
1. Install [Python 3.10.6](https://www.python.org/downloads/windows/), checking "Add Python to PATH"
+4 -9
View File
@@ -18,9 +18,9 @@ function dimensionChange(e,dimname){
return;
}
var img2imgMode = gradioApp().querySelector("input[name=radio-img2img_mode]:checked")
var img2imgMode = gradioApp().querySelector('#mode_img2img.tabs > div > button.rounded-t-lg.border-gray-200')
if(img2imgMode){
img2imgMode=img2imgMode.value
img2imgMode=img2imgMode.innerText
}else{
return;
}
@@ -30,12 +30,10 @@ function dimensionChange(e,dimname){
var targetElement = null;
if(img2imgMode=='Redraw whole image' && redrawImage){
if(img2imgMode=='img2img' && redrawImage){
targetElement = redrawImage;
}else if(img2imgMode=='Inpaint a part of image' && inpaintImage){
}else if(img2imgMode=='Inpaint' && inpaintImage){
targetElement = inpaintImage;
}else if(img2imgMode=='SD upscale' && redrawImage){
targetElement = redrawImage;
}
if(targetElement){
@@ -119,6 +117,3 @@ onUiUpdate(function(){
})
}
});
+3 -2
View File
@@ -13,6 +13,8 @@ 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",
"\u{1f3b2}\ufe0f": "Set seed to -1, which will cause a new random number to be used every time",
"\u267b\ufe0f": "Reuse seed from last generation, mostly useful if it was randomed",
"\u{1f3a8}": "Add a random artist to the prompt.",
"\u2199\ufe0f": "Read generation parameters from prompt into user interface.",
"Inpaint a part of image": "Draw a mask over an image, and the script will regenerate the masked area with content according to prompt",
"SD upscale": "Upscale image normally, split result into tiles, improve each tile using img2img, merge whole image back",
@@ -48,8 +50,6 @@ titles = {
"Tiling": "Produce an image that can be tiled.",
"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",
@@ -59,6 +59,7 @@ titles = {
"Images filename pattern": "Use following tags to define how filenames for images are chosen: [steps], [cfg], [prompt], [prompt_spaces], [width], [height], [sampler], [seed], [model_hash], [prompt_words], [date]; leave empty for default.",
"Directory name pattern": "Use following tags to define how subdirectories for images and grids are chosen: [steps], [cfg], [prompt], [prompt_spaces], [width], [height], [sampler], [seed], [model_hash], [prompt_words], [date]; leave empty for default.",
"Max prompt words": "Set the maximum number of words to be used in the [prompt_words] option; ATTENTION: If the words are too long, they may exceed the maximum length of the file path that the system can handle",
"Loopback": "Process an image, use it as an input, repeat.",
"Loops": "How many times to repeat processing an image and using it as input for the next iteration",
+45
View File
@@ -0,0 +1,45 @@
/**
* temporary fix for https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/668
* @see https://github.com/gradio-app/gradio/issues/1721
*/
window.addEventListener( 'resize', () => imageMaskResize());
function imageMaskResize() {
const canvases = gradioApp().querySelectorAll('#img2maskimg .touch-none canvas');
if ( ! canvases.length ) {
canvases_fixed = false;
window.removeEventListener( 'resize', imageMaskResize );
return;
}
const wrapper = canvases[0].closest('.touch-none');
const previewImage = wrapper.previousElementSibling;
if ( ! previewImage.complete ) {
previewImage.addEventListener( 'load', () => imageMaskResize());
return;
}
const w = previewImage.width;
const h = previewImage.height;
const nw = previewImage.naturalWidth;
const nh = previewImage.naturalHeight;
const portrait = nh > nw;
const factor = portrait;
const wW = Math.min(w, portrait ? h/nh*nw : w/nw*nw);
const wH = Math.min(h, portrait ? h/nh*nh : w/nw*nh);
wrapper.style.width = `${wW}px`;
wrapper.style.height = `${wH}px`;
wrapper.style.left = `${(w-wW)/2}px`;
wrapper.style.top = `${(h-wH)/2}px`;
canvases.forEach( c => {
c.style.width = c.style.height = '';
c.style.maxWidth = '100%';
c.style.maxHeight = '100%';
c.style.objectFit = 'contain';
});
}
onUiUpdate(() => imageMaskResize());
+14 -2
View File
@@ -22,10 +22,22 @@ function negmod(n, m) {
}
function modalImageSwitch(offset){
var galleryButtons = gradioApp().querySelectorAll(".gallery-item.transition-all")
var allgalleryButtons = gradioApp().querySelectorAll(".gallery-item.transition-all")
var galleryButtons = []
allgalleryButtons.forEach(function(elem){
if(elem.parentElement.offsetParent){
galleryButtons.push(elem);
}
})
if(galleryButtons.length>1){
var currentButton = gradioApp().querySelector(".gallery-item.transition-all.\\!ring-2")
var allcurrentButtons = gradioApp().querySelectorAll(".gallery-item.transition-all.\\!ring-2")
var currentButton = null
allcurrentButtons.forEach(function(elem){
if(elem.parentElement.offsetParent){
currentButton = elem;
}
})
var result = -1
galleryButtons.forEach(function(v, i){ if(v==currentButton) { result = i } })
+12
View File
@@ -2,7 +2,19 @@
let lastHeadImg = null;
notificationButton = null
onUiUpdate(function(){
if(notificationButton == null){
notificationButton = gradioApp().getElementById('request_notifications')
if(notificationButton != null){
notificationButton.addEventListener('click', function (evt) {
Notification.requestPermission();
},true);
}
}
const galleryPreviews = gradioApp().querySelectorAll('img.h-full.w-full.overflow-hidden');
if (galleryPreviews == null) return;
+27 -29
View File
@@ -1,53 +1,51 @@
// code related to showing and updating progressbar shown as the image is being made
global_progressbar = null
global_progressbars = {}
onUiUpdate(function(){
progressbar = gradioApp().getElementById('progressbar')
progressDiv = gradioApp().querySelectorAll('.progressDiv').length > 0;
interrupt = gradioApp().getElementById('interrupt')
if(progressbar!= null && progressbar != global_progressbar){
global_progressbar = progressbar
function check_progressbar(id_part, id_progressbar, id_progressbar_span, id_interrupt, id_preview, id_gallery){
var progressbar = gradioApp().getElementById(id_progressbar)
var interrupt = gradioApp().getElementById(id_interrupt)
if(progressbar!= null && progressbar != global_progressbars[id_progressbar]){
global_progressbars[id_progressbar] = progressbar
var mutationObserver = new MutationObserver(function(m){
txt2img_preview = gradioApp().getElementById('txt2img_preview')
txt2img_gallery = gradioApp().getElementById('txt2img_gallery')
img2img_preview = gradioApp().getElementById('img2img_preview')
img2img_gallery = gradioApp().getElementById('img2img_gallery')
preview = gradioApp().getElementById(id_preview)
gallery = gradioApp().getElementById(id_gallery)
if(txt2img_preview != null && txt2img_gallery != null){
txt2img_preview.style.width = txt2img_gallery.clientWidth + "px"
txt2img_preview.style.height = txt2img_gallery.clientHeight + "px"
if(preview != null && gallery != null){
preview.style.width = gallery.clientWidth + "px"
preview.style.height = gallery.clientHeight + "px"
var progressDiv = gradioApp().querySelectorAll('#' + id_progressbar_span).length > 0;
if(!progressDiv){
interrupt.style.display = "none"
}
}
if(img2img_preview != null && img2img_gallery != null){
img2img_preview.style.width = img2img_gallery.clientWidth + "px"
img2img_preview.style.height = img2img_gallery.clientHeight + "px"
if(!progressDiv){
interrupt.style.display = "none"
}
}
window.setTimeout(requestMoreProgress, 500)
window.setTimeout(function(){ requestMoreProgress(id_part, id_progressbar_span, id_interrupt) }, 500)
});
mutationObserver.observe( progressbar, { childList:true, subtree:true })
}
}
onUiUpdate(function(){
check_progressbar('txt2img', 'txt2img_progressbar', 'txt2img_progress_span', 'txt2img_interrupt', 'txt2img_preview', 'txt2img_gallery')
check_progressbar('img2img', 'img2img_progressbar', 'img2img_progress_span', 'img2img_interrupt', 'img2img_preview', 'img2img_gallery')
})
function requestMoreProgress(){
btn = gradioApp().getElementById("check_progress");
function requestMoreProgress(id_part, id_progressbar_span, id_interrupt){
btn = gradioApp().getElementById(id_part+"_check_progress");
if(btn==null) return;
btn.click();
progressDiv = gradioApp().querySelectorAll('.progressDiv').length > 0;
if(progressDiv){
var progressDiv = gradioApp().querySelectorAll('#' + id_progressbar_span).length > 0;
var interrupt = gradioApp().getElementById(id_interrupt)
if(progressDiv && interrupt){
interrupt.style.display = "block"
}
}
function requestProgress(){
btn = gradioApp().getElementById("check_progress_initial");
function requestProgress(id_part){
btn = gradioApp().getElementById(id_part+"_check_progress_initial");
if(btn==null) return;
btn.click();
+48 -4
View File
@@ -25,13 +25,57 @@ function extract_image_from_gallery(gallery){
return gallery[index];
}
function extract_image_from_gallery_img2img(gallery){
function args_to_array(args){
res = []
for(var i=0;i<args.length;i++){
res.push(args[i])
}
return res
}
function switch_to_txt2img(){
gradioApp().querySelectorAll('button')[0].click();
return args_to_array(arguments);
}
function switch_to_img2img_img2img(){
gradioApp().querySelectorAll('button')[1].click();
gradioApp().getElementById('mode_img2img').querySelectorAll('button')[0].click();
return args_to_array(arguments);
}
function switch_to_img2img_inpaint(){
gradioApp().querySelectorAll('button')[1].click();
gradioApp().getElementById('mode_img2img').querySelectorAll('button')[1].click();
return args_to_array(arguments);
}
function switch_to_extras(){
gradioApp().querySelectorAll('button')[2].click();
return args_to_array(arguments);
}
function extract_image_from_gallery_txt2img(gallery){
switch_to_txt2img()
return extract_image_from_gallery(gallery);
}
function extract_image_from_gallery_img2img(gallery){
switch_to_img2img_img2img()
return extract_image_from_gallery(gallery);
}
function extract_image_from_gallery_inpaint(gallery){
switch_to_img2img_inpaint()
return extract_image_from_gallery(gallery);
}
function extract_image_from_gallery_extras(gallery){
gradioApp().querySelectorAll('button')[2].click();
switch_to_extras()
return extract_image_from_gallery(gallery);
}
@@ -79,13 +123,13 @@ function create_submit_args(args){
}
function submit(){
requestProgress()
requestProgress('txt2img')
return create_submit_args(arguments)
}
function submit_img2img(){
requestProgress()
requestProgress('img2img')
res = create_submit_args(arguments)
+4 -2
View File
@@ -102,6 +102,7 @@ def run_pnginfo(image):
return '', '', ''
items = image.info
geninfo = ''
if "exif" in image.info:
exif = piexif.load(image.info["exif"])
@@ -111,13 +112,14 @@ def run_pnginfo(image):
except ValueError:
exif_comment = exif_comment.decode('utf8', errors="ignore")
items['exif comment'] = exif_comment
geninfo = exif_comment
for field in ['jfif', 'jfif_version', 'jfif_unit', 'jfif_density', 'dpi', 'exif',
'loop', 'background', 'timestamp', 'duration']:
items.pop(field, None)
geninfo = items.get('parameters', geninfo)
info = ''
for key, text in items.items():
@@ -132,4 +134,4 @@ def run_pnginfo(image):
message = "Nothing found in the image."
info = f"<div><p>{message}<p></div>"
return '', '', info
return '', geninfo, info
@@ -0,0 +1,88 @@
from collections import namedtuple
import re
import gradio as gr
re_param = re.compile(r"\s*([\w ]+):\s*([^,]+)(?:,|$)")
re_imagesize = re.compile(r"^(\d+)x(\d+)$")
def parse_generation_parameters(x: str):
"""parses generation parameters string, the one you see in text field under the picture in UI:
```
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
Negative prompt: ugly, fat, obese, chubby, (((deformed))), [blurry], bad anatomy, disfigured, poorly drawn face, mutation, mutated, (extra_limb), (ugly), (poorly drawn hands), messy drawing
Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model hash: 45dee52b
```
returns a dict with field values
"""
res = {}
prompt = ""
negative_prompt = ""
done_with_prompt = False
*lines, lastline = x.strip().split("\n")
for i, line in enumerate(lines):
line = line.strip()
if line.startswith("Negative prompt:"):
done_with_prompt = True
line = line[16:].strip()
if done_with_prompt:
negative_prompt += line
else:
prompt += line
if len(prompt) > 0:
res["Prompt"] = prompt
if len(negative_prompt) > 0:
res["Negative prompt"] = negative_prompt
for k, v in re_param.findall(lastline):
m = re_imagesize.match(v)
if m is not None:
res[k+"-1"] = m.group(1)
res[k+"-2"] = m.group(2)
else:
res[k] = v
return res
def connect_paste(button, d, input_comp, js=None):
items = []
outputs = []
def paste_func(prompt):
params = parse_generation_parameters(prompt)
res = []
for key, output in zip(items, outputs):
v = params.get(key, None)
if v is None:
res.append(gr.update())
else:
try:
valtype = type(output.value)
val = valtype(v)
res.append(gr.update(value=val))
except Exception:
res.append(gr.update())
return res
for k, v in d.items():
items.append(k)
outputs.append(v)
button.click(
fn=paste_func,
_js=js,
inputs=[input_comp],
outputs=outputs,
)
+13 -6
View File
@@ -1,6 +1,7 @@
import os
import sys
import traceback
from glob import glob
from modules import shared, devices
from modules.shared import cmd_opts
@@ -11,14 +12,20 @@ import modules.face_restoration
def gfpgan_model_path():
from modules.shared import cmd_opts
filemask = 'GFPGAN*.pth'
if cmd_opts.gfpgan_model is not None:
return cmd_opts.gfpgan_model
places = [script_path, '.', os.path.join(cmd_opts.gfpgan_dir, 'experiments/pretrained_models')]
files = [cmd_opts.gfpgan_model] + [os.path.join(dirname, cmd_opts.gfpgan_model) for dirname in places]
found = [x for x in files if os.path.exists(x)]
if len(found) == 0:
raise Exception("GFPGAN model not found in paths: " + ", ".join(files))
filename = None
for place in places:
filename = next(iter(glob(os.path.join(place, filemask))), None)
if filename is not None:
break
return found[0]
return filename
loaded_gfpgan_model = None
@@ -34,7 +41,7 @@ def gfpgan():
if gfpgan_constructor is None:
return None
model = gfpgan_constructor(model_path=gfpgan_model_path(), upscale=1, arch='clean', channel_multiplier=2, bg_upsampler=None)
model = gfpgan_constructor(model_path=gfpgan_model_path() or 'https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.4.pth', upscale=1, arch='clean', channel_multiplier=2, bg_upsampler=None)
model.gfpgan.to(shared.device)
loaded_gfpgan_model = model
+16 -6
View File
@@ -209,8 +209,16 @@ def draw_prompt_matrix(im, width, height, all_prompts):
def resize_image(resize_mode, im, width, height):
def resize(im, w, h):
if opts.upscaler_for_img2img is None or opts.upscaler_for_img2img == "None" or im.mode == 'L':
return im.resize((w, h), resample=LANCZOS)
upscaler = [x for x in shared.sd_upscalers if x.name == opts.upscaler_for_img2img][0]
return upscaler.upscale(im, w, h)
if resize_mode == 0:
res = im.resize((width, height), resample=LANCZOS)
res = resize(im, width, height)
elif resize_mode == 1:
ratio = width / height
src_ratio = im.width / im.height
@@ -218,9 +226,10 @@ def resize_image(resize_mode, im, width, height):
src_w = width if ratio > src_ratio else im.width * height // im.height
src_h = height if ratio <= src_ratio else im.height * width // im.width
resized = im.resize((src_w, src_h), resample=LANCZOS)
resized = resize(im, src_w, src_h)
res = Image.new("RGB", (width, height))
res.paste(resized, box=(width // 2 - src_w // 2, height // 2 - src_h // 2))
else:
ratio = width / height
src_ratio = im.width / im.height
@@ -228,7 +237,7 @@ def resize_image(resize_mode, im, width, height):
src_w = width if ratio < src_ratio else im.width * height // im.height
src_h = height if ratio >= src_ratio else im.height * width // im.width
resized = im.resize((src_w, src_h), resample=LANCZOS)
resized = resize(im, src_w, src_h)
res = Image.new("RGB", (width, height))
res.paste(resized, box=(width // 2 - src_w // 2, height // 2 - src_h // 2))
@@ -249,7 +258,6 @@ invalid_filename_prefix = ' '
invalid_filename_postfix = ' .'
re_nonletters = re.compile(r'[\s'+string.punctuation+']+')
max_filename_part_length = 128
max_prompt_words = 8
def sanitize_filename_part(text, replace_spaces=True):
@@ -263,6 +271,8 @@ def sanitize_filename_part(text, replace_spaces=True):
def apply_filename_pattern(x, p, seed, prompt):
max_prompt_words = opts.directories_max_prompt_words
if seed is not None:
x = x.replace("[seed]", str(seed))
@@ -311,7 +321,7 @@ def get_next_sequence_number(path, basename):
return result + 1
def save_image(image, path, basename, seed=None, prompt=None, extension='png', info=None, short_filename=False, no_prompt=False, grid=False, pnginfo_section_name='parameters', p=None, existing_info=None, forced_filename=None):
def save_image(image, path, basename, seed=None, prompt=None, extension='png', info=None, short_filename=False, no_prompt=False, grid=False, pnginfo_section_name='parameters', p=None, existing_info=None, forced_filename=None, suffix=""):
if short_filename or prompt is None or seed is None:
file_decoration = ""
elif opts.save_to_dirs:
@@ -322,7 +332,7 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='png', i
if file_decoration != "":
file_decoration = "-" + file_decoration.lower()
file_decoration = apply_filename_pattern(file_decoration, p, seed, prompt)
file_decoration = apply_filename_pattern(file_decoration, p, seed, prompt) + suffix
if extension == 'png' and opts.enable_pnginfo and info is not None:
pnginfo = PngImagePlugin.PngInfo()
+5 -1
View File
@@ -118,4 +118,8 @@ def img2img(mode: int, prompt: str, negative_prompt: str, prompt_style: str, pro
shared.total_tqdm.clear()
return processed.images, processed.js(), plaintext_to_html(processed.info)
generation_info_js = processed.js()
if opts.samples_log_stdout:
print(generation_info_js)
return processed.images, generation_info_js, plaintext_to_html(processed.info)
+8 -3
View File
@@ -20,6 +20,7 @@ import modules.shared as shared
import modules.face_restoration
import modules.images as images
import modules.styles
import logging
# some of those options should not be changed at all because they would break the model, so I removed them from options.
@@ -28,11 +29,13 @@ opt_f = 8
def setup_color_correction(image):
logging.info("Calibrating color correction.")
correction_target = cv2.cvtColor(np.asarray(image.copy()), cv2.COLOR_RGB2LAB)
return correction_target
def apply_color_correction(correction, image):
logging.info("Applying color correction.")
image = Image.fromarray(cv2.cvtColor(exposure.match_histograms(
cv2.cvtColor(
np.asarray(image),
@@ -357,7 +360,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
if p.restore_faces:
if opts.save and not p.do_not_save_samples and opts.save_images_before_face_restoration:
images.save_image(Image.fromarray(x_sample), p.outpath_samples, "", seeds[i], prompts[i], opts.samples_format, info=infotext(n, i), p=p)
images.save_image(Image.fromarray(x_sample), p.outpath_samples, "", seeds[i], prompts[i], opts.samples_format, info=infotext(n, i), p=p, suffix="-before-face-restoration")
devices.torch_gc()
@@ -366,6 +369,8 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
image = Image.fromarray(x_sample)
if p.color_corrections is not None and i < len(p.color_corrections):
if opts.save and not p.do_not_save_samples and opts.save_images_before_color_correction:
images.save_image(image, p.outpath_samples, "", seeds[i], prompts[i], opts.samples_format, info=infotext(n, i), p=p, suffix="-before-color-correction")
image = apply_color_correction(p.color_corrections[i], image)
if p.overlay_images is not None and i < len(p.overlay_images):
@@ -457,7 +462,7 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
else:
decoded_samples = self.sd_model.decode_first_stage(samples)
if opts.upscaler_for_hires_fix is None or opts.upscaler_for_hires_fix == "None":
if opts.upscaler_for_img2img is None or opts.upscaler_for_img2img == "None":
decoded_samples = torch.nn.functional.interpolate(decoded_samples, size=(self.height, self.width), mode="bilinear")
else:
lowres_samples = torch.clamp((decoded_samples + 1.0) / 2.0, min=0.0, max=1.0)
@@ -467,7 +472,7 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
x_sample = 255. * np.moveaxis(x_sample.cpu().numpy(), 0, 2)
x_sample = x_sample.astype(np.uint8)
image = Image.fromarray(x_sample)
upscaler = [x for x in shared.sd_upscalers if x.name == opts.upscaler_for_hires_fix][0]
upscaler = [x for x in shared.sd_upscalers if x.name == opts.upscaler_for_img2img][0]
image = upscaler.upscale(image, self.width, self.height)
image = np.array(image).astype(np.float32) / 255.0
image = np.moveaxis(image, 2, 0)
+139 -74
View File
@@ -2,7 +2,6 @@ import sys
import argparse
import json
import os
from glob import glob
import gradio as gr
import tqdm
@@ -22,7 +21,7 @@ parser.add_argument("--config", type=str, default=os.path.join(sd_path, "configs
parser.add_argument("--ckpt", type=str, default=sd_model_file, help="path to checkpoint of stable diffusion model; this checkpoint will be added to the list of checkpoints and loaded by default if you don't have a checkpoint selected in settings",)
parser.add_argument("--ckpt-dir", type=str, default=os.path.join(script_path, 'models'), help="path to directory with stable diffusion checkpoints",)
parser.add_argument("--gfpgan-dir", type=str, help="GFPGAN directory", default=('./src/gfpgan' if os.path.exists('./src/gfpgan') else './GFPGAN'))
parser.add_argument("--gfpgan-model", type=str, help="GFPGAN model file name", default=next(iter(glob('GFPGAN*.pth')), ''))
parser.add_argument("--gfpgan-model", type=str, help="GFPGAN model file name", default=None)
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 acceleration in browser)")
parser.add_argument("--max-batch-count", type=int, default=16, help="maximum batch count value for the UI")
@@ -100,80 +99,127 @@ def realesrgan_models_names():
return [x.name for x in modules.realesrgan_model.get_realesrgan_models()]
class Options:
class OptionInfo:
def __init__(self, default=None, label="", component=None, component_args=None, onchange=None):
self.default = default
self.label = label
self.component = component
self.component_args = component_args
self.onchange = onchange
class OptionInfo:
def __init__(self, default=None, label="", component=None, component_args=None, onchange=None):
self.default = default
self.label = label
self.component = component
self.component_args = component_args
self.onchange = onchange
self.section = None
def options_section(section_identifer, options_dict):
for k, v in options_dict.items():
v.section = section_identifer
return options_dict
hide_dirs = {"visible": False} if cmd_opts.hide_ui_dir_config else None
options_templates = {}
options_templates.update(options_section(('saving-images', "Saving images/grids"), {
"samples_save": OptionInfo(True, "Always save all generated images"),
"samples_format": OptionInfo('png', 'File format for images'),
"samples_filename_pattern": OptionInfo("", "Images filename pattern"),
"grid_save": OptionInfo(True, "Always save all generated image grids"),
"grid_format": OptionInfo('png', 'File format for grids'),
"grid_extended_filename": OptionInfo(False, "Add extended info (seed, prompt) to filename when saving grid"),
"grid_only_if_multiple": OptionInfo(True, "Do not save grids consisting of one picture"),
"n_rows": OptionInfo(-1, "Grid row count; use -1 for autodetect and 0 for it to be same as batch size", gr.Slider, {"minimum": -1, "maximum": 16, "step": 1}),
"enable_pnginfo": OptionInfo(True, "Save text information about generation parameters as chunks to png files"),
"save_txt": OptionInfo(False, "Create a text file next to every image with generation parameters."),
"save_images_before_face_restoration": OptionInfo(False, "Save a copy of image before doing face restoration."),
"jpeg_quality": OptionInfo(80, "Quality for saved jpeg images", gr.Slider, {"minimum": 1, "maximum": 100, "step": 1}),
"export_for_4chan": OptionInfo(True, "If PNG image is larger than 4MB or any dimension is larger than 4000, downscale and save copy as JPG"),
"use_original_name_batch": OptionInfo(False, "Use original name for output filename during batch process in extras tab"),
}))
options_templates.update(options_section(('saving-paths', "Paths for saving"), {
"outdir_samples": OptionInfo("", "Output directory for images; if empty, defaults to three directories below", component_args=hide_dirs),
"outdir_txt2img_samples": OptionInfo("outputs/txt2img-images", 'Output directory for txt2img images', component_args=hide_dirs),
"outdir_img2img_samples": OptionInfo("outputs/img2img-images", 'Output directory for img2img images', component_args=hide_dirs),
"outdir_extras_samples": OptionInfo("outputs/extras-images", 'Output directory for images from extras tab', component_args=hide_dirs),
"outdir_grids": OptionInfo("", "Output directory for grids; if empty, defaults to two directories below", component_args=hide_dirs),
"outdir_txt2img_grids": OptionInfo("outputs/txt2img-grids", 'Output directory for txt2img grids', component_args=hide_dirs),
"outdir_img2img_grids": OptionInfo("outputs/img2img-grids", 'Output directory for img2img grids', component_args=hide_dirs),
"outdir_save": OptionInfo("log/images", "Directory for saving images using the Save button", component_args=hide_dirs),
}))
options_templates.update(options_section(('saving-to-dirs', "Saving to a directory"), {
"save_to_dirs": OptionInfo(False, "Save images to a subdirectory"),
"grid_save_to_dirs": OptionInfo(False, "Save grids to subdirectory"),
"directories_filename_pattern": OptionInfo("", "Directory name pattern"),
"directories_max_prompt_words": OptionInfo(8, "Max prompt words", gr.Slider, {"minimum": 1, "maximum": 20, "step": 1}),
}))
options_templates.update(options_section(('upscaling', "Upscaling"), {
"ESRGAN_tile": OptionInfo(192, "Tile size for ESRGAN upscalers. 0 = no tiling.", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}),
"ESRGAN_tile_overlap": OptionInfo(8, "Tile overlap, in pixels for ESRGAN upscalers. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}),
"realesrgan_enabled_models": OptionInfo(["Real-ESRGAN 4x plus", "Real-ESRGAN 4x plus anime 6B"], "Select which RealESRGAN models to show in the web UI. (Requires restart)", gr.CheckboxGroup, lambda: {"choices": realesrgan_models_names()}),
"SWIN_tile": OptionInfo(192, "Tile size for all SwinIR.", gr.Slider, {"minimum": 16, "maximum": 512, "step": 16}),
"SWIN_tile_overlap": OptionInfo(8, "Tile overlap, in pixels for SwinIR. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}),
"ldsr_steps": OptionInfo(100, "LDSR processing steps. Lower = faster", gr.Slider, {"minimum": 1, "maximum": 200, "step": 1}),
"ldsr_pre_down": OptionInfo(1, "LDSR Pre-process downssample scale. 1 = no down-sampling, 4 = 1/4 scale.", gr.Slider, {"minimum": 1, "maximum": 4, "step": 1}),
"ldsr_post_down": OptionInfo(1, "LDSR Post-process down-sample scale. 1 = no down-sampling, 4 = 1/4 scale.", gr.Slider, {"minimum": 1, "maximum": 4, "step": 1}),
"upscaler_for_img2img": OptionInfo(None, "Upscaler for img2img", gr.Radio, lambda: {"choices": [x.name for x in sd_upscalers]}),
}))
options_templates.update(options_section(('face-restoration', "Face restoration"), {
"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}),
"face_restoration_unload": OptionInfo(False, "Move face restoration model from VRAM into RAM after processing"),
"save_selected_only": OptionInfo(False, "When using 'Save' button, only save a single selected image"),
}))
options_templates.update(options_section(('system', "System"), {
"memmon_poll_rate": OptionInfo(8, "VRAM usage polls per second during generation. Set to 0 to disable.", gr.Slider, {"minimum": 0, "maximum": 40, "step": 1}),
"samples_log_stdout": OptionInfo(False, "Always print all generation info to standard output"),
"multiple_tqdm": OptionInfo(True, "Add a second progress bar to the console that shows progress for an entire job. Broken in PyCharm console."),
}))
options_templates.update(options_section(('sd', "Stable Diffusion"), {
"sd_model_checkpoint": OptionInfo(None, "Stable Diffusion checkpoint", gr.Radio, lambda: {"choices": [x.title for x in modules.sd_models.checkpoints_list.values()]}),
"img2img_color_correction": OptionInfo(False, "Apply color correction to img2img results to match original colors."),
"save_images_before_color_correction": OptionInfo(False, "Save a copy of image before applying color correction to img2img results"),
"img2img_fix_steps": OptionInfo(False, "With img2img, do exactly the amount of steps the slider specifies (normally you'd do less with less denoising)."),
"enable_quantization": OptionInfo(False, "Enable quantization in K samplers for sharper and cleaner results. This may change existing seeds. Requires restart to apply."),
"enable_emphasis": OptionInfo(True, "Use (text) to make model pay more attention to text and [text] to make it pay less attention"),
"enable_batch_seeds": OptionInfo(True, "Make K-diffusion samplers produce same images in a batch as when making a single image"),
"filter_nsfw": OptionInfo(False, "Filter NSFW content"),
"random_artist_categories": OptionInfo([], "Allowed categories for random artists selection when using the Roll button", gr.CheckboxGroup, {"choices": artist_db.categories()}),
}))
options_templates.update(options_section(('interrogate', "Interrogate Options"), {
"interrogate_keep_models_in_memory": OptionInfo(False, "Interrogate: keep models in VRAM"),
"interrogate_use_builtin_artists": OptionInfo(True, "Interrogate: use artists from artists.csv"),
"interrogate_clip_num_beams": OptionInfo(1, "Interrogate: num_beams for BLIP", gr.Slider, {"minimum": 1, "maximum": 16, "step": 1}),
"interrogate_clip_min_length": OptionInfo(24, "Interrogate: minimum description length (excluding artists, etc..)", gr.Slider, {"minimum": 1, "maximum": 128, "step": 1}),
"interrogate_clip_max_length": OptionInfo(48, "Interrogate: maximum description length", gr.Slider, {"minimum": 1, "maximum": 256, "step": 1}),
"interrogate_clip_dict_limit": OptionInfo(1500, "Interrogate: maximum number of lines in text file (0 = No limit)"),
}))
options_templates.update(options_section(('ui', "User interface"), {
"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}),
"return_grid": OptionInfo(True, "Show grid in results for web"),
"add_model_hash_to_info": OptionInfo(True, "Add model hash to generation information"),
"font": OptionInfo("", "Font for image grids that have text"),
"js_modal_lightbox": OptionInfo(True, "Enable full page image viewer"),
"js_modal_lightbox_initialy_zoomed": OptionInfo(True, "Show images zoomed in by default in full page image viewer"),
}))
class Options:
data = None
hide_dirs = {"visible": False} if cmd_opts.hide_ui_dir_config else None
data_labels = {
"samples_filename_pattern": OptionInfo("", "Images filename pattern"),
"save_to_dirs": OptionInfo(False, "Save images to a subdirectory"),
"grid_save_to_dirs": OptionInfo(False, "Save grids to subdirectory"),
"directories_filename_pattern": OptionInfo("", "Directory name pattern"),
"outdir_samples": OptionInfo("", "Output directory for images; if empty, defaults to two directories below", component_args=hide_dirs),
"outdir_txt2img_samples": OptionInfo("outputs/txt2img-images", 'Output directory for txt2img images', component_args=hide_dirs),
"outdir_img2img_samples": OptionInfo("outputs/img2img-images", 'Output directory for img2img images', component_args=hide_dirs),
"outdir_extras_samples": OptionInfo("outputs/extras-images", 'Output directory for images from extras tab', component_args=hide_dirs),
"outdir_grids": OptionInfo("", "Output directory for grids; if empty, defaults to two directories below", component_args=hide_dirs),
"outdir_txt2img_grids": OptionInfo("outputs/txt2img-grids", 'Output directory for txt2img grids', component_args=hide_dirs),
"outdir_img2img_grids": OptionInfo("outputs/img2img-grids", 'Output directory for img2img grids', component_args=hide_dirs),
"outdir_save": OptionInfo("log/images", "Directory for saving images using the Save button", component_args=hide_dirs),
"samples_save": OptionInfo(True, "Always save all generated images"),
"save_selected_only": OptionInfo(False, "When using 'Save' button, only save a single selected image"),
"samples_format": OptionInfo('png', 'File format for individual samples'),
"filter_nsfw": OptionInfo(False, "Filter NSFW content"),
"grid_save": OptionInfo(True, "Always save all generated image grids"),
"return_grid": OptionInfo(True, "Show grid in results for web"),
"grid_format": OptionInfo('png', 'File format for grids'),
"grid_extended_filename": OptionInfo(False, "Add extended info (seed, prompt) to filename when saving grid"),
"grid_only_if_multiple": OptionInfo(True, "Do not save grids consisting of one picture"),
"n_rows": OptionInfo(-1, "Grid row count; use -1 for autodetect and 0 for it to be same as batch size", gr.Slider, {"minimum": -1, "maximum": 16, "step": 1}),
"jpeg_quality": OptionInfo(80, "Quality for saved jpeg images", gr.Slider, {"minimum": 1, "maximum": 100, "step": 1}),
"export_for_4chan": OptionInfo(True, "If PNG image is larger than 4MB or any dimension is larger than 4000, downscale and save copy as JPG"),
"enable_pnginfo": OptionInfo(True, "Save text information about generation parameters as chunks to png files"),
"add_model_hash_to_info": OptionInfo(False, "Add model hash to generation information"),
"img2img_color_correction": OptionInfo(False, "Apply color correction to img2img results to match original colors."),
"img2img_fix_steps": OptionInfo(False, "With img2img, do exactly the amount of steps the slider specifies (normally you'd do less with less denoising)."),
"enable_quantization": OptionInfo(False, "Enable quantization in K samplers for sharper and cleaner results. This may change existing seeds. Requires restart to apply."),
"font": OptionInfo("", "Font for image grids that have text"),
"enable_emphasis": OptionInfo(True, "Use (text) to make model pay more attention to text and [text] to make it pay less attention"),
"enable_batch_seeds": OptionInfo(True, "Make K-diffusion samplers produce same images in a batch as when making a single image"),
"save_txt": OptionInfo(False, "Create a text file next to every image with generation parameters."),
"ESRGAN_tile": OptionInfo(192, "Tile size for ESRGAN upscalers. 0 = no tiling.", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}),
"ESRGAN_tile_overlap": OptionInfo(8, "Tile overlap, in pixels for ESRGAN upscalers. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}),
"realesrgan_enabled_models": OptionInfo(["Real-ESRGAN 4x plus", "Real-ESRGAN 4x plus anime 6B"], "Select which RealESRGAN models to show in the web UI. (Requires restart)", gr.CheckboxGroup, lambda: {"choices": realesrgan_models_names()}),
"SWIN_tile": OptionInfo(192, "Tile size for all SwinIR.", gr.Slider, {"minimum": 16, "maximum": 512, "step": 16}),
"SWIN_tile_overlap": OptionInfo(8, "Tile overlap, in pixels for SwinIR. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}),
"ldsr_steps": OptionInfo(100, "LDSR processing steps. Lower = faster", gr.Slider, {"minimum": 1, "maximum": 200, "step": 1}),
"ldsr_pre_down":OptionInfo(1, "LDSR Pre-process downssample scale. 1 = no down-sampling, 4 = 1/4 scale.", gr.Slider, {"minimum": 1, "maximum": 4, "step": 1}),
"ldsr_post_down":OptionInfo(1, "LDSR Post-process down-sample scale. 1 = no down-sampling, 4 = 1/4 scale.", gr.Slider, {"minimum": 1, "maximum": 4, "step": 1}),
"random_artist_categories": OptionInfo([], "Allowed categories for random artists selection when using the Roll button", gr.CheckboxGroup, {"choices": artist_db.categories()}),
"upscaler_for_hires_fix": OptionInfo(None, "Upscaler for highres. fix", gr.Radio, lambda: {"choices": [x.name for x in sd_upscalers]}),
"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."),
"memmon_poll_rate": OptionInfo(8, "VRAM usage polls per second during generation. Set to 0 to disable.", gr.Slider, {"minimum": 0, "maximum": 40, "step":1}),
"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}),
"save_images_before_face_restoration": OptionInfo(False, "Save a copy of image before doing face restoration."),
"face_restoration_unload": OptionInfo(False, "Move face restoration model from VRAM into RAM after processing"),
"interrogate_keep_models_in_memory": OptionInfo(False, "Interrogate: keep models in VRAM"),
"interrogate_use_builtin_artists": OptionInfo(True, "Interrogate: use artists from artists.csv"),
"interrogate_clip_num_beams": OptionInfo(1, "Interrogate: num_beams for BLIP", gr.Slider, {"minimum": 1, "maximum": 16, "step": 1}),
"interrogate_clip_min_length": OptionInfo(24, "Interrogate: minimum description length (excluding artists, etc..)", gr.Slider, {"minimum": 1, "maximum": 128, "step": 1}),
"interrogate_clip_max_length": OptionInfo(48, "Interrogate: maximum description length", gr.Slider, {"minimum": 1, "maximum": 256, "step": 1}),
"interrogate_clip_dict_limit": OptionInfo(1500, "Interrogate: maximum number of lines in text file (0 = No limit)"),
"sd_model_checkpoint": OptionInfo(None, "Stable Diffusion checkpoint", gr.Radio, lambda: {"choices": [x.title for x in modules.sd_models.checkpoints_list.values()]}),
"js_modal_lightbox": OptionInfo(True, "Enable full page image viewer"),
"js_modal_lightbox_initialy_zoomed": OptionInfo(True, "Show images zoomed in by default in full page image viewer"),
"use_original_name_batch": OptionInfo(False, "Use original name for output filename during batch process"),
}
data_labels = options_templates
typemap = {int: float}
def __init__(self):
self.data = {k: v.default for k, v in self.data_labels.items()}
@@ -199,10 +245,29 @@ class Options:
with open(filename, "w", encoding="utf8") as file:
json.dump(self.data, file)
def same_type(self, x, y):
if x is None or y is None:
return True
type_x = self.typemap.get(type(x), type(x))
type_y = self.typemap.get(type(y), type(y))
return type_x == type_y
def load(self, filename):
with open(filename, "r", encoding="utf8") as file:
self.data = json.load(file)
bad_settings = 0
for k, v in self.data.items():
info = self.data_labels.get(k, None)
if info is not None and not self.same_type(info.default, v):
print(f"Warning: bad setting value: {k}: {v} ({type(v).__name__}; expected {type(info.default).__name__})", file=sys.stderr)
bad_settings += 1
if bad_settings > 0:
print(f"The program is likely to not work with bad settings.\nSettings file: {filename}\nEither fix the file, or delete it and restart.", file=sys.stderr)
def onchange(self, key, func):
item = self.data_labels.get(key)
item.onchange = func
+6 -4
View File
@@ -37,12 +37,14 @@ def txt2img(prompt: str, negative_prompt: str, prompt_style: str, prompt_style2:
print(f"\ntxt2img: {prompt}", file=shared.progress_print_out)
processed = modules.scripts.scripts_txt2img.run(p, *args)
if processed is not None:
pass
else:
if processed is None:
processed = process_images(p)
shared.total_tqdm.clear()
return processed.images, processed.js(), plaintext_to_html(processed.info)
generation_info_js = processed.js()
if opts.samples_log_stdout:
print(generation_info_js)
return processed.images, generation_info_js, plaintext_to_html(processed.info)
+173 -105
View File
@@ -22,12 +22,12 @@ from modules.paths import script_path
from modules.shared import opts, cmd_opts
import modules.shared as shared
from modules.sd_samplers import samplers, samplers_for_img2img
import modules.realesrgan_model as realesrgan
import modules.ldsr_model
import modules.scripts
import modules.gfpgan_model
import modules.codeformer_model
import modules.styles
import modules.generation_parameters_copypaste
# this is a fix for Windows users. Without it, javascript files will be served with text/html content-type and the bowser will not show any UI
mimetypes.init()
@@ -58,6 +58,8 @@ css_hide_progressbar = """
# Important that they exactly match script.js for tooltip to work.
random_symbol = '\U0001f3b2\ufe0f' # 🎲️
reuse_symbol = '\u267b\ufe0f' # ♻️
art_symbol = '\U0001f3a8' # 🎨
paste_symbol = '\u2199\ufe0f' # ↙
def plaintext_to_html(text):
@@ -167,7 +169,7 @@ def wrap_gradio_call(func):
return f
def check_progress_call():
def check_progress_call(id_part):
if shared.state.job_count == 0:
return "", gr_show(False), gr_show(False)
@@ -201,15 +203,15 @@ def check_progress_call():
else:
preview_visibility = gr_show(True)
return f"<span style='display: none'>{time.time()}</span><p>{progressbar}</p>", preview_visibility, image
return f"<span id='{id_part}_progress_span' style='display: none'>{time.time()}</span><p>{progressbar}</p>", preview_visibility, image
def check_progress_call_initial():
def check_progress_call_initial(id_part):
shared.state.job_count = -1
shared.state.current_latent = None
shared.state.current_image = None
return check_progress_call()
return check_progress_call(id_part)
def roll_artist(prompt):
@@ -237,8 +239,7 @@ def add_style(name: str, prompt: str, negative_prompt: str):
# reserialize all styles every time we save them
shared.prompt_styles.save_styles(shared.styles_filename)
update = {"visible": True, "choices": list(shared.prompt_styles.styles), "__type__": "update"}
return [update, update, update, update]
return [gr.Dropdown.update(visible=True, choices=list(shared.prompt_styles.styles)) for _ in range(4)]
def apply_styles(prompt, prompt_neg, style1_name, style2_name):
@@ -330,27 +331,32 @@ def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info:
def create_toprow(is_img2img):
id_part = "img2img" if is_img2img else "txt2img"
with gr.Row(elem_id="toprow"):
with gr.Column(scale=4):
with gr.Row():
with gr.Column(scale=8):
with gr.Column(scale=80):
with gr.Row():
prompt = gr.Textbox(label="Prompt", elem_id="prompt", show_label=False, placeholder="Prompt", lines=2)
roll = gr.Button('Roll', elem_id="roll", visible=len(shared.artist_db.artists) > 0)
with gr.Column(scale=1, elem_id="style_pos_col"):
prompt_style = gr.Dropdown(label="Style 1", elem_id="style_index", choices=[k for k, v in shared.prompt_styles.styles.items()], value=next(iter(shared.prompt_styles.styles.keys())), visible=len(shared.prompt_styles.styles) > 1)
with gr.Column(scale=1, elem_id="roll_col"):
roll = gr.Button(value=art_symbol, elem_id="roll", visible=len(shared.artist_db.artists) > 0)
paste = gr.Button(value=paste_symbol, elem_id="paste")
with gr.Column(scale=10, elem_id="style_pos_col"):
prompt_style = gr.Dropdown(label="Style 1", elem_id=f"{id_part}_style_index", choices=[k for k, v in shared.prompt_styles.styles.items()], value=next(iter(shared.prompt_styles.styles.keys())), visible=len(shared.prompt_styles.styles) > 1)
with gr.Row():
with gr.Column(scale=8):
negative_prompt = gr.Textbox(label="Negative prompt", elem_id="negative_prompt", show_label=False, placeholder="Negative prompt", lines=2)
with gr.Column(scale=1, elem_id="style_neg_col"):
prompt_style2 = gr.Dropdown(label="Style 2", elem_id="style2_index", choices=[k for k, v in shared.prompt_styles.styles.items()], value=next(iter(shared.prompt_styles.styles.keys())), visible=len(shared.prompt_styles.styles) > 1)
prompt_style2 = gr.Dropdown(label="Style 2", elem_id=f"{id_part}_style2_index", choices=[k for k, v in shared.prompt_styles.styles.items()], value=next(iter(shared.prompt_styles.styles.keys())), visible=len(shared.prompt_styles.styles) > 1)
with gr.Column(scale=1):
with gr.Row():
interrupt = gr.Button('Interrupt', elem_id="interrupt")
interrupt = gr.Button('Interrupt', elem_id=f"{id_part}_interrupt")
submit = gr.Button('Generate', elem_id="generate", variant='primary')
interrupt.click(
@@ -367,21 +373,21 @@ def create_toprow(is_img2img):
prompt_style_apply = gr.Button('Apply style', elem_id="style_apply")
save_style = gr.Button('Create style', elem_id="style_create")
return prompt, roll, prompt_style, negative_prompt, prompt_style2, submit, interrogate, prompt_style_apply, save_style
return prompt, roll, prompt_style, negative_prompt, prompt_style2, submit, interrogate, prompt_style_apply, save_style, paste
def setup_progressbar(progressbar, preview):
check_progress = gr.Button('Check progress', elem_id="check_progress", visible=False)
def setup_progressbar(progressbar, preview, id_part):
check_progress = gr.Button('Check progress', elem_id=f"{id_part}_check_progress", visible=False)
check_progress.click(
fn=check_progress_call,
fn=lambda: check_progress_call(id_part),
show_progress=False,
inputs=[],
outputs=[progressbar, preview, preview],
)
check_progress_initial = gr.Button('Check progress (first)', elem_id="check_progress_initial", visible=False)
check_progress_initial = gr.Button('Check progress (first)', elem_id=f"{id_part}_check_progress_initial", visible=False)
check_progress_initial.click(
fn=check_progress_call_initial,
fn=lambda: check_progress_call_initial(id_part),
show_progress=False,
inputs=[],
outputs=[progressbar, preview, preview],
@@ -390,17 +396,17 @@ def setup_progressbar(progressbar, preview):
def create_ui(txt2img, img2img, run_extras, run_pnginfo):
with gr.Blocks(analytics_enabled=False) as txt2img_interface:
txt2img_prompt, roll, txt2img_prompt_style, txt2img_negative_prompt, txt2img_prompt_style2, submit, _, txt2img_prompt_style_apply, txt2img_save_style = create_toprow(is_img2img=False)
txt2img_prompt, roll, txt2img_prompt_style, txt2img_negative_prompt, txt2img_prompt_style2, submit, _, txt2img_prompt_style_apply, txt2img_save_style, paste = create_toprow(is_img2img=False)
dummy_component = gr.Label(visible=False)
with gr.Row(elem_id='progressRow'):
with gr.Column(scale=1):
columnEmpty = "Empty"
with gr.Row(elem_id='txt2img_progress_row'):
with gr.Column(scale=1):
pass
with gr.Column(scale=1):
progressbar = gr.HTML(elem_id="progressbar")
with gr.Column(scale=1):
progressbar = gr.HTML(elem_id="txt2img_progressbar")
txt2img_preview = gr.Image(elem_id='txt2img_preview', visible=False)
setup_progressbar(progressbar, txt2img_preview)
setup_progressbar(progressbar, txt2img_preview, 'txt2img')
with gr.Row().style(equal_height=False):
with gr.Column(variant='panel'):
@@ -435,7 +441,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
with gr.Group():
txt2img_preview = gr.Image(elem_id='txt2img_preview', visible=False)
txt2img_gallery = gr.Gallery(label='Output', elem_id='txt2img_gallery').style(grid=4)
txt2img_gallery = gr.Gallery(label='Output', show_label=False, elem_id='txt2img_gallery').style(grid=4)
with gr.Group():
with gr.Row():
@@ -516,28 +522,46 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
]
)
txt2img_paste_fields = {
"Prompt": txt2img_prompt,
"Negative prompt": txt2img_negative_prompt,
"Steps": steps,
"Sampler": sampler_index,
"Face restoration": restore_faces,
"CFG scale": cfg_scale,
"Seed": seed,
"Size-1": width,
"Size-2": height,
"Batch size": batch_size,
"Variation seed": subseed,
"Variation seed strength": subseed_strength,
"Seed resize from-1": seed_resize_from_w,
"Seed resize from-2": seed_resize_from_h,
"Denoising strength": denoising_strength,
}
modules.generation_parameters_copypaste.connect_paste(paste, txt2img_paste_fields, txt2img_prompt)
with gr.Blocks(analytics_enabled=False) as img2img_interface:
img2img_prompt, roll, img2img_prompt_style, img2img_negative_prompt, img2img_prompt_style2, submit, img2img_interrogate, img2img_prompt_style_apply, img2img_save_style = create_toprow(is_img2img=True)
img2img_prompt, roll, img2img_prompt_style, img2img_negative_prompt, img2img_prompt_style2, submit, img2img_interrogate, img2img_prompt_style_apply, img2img_save_style, paste = create_toprow(is_img2img=True)
with gr.Row(elem_id='progressRow'):
with gr.Column(scale=1):
columnEmpty = "Empty"
with gr.Row(elem_id='img2img_progress_row'):
with gr.Column(scale=1):
pass
with gr.Column(scale=1):
progressbar = gr.HTML(elem_id="progressbar")
with gr.Column(scale=1):
progressbar = gr.HTML(elem_id="img2img_progressbar")
img2img_preview = gr.Image(elem_id='img2img_preview', visible=False)
setup_progressbar(progressbar, img2img_preview)
setup_progressbar(progressbar, img2img_preview, 'img2img')
with gr.Row().style(equal_height=False):
with gr.Column(variant='panel'):
with gr.Tabs(elem_id="mode_img2img") as tabs_img2img_mode:
with gr.TabItem('img2img'):
with gr.TabItem('img2img', id='img2img'):
init_img = gr.Image(label="Image for img2img", show_label=False, source="upload", interactive=True, type="pil")
with gr.TabItem('Inpaint'):
with gr.TabItem('Inpaint', id='inpaint'):
init_img_with_mask = gr.Image(label="Image for inpainting with mask", show_label=False, elem_id="img2maskimg", source="upload", interactive=True, type="pil", tool="sketch", image_mode="RGBA")
init_img_with_mask_comment = gr.HTML(elem_id="mask_bug_info", value="<small>if the editor shows ERROR, switch to another tab and back, then to \"Upload mask\" mode above and back</small>")
init_img_inpaint = gr.Image(label="Image for img2img", show_label=False, source="upload", interactive=True, type="pil", visible=False)
init_mask_inpaint = gr.Image(label="Mask", source="upload", interactive=True, type="pil", visible=False)
@@ -554,7 +578,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
inpaint_full_res = gr.Checkbox(label='Inpaint at full resolution', value=False)
inpaint_full_res_padding = gr.Slider(label='Inpaint at full resolution padding, pixels', minimum=0, maximum=256, step=4, value=32)
with gr.TabItem('Batch img2img'):
with gr.TabItem('Batch img2img', id='batch'):
gr.HTML("<p class=\"text-gray-500\">Process images in a directory on the same machine where the server is running.</p>")
img2img_batch_input_dir = gr.Textbox(label="Input directory")
img2img_batch_output_dir = gr.Textbox(label="Output directory")
@@ -590,7 +614,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
with gr.Group():
img2img_preview = gr.Image(elem_id='img2img_preview', visible=False)
img2img_gallery = gr.Gallery(label='Output', elem_id='img2img_gallery').style(grid=4)
img2img_gallery = gr.Gallery(label='Output', show_label=False, elem_id='img2img_gallery').style(grid=4)
with gr.Group():
with gr.Row():
@@ -598,7 +622,6 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
img2img_send_to_img2img = gr.Button('Send to img2img')
img2img_send_to_inpaint = gr.Button('Send to inpaint')
img2img_send_to_extras = gr.Button('Send to extras')
img2img_save_style = gr.Button('Save prompt as style')
with gr.Group():
html_info = gr.HTML()
@@ -609,16 +632,13 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
mask_mode.change(
lambda mode, img: {
#init_img_with_mask: gr.Image.update(visible=mode == 0, value=img["image"]),
init_img_with_mask: gr_show(mode == 0),
init_img_with_mask_comment: gr_show(mode == 0),
init_img_inpaint: gr_show(mode == 1),
init_mask_inpaint: gr_show(mode == 1),
},
inputs=[mask_mode, init_img_with_mask],
outputs=[
init_img_with_mask,
init_img_with_mask_comment,
init_img_inpaint,
init_mask_inpaint,
],
@@ -721,12 +741,31 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
outputs=[prompt, negative_prompt, style1, style2],
)
img2img_paste_fields = {
"Prompt": img2img_prompt,
"Negative prompt": img2img_negative_prompt,
"Steps": steps,
"Sampler": sampler_index,
"Face restoration": restore_faces,
"CFG scale": cfg_scale,
"Seed": seed,
"Size-1": width,
"Size-2": height,
"Batch size": batch_size,
"Variation seed": subseed,
"Variation seed strength": subseed_strength,
"Seed resize from-1": seed_resize_from_w,
"Seed resize from-2": seed_resize_from_h,
"Denoising strength": denoising_strength,
}
modules.generation_parameters_copypaste.connect_paste(paste, img2img_paste_fields, img2img_prompt)
with gr.Blocks(analytics_enabled=False) as extras_interface:
with gr.Row().style(equal_height=False):
with gr.Column(variant='panel'):
with gr.Tabs(elem_id="mode_extras"):
with gr.TabItem('Single Image'):
image = gr.Image(label="Source", source="upload", interactive=True, type="pil")
extras_image = gr.Image(label="Source", source="upload", interactive=True, type="pil")
with gr.TabItem('Batch Process'):
image_batch = gr.File(label="Batch Process", file_count="multiple", interactive=True, type="file")
@@ -750,16 +789,18 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
submit = gr.Button('Generate', elem_id="extras_generate", variant='primary')
with gr.Column(variant='panel'):
result_images = gr.Gallery(label="Result")
result_images = gr.Gallery(label="Result", show_label=False)
html_info_x = gr.HTML()
html_info = gr.HTML()
extras_send_to_img2img = gr.Button('Send to img2img')
extras_send_to_inpaint = gr.Button('Send to inpaint')
submit.click(
fn=run_extras,
_js="get_extras_tab_index",
inputs=[
dummy_component,
image,
extras_image,
image_batch,
gfpgan_visibility,
codeformer_visibility,
@@ -775,21 +816,40 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
html_info,
]
)
extras_send_to_img2img.click(
fn=lambda x: image_from_url_text(x),
_js="extract_image_from_gallery_img2img",
inputs=[result_images],
outputs=[init_img],
)
extras_send_to_inpaint.click(
fn=lambda x: image_from_url_text(x),
_js="extract_image_from_gallery_img2img",
inputs=[result_images],
outputs=[init_img_with_mask],
)
pnginfo_interface = gr.Interface(
wrap_gradio_call(run_pnginfo),
inputs=[
gr.Image(elem_id="pnginfo_image", label="Source", source="upload", interactive=True, type="pil"),
],
outputs=[
gr.HTML(),
gr.HTML(),
gr.HTML(),
],
allow_flagging="never",
analytics_enabled=False,
live=True,
)
with gr.Blocks(analytics_enabled=False) as pnginfo_interface:
with gr.Row().style(equal_height=False):
with gr.Column(variant='panel'):
image = gr.Image(elem_id="pnginfo_image", label="Source", source="upload", interactive=True, type="pil")
with gr.Column(variant='panel'):
html = gr.HTML()
generation_info = gr.Textbox(visible=False)
html2 = gr.HTML()
with gr.Row():
pnginfo_send_to_txt2img = gr.Button('Send to txt2img')
pnginfo_send_to_img2img = gr.Button('Send to img2img')
image.change(
fn=wrap_gradio_call(run_pnginfo),
inputs=[image],
outputs=[html, generation_info, html2],
)
def create_setting_component(key):
def fun():
@@ -814,12 +874,13 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
return comp(label=info.label, value=fun, **(args or {}))
components = []
keys = list(opts.data_labels.keys())
settings_cols = 3
items_per_col = math.ceil(len(keys) / settings_cols)
def run_settings(*args):
up = []
changed = 0
for key, value, comp in zip(opts.data_labels.keys(), args, components):
if not opts.same_type(value, opts.data_labels[key].default):
return f"Bad value for setting {key}: {value}; expecting {type(opts.data_labels[key].default).__name__}"
for key, value, comp in zip(opts.data_labels.keys(), args, components):
comp_args = opts.data_labels[key].component_args
@@ -829,42 +890,59 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
oldval = opts.data.get(key, None)
opts.data[key] = value
if oldval != value and opts.data_labels[key].onchange is not None:
opts.data_labels[key].onchange()
if oldval != value:
if opts.data_labels[key].onchange is not None:
opts.data_labels[key].onchange()
up.append(comp.update(value=value))
changed += 1
opts.save(shared.config_filename)
return 'Settings applied.'
return f'{changed} settings changed.', opts.dumpjson()
with gr.Blocks(analytics_enabled=False) as settings_interface:
settings_submit = gr.Button(value="Apply settings", variant='primary')
result = gr.HTML()
settings_cols = 3
items_per_col = int(len(opts.data_labels) * 0.9 / settings_cols)
cols_displayed = 0
items_displayed = 0
previous_section = None
column = None
with gr.Row(elem_id="settings").style(equal_height=False):
for colno in range(settings_cols):
with gr.Column(variant='panel'):
for rowno in range(items_per_col):
index = rowno + colno * items_per_col
for i, (k, item) in enumerate(opts.data_labels.items()):
if index < len(keys):
components.append(create_setting_component(keys[index]))
if previous_section != item.section:
if cols_displayed < settings_cols and (items_displayed >= items_per_col or previous_section is None):
if column is not None:
column.__exit__()
settings_submit.click(
fn=run_settings,
inputs=components,
outputs=[result]
)
column = gr.Column(variant='panel')
column.__enter__()
request_notifications = gr.Button(value='Request browser notifications')
items_displayed = 0
cols_displayed += 1
previous_section = item.section
gr.HTML(elem_id="settings_header_text_{}".format(item.section[0]), value='<h1 class="gr-button-lg">{}</h1>'.format(item.section[1]))
components.append(create_setting_component(k))
items_displayed += 1
request_notifications = gr.Button(value='Request browser notifications', elem_id="request_notifications")
request_notifications.click(
fn=lambda: None,
inputs=[],
outputs=[],
_js='() => Notification.requestPermission()'
_js='function(){}'
)
if column is not None:
column.__exit__()
interfaces = [
(txt2img_interface, "txt2img", "txt2img"),
(img2img_interface, "img2img", "img2img"),
@@ -892,49 +970,36 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
interface.render()
text_settings = gr.Textbox(elem_id="settings_json", value=lambda: opts.dumpjson(), visible=False)
settings_submit.click(
fn=lambda: opts.dumpjson(),
inputs=[],
outputs=[text_settings],
)
tabs.change(
fn=lambda x: x,
inputs=[init_img_with_mask],
outputs=[init_img_with_mask],
)
tabs_img2img_mode.change(
fn=lambda x: x,
inputs=[init_img_with_mask],
outputs=[init_img_with_mask],
fn=run_settings,
inputs=components,
outputs=[result, text_settings],
)
send_to_img2img.click(
fn=lambda x: image_from_url_text(x),
fn=lambda x: (image_from_url_text(x)),
_js="extract_image_from_gallery_img2img",
inputs=[txt2img_gallery],
outputs=[init_img],
)
send_to_inpaint.click(
fn=lambda x: image_from_url_text(x),
_js="extract_image_from_gallery_img2img",
fn=lambda x: (image_from_url_text(x)),
_js="extract_image_from_gallery_inpaint",
inputs=[txt2img_gallery],
outputs=[init_img_with_mask],
)
img2img_send_to_img2img.click(
fn=lambda x: image_from_url_text(x),
_js="extract_image_from_gallery",
_js="extract_image_from_gallery_img2img",
inputs=[img2img_gallery],
outputs=[init_img],
)
img2img_send_to_inpaint.click(
fn=lambda x: image_from_url_text(x),
_js="extract_image_from_gallery",
_js="extract_image_from_gallery_inpaint",
inputs=[img2img_gallery],
outputs=[init_img_with_mask],
)
@@ -943,16 +1008,19 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo):
fn=lambda x: image_from_url_text(x),
_js="extract_image_from_gallery_extras",
inputs=[txt2img_gallery],
outputs=[image],
outputs=[extras_image],
)
img2img_send_to_extras.click(
fn=lambda x: image_from_url_text(x),
_js="extract_image_from_gallery_extras",
inputs=[img2img_gallery],
outputs=[image],
outputs=[extras_image],
)
modules.generation_parameters_copypaste.connect_paste(pnginfo_send_to_txt2img, txt2img_paste_fields, generation_info, 'switch_to_txt2img')
modules.generation_parameters_copypaste.connect_paste(pnginfo_send_to_img2img, img2img_paste_fields, generation_info, 'switch_to_img2img_img2img')
ui_config_file = cmd_opts.ui_config_file
ui_settings = {}
settings_count = len(ui_settings)
@@ -1001,7 +1069,7 @@ with open(os.path.join(script_path, "script.js"), "r", encoding="utf8") as jsfil
javascript = f'<script>{jsfile.read()}</script>'
jsdir = os.path.join(script_path, "javascript")
for filename in os.listdir(jsdir):
for filename in sorted(os.listdir(jsdir)):
with open(os.path.join(jsdir, filename), "r", encoding="utf8") as jsfile:
javascript += f"\n<script>{jsfile.read()}</script>"
+1 -1
View File
@@ -2,7 +2,7 @@ transformers==4.19.2
diffusers==0.3.0
basicsr==1.4.2
gfpgan==1.3.8
gradio==3.3.1
gradio==3.4b3
numpy==1.23.3
Pillow==9.2.0
realesrgan==0.3.0
+25 -9
View File
@@ -2,24 +2,40 @@ function gradioApp(){
return document.getElementsByTagName('gradio-app')[0].shadowRoot;
}
function get_uiCurrentTab() {
return gradioApp().querySelector('.tabs button:not(.border-transparent)')
}
uiUpdateCallbacks = []
uiTabChangeCallbacks = []
let uiCurrentTab = null
function onUiUpdate(callback){
uiUpdateCallbacks.push(callback)
}
function onUiTabChange(callback){
uiTabChangeCallbacks.push(callback)
}
function uiUpdate(root){
uiUpdateCallbacks.forEach(function(x){
try {
x()
} catch (e) {
(console.error || console.log).call(console, e.message, e);
}
})
function runCallback(x){
try {
x()
} catch (e) {
(console.error || console.log).call(console, e.message, e);
}
}
function executeCallbacks(queue) {
queue.forEach(runCallback)
}
document.addEventListener("DOMContentLoaded", function() {
var mutationObserver = new MutationObserver(function(m){
uiUpdate(gradioApp());
executeCallbacks(uiUpdateCallbacks);
const newTab = get_uiCurrentTab();
if ( newTab && ( newTab !== uiCurrentTab ) ) {
uiCurrentTab = newTab;
executeCallbacks(uiTabChangeCallbacks);
}
});
mutationObserver.observe( gradioApp(), { childList:true, subtree:true })
});
+1 -1
View File
@@ -11,7 +11,7 @@ from modules import images, processing, devices
from modules.processing import Processed, process_images
from modules.shared import opts, cmd_opts, state
# https://github.com/parlance-zz/g-diffuser-bot
def expand(x, dir, amount, power=0.75):
is_left = dir == 3
is_right = dir == 1
+35 -8
View File
@@ -74,10 +74,21 @@
height: 100%;
}
#roll{
min-width: 1em;
max-width: 4em;
margin: 0.5em;
#roll_col{
min-width: unset !important;
flex-grow: 0 !important;
padding: 0.4em 0;
}
#roll, #paste{
min-width: 2em;
min-height: 2em;
max-width: 2em;
max-height: 2em;
flex-grow: 0;
padding-left: 0.25em;
padding-right: 0.25em;
margin: 0.1em 0;
}
#style_apply, #style_create, #interrogate{
@@ -89,7 +100,7 @@
min-width: 8em !important;
}
#style_index, #style2_index{
#txt2img_style_index, #txt2img_style2_index, #img2img_style_index, #img2img_style2_index{
margin-top: 1em;
}
@@ -143,7 +154,23 @@ button{
right: 0;
margin-left: auto;
margin-right: auto;
margin-top: 34px;
z-index: 100;
border: none;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
@media screen and (min-width: 768px) {
#txt2img_preview, #img2img_preview {
position: absolute;
}
}
@media screen and (max-width: 767px) {
#txt2img_preview, #img2img_preview {
position: relative;
}
}
#txt2img_preview div.left-0.top-0, #img2img_preview div.left-0.top-0{
@@ -210,7 +237,7 @@ input[type="range"]{
#txt2img_negative_prompt, #img2img_negative_prompt{
}
#progressbar{
#txt2img_progressbar, #img2img_progressbar{
position: absolute;
z-index: 1000;
right: 0;
@@ -219,7 +246,7 @@ input[type="range"]{
display: block;
}
#progressRow{
#txt2img_progress_row, #img2img_progress_row{
margin-bottom: 10px;
margin-top: -18px;
}
@@ -354,7 +381,7 @@ input[type="range"]{
display:none
}
#interrupt{
#txt2img_interrupt, #img2img_interrupt{
position: absolute;
width: 100%;
height: 72px;
+1
View File
@@ -53,6 +53,7 @@ def wrap_gradio_gpu_call(func):
shared.state.current_latent = None
shared.state.current_image = None
shared.state.current_image_sampling_step = 0
shared.state.interrupted = False
with queue_lock:
res = func(*args, **kwargs)