From 65dfed93a1245118389fa83070614f361b8408ec Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Sun, 8 Oct 2023 07:25:45 -0400 Subject: [PATCH] add process batch --- CHANGELOG.md | 7 +++++-- modules/img2img.py | 13 ++++++------- modules/shared.py | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0009f46e8..7a72300dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,10 @@ # Change Log for SD.Next -## Update for 2023-10-07 +## Update for 2023-10-08 **TBD**: Candidates before release: - Note: Free-U requires unreleased diffusers - Update Lora handler for *backend:diffusers* -- Merge parallel batch processing This is a big one, with some major changes and new functionality... And probably the biggest release since introduction of **Diffusers** @@ -165,6 +164,10 @@ or even free speedups and quality improvements (regardless of which workflows yo available for *diffusers* and *original* backends 2x speed-up your generations for free :) thanks @tfernd + - **Batch mode** + new option *settings -> inference -> batch mode* + when using img2img process batch, process multiple images in batch in parallel + thanks @Symbiomatrix - **General** - **Startup** - all main CLI parameters can now be set as environment variable as well diff --git a/modules/img2img.py b/modules/img2img.py index 841ace038..a3a77180c 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -16,7 +16,7 @@ def process_batch(p, input_files, input_dir, output_dir, inpaint_mask_dir, args) image_files = [f.name for f in input_files] else: if not os.path.isdir(input_dir): - shared.log.error(f"Input directory not found: {input_dir}") + shared.log.error(f"Process batch: directory not found: {input_dir}") return image_files = shared.listfiles(input_dir) is_inpaint_batch = False @@ -24,22 +24,21 @@ def process_batch(p, input_files, input_dir, output_dir, inpaint_mask_dir, args) inpaint_masks = shared.listfiles(inpaint_mask_dir) is_inpaint_batch = len(inpaint_masks) > 0 if is_inpaint_batch: - shared.log.info(f"\nInpaint batch is enabled. {len(inpaint_masks)} masks found.") - # SBM Batch frame should actually print btcrept. Or customise the messages. - shared.log.info(f"Will process {len(image_files)} images, creating {p.n_iter * p.batch_size} new images for each.") + shared.log.info(f"Process batch: inpaint batch masks={len(inpaint_masks)}") save_normally = output_dir == '' p.do_not_save_grid = True p.do_not_save_samples = not save_normally shared.state.job_count = len(image_files) * p.n_iter - # SBM Batch frame mode, take 2. - if shared.opts.batch_frame_mode: + if shared.opts.batch_frame_mode: # SBM Frame mode is on, process each image in batch with same seed window_size = p.batch_size btcrept = 1 p.seed = [p.seed] * window_size # SBM MONKEYPATCH: Need to change processing to support a fixed seed value. p.subseed = [p.subseed] * window_size # SBM MONKEYPATCH + shared.log.info(f"Process batch: inputs={len(image_files)} parallel={window_size} outputs={p.n_iter} per input ") else: # SBM Frame mode is off, standard operation of repeating same images with sequential seed. window_size = 1 btcrept = p.batch_size + shared.log.info(f"Process batch: inputs={len(image_files)} outputs={p.n_iter * p.batch_size} per input") for i in range(0, len(image_files), window_size): shared.state.job = f"{i+1} to {min(i+window_size, len(image_files))} out of {len(image_files)}" if shared.state.skipped: @@ -99,7 +98,7 @@ def process_batch(p, input_files, input_dir, output_dir, inpaint_mask_dir, args) for k, v in items.items(): image.info[k] = v images.save_image(image, path=output_dir, basename=basename, seed=None, prompt=None, extension=ext, info=geninfo, short_filename=True, no_prompt=True, grid=False, pnginfo_section_name="extras", existing_info=image.info, forced_filename=None) - shared.log.debug(f'Processed: {len(batch_image_files)} Memory: {memory_stats()} batch') + shared.log.debug(f'Processed: images={len(batch_image_files)} memory={memory_stats()} batch') def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_styles, init_img, sketch, init_img_with_mask, inpaint_color_sketch, inpaint_color_sketch_orig, init_img_inpaint, init_mask_inpaint, steps: int, sampler_index: int, latent_index: int, mask_blur: int, mask_alpha: float, inpainting_fill: int, full_quality: bool, restore_faces: bool, tiling: bool, n_iter: int, batch_size: int, cfg_scale: float, image_cfg_scale: float, diffusers_guidance_rescale: float, refiner_steps: int, refiner_start: float, clip_skip: int, denoising_strength: float, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, selected_scale_tab: int, height: int, width: int, scale_by: float, resize_mode: int, inpaint_full_res: bool, inpaint_full_res_padding: int, inpainting_mask_invert: int, img2img_batch_files: list, img2img_batch_input_dir: str, img2img_batch_output_dir: str, img2img_batch_inpaint_mask_dir: str, override_settings_texts, *args): # pylint: disable=unused-argument diff --git a/modules/shared.py b/modules/shared.py index 3e50f5139..40677344a 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -464,7 +464,7 @@ options_templates.update(options_section(('advanced', "Inference Settings"), { "hypertile_unet_tile": OptionInfo(256, "HyperTile for UNet tile size", gr.Slider, {"minimum": 256, "maximum": 1024, "step": 8}), "inference_other_sep": OptionInfo("

Other

", "", gr.HTML), - "batch_frame_mode": OptionInfo(False, "Use batchsize to process multiple images in batch mode"), + "batch_frame_mode": OptionInfo(False, "Process multiple images in batch in parallel"), "inference_mode": OptionInfo("no-grad", "Torch inference mode", gr.Radio, lambda: {"choices": ["no-grad", "inference-mode", "none"]}), "sd_vae_sliced_encode": OptionInfo(False, "VAE Slicing (original)"), }))