From 12af43e4deb34e100a4c54a3a213f431d611371f Mon Sep 17 00:00:00 2001 From: resonantsky Date: Wed, 25 Mar 2026 19:29:59 +0200 Subject: [PATCH 1/6] CeeTeeDee's I2I folder batch inference. --- scripts/i2i_folder.py | 195 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 scripts/i2i_folder.py diff --git a/scripts/i2i_folder.py b/scripts/i2i_folder.py new file mode 100644 index 000000000..9f09cc734 --- /dev/null +++ b/scripts/i2i_folder.py @@ -0,0 +1,195 @@ +import os +import glob +import copy +import gradio as gr +from PIL import Image +from modules import processing, scripts_manager, sd_samplers, images +from modules import shared +from modules.processing import Processed, get_processed +from modules.shared import state, log + + +class Script(scripts_manager.Script): + def title(self): + return "CeeTeeDee's I2I folder batch inference" + + def show(self, is_img2img): # pylint: disable=unused-argument + return True + + def ui(self, is_img2img): # pylint: disable=unused-argument + with gr.Row(): + gr.HTML('

Image folder batch inference by CeeTeeDee


') + with gr.Row(): + gr.HTML('

The purpose of this script is to assist in Img2Img of folders containing incrementally named images such as one would use when extracting frames from video.


It can use any model though is best done with high consistency models such as Flux.


Note the full path of your folder containing incrementally numbered images such as Frame000.png to Frame900.png and the script will run Inference on each image in order saving the images with identical names in an output subfolder.

