From a96076f49ce7755ebdf3cad588b5c0d2f1f93035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Aires=20Rast=C3=A9n?= Date: Fri, 16 Sep 2022 21:20:56 +0200 Subject: [PATCH] Add buttons for random and reuse seed. Random button sets seed to -1, reuse copies the seed from the last generated image. --- javascript/hints.js | 2 + modules/processing.py | 8 ++- modules/ui.py | 138 ++++++++++++++++++++++++++++---------- requirements_versions.txt | 2 +- style.css | 26 ++++++- 5 files changed, 135 insertions(+), 41 deletions(-) diff --git a/javascript/hints.js b/javascript/hints.js index c5b73da2..6d5ffc01 100644 --- a/javascript/hints.js +++ b/javascript/hints.js @@ -11,6 +11,8 @@ titles = { "Batch size": "How many image to create in a single batch", "CFG Scale": "Classifier Free Guidance Scale - how strongly the image should conform to prompt - lower values produce more creative results", "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", "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", diff --git a/modules/processing.py b/modules/processing.py index 1afbe39c..244ed639 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -83,11 +83,13 @@ class StableDiffusionProcessing: class Processed: - def __init__(self, p: StableDiffusionProcessing, images_list, seed, info): + def __init__(self, p: StableDiffusionProcessing, images_list, seed, subseed, info): self.images = images_list self.prompt = p.prompt self.negative_prompt = p.negative_prompt self.seed = seed + self.subseed = subseed + self.subseed_strength = p.subseed_strength self.info = info self.width = p.width self.height = p.height @@ -100,6 +102,8 @@ class Processed: "prompt": self.prompt if type(self.prompt) != list else self.prompt[0], "negative_prompt": self.negative_prompt if type(self.negative_prompt) != list else self.negative_prompt[0], "seed": int(self.seed if type(self.seed) != list else self.seed[0]), + "subseed": int(self.subseed if type(self.subseed) != list else self.subseed[0]), + "subseed_strength": self.subseed_strength, "width": self.width, "height": self.height, "sampler": self.sampler, @@ -352,7 +356,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed: 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, p=p) devices.torch_gc() - return Processed(p, output_images, all_seeds[0], infotext()) + return Processed(p, output_images, all_seeds[0], all_subseeds[0], infotext()) class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): diff --git a/modules/ui.py b/modules/ui.py index ada84d33..6ecc6e11 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -53,6 +53,12 @@ css_hide_progressbar = """ .meta-text { display:none!important; } """ +# Using constants for these since the variation selector isn't visible. +# Important that they exactly match script.js for tooltip to work. +random_symbol = '\U0001f3b2\ufe0f' # 🎲️ +reuse_symbol = '\u267b\ufe0f' # ♻️ + + def plaintext_to_html(text): text = "

" + "
\n".join([f"{html.escape(x)}" for x in text.split('\n')]) + "

