From 3493a4268c8c53db0f3fef26863a265c31f20d74 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Sun, 31 Aug 2025 16:11:14 -0400 Subject: [PATCH] refactor reuse-seed Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 2 ++ modules/processing.py | 32 ++++++++++++++++++++------------ modules/ui_common.py | 27 +++++++++++++++++++++++---- modules/ui_control.py | 4 +++- modules/ui_img2img.py | 4 ++-- modules/ui_txt2img.py | 4 ++-- 6 files changed, 52 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6faf5448d..bc2728227 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,8 @@ - improve offloading of models with controlnet - **SDNQ** - add quantized matmul support for all quantization types and group sizes +- **Other** + - refactor reuse-seed and add functionality to all tabs - **Fixes** - normalize path hanlding when deleting images - remove samplers filtering diff --git a/modules/processing.py b/modules/processing.py index 7a33031d4..89fbc4bb0 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -32,46 +32,54 @@ processed = None # last known processed results class Processed: def __init__(self, p: StableDiffusionProcessing, images_list, seed=-1, info=None, subseed=None, all_prompts=None, all_negative_prompts=None, all_seeds=None, all_subseeds=None, index_of_first_image=0, infotexts=None, comments=""): - self.images = images_list + self.sd_model_hash = getattr(shared.sd_model, 'sd_model_hash', '') if model_data.sd_model is not None else '' + self.prompt = p.prompt or '' self.negative_prompt = p.negative_prompt or '' - self.seed = seed if seed != -1 else p.seed - self.subseed = subseed - self.subseed_strength = p.subseed_strength - self.info = info or create_infotext(p) - self.comments = comments or '' + self.prompt = self.prompt if type(self.prompt) != list else self.prompt[0] + self.negative_prompt = self.negative_prompt if type(self.negative_prompt) != list else self.negative_prompt[0] + self.styles = p.styles + + self.images = images_list self.width = p.width if hasattr(p, 'width') else (self.images[0].width if len(self.images) > 0 else 0) self.height = p.height if hasattr(p, 'height') else (self.images[0].height if len(self.images) > 0 else 0) + self.sampler_name = p.sampler_name or '' self.cfg_scale = p.cfg_scale if p.cfg_scale > 1 else None self.cfg_end = p.cfg_end if p.cfg_end < 0 else None self.image_cfg_scale = p.image_cfg_scale or 0 self.steps = p.steps or 0 self.batch_size = max(1, p.batch_size) + self.denoising_strength = p.denoising_strength + self.restore_faces = p.restore_faces or False self.face_restoration_model = shared.opts.face_restoration_model if p.restore_faces else None self.detailer = p.detailer_enabled or False self.detailer_model = shared.opts.detailer_model if p.detailer_enabled else None - self.sd_model_hash = getattr(shared.sd_model, 'sd_model_hash', '') if model_data.sd_model is not None else '' self.seed_resize_from_w = p.seed_resize_from_w self.seed_resize_from_h = p.seed_resize_from_h - self.denoising_strength = p.denoising_strength self.extra_generation_params = p.extra_generation_params self.index_of_first_image = index_of_first_image - self.styles = p.styles self.job_timestamp = shared.state.job_timestamp self.clip_skip = p.clip_skip self.eta = p.eta - self.prompt = self.prompt if type(self.prompt) != list else self.prompt[0] - self.negative_prompt = self.negative_prompt if type(self.negative_prompt) != list else self.negative_prompt[0] + + self.seed = seed if seed != -1 else p.seed + self.subseed = subseed self.seed = int(self.seed if type(self.seed) != list else self.seed[0]) if self.seed is not None else -1 self.subseed = int(self.subseed if type(self.subseed) != list else self.subseed[0]) if self.subseed is not None else -1 + self.subseed_strength = p.subseed_strength + self.is_using_inpainting_conditioning = p.is_using_inpainting_conditioning + self.all_prompts = all_prompts or p.all_prompts or [self.prompt] self.all_negative_prompts = all_negative_prompts or p.all_negative_prompts or [self.negative_prompt] self.all_seeds = all_seeds or p.all_seeds or [self.seed] self.all_subseeds = all_subseeds or p.all_subseeds or [self.subseed] + + self.info = info or create_infotext(p) self.infotexts = infotexts or [self.info] + self.comments = comments or '' memstats.reset_stats() def js(self): @@ -460,7 +468,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: if shared.opts.grid_save: images.save_image(grid, p.outpath_grids, "", p.all_seeds[0], p.all_prompts[0], shared.opts.grid_format, info=grid_info, p=p, grid=True) # main save grid - results = Processed( + results = get_processed( p, images_list=output_images, seed=p.all_seeds[0], diff --git a/modules/ui_common.py b/modules/ui_common.py index 046ae93b3..8d7147d26 100644 --- a/modules/ui_common.py +++ b/modules/ui_common.py @@ -336,7 +336,7 @@ def create_refresh_button(refresh_component, refresh_method, refreshed_args = No return gr.update(**args) refresh_button = ui_components.ToolButton(value=ui_symbols.refresh, elem_id=elem_id, visible=visible) - refresh_button.click(fn=refresh, inputs=[], outputs=[refresh_component]) + refresh_button.click(fn=refresh, inputs=[], outputs=[refresh_component], show_progress=False) return refresh_button @@ -347,7 +347,26 @@ def create_override_inputs(tab): # pylint: disable=unused-argument return override_settings -def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: gr.Textbox, is_subseed, subseed_strength=None): +def reuse_seed(seed_component: gr.Number, reuse_button: gr.Button, subseed:bool=False): + def reuse_click(selected_gallery_index): + selected_gallery_index = int(selected_gallery_index) + from modules import processing + if processing.processed is None: + seed = -1 + elif selected_gallery_index >= len(processing.processed.all_seeds): + selected_gallery_index -= len(processing.processed.images) - len(processing.processed.all_seeds) # if we have more images than seeds it is likely the grid image + seed = processing.processed.all_seeds[selected_gallery_index] if not subseed else processing.processed.all_subseeds[selected_gallery_index] + elif len(processing.processed.all_seeds) > 0: + seed = processing.processed.all_seeds[0] if not subseed else processing.processed.all_subseeds[0] + else: + seed = -1 + shared.log.debug(f'Reuse seed: index={selected_gallery_index} seed={seed} subseed={subseed}') + return seed + + reuse_button.click(fn=reuse_click, _js="selected_gallery_index", inputs=[seed_component], outputs=[seed_component], show_progress=False) + + +def connect_reuse_seed(seed: gr.Number, reuse_seed_btn: gr.Button, generation_info: gr.Textbox, is_subseed, subseed_strength=None): """ Connects a 'reuse (sub)seed' button's click event so that it copies last used (sub)seed value from generation info the to the seed field. If copying subseed and subseed strength was 0, i.e. no variation seed was used, it copies the normal seed value instead.""" @@ -375,9 +394,9 @@ def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: return [restore_seed, gr_show(False)] dummy_component = gr.Number(visible=False, value=0) if subseed_strength is None: - reuse_seed.click(fn=copy_seed, _js="(x, y) => [x, selected_gallery_index()]", show_progress=False, inputs=[generation_info, dummy_component], outputs=[seed, dummy_component]) + reuse_seed_btn.click(fn=copy_seed, _js="(x, y) => [x, selected_gallery_index()]", show_progress=False, inputs=[generation_info, dummy_component], outputs=[seed, dummy_component]) else: - reuse_seed.click(fn=copy_seed, _js="(x, y) => [x, selected_gallery_index()]", show_progress=False, inputs=[generation_info, dummy_component], outputs=[seed, dummy_component, subseed_strength]) + reuse_seed_btn.click(fn=copy_seed, _js="(x, y) => [x, selected_gallery_index()]", show_progress=False, inputs=[generation_info, dummy_component], outputs=[seed, dummy_component, subseed_strength]) def update_token_counter(text): diff --git a/modules/ui_control.py b/modules/ui_control.py index dfa90f54f..deb812779 100644 --- a/modules/ui_control.py +++ b/modules/ui_control.py @@ -157,7 +157,9 @@ def create_ui(_blocks: gr.Blocks=None): batch_count, batch_size = ui_sections.create_batch_inputs('control', accordion=True) - seed, _reuse_seed, subseed, _reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = ui_sections.create_seed_inputs('control', reuse_visible=False) + seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = ui_sections.create_seed_inputs('control') + ui_common.reuse_seed(seed, reuse_seed, subseed=False) + ui_common.reuse_seed(subseed, reuse_subseed, subseed=True) mask_controls = masking.create_segment_ui() diff --git a/modules/ui_img2img.py b/modules/ui_img2img.py index 6d23a9f37..66b770c69 100644 --- a/modules/ui_img2img.py +++ b/modules/ui_img2img.py @@ -159,8 +159,8 @@ def create_ui(): img2img_gallery, img2img_generation_info, img2img_html_info, _img2img_html_info_formatted, img2img_html_log = ui_common.create_output_panel("img2img", prompt=img2img_prompt) - ui_common.connect_reuse_seed(seed, reuse_seed, img2img_generation_info, is_subseed=False) - ui_common.connect_reuse_seed(subseed, reuse_subseed, img2img_generation_info, is_subseed=True, subseed_strength=subseed_strength) + ui_common.reuse_seed(seed, reuse_seed, subseed=False) + ui_common.reuse_seed(subseed, reuse_subseed, subseed=True) img2img_prompt_img.change(fn=modules.images.image_data, inputs=[img2img_prompt_img], outputs=[img2img_prompt, img2img_prompt_img]) dummy_component1 = gr.Textbox(visible=False, value='dummy') diff --git a/modules/ui_txt2img.py b/modules/ui_txt2img.py index e610305e7..7fb672562 100644 --- a/modules/ui_txt2img.py +++ b/modules/ui_txt2img.py @@ -45,8 +45,8 @@ def create_ui(): txt2img_script_inputs = modules.scripts_manager.scripts_txt2img.setup_ui(parent='txt2img', accordion=True) txt2img_gallery, txt2img_generation_info, txt2img_html_info, _txt2img_html_info_formatted, txt2img_html_log = ui_common.create_output_panel("txt2img", preview=True, prompt=txt2img_prompt) - ui_common.connect_reuse_seed(seed, reuse_seed, txt2img_generation_info, is_subseed=False) - ui_common.connect_reuse_seed(subseed, reuse_subseed, txt2img_generation_info, is_subseed=True, subseed_strength=subseed_strength) + ui_common.reuse_seed(seed, reuse_seed, subseed=False) + ui_common.reuse_seed(subseed, reuse_subseed, subseed=True) dummy_component = gr.Textbox(visible=False, value='dummy')