') + with gr.Row(): + + folder = gr.Textbox( + label="Input folder", + placeholder="Path to folder containing PNG images", + elem_id=self.elem_id("folder"), + ) + with gr.Row(): + output_dir = gr.Textbox( + label="Output folder", + placeholder="Leave empty to save alongside inputs in /output/", + elem_id=self.elem_id("output_dir"), + ) + with gr.Row(): + prompt_override = gr.Textbox( + label="Prompt override", + placeholder="Leave empty to use the prompt from the main panel", + elem_id=self.elem_id("prompt_override"), + ) + with gr.Row(): + negative_override = gr.Textbox( + label="Negative prompt override", + placeholder="Leave empty to use the negative prompt from the main panel", + elem_id=self.elem_id("negative_override"), + ) + with gr.Row(): + seed_override = gr.Textbox( + label="Seed override (-1 = use panel seed)", + value="-1", + elem_id=self.elem_id("seed_override"), + ) + with gr.Row(): + steps_override = gr.Slider( + minimum=0, maximum=150, step=1, value=0, + label="Steps override (0 = use panel steps)", + elem_id=self.elem_id("steps_override"), + ) + with gr.Row(): + cfg_scale_override = gr.Slider( + minimum=0.0, maximum=30.0, step=0.5, value=0.0, + label="Guidance scale override (0.0 = use panel value)", + elem_id=self.elem_id("cfg_scale_override"), + ) + with gr.Row(): + sampler_override = gr.Dropdown( + label="Sampler override (empty = use panel sampler)", + choices=[""] + [s.name for s in sd_samplers.samplers_for_img2img], + value="", + elem_id=self.elem_id("sampler_override"), + ) + with gr.Row(): + strength_override = gr.Slider( + minimum=0.0, maximum=1.0, step=0.01, value=0.0, + label="Denoising strength override (0.0 = use panel value)", + elem_id=self.elem_id("strength_override"), + ) + with gr.Row(): + gr.HTML('Post-inference resize') + with gr.Row(): + _upscaler_choices = [x.name for x in shared.sd_upscalers] or ["None"] + resize_mode = gr.Dropdown( + label="Resize mode", + choices=shared.resize_modes, + type="index", + value="None", + elem_id=self.elem_id("resize_mode"), + ) + resize_name = gr.Dropdown( + label="Resize method", + choices=_upscaler_choices, + value=_upscaler_choices[0], + elem_id=self.elem_id("resize_name"), + ) + with gr.Row(): + resize_scale = gr.Slider( + minimum=1.0, maximum=8.0, step=0.05, value=2.0, + label="Scale factor", + elem_id=self.elem_id("resize_scale"), + ) + return [folder, output_dir, prompt_override, negative_override, seed_override, steps_override, cfg_scale_override, sampler_override, strength_override, resize_mode, resize_name, resize_scale] + + def run(self, p, folder, output_dir, prompt_override, negative_override, seed_override, steps_override, cfg_scale_override, sampler_override, strength_override, resize_mode, resize_name, resize_scale): # pylint: disable=arguments-differ + folder = (folder or "").strip() + if not folder or not os.path.isdir(folder): + log.error(f"Image folder batch: invalid or missing folder: {folder!r}") + return Processed(p, [], p.seed, "Invalid or missing folder") + + files = sorted(glob.glob(os.path.join(folder, "*.png"))) + if not files: + log.error(f"Image folder batch: no PNG files found in: {folder!r}") + return Processed(p, [], p.seed, "No PNG files found") + + out_dir = (output_dir or "").strip() or os.path.join(folder, "output") + os.makedirs(out_dir, exist_ok=True) + + log.info(f"Image folder batch: folder={folder!r} images={len(files)} output={out_dir!r}") + + processing.fix_seed(p) + + try: + seed_val = int(str(seed_override).strip()) + except (ValueError, TypeError): + seed_val = -1 + if seed_val >= 0: + p.seed = seed_val + if int(steps_override) > 0: + p.steps = int(steps_override) + if float(cfg_scale_override) > 0.0: + p.cfg_scale = float(cfg_scale_override) + if str(sampler_override).strip(): + p.sampler_name = str(sampler_override).strip() + if float(strength_override) > 0.0: + p.denoising_strength = float(strength_override) + if prompt_override.strip(): + p.prompt = prompt_override.strip() + if negative_override.strip(): + p.negative_prompt = negative_override.strip() + + state.job_count = len(files) + + all_images = [] + all_prompts = [] + all_seeds = [] + all_negative = [] + infotexts = [] + + for i, filepath in enumerate(files): + if state.interrupted: + break + state.job = f"{i + 1}/{len(files)}" + state.job_no = i + + img = Image.open(filepath) + if img.mode not in ('RGB', 'L'): + img = img.convert('RGB') + + cp = copy.copy(p) + cp.init_images = [img] + cp.width = img.width + cp.height = img.height + cp.batch_size = 1 + cp.n_iter = 1 + cp.do_not_save_samples = True + cp.do_not_save_grid = True + + log.info(f"Image folder batch: [{i + 1}/{len(files)}] file={os.path.basename(filepath)} size={img.size} seed={cp.seed}") + + proc = processing.process_images(cp) + img.close() + + if proc is None or not proc.images: + log.warning(f"Image folder batch: no output for {filepath!r}") + continue + + out_img = proc.images[0] + if resize_mode != 0 and resize_name != 'None': + target_w = int(out_img.width * resize_scale) + target_h = int(out_img.height * resize_scale) + out_img = images.resize_image(resize_mode, out_img, target_w, target_h, resize_name) + log.info(f"Image folder batch: resized to {out_img.size} mode={shared.resize_modes[resize_mode]!r} method={resize_name!r}") + out_name = os.path.splitext(os.path.basename(filepath))[0] + ".png" + out_path = os.path.join(out_dir, out_name) + out_img.save(out_path) + log.info(f"Image folder batch: saved {out_path!r}") + + all_images.append(out_img) + all_prompts += proc.all_prompts + all_seeds += proc.all_seeds + all_negative += proc.all_negative_prompts + infotexts += proc.infotexts + + return get_processed(p, all_images, p.seed, "", all_prompts=all_prompts, all_seeds=all_seeds, all_negative_prompts=all_negative, infotexts=infotexts) From c37e788c0b405ccd1982da613ab41f4e04caf2e4 Mon Sep 17 00:00:00 2001 From: resonantsky Date: Wed, 25 Mar 2026 19:51:25 +0200 Subject: [PATCH 2/6] fixes --- scripts/i2i_folder.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/scripts/i2i_folder.py b/scripts/i2i_folder.py index 9f09cc734..c444f399d 100644 --- a/scripts/i2i_folder.py +++ b/scripts/i2i_folder.py @@ -79,6 +79,12 @@ class Script(scripts_manager.Script): ) with gr.Row(): gr.HTML('Post-inference resize') + with gr.Row(): + resize_enabled = gr.Checkbox( + label="Enable post-inference resize", + value=False, + elem_id=self.elem_id("resize_enabled"), + ) with gr.Row(): _upscaler_choices = [x.name for x in shared.sd_upscalers] or ["None"] resize_mode = gr.Dropdown( @@ -100,9 +106,9 @@ class Script(scripts_manager.Script): label="Scale factor", elem_id=self.elem_id("resize_scale"), ) - return [folder, output_dir, prompt_override, negative_override, seed_override, steps_override, cfg_scale_override, sampler_override, strength_override, resize_mode, resize_name, resize_scale] + return [folder, output_dir, prompt_override, negative_override, seed_override, steps_override, cfg_scale_override, sampler_override, strength_override, resize_enabled, resize_mode, resize_name, resize_scale] - def run(self, p, folder, output_dir, prompt_override, negative_override, seed_override, steps_override, cfg_scale_override, sampler_override, strength_override, resize_mode, resize_name, resize_scale): # pylint: disable=arguments-differ + def run(self, p, folder, output_dir, prompt_override, negative_override, seed_override, steps_override, cfg_scale_override, sampler_override, strength_override, resize_enabled, resize_mode, resize_name, resize_scale): # pylint: disable=arguments-differ folder = (folder or "").strip() if not folder or not os.path.isdir(folder): log.error(f"Image folder batch: invalid or missing folder: {folder!r}") @@ -115,6 +121,9 @@ class Script(scripts_manager.Script): out_dir = (output_dir or "").strip() or os.path.join(folder, "output") os.makedirs(out_dir, exist_ok=True) + resize_out_dir = os.path.join(os.path.dirname(out_dir), "output-resized") if resize_enabled else None + if resize_out_dir: + os.makedirs(resize_out_dir, exist_ok=True) log.info(f"Image folder batch: folder={folder!r} images={len(files)} output={out_dir!r}") @@ -176,11 +185,13 @@ class Script(scripts_manager.Script): continue out_img = proc.images[0] - if resize_mode != 0 and resize_name != 'None': + if resize_enabled and resize_mode != 0 and resize_name != 'None': target_w = int(out_img.width * resize_scale) target_h = int(out_img.height * resize_scale) - out_img = images.resize_image(resize_mode, out_img, target_w, target_h, resize_name) - log.info(f"Image folder batch: resized to {out_img.size} mode={shared.resize_modes[resize_mode]!r} method={resize_name!r}") + resized_img = images.resize_image(resize_mode, out_img, target_w, target_h, resize_name) + log.info(f"Image folder batch: resized to {resized_img.size} mode={shared.resize_modes[resize_mode]!r} method={resize_name!r}") + res_name = os.path.splitext(os.path.basename(filepath))[0] + ".png" + resized_img.save(os.path.join(resize_out_dir, res_name)) out_name = os.path.splitext(os.path.basename(filepath))[0] + ".png" out_path = os.path.join(out_dir, out_name) out_img.save(out_path) From 8b6de3c2150ff87c62c5927648e86d51cbec5727 Mon Sep 17 00:00:00 2001 From: resonantsky Date: Wed, 25 Mar 2026 20:52:22 +0200 Subject: [PATCH 3/6] added pre-inference resize --- scripts/i2i_folder.py | 44 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/scripts/i2i_folder.py b/scripts/i2i_folder.py index c444f399d..9758af503 100644 --- a/scripts/i2i_folder.py +++ b/scripts/i2i_folder.py @@ -77,6 +77,35 @@ class Script(scripts_manager.Script): label="Denoising strength override (0.0 = use panel value)", elem_id=self.elem_id("strength_override"), ) + with gr.Row(): + gr.HTML('Pre-inference resize') + _upscaler_choices = [x.name for x in shared.sd_upscalers] or ["None"] + with gr.Row(): + pre_resize_enabled = gr.Checkbox( + label="Enable pre-inference resize", + value=False, + elem_id=self.elem_id("pre_resize_enabled"), + ) + with gr.Row(): + pre_resize_mode = gr.Dropdown( + label="Resize mode", + choices=shared.resize_modes, + type="index", + value="None", + elem_id=self.elem_id("pre_resize_mode"), + ) + pre_resize_name = gr.Dropdown( + label="Resize method", + choices=_upscaler_choices, + value=_upscaler_choices[0], + elem_id=self.elem_id("pre_resize_name"), + ) + with gr.Row(): + pre_resize_scale = gr.Slider( + minimum=0.25, maximum=4.0, step=0.05, value=1.0, + label="Scale factor", + elem_id=self.elem_id("pre_resize_scale"), + ) with gr.Row(): gr.HTML('Post-inference resize') with gr.Row(): @@ -86,7 +115,6 @@ class Script(scripts_manager.Script): elem_id=self.elem_id("resize_enabled"), ) with gr.Row(): - _upscaler_choices = [x.name for x in shared.sd_upscalers] or ["None"] resize_mode = gr.Dropdown( label="Resize mode", choices=shared.resize_modes, @@ -106,9 +134,9 @@ class Script(scripts_manager.Script): label="Scale factor", elem_id=self.elem_id("resize_scale"), ) - return [folder, output_dir, prompt_override, negative_override, seed_override, steps_override, cfg_scale_override, sampler_override, strength_override, resize_enabled, resize_mode, resize_name, resize_scale] + return [folder, output_dir, prompt_override, negative_override, seed_override, steps_override, cfg_scale_override, sampler_override, strength_override, pre_resize_enabled, pre_resize_mode, pre_resize_name, pre_resize_scale, resize_enabled, resize_mode, resize_name, resize_scale] - def run(self, p, folder, output_dir, prompt_override, negative_override, seed_override, steps_override, cfg_scale_override, sampler_override, strength_override, resize_enabled, resize_mode, resize_name, resize_scale): # pylint: disable=arguments-differ + def run(self, p, folder, output_dir, prompt_override, negative_override, seed_override, steps_override, cfg_scale_override, sampler_override, strength_override, pre_resize_enabled, pre_resize_mode, pre_resize_name, pre_resize_scale, resize_enabled, resize_mode, resize_name, resize_scale): # pylint: disable=arguments-differ folder = (folder or "").strip() if not folder or not os.path.isdir(folder): log.error(f"Image folder batch: invalid or missing folder: {folder!r}") @@ -170,6 +198,14 @@ class Script(scripts_manager.Script): cp.init_images = [img] cp.width = img.width cp.height = img.height + if pre_resize_enabled and pre_resize_mode != 0 and pre_resize_name not in ('None', '') and shared.sd_upscalers: + pre_w = int(img.width * pre_resize_scale) + pre_h = int(img.height * pre_resize_scale) + img = images.resize_image(pre_resize_mode, img, pre_w, pre_h, pre_resize_name) + cp.init_images = [img] + cp.width = img.width + cp.height = img.height + log.info(f"Image folder batch: pre-resize to {img.size} mode={shared.resize_modes[pre_resize_mode]!r} method={pre_resize_name!r}") cp.batch_size = 1 cp.n_iter = 1 cp.do_not_save_samples = True @@ -185,7 +221,7 @@ class Script(scripts_manager.Script): continue out_img = proc.images[0] - if resize_enabled and resize_mode != 0 and resize_name != 'None': + if resize_enabled and resize_mode != 0 and resize_name not in ('None', '') and shared.sd_upscalers: target_w = int(out_img.width * resize_scale) target_h = int(out_img.height * resize_scale) resized_img = images.resize_image(resize_mode, out_img, target_w, target_h, resize_name) From a51755dcf4d277c2fa20dde2cf3e68e5350f948b Mon Sep 17 00:00:00 2001 From: resonantsky Date: Wed, 25 Mar 2026 22:50:31 +0200 Subject: [PATCH 4/6] further edits --- scripts/i2i_folder.py | 62 ++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/scripts/i2i_folder.py b/scripts/i2i_folder.py index 9758af503..42129c3c4 100644 --- a/scripts/i2i_folder.py +++ b/scripts/i2i_folder.py @@ -18,9 +18,7 @@ class Script(scripts_manager.Script): def ui(self, is_img2img): # pylint: disable=unused-argument with gr.Row(): - gr.HTML('