" return text @@ -221,40 +227,6 @@ 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_w = gr.Slider(minimum=0, maximum=2048, step=64, label="Resize seed from width", value=0, visible=False) - seed_resize_from_h = gr.Slider(minimum=0, maximum=2048, step=64, label="Resize seed from height", 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 add_style(name: str, prompt: str, negative_prompt: str): if name is None: return [gr_show(), gr_show()] @@ -282,6 +254,94 @@ def interrogate(image): return gr_show(True) if prompt is None else prompt +def create_seed_inputs(): + with gr.Row(): + with gr.Box(): + with gr.Row(elem_id='seed_row'): + seed = gr.Number(label='Seed', value=-1) + seed.style(container=False) + random_seed = gr.Button('🎲', elem_id='random_seed') + reuse_seed = gr.Button('♻️', elem_id='reuse_seed') + + with gr.Box(elem_id='subseed_show_box'): + seed_checkbox = gr.Checkbox(label='Extra', elem_id='subseed_show', value=False) + + # Components to show/hide based on the 'Extra' checkbox + seed_extras = [] + + with gr.Row(visible=False) as seed_extra_row_1: + seed_extras.append(seed_extra_row_1) + with gr.Box(): + with gr.Row(elem_id='subseed_row'): + subseed = gr.Number(label='Variation seed', value=-1) + subseed.style(container=False) + random_subseed = gr.Button(random_symbol, elem_id='random_subseed') + reuse_subseed = gr.Button(reuse_symbol, elem_id='reuse_subseed') + subseed_strength = gr.Slider(label='Variation strength', value=0.0, minimum=0, maximum=1, step=0.01) + + with gr.Row(visible=False) as seed_extra_row_2: + seed_extras.append(seed_extra_row_2) + seed_resize_from_w = gr.Slider(minimum=0, maximum=2048, step=64, label="Resize seed from width", value=0) + seed_resize_from_h = gr.Slider(minimum=0, maximum=2048, step=64, label="Resize seed from height", value=0) + + random_seed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[seed]) + random_subseed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[subseed]) + + def change_visibility(show): + return {comp: gr_show(show) for comp in seed_extras} + + seed_checkbox.change(change_visibility, show_progress=False, inputs=[seed_checkbox], outputs=seed_extras) + + return seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w + + +def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: gr.Textbox): + """ Connects a 'reuse seed' button's click event so that it copies last used + seed value from generation info the to the seed.""" + def copy_seed(gen_info_string: str): + try: + gen_info = json.loads(gen_info_string) + return gen_info.get('seed', -1) + except json.decoder.JSONDecodeError as e: + if gen_info_string != '': + print("Error parsing JSON generation info:", file=sys.stderr) + print(gen_info_string, file=sys.stderr) + return -1 + + reuse_seed.click( + fn=copy_seed, + show_progress=False, + inputs=[generation_info], + outputs=[seed] + ) + + +def connect_reuse_subseed(seed: gr.Number, reuse_seed: gr.Button, generation_info: gr.Textbox): + """ Connects a 'reuse subseed' button's click event so that it copies last used + subseed value from generation info the to the subseed. If subseed strength + was 0, i.e. no variation seed was used, it copies the normal seed value instead.""" + def copy_seed(gen_info_string: str): + try: + gen_info = json.loads(gen_info_string) + subseed_strength = gen_info.get('subseed_strength', 0) + if subseed_strength > 0: + return gen_info.get('subseed', -1) + else: + return gen_info.get('seed', -1) + except json.decoder.JSONDecodeError as e: + if gen_info_string != '': + print("Error parsing JSON generation info:", file=sys.stderr) + print(gen_info_string, file=sys.stderr) + return -1 + + reuse_seed.click( + fn=copy_seed, + show_progress=False, + inputs=[generation_info], + outputs=[seed] + ) + + def create_toprow(is_img2img): with gr.Row(elem_id="toprow"): with gr.Column(scale=4): @@ -357,7 +417,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width", value=512) height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height", value=512) - seed, subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = create_seed_inputs() + seed, reuse_seed, subseed, reuse_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) @@ -383,6 +443,9 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): html_info = gr.HTML() generation_info = gr.Textbox(visible=False) + connect_reuse_seed(seed, reuse_seed, generation_info) + connect_reuse_subseed(subseed, reuse_subseed, generation_info) + txt2img_args = dict( fn=txt2img, _js="submit", @@ -490,7 +553,7 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): width = gr.Slider(minimum=64, maximum=2048, step=64, label="Width", value=512) height = gr.Slider(minimum=64, maximum=2048, step=64, label="Height", value=512) - seed, subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = create_seed_inputs() + seed, reuse_seed, subseed, reuse_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) @@ -518,6 +581,9 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): html_info = gr.HTML() generation_info = gr.Textbox(visible=False) + connect_reuse_seed(seed, reuse_seed, generation_info) + connect_reuse_subseed(subseed, reuse_subseed, generation_info) + def apply_mode(mode, uploadmask): is_classic = mode == 0 is_inpaint = mode == 1 diff --git a/requirements_versions.txt b/requirements_versions.txt index e5e54e6e..bb88eda9 100644 --- a/requirements_versions.txt +++ b/requirements_versions.txt @@ -2,7 +2,7 @@ transformers==4.19.2 diffusers==0.2.4 basicsr==1.3.5 gfpgan -gradio==3.3 +gradio==3.3.1 numpy==1.23.3 Pillow==9.2.0 realesrgan==0.2.5.0 diff --git a/style.css b/style.css index 26c759d7..fb937f3a 100644 --- a/style.css +++ b/style.css @@ -43,9 +43,31 @@ margin-right: auto; } +#random_seed, #random_subseed, #reuse_seed, #reuse_subseed{ + min-width: auto; + flex-grow: 0; + padding-left: 0.25em; + padding-right: 0.25em; +} + +#seed_row, #subseed_row{ + gap: 0.5rem; +} + +#subseed_show_box{ + min-width: auto; + flex-grow: 0; +} + +#subseed_show_box > div{ + border: 0; + height: 100%; +} + #subseed_show{ - min-width: 6em; - max-width: 6em; + min-width: auto; + flex-grow: 0; + padding: 0; } #subseed_show label{