Image folder batch inference by CeeTeeDee


') - with gr.Row(): - gr.HTML('

The purpose of this script is to assist in Img2Img of folders containing incrementally named images such as one would use when extracting frames from video.


It can use any model though is best done with high consistency models such as Flux.


Note the full path of your folder containing incrementally numbered images such as Frame000.png to Frame900.png and the script will run Inference on each image in order saving the images with identical names in an output subfolder.

') + gr.HTML('

The purpose of this script is to assist in Img2Img of folders containing incrementally named images such as one would use when extracting frames from video.


It can use any model though is best done with high consistency models such as Flux.


Note the full path of your folder containing incrementally numbered images such as Frame000.png to Frame900.png and the script will run Inference on each image in order saving the images with identical names in an output subfolder.

') with gr.Row(): folder = gr.Textbox( @@ -31,42 +29,42 @@ class Script(scripts_manager.Script): with gr.Row(): output_dir = gr.Textbox( label="Output folder", - placeholder="Leave empty to save alongside inputs in /output/", + placeholder="/output/", elem_id=self.elem_id("output_dir"), ) with gr.Row(): prompt_override = gr.Textbox( - label="Prompt override", - placeholder="Leave empty to use the prompt from the main panel", + label="Prompt", + placeholder="Leave empty to use the panel prompt", elem_id=self.elem_id("prompt_override"), ) with gr.Row(): negative_override = gr.Textbox( - label="Negative prompt override", - placeholder="Leave empty to use the negative prompt from the main panel", + label="Negative prompt", + placeholder="Leave empty to use the panel value", elem_id=self.elem_id("negative_override"), ) with gr.Row(): seed_override = gr.Textbox( - label="Seed override (-1 = use panel seed)", + label="Seed (-1 = use panel)", value="-1", elem_id=self.elem_id("seed_override"), ) with gr.Row(): steps_override = gr.Slider( minimum=0, maximum=150, step=1, value=0, - label="Steps override (0 = use panel steps)", + label="Steps (0 = use panel)", elem_id=self.elem_id("steps_override"), ) with gr.Row(): cfg_scale_override = gr.Slider( minimum=0.0, maximum=30.0, step=0.5, value=0.0, - label="Guidance scale override (0.0 = use panel value)", + label="Guidance scale (0.0 = use panel)", elem_id=self.elem_id("cfg_scale_override"), ) with gr.Row(): sampler_override = gr.Dropdown( - label="Sampler override (empty = use panel sampler)", + label="Sampler (empty = use panel)", choices=[""] + [s.name for s in sd_samplers.samplers_for_img2img], value="", elem_id=self.elem_id("sampler_override"), @@ -74,7 +72,7 @@ class Script(scripts_manager.Script): with gr.Row(): strength_override = gr.Slider( minimum=0.0, maximum=1.0, step=0.01, value=0.0, - label="Denoising strength override (0.0 = use panel value)", + label="Denoising strength (0.0 = use panel)", elem_id=self.elem_id("strength_override"), ) with gr.Row(): @@ -103,9 +101,20 @@ class Script(scripts_manager.Script): with gr.Row(): pre_resize_scale = gr.Slider( minimum=0.25, maximum=4.0, step=0.05, value=1.0, - label="Scale factor", + label="Scale factor (ignored if width/height set)", elem_id=self.elem_id("pre_resize_scale"), ) + with gr.Row(): + pre_resize_width = gr.Number( + label="Width (0 = use scale factor)", + value=0, precision=0, + elem_id=self.elem_id("pre_resize_width"), + ) + pre_resize_height = gr.Number( + label="Height (0 = use scale factor)", + value=0, precision=0, + elem_id=self.elem_id("pre_resize_height"), + ) with gr.Row(): gr.HTML('Post-inference resize') with gr.Row(): @@ -131,12 +140,23 @@ class Script(scripts_manager.Script): with gr.Row(): resize_scale = gr.Slider( minimum=1.0, maximum=8.0, step=0.05, value=2.0, - label="Scale factor", + label="Scale factor (ignored if width/height set)", elem_id=self.elem_id("resize_scale"), ) - return [folder, output_dir, prompt_override, negative_override, seed_override, steps_override, cfg_scale_override, sampler_override, strength_override, pre_resize_enabled, pre_resize_mode, pre_resize_name, pre_resize_scale, resize_enabled, resize_mode, resize_name, resize_scale] + with gr.Row(): + resize_width = gr.Number( + label="Width (0 = use scale factor)", + value=0, precision=0, + elem_id=self.elem_id("resize_width"), + ) + resize_height = gr.Number( + label="Height (0 = use scale factor)", + value=0, precision=0, + elem_id=self.elem_id("resize_height"), + ) + return [folder, output_dir, prompt_override, negative_override, seed_override, steps_override, cfg_scale_override, sampler_override, strength_override, pre_resize_enabled, pre_resize_mode, pre_resize_name, pre_resize_scale, pre_resize_width, pre_resize_height, resize_enabled, resize_mode, resize_name, resize_scale, resize_width, resize_height] - def run(self, p, folder, output_dir, prompt_override, negative_override, seed_override, steps_override, cfg_scale_override, sampler_override, strength_override, pre_resize_enabled, pre_resize_mode, pre_resize_name, pre_resize_scale, resize_enabled, resize_mode, resize_name, resize_scale): # pylint: disable=arguments-differ + def run(self, p, folder, output_dir, prompt_override, negative_override, seed_override, steps_override, cfg_scale_override, sampler_override, strength_override, pre_resize_enabled, pre_resize_mode, pre_resize_name, pre_resize_scale, pre_resize_width, pre_resize_height, resize_enabled, resize_mode, resize_name, resize_scale, resize_width, resize_height): # pylint: disable=arguments-differ folder = (folder or "").strip() if not folder or not os.path.isdir(folder): log.error(f"Image folder batch: invalid or missing folder: {folder!r}") @@ -199,8 +219,8 @@ class Script(scripts_manager.Script): cp.width = img.width cp.height = img.height if pre_resize_enabled and pre_resize_mode != 0 and pre_resize_name not in ('None', '') and shared.sd_upscalers: - pre_w = int(img.width * pre_resize_scale) - pre_h = int(img.height * pre_resize_scale) + pre_w = int(pre_resize_width) if int(pre_resize_width) > 0 else int(img.width * pre_resize_scale) + pre_h = int(pre_resize_height) if int(pre_resize_height) > 0 else int(img.height * pre_resize_scale) img = images.resize_image(pre_resize_mode, img, pre_w, pre_h, pre_resize_name) cp.init_images = [img] cp.width = img.width @@ -222,8 +242,8 @@ class Script(scripts_manager.Script): out_img = proc.images[0] if resize_enabled and resize_mode != 0 and resize_name not in ('None', '') and shared.sd_upscalers: - target_w = int(out_img.width * resize_scale) - target_h = int(out_img.height * resize_scale) + target_w = int(resize_width) if int(resize_width) > 0 else int(out_img.width * resize_scale) + target_h = int(resize_height) if int(resize_height) > 0 else int(out_img.height * resize_scale) resized_img = images.resize_image(resize_mode, out_img, target_w, target_h, resize_name) log.info(f"Image folder batch: resized to {resized_img.size} mode={shared.resize_modes[resize_mode]!r} method={resize_name!r}") res_name = os.path.splitext(os.path.basename(filepath))[0] + ".png" From b3837aa944b29a5f6166b5937418f8ae3b232071 Mon Sep 17 00:00:00 2001 From: resonantsky Date: Wed, 25 Mar 2026 23:28:38 +0200 Subject: [PATCH 5/6] Added inference bypass for upscale only passes. --- scripts/i2i_folder.py | 45 +++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/scripts/i2i_folder.py b/scripts/i2i_folder.py index 42129c3c4..ec8d1cca4 100644 --- a/scripts/i2i_folder.py +++ b/scripts/i2i_folder.py @@ -32,6 +32,12 @@ class Script(scripts_manager.Script): placeholder="/output/", elem_id=self.elem_id("output_dir"), ) + with gr.Row(): + upscale_only = gr.Checkbox( + label="Upscale only (skip inference, apply post-resize directly to inputs)", + value=False, + elem_id=self.elem_id("upscale_only"), + ) with gr.Row(): prompt_override = gr.Textbox( label="Prompt", @@ -154,9 +160,9 @@ class Script(scripts_manager.Script): value=0, precision=0, elem_id=self.elem_id("resize_height"), ) - return [folder, output_dir, prompt_override, negative_override, seed_override, steps_override, cfg_scale_override, sampler_override, strength_override, pre_resize_enabled, pre_resize_mode, pre_resize_name, pre_resize_scale, pre_resize_width, pre_resize_height, resize_enabled, resize_mode, resize_name, resize_scale, resize_width, resize_height] + return [folder, output_dir, upscale_only, prompt_override, negative_override, seed_override, steps_override, cfg_scale_override, sampler_override, strength_override, pre_resize_enabled, pre_resize_mode, pre_resize_name, pre_resize_scale, pre_resize_width, pre_resize_height, resize_enabled, resize_mode, resize_name, resize_scale, resize_width, resize_height] - def run(self, p, folder, output_dir, prompt_override, negative_override, seed_override, steps_override, cfg_scale_override, sampler_override, strength_override, pre_resize_enabled, pre_resize_mode, pre_resize_name, pre_resize_scale, pre_resize_width, pre_resize_height, resize_enabled, resize_mode, resize_name, resize_scale, resize_width, resize_height): # pylint: disable=arguments-differ + def run(self, p, folder, output_dir, upscale_only, prompt_override, negative_override, seed_override, steps_override, cfg_scale_override, sampler_override, strength_override, pre_resize_enabled, pre_resize_mode, pre_resize_name, pre_resize_scale, pre_resize_width, pre_resize_height, resize_enabled, resize_mode, resize_name, resize_scale, resize_width, resize_height): # pylint: disable=arguments-differ folder = (folder or "").strip() if not folder or not os.path.isdir(folder): log.error(f"Image folder batch: invalid or missing folder: {folder!r}") @@ -226,21 +232,30 @@ class Script(scripts_manager.Script): cp.width = img.width cp.height = img.height log.info(f"Image folder batch: pre-resize to {img.size} mode={shared.resize_modes[pre_resize_mode]!r} method={pre_resize_name!r}") - cp.batch_size = 1 - cp.n_iter = 1 - cp.do_not_save_samples = True - cp.do_not_save_grid = True + if upscale_only: + log.info(f"Image folder batch: [{i + 1}/{len(files)}] upscale-only file={os.path.basename(filepath)} size={img.size}") + out_img = img + else: + cp.batch_size = 1 + cp.n_iter = 1 + cp.do_not_save_samples = True + cp.do_not_save_grid = True - log.info(f"Image folder batch: [{i + 1}/{len(files)}] file={os.path.basename(filepath)} size={img.size} seed={cp.seed}") + log.info(f"Image folder batch: [{i + 1}/{len(files)}] file={os.path.basename(filepath)} size={img.size} seed={cp.seed}") - proc = processing.process_images(cp) - img.close() + proc = processing.process_images(cp) + img.close() - if proc is None or not proc.images: - log.warning(f"Image folder batch: no output for {filepath!r}") - continue + if proc is None or not proc.images: + log.warning(f"Image folder batch: no output for {filepath!r}") + continue + + out_img = proc.images[0] + all_prompts += proc.all_prompts + all_seeds += proc.all_seeds + all_negative += proc.all_negative_prompts + infotexts += proc.infotexts - out_img = proc.images[0] if resize_enabled and resize_mode != 0 and resize_name not in ('None', '') and shared.sd_upscalers: target_w = int(resize_width) if int(resize_width) > 0 else int(out_img.width * resize_scale) target_h = int(resize_height) if int(resize_height) > 0 else int(out_img.height * resize_scale) @@ -254,9 +269,5 @@ class Script(scripts_manager.Script): log.info(f"Image folder batch: saved {out_path!r}") all_images.append(out_img) - all_prompts += proc.all_prompts - all_seeds += proc.all_seeds - all_negative += proc.all_negative_prompts - infotexts += proc.infotexts return get_processed(p, all_images, p.seed, "", all_prompts=all_prompts, all_seeds=all_seeds, all_negative_prompts=all_negative, infotexts=infotexts) From 9003e67ec4ab5a02793c4cc1be2aff513977aa72 Mon Sep 17 00:00:00 2001 From: resonantsky Date: Thu, 26 Mar 2026 10:11:49 +0200 Subject: [PATCH 6/6] Added multifile support through modules\file_cache.py, tested. --- scripts/i2i_folder.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/scripts/i2i_folder.py b/scripts/i2i_folder.py index ec8d1cca4..36d84de20 100644 --- a/scripts/i2i_folder.py +++ b/scripts/i2i_folder.py @@ -1,6 +1,6 @@ import os -import glob import copy +from modules.files_cache import list_files import gradio as gr from PIL import Image from modules import processing, scripts_manager, sd_samplers, images @@ -11,19 +11,19 @@ from modules.shared import state, log class Script(scripts_manager.Script): def title(self): - return "CeeTeeDee's I2I folder batch inference" + return "CeeTeeDees I2I folder batch inference" def show(self, is_img2img): # pylint: disable=unused-argument return True def ui(self, is_img2img): # pylint: disable=unused-argument with gr.Row(): - gr.HTML('

The purpose of this script is to assist in Img2Img of folders containing incrementally named images such as one would use when extracting frames from video.


It can use any model though is best done with high consistency models such as Flux.


Note the full path of your folder containing incrementally numbered images such as Frame000.png to Frame900.png and the script will run Inference on each image in order saving the images with identical names in an output subfolder.

') + gr.HTML('

The purpose of this script is to assist in Img2Img of folders containing incrementally named images such as one would use when extracting frames from video.


It can use any model though is best done with high consistency models such as Flux.


Note the full path of your folder containing incrementally numbered images such as Frame000 to Frame900 and the script will run Inference on each image in order saving the images with identical names in an output subfolder.

') with gr.Row(): folder = gr.Textbox( label="Input folder", - placeholder="Path to folder containing PNG images", + placeholder="Path to folder containing png, jpg, jpeg, webp or jxl images", elem_id=self.elem_id("folder"), ) with gr.Row(): @@ -168,10 +168,10 @@ class Script(scripts_manager.Script): log.error(f"Image folder batch: invalid or missing folder: {folder!r}") return Processed(p, [], p.seed, "Invalid or missing folder") - files = sorted(glob.glob(os.path.join(folder, "*.png"))) + files = sorted(list_files(folder, ext_filter=['.png', '.jpg', '.jpeg', '.webp', '.jxl'], recursive=False)) if not files: - log.error(f"Image folder batch: no PNG files found in: {folder!r}") - return Processed(p, [], p.seed, "No PNG files found") + log.error(f"Image folder batch: no image files found in: {folder!r}") + return Processed(p, [], p.seed, "No image files found") out_dir = (output_dir or "").strip() or os.path.join(folder, "output") os.makedirs(out_dir, exist_ok=True) @@ -261,9 +261,9 @@ class Script(scripts_manager.Script): target_h = int(resize_height) if int(resize_height) > 0 else int(out_img.height * resize_scale) resized_img = images.resize_image(resize_mode, out_img, target_w, target_h, resize_name) log.info(f"Image folder batch: resized to {resized_img.size} mode={shared.resize_modes[resize_mode]!r} method={resize_name!r}") - res_name = os.path.splitext(os.path.basename(filepath))[0] + ".png" + res_name = os.path.basename(filepath) resized_img.save(os.path.join(resize_out_dir, res_name)) - out_name = os.path.splitext(os.path.basename(filepath))[0] + ".png" + out_name = os.path.basename(filepath) out_path = os.path.join(out_dir, out_name) out_img.save(out_path) log.info(f"Image folder batch: saved {out_path!r}")