diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ee3aa807..0fd5bd560 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,6 @@ - **AnimateDiff** can now be used with *second pass* - enhance, upscale and hires your videos! - **IP Adapter** add support for `ip-adapter-plus_sd15`, `ip-adapter-plus-face_sd15` and `ip-adapter-full-face_sd15` additionally, ip-adapter can now be used in xyz-grid - - **HDR controls** are now batch-aware for enhancement of multiple images or video frames - [Playground v1](https://huggingface.co/playgroundai/playground-v1), [Playground v2 256](https://huggingface.co/playgroundai/playground-v2-256px-base), [Playground v2 512](https://huggingface.co/playgroundai/playground-v2-512px-base), [Playground v2 1024](https://huggingface.co/playgroundai/playground-v2-1024px-aesthetic) model support - simply select from *networks -> reference* and use as usual - [DemoFusion](https://github.com/PRIS-CV/DemoFusion) run your SDXL generations at any resolution! @@ -37,28 +36,38 @@ - **Extra networks** new *settting -> extra networks -> build info on first access* indexes all networks on first access instead of server startup - **IPEX**, thanks @disty0 - - update to **Torch 2.1** - if you get file not found errors, set DISABLE_IPEXRUN=1 and run the webui with --reinstall + - update to **Torch 2.1** + if you get file not found errors, set DISABLE_IPEXRUN=1 and run the webui with --reinstall - built-in *MKL* and *DPCPP* for IPEX, no need to install OneAPI anymore - fix IPEX Optimize not applying with Diffusers backend - disable 32 bit workarounds if the GPU supports 64 bit - add DISABLE_IPEXRUN environment variable - compatibility improvements - - **OpenVINO**, thanks @disty0 - - Add *Directory for OpenVINO cache* option to *System Paths* - - Remove Intel ARC specific 1024x1024 workaround - - **Inpaint** add option `apply_overlay` to control if inpaint result should be applied as overlay or as-is - can remove artifacts and hard edges of inpaint area but also remove some details from original - - disable google fonts check on server startup - - fix torchvision/basicsr compatibility - - fix extra networks sort - - add hdr settings to metadata - - improve handling of long filenames and filenames during batch processing - - do not set preview samples when using via api - - avoid unnecessary resizes in img2img and inpaint - - updated `cli/simple-txt2img.py` and `cli/simple-img2img.py` scripts - - save `params.txt` regardless of image save status - - update built-in log monitor in ui, thanks @midcoastal + - **OpenVINO**, thanks @disty0 + - add *Directory for OpenVINO cache* option to *System Paths* + - remove Intel ARC specific 1024x1024 workaround + - **UI** + - more dynamic controls depending on the backend (original or diffusers) + controls that are not applicable in current mode are now hidden + - allow setting of resize method directly in image tab + (previously via settings -> upscaler_for_img2img) + - **HDR controls** + - batch-aware for enhancement of multiple images or video frames + - available in image tab + - **Other** + - **Inpaint** add option `apply_overlay` to control if inpaint result should be applied as overlay or as-is + can remove artifacts and hard edges of inpaint area but also remove some details from original + - **chaiNNer** fix NaN issues due to autocast + - **Extra Networks** fix sort + - disable google fonts check on server startup + - fix torchvision/basicsr compatibility + - add hdr settings to metadata + - improve handling of long filenames and filenames during batch processing + - do not set preview samples when using via api + - avoid unnecessary resizes in img2img and inpaint + - updated `cli/simple-txt2img.py` and `cli/simple-img2img.py` scripts + - save `params.txt` regardless of image save status + - update built-in log monitor in ui, thanks @midcoastal ## Update for 2023-12-04 diff --git a/extensions-builtin/sd-extension-chainner b/extensions-builtin/sd-extension-chainner index 1cdc8578a..e48020d03 160000 --- a/extensions-builtin/sd-extension-chainner +++ b/extensions-builtin/sd-extension-chainner @@ -1 +1 @@ -Subproject commit 1cdc8578a3c0538177c3ac13721b9764f8782c8f +Subproject commit e48020d035b0d4daffec37a2bbce61c3ea04798c diff --git a/extensions-builtin/sd-extension-system-info b/extensions-builtin/sd-extension-system-info index ce1b2a4a1..f93480869 160000 --- a/extensions-builtin/sd-extension-system-info +++ b/extensions-builtin/sd-extension-system-info @@ -1 +1 @@ -Subproject commit ce1b2a4a10eb25d33d716132a209599cb3733164 +Subproject commit f934808698523465e7abb2d1fb0d290065def776 diff --git a/html/locale_en.json b/html/locale_en.json index 82383808f..14ea99c9b 100644 --- a/html/locale_en.json +++ b/html/locale_en.json @@ -226,10 +226,10 @@ {"id":"","label":"Inpaint batch input directory","localized":"","hint":""}, {"id":"","label":"Inpaint batch output directory","localized":"","hint":""}, {"id":"","label":"Inpaint batch mask directory","localized":"","hint":""}, - {"id":"","label":"Resize fixed","localized":"","hint":"Resize image to target resolution. Unless height and width match, you will get incorrect aspect ratio"}, - {"id":"","label":"Crop and resize","localized":"","hint":"Resize the image so that entirety of target resolution is filled with the image. Crop parts that stick out"}, - {"id":"","label":"Resize and fill","localized":"","hint":"Resize the image so that entirety of image is inside target resolution. Fill empty space with image's colors"}, - {"id":"","label":"Latent upscale","localized":"","hint":""}, + {"id":"","label":"Fixed","localized":"","hint":"Resize image to target resolution. Unless height and width match, you will get incorrect aspect ratio"}, + {"id":"","label":"Crop","localized":"","hint":"Resize the image so that entirety of target resolution is filled with the image. Crop parts that stick out"}, + {"id":"","label":"Fill","localized":"","hint":"Resize the image so that entirety of image is inside target resolution. Fill empty space with image's colors"}, + {"id":"","label":"Latent","localized":"","hint":""}, {"id":"","label":"Mask blur","localized":"","hint":"How much to blur the mask before processing, in pixels"}, {"id":"","label":"Mask transparency","localized":"","hint":""}, {"id":"","label":"Inpaint masked","localized":"","hint":""}, diff --git a/javascript/sdnext.css b/javascript/sdnext.css index 43a4f159c..afc3f9095 100644 --- a/javascript/sdnext.css +++ b/javascript/sdnext.css @@ -52,7 +52,7 @@ textarea { overflow-y: auto !important; } .small-accordion .form { min-width: var(--left-column) !important; max-width: max-content; } .small-accordion .label-wrap .icon { margin-right: 1.6em; margin-left: 0.6em; color: var(--button-primary-border-color); } .small-accordion .label-wrap { padding: 16px 0px 8px 0px; margin: 0; border-top: 2px solid var(--button-secondary-border-color); } -.small-accordion { width: fit-content !important; padding-left: 0 !important; } +.small-accordion { width: fit-content !important; min-width: fit-content !important; padding-left: 0 !important; } .extension-script { max-width: 48vw; } button.custom-button{ border-radius: var(--button-large-radius); padding: var(--button-large-padding); font-weight: var(--button-large-text-weight); border: var(--button-border-width) solid var(--button-secondary-border-color); background: var(--button-secondary-background-fill); color: var(--button-secondary-text-color); font-size: var(--button-large-text-size); diff --git a/modules/images.py b/modules/images.py index 2e467105c..db4d16e82 100644 --- a/modules/images.py +++ b/modules/images.py @@ -215,7 +215,7 @@ def resize_image(resize_mode, im, width, height, upscaler_name=None, output_type Resizes an image with the specified resize_mode, width, and height. Args: resize_mode: The mode to use when resizing the image. - 0: No resie + 0: No resize 1: Resize the image to the specified width and height. 2: Resize the image to fill the specified width and height, maintaining the aspect ratio, and then center the image within the dimensions, cropping the excess. 3: Resize the image to fit within the specified width and height, maintaining the aspect ratio, and then center the image within the dimensions, filling empty with data from image. diff --git a/modules/img2img.py b/modules/img2img.py index db726d331..20ef6fe6d 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -103,13 +103,43 @@ def process_batch(p, input_files, input_dir, output_dir, inpaint_mask_dir, args) 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 +def img2img(id_task: str, mode: int, + prompt, negative_prompt, prompt_styles, + init_img, + sketch, + init_img_with_mask, + inpaint_color_sketch, + inpaint_color_sketch_orig, + init_img_inpaint, + init_mask_inpaint, + steps, + sampler_index, latent_index, + mask_blur, mask_alpha, + inpainting_fill, + full_quality, restore_faces, tiling, + n_iter, batch_size, + cfg_scale, image_cfg_scale, + diffusers_guidance_rescale, + refiner_steps, + refiner_start, + clip_skip, + denoising_strength, + seed, subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, + selected_scale_tab, + height, width, + scale_by, + resize_mode, resize_name, + inpaint_full_res, inpaint_full_res_padding, inpainting_mask_invert, + img2img_batch_files, img2img_batch_input_dir, img2img_batch_output_dir, img2img_batch_inpaint_mask_dir, + hdr_clamp, hdr_boundary, hdr_threshold, hdr_center, hdr_channel_shift, hdr_full_shift, hdr_maximize, hdr_max_center, hdr_max_boundry, + override_settings_texts, + *args): # pylint: disable=unused-argument if shared.sd_model is None: shared.log.warning('Model not loaded') return [], '', '', 'Error: model not loaded' - debug(f'img2img: id_task={id_task}|mode={mode}|prompt={prompt}|negative_prompt={negative_prompt}|prompt_styles={prompt_styles}|init_img={init_img}|sketch={sketch}|init_img_with_mask={init_img_with_mask}|inpaint_color_sketch={inpaint_color_sketch}|inpaint_color_sketch_orig={inpaint_color_sketch_orig}|init_img_inpaint={init_img_inpaint}|init_mask_inpaint={init_mask_inpaint}|steps={steps}|sampler_index={sampler_index}|latent_index={latent_index}|mask_blur={mask_blur}|mask_alpha={mask_alpha}|inpainting_fill={inpainting_fill}|full_quality={full_quality}|restore_faces={restore_faces}|tiling={tiling}|n_iter={n_iter}|batch_size={batch_size}|cfg_scale={cfg_scale}|image_cfg_scale={image_cfg_scale}|clip_skip={clip_skip}|denoising_strength={denoising_strength}|seed={seed}|subseed{subseed}|subseed_strength={subseed_strength}|seed_resize_from_h={seed_resize_from_h}|seed_resize_from_w={seed_resize_from_w}|selected_scale_tab={selected_scale_tab}|height={height}|width={width}|scale_by={scale_by}|resize_mode={resize_mode}|inpaint_full_res={inpaint_full_res}|inpaint_full_res_padding={inpaint_full_res_padding}|inpainting_mask_invert={inpainting_mask_invert}|img2img_batch_files={img2img_batch_files}|img2img_batch_input_dir={img2img_batch_input_dir}|img2img_batch_output_dir={img2img_batch_output_dir}|img2img_batch_inpaint_mask_dir={img2img_batch_inpaint_mask_dir}|override_settings_texts={override_settings_texts}') + debug(f'img2img: id_task={id_task}|mode={mode}|prompt={prompt}|negative_prompt={negative_prompt}|prompt_styles={prompt_styles}|init_img={init_img}|sketch={sketch}|init_img_with_mask={init_img_with_mask}|inpaint_color_sketch={inpaint_color_sketch}|inpaint_color_sketch_orig={inpaint_color_sketch_orig}|init_img_inpaint={init_img_inpaint}|init_mask_inpaint={init_mask_inpaint}|steps={steps}|sampler_index={sampler_index}|latent_index={latent_index}|mask_blur={mask_blur}|mask_alpha={mask_alpha}|inpainting_fill={inpainting_fill}|full_quality={full_quality}|restore_faces={restore_faces}|tiling={tiling}|n_iter={n_iter}|batch_size={batch_size}|cfg_scale={cfg_scale}|image_cfg_scale={image_cfg_scale}|clip_skip={clip_skip}|denoising_strength={denoising_strength}|seed={seed}|subseed{subseed}|subseed_strength={subseed_strength}|seed_resize_from_h={seed_resize_from_h}|seed_resize_from_w={seed_resize_from_w}|selected_scale_tab={selected_scale_tab}|height={height}|width={width}|scale_by={scale_by}|resize_mode={resize_mode}|resize_name={resize_name}|inpaint_full_res={inpaint_full_res}|inpaint_full_res_padding={inpaint_full_res_padding}|inpainting_mask_invert={inpainting_mask_invert}|img2img_batch_files={img2img_batch_files}|img2img_batch_input_dir={img2img_batch_input_dir}|img2img_batch_output_dir={img2img_batch_output_dir}|img2img_batch_inpaint_mask_dir={img2img_batch_inpaint_mask_dir}|override_settings_texts={override_settings_texts}') if mode == 5: if img2img_batch_files is None or len(img2img_batch_files) == 0: @@ -159,6 +189,7 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s image = init_img_inpaint mask = init_mask_inpaint else: + shared.log.error(f'Image processing unknown mode: {mode}') image = None mask = None if image is not None: @@ -197,6 +228,7 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s mask_blur=mask_blur, inpainting_fill=inpainting_fill, resize_mode=resize_mode, + resize_name=resize_name, denoising_strength=denoising_strength, image_cfg_scale=image_cfg_scale, diffusers_guidance_rescale=diffusers_guidance_rescale, @@ -205,6 +237,9 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s inpaint_full_res=inpaint_full_res, inpaint_full_res_padding=inpaint_full_res_padding, inpainting_mask_invert=inpainting_mask_invert, + hdr_clamp=hdr_clamp, hdr_boundary=hdr_boundary, hdr_threshold=hdr_threshold, + hdr_center=hdr_center, hdr_channel_shift=hdr_channel_shift, hdr_full_shift=hdr_full_shift, + hdr_maximize=hdr_maximize, hdr_max_center=hdr_max_center, hdr_max_boundry=hdr_max_boundry, override_settings=override_settings, ) if selected_scale_tab == 1 and resize_mode != 0: diff --git a/modules/processing.py b/modules/processing.py index 31170d8d5..bb9753e72 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -126,7 +126,7 @@ class StableDiffusionProcessing: """ The first set of paramaters: sd_models -> do_not_reload_embeddings represent the minimum required to create a StableDiffusionProcessing """ - def __init__(self, sd_model=None, outpath_samples=None, outpath_grids=None, prompt: str = "", styles: List[str] = None, seed: int = -1, subseed: int = -1, subseed_strength: float = 0, seed_resize_from_h: int = -1, seed_resize_from_w: int = -1, seed_enable_extras: bool = True, sampler_name: str = None, latent_sampler: str = None, batch_size: int = 1, n_iter: int = 1, steps: int = 50, cfg_scale: float = 7.0, image_cfg_scale: float = None, clip_skip: int = 1, width: int = 512, height: int = 512, full_quality: bool = True, restore_faces: bool = False, tiling: bool = False, do_not_save_samples: bool = False, do_not_save_grid: bool = False, extra_generation_params: Dict[Any, Any] = None, overlay_images: Any = None, negative_prompt: str = None, eta: float = None, do_not_reload_embeddings: bool = False, denoising_strength: float = 0, diffusers_guidance_rescale: float = 0.7, hdr_clamp: bool = False, hdr_boundary: float = 4.0, hdr_threshold: float = 3.5, hdr_center: bool = False, hdr_channel_shift: float = 0.8, hdr_full_shift: float = 0.8, hdr_maximize: bool = False, hdr_max_center: float = 0.6, hdr_max_boundry: float = 1.0, override_settings: Dict[str, Any] = None, override_settings_restore_afterwards: bool = True, sampler_index: int = None, script_args: list = None): # pylint: disable=unused-argument + def __init__(self, sd_model=None, outpath_samples=None, outpath_grids=None, prompt: str = "", styles: List[str] = None, seed: int = -1, subseed: int = -1, subseed_strength: float = 0, seed_resize_from_h: int = -1, seed_resize_from_w: int = -1, seed_enable_extras: bool = True, sampler_name: str = None, latent_sampler: str = None, batch_size: int = 1, n_iter: int = 1, steps: int = 50, cfg_scale: float = 7.0, image_cfg_scale: float = None, clip_skip: int = 1, width: int = 512, height: int = 512, full_quality: bool = True, restore_faces: bool = False, tiling: bool = False, do_not_save_samples: bool = False, do_not_save_grid: bool = False, extra_generation_params: Dict[Any, Any] = None, overlay_images: Any = None, negative_prompt: str = None, eta: float = None, do_not_reload_embeddings: bool = False, denoising_strength: float = 0, diffusers_guidance_rescale: float = 0.7, resize_mode: int = 0, resize_name: str = 'None', scale_by: float = 0, selected_scale_tab: int = 0, hdr_clamp: bool = False, hdr_boundary: float = 4.0, hdr_threshold: float = 3.5, hdr_center: bool = False, hdr_channel_shift: float = 0.8, hdr_full_shift: float = 0.8, hdr_maximize: bool = False, hdr_max_center: float = 0.6, hdr_max_boundry: float = 1.0, override_settings: Dict[str, Any] = None, override_settings_restore_afterwards: bool = True, sampler_index: int = None, script_args: list = None): # pylint: disable=unused-argument self.outpath_samples: str = outpath_samples self.outpath_grids: str = outpath_grids self.prompt: str = prompt @@ -145,6 +145,7 @@ class StableDiffusionProcessing: self.steps: int = steps self.hr_second_pass_steps = 0 self.cfg_scale: float = cfg_scale + self.scale_by: float = scale_by self.image_cfg_scale = image_cfg_scale self.diffusers_guidance_rescale = diffusers_guidance_rescale if devices.backend == "ipex" and width == 1024 and height == 1024: @@ -197,7 +198,8 @@ class StableDiffusionProcessing: self.refiner_prompt = '' self.refiner_negative = '' self.ops = [] - self.resize_mode: int = 0 + self.resize_mode: int = resize_mode + self.resize_name: str = resize_name self.ddim_discretize = shared.opts.ddim_discretize self.s_min_uncond = shared.opts.s_min_uncond self.s_churn = shared.opts.s_churn @@ -636,9 +638,7 @@ def create_infotext(p: StableDiffusionProcessing, all_prompts=None, all_seeds=No args["Denoising strength"] = getattr(p, 'denoising_strength', None) # lookup by index if getattr(p, 'resize_mode', None) is not None: - RESIZE_MODES = ["None", "Resize fixed", "Crop and resize", "Resize and fill", "Latent upscale"] - args['Resize mode'] = RESIZE_MODES[p.resize_mode] - # TODO missing-by-index: inpainting_fill, inpaint_full_res, inpainting_mask_invert + args['Resize mode'] = shared.resize_modes[p.resize_mode] if 'face' in p.ops: args["Face restoration"] = shared.opts.face_restoration_model if 'color' in p.ops: @@ -846,6 +846,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: with devices.autocast(): p.init(p.all_prompts, p.all_seeds, p.all_subseeds) extra_network_data = None + debug(f'Processing inner: args={vars(p)}') for n in range(p.n_iter): p.iteration = n if shared.state.skipped: @@ -1199,10 +1200,11 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): - def __init__(self, init_images: list = None, resize_mode: int = 0, denoising_strength: float = 0.3, image_cfg_scale: float = None, mask: Any = None, mask_blur: int = 4, inpainting_fill: int = 0, inpaint_full_res: bool = True, inpaint_full_res_padding: int = 0, inpainting_mask_invert: int = 0, initial_noise_multiplier: float = None, refiner_steps: int = 5, refiner_start: float = 0, refiner_prompt: str = '', refiner_negative: str = '', **kwargs): + def __init__(self, init_images: list = None, resize_mode: int = 0, resize_name: str = 'None', denoising_strength: float = 0.3, image_cfg_scale: float = None, mask: Any = None, mask_blur: int = 4, inpainting_fill: int = 0, inpaint_full_res: bool = True, inpaint_full_res_padding: int = 0, inpainting_mask_invert: int = 0, initial_noise_multiplier: float = None, refiner_steps: int = 5, refiner_start: float = 0, refiner_prompt: str = '', refiner_negative: str = '', **kwargs): super().__init__(**kwargs) self.init_images = init_images self.resize_mode: int = resize_mode + self.resize_name: str = resize_name self.denoising_strength: float = denoising_strength self.image_cfg_scale: float = image_cfg_scale self.init_latent = None @@ -1264,11 +1266,11 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): x1, y1, x2, y2 = crop_region mask = mask.crop(crop_region) if mask.width != self.width or mask.height != self.height: - image_mask = images.resize_image(3, mask, self.width, self.height) + image_mask = images.resize_image(3, mask, self.width, self.height, self.resize_name) self.paste_to = (x1, y1, x2-x1, y2-y1) else: if image_mask.width != self.width or image_mask.height != self.height: - image_mask = images.resize_image(self.resize_mode, image_mask, self.width, self.height) + image_mask = images.resize_image(self.resize_mode, image_mask, self.width, self.height, self.resize_name) np_mask = np.array(image_mask) np_mask = np.clip((np_mask.astype(np.float32)) * 2, 0, 255).astype(np.uint8) self.mask_for_overlay = Image.fromarray(np_mask) @@ -1280,7 +1282,13 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): self.color_corrections = [] imgs = [] unprocessed = [] + if getattr(self, 'init_images', None) is None: + return + # raise RuntimeError("No images provided") for img in self.init_images: + if img is None: + shared.log.warning(f"Skipping empty image: images={self.init_images}") + continue self.init_img_hash = hashlib.sha256(img.tobytes()).hexdigest()[0:8] # pylint: disable=attribute-defined-outside-init self.init_img_width = img.width # pylint: disable=attribute-defined-outside-init self.init_img_height = img.height # pylint: disable=attribute-defined-outside-init @@ -1289,7 +1297,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): image = images.flatten(img, shared.opts.img2img_background_color) if crop_region is None and self.resize_mode != 4: if image.width != self.width or image.height != self.height: - image = images.resize_image(self.resize_mode, image, self.width, self.height) + image = images.resize_image(self.resize_mode, image, self.width, self.height, self.resize_name) self.width = image.width self.height = image.height if image_mask is not None: @@ -1297,6 +1305,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): image_masked = Image.new('RGBa', (image.width, image.height)) image_to_paste = image.convert("RGBA").convert("RGBa") image_to_mask = ImageOps.invert(self.mask_for_overlay.convert('L')) if self.mask_for_overlay is not None else None + image_to_mask = image_to_mask.resize((image.width, image.height), Image.Resampling.BILINEAR) if image_to_mask is not None else None image_masked.paste(image_to_paste, mask=image_to_mask) self.overlay_images.append(image_masked.convert('RGBA')) except Exception as e: @@ -1306,7 +1315,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): if crop_region is not None: image = image.crop(crop_region) if image.width != self.width or image.height != self.height: - image = images.resize_image(3, image, self.width, self.height) + image = images.resize_image(3, image, self.width, self.height, self.resize_name) if image_mask is not None and self.inpainting_fill != 1: image = modules.masking.fill(image, latent_mask) if add_color_corrections: @@ -1327,7 +1336,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): self.batch_size = len(imgs) batch_images = np.array(imgs) else: - raise RuntimeError(f"bad number of images passed: {len(imgs)}; expecting {self.batch_size} or less") + raise RuntimeError(f"Incorrect number of of images={len(imgs)} expected={self.batch_size} or less") if shared.backend == shared.Backend.DIFFUSERS: return # we've already set self.init_images and self.mask and we dont need any more processing diff --git a/modules/processing_diffusers.py b/modules/processing_diffusers.py index 4c40957d5..1fe11b817 100644 --- a/modules/processing_diffusers.py +++ b/modules/processing_diffusers.py @@ -23,7 +23,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro results = [] is_refiner_enabled = p.enable_hr and p.refiner_steps > 0 and p.refiner_start > 0 and p.refiner_start < 1 and shared.sd_refiner is not None - if hasattr(p, 'init_images') and len(p.init_images) > 0: + if getattr(p, 'init_images', None) is not None and len(p.init_images) > 0: tgt_width, tgt_height = 8 * math.ceil(p.init_images[0].width / 8), 8 * math.ceil(p.init_images[0].height / 8) if p.init_images[0].width != tgt_width or p.init_images[0].height != tgt_height: shared.log.debug(f'Resizing init images: original={p.init_images[0].width}x{p.init_images[0].height} target={tgt_width}x{tgt_height}') @@ -358,7 +358,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro if k in possible: args[k] = v - hypertile_set(p, hr=len(getattr(p, 'init_images', []))) + hypertile_set(p, hr=len(getattr(p, 'init_images', [])) > 0) clean = args.copy() clean.pop('callback', None) clean.pop('callback_steps', None) diff --git a/modules/sd_hijack_hypertile.py b/modules/sd_hijack_hypertile.py index 217ffad48..dfd54dc64 100644 --- a/modules/sd_hijack_hypertile.py +++ b/modules/sd_hijack_hypertile.py @@ -74,7 +74,7 @@ def split_attention(layer: nn.Module, tile_size: int=256, min_tile_size: int=256 out = forward(x, *args[1:], **kwargs) return out if x.ndim == 4: # VAE - # TODO hyperlink vae breaks for diffusers when using non-standard sizes + # TODO hypertile vae breaks for diffusers when using non-standard sizes if nh * nw > 1: x = rearrange(x, "b c (nh h) (nw w) -> (b nh nw) c h w", nh=nh, nw=nw) out = forward(x, *args[1:], **kwargs) @@ -190,10 +190,12 @@ def hypertile_set(p, hr=False): if not shared.opts.hypertile_unet_enabled: return error_reported = False - if not hr: - height=p.height if not hr else getattr(p, 'hr_upscale_to_y', p.height) - width=p.width if not hr else getattr(p, 'hr_upscale_to_x', p.width) + if hr: + x = getattr(p, 'hr_upscale_to_x', 0) + y = getattr(p, 'hr_upscale_to_y', 0) + width = y if y > 0 else p.width + height = x if x > 0 else p.height else: - height=max(p.height, getattr(p, 'hr_upscale_to_y', p.height)) - width=max(p.width, getattr(p, 'hr_upscale_to_x', p.width)) + width=p.width + height=p.height reset_needed = True diff --git a/modules/shared.py b/modules/shared.py index 6ef5d382c..1a8e664b2 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -63,6 +63,7 @@ restricted_opts = { "outdir_save", "outdir_init_images" } +resize_modes = ["None", "Fixed", "Crop", "Fill", "Latent"] compatibility_opts = ['clip_skip', 'uni_pc_lower_order_final', 'uni_pc_order'] console = Console(log_time=True, log_time_format='%H:%M:%S-%f') @@ -563,7 +564,7 @@ options_templates.update(options_section(('postprocessing', "Postprocessing"), { "postprocessing_sep_upscalers": OptionInfo("

Upscaling

", "", gr.HTML), "upscaler_unload": OptionInfo(False, "Unload upscaler after processing"), # 'upscaling_max_images_in_cache': OptionInfo(5, "Maximum number of images in upscaling cache", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1, "visible": False}), - "upscaler_for_img2img": OptionInfo("None", "Default upscaler for image resize operations", gr.Dropdown, lambda: {"choices": [x.name for x in sd_upscalers]}), + "upscaler_for_img2img": OptionInfo("None", "Default upscaler for image resize operations", gr.Dropdown, lambda: {"choices": [x.name for x in sd_upscalers], "visible": False}), "upscaler_tile_size": OptionInfo(192, "Upscaler tile size", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}), "upscaler_tile_overlap": OptionInfo(8, "Upscaler tile overlap", gr.Slider, {"minimum": 0, "maximum": 64, "step": 1}), })) diff --git a/modules/txt2img.py b/modules/txt2img.py index 4ef523d83..0b7a1aba6 100644 --- a/modules/txt2img.py +++ b/modules/txt2img.py @@ -8,9 +8,23 @@ from modules.ui import plaintext_to_html debug = shared.log.debug if os.environ.get('SD_PROCESS_DEBUG', None) is not None else lambda *args, **kwargs: None -def txt2img(id_task: str, prompt: str, negative_prompt: str, prompt_styles, steps: int, sampler_index: int, latent_index: 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, clip_skip: int, seed: int, subseed: int, subseed_strength: float, seed_resize_from_h: int, seed_resize_from_w: int, height: int, width: int, enable_hr: bool, denoising_strength: float, hr_scale: float, hr_upscaler: str, hr_force: bool, hr_second_pass_steps: int, hr_resize_x: int, hr_resize_y: int, refiner_steps: int, refiner_start: int, refiner_prompt: str, refiner_negative: str, hdr_clamp, hdr_boundary, hdr_threshold, hdr_center, hdr_channel_shift, hdr_full_shift, hdr_maximize, hdr_max_center, hdr_max_boundry, override_settings_texts, *args): # pylint: disable=unused-argument +def txt2img(id_task, + prompt, negative_prompt, prompt_styles, + steps, sampler_index, latent_index, + full_quality, restore_faces, tiling, + n_iter, batch_size, + cfg_scale, image_cfg_scale, diffusers_guidance_rescale, + clip_skip, + seed, subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, + height, width, + enable_hr, denoising_strength, + hr_scale, hr_upscaler, hr_force, hr_second_pass_steps, hr_resize_x, hr_resize_y, + refiner_steps, refiner_start, refiner_prompt, refiner_negative, + hdr_clamp, hdr_boundary, hdr_threshold, hdr_center, hdr_channel_shift, hdr_full_shift, hdr_maximize, hdr_max_center, hdr_max_boundry, + override_settings_texts, + *args): - debug(f'txt2img: id_task={id_task}|prompt={prompt}|negative_prompt={negative_prompt}|prompt_styles={prompt_styles}|steps={steps}|sampler_index={sampler_index}|latent_index={latent_index}|full_quality={full_quality}|restore_faces={restore_faces}|tiling={tiling}|n_iter={n_iter}|batch_size={batch_size}|cfg_scale={cfg_scale}|clip_skip={clip_skip}|seed={seed}|subseed={subseed}|subseed_strength={subseed_strength}|seed_resize_from_h={seed_resize_from_h}|seed_resize_from_w={seed_resize_from_w}||height={height}|width={width}|enable_hr={enable_hr}|denoising_strength={denoising_strength}|hr_scale={hr_scale}|hr_upscaler={hr_upscaler}|hr_force={hr_force}|hr_second_pass_steps={hr_second_pass_steps}|hr_resize_x={hr_resize_x}|hr_resize_y={hr_resize_y}|image_cfg_scale={image_cfg_scale}|diffusers_guidance_rescale={diffusers_guidance_rescale}|refiner_steps={refiner_steps}|refiner_start={refiner_start}|refiner_prompt={refiner_prompt}|refiner_negative={refiner_negative}|override_settings_texts={override_settings_texts}') + debug(f'txt2img: id_task={id_task}|prompt={prompt}|negative={negative_prompt}|styles={prompt_styles}|steps={steps}|sampler_index={sampler_index}|latent_index={latent_index}|full_quality={full_quality}|restore_faces={restore_faces}|tiling={tiling}|batch_count={n_iter}|batch_size={batch_size}|cfg_scale={cfg_scale}|clip_skip={clip_skip}|seed={seed}|subseed={subseed}|subseed_strength={subseed_strength}|seed_resize_from_h={seed_resize_from_h}|seed_resize_from_w={seed_resize_from_w}|height={height}|width={width}|enable_hr={enable_hr}|denoising_strength={denoising_strength}|hr_scale={hr_scale}|hr_upscaler={hr_upscaler}|hr_force={hr_force}|hr_second_pass_steps={hr_second_pass_steps}|hr_resize_x={hr_resize_x}|hr_resize_y={hr_resize_y}|image_cfg_scale={image_cfg_scale}|diffusers_guidance_rescale={diffusers_guidance_rescale}|refiner_steps={refiner_steps}|refiner_start={refiner_start}|refiner_prompt={refiner_prompt}|refiner_negative={refiner_negative}|override_settings={override_settings_texts}') if shared.sd_model is None: shared.log.warning('Model not loaded') diff --git a/modules/ui.py b/modules/ui.py index fd359a680..4100e290b 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -48,6 +48,7 @@ txt2img_paste_fields = [] img2img_paste_fields = [] txt2img_args = [] img2img_args = [] +paste_function = None if not cmd_opts.share and not cmd_opts.listen: @@ -60,18 +61,15 @@ def gr_show(visible=True): return {"visible": visible, "__type__": "update"} -sample_img2img = "assets/stable-samples/img2img/sketch-mountains-input.jpg" -sample_img2img = sample_img2img if os.path.exists(sample_img2img) else None -paste_function = None - - def create_output_panel(tabname, outdir): # pylint: disable=unused-argument # outdir is used by extensions a, b, c, _d, e = ui_common.create_output_panel(tabname) return a, b, c, e + def plaintext_to_html(text): # may be referenced by extensions return ui_common.plaintext_to_html(text) + def infotext_to_html(text): # may be referenced by extensions return ui_common.infotext_to_html(text) @@ -158,6 +156,16 @@ def interrogate_deepbooru(image): return gr.update() if prompt is None else prompt +def create_batch_inputs(tab): + with gr.Accordion(open=False, label="Batch", elem_id=f"{tab}_batch", elem_classes=["small-accordion"]): + with FormRow(elem_id=f"{tab}_row_batch"): + batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id=f"{tab}_batch_count") + batch_size = gr.Slider(minimum=1, maximum=32, step=1, label='Batch size', value=1, elem_id=f"{tab}_batch_size") + batch_switch_btn = ToolButton(value=symbols.switch, elem_id=f"{tab}_batch_switch_btn", label="Switch dims") + batch_switch_btn.click(lambda w, h: (h, w), inputs=[batch_count, batch_size], outputs=[batch_count, batch_size], show_progress=False) + return batch_count, batch_size + + def create_seed_inputs(tab): with gr.Accordion(open=False, label="Seed", elem_id=f"{tab}_seed_group", elem_classes=["small-accordion"]): with FormRow(elem_id=f"{tab}_seed_row", variant="compact"): @@ -174,14 +182,89 @@ def create_seed_inputs(tab): seed_resize_from_h = gr.Slider(minimum=0, maximum=4096, step=8, label="Resize seed from height", value=0, elem_id=f"{tab}_seed_resize_from_h") random_seed.click(fn=lambda: [-1, -1], show_progress=False, inputs=[], outputs=[seed, subseed]) random_subseed.click(fn=lambda: -1, show_progress=False, inputs=[], outputs=[subseed]) - return seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w + return seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w + + +def create_advanced_inputs(tab): + with gr.Accordion(open=False, label="Advanced", elem_id=f"{tab}_advanced", elem_classes=["small-accordion"]): + with gr.Group(): + with FormRow(): + cfg_scale = gr.Slider(minimum=0.0, maximum=30.0, step=0.1, label='CFG scale', value=6.0, elem_id=f"{tab}_cfg_scale") + clip_skip = gr.Slider(label='CLIP skip', value=1, minimum=1, maximum=14, step=1, elem_id=f"{tab}_clip_skip", interactive=True) + with FormRow(): + image_cfg_scale = gr.Slider(minimum=0.0, maximum=30.0, step=0.1, label='Secondary CFG scale', value=6.0, elem_id=f"{tab}_image_cfg_scale") + diffusers_guidance_rescale = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Guidance rescale', value=0.7, elem_id=f"{tab}_image_cfg_rescale", visible=modules.shared.backend == modules.shared.Backend.DIFFUSERS) + with gr.Group(): + with FormRow(): + full_quality = gr.Checkbox(label='Full quality', value=True, elem_id=f"{tab}_full_quality") + restore_faces = gr.Checkbox(label='Face restore', value=False, visible=len(modules.shared.face_restorers) > 1, elem_id=f"{tab}_restore_faces") + tiling = gr.Checkbox(label='Tiling', value=False, elem_id=f"{tab}_tiling", visible=modules.shared.backend == modules.shared.Backend.ORIGINAL) + with gr.Group(visible=modules.shared.backend == modules.shared.Backend.DIFFUSERS): + with FormRow(): + hdr_clamp = gr.Checkbox(label='HDR clamp', value=False, elem_id=f"{tab}_hdr_clamp") + hdr_boundary = gr.Slider(minimum=0.0, maximum=10.0, step=0.1, value=4.0, label='Range', elem_id=f"{tab}_hdr_boundary") + hdr_threshold = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, value=0.95, label='Threshold', elem_id=f"{tab}_hdr_threshold") + with FormRow(): + hdr_center = gr.Checkbox(label='HDR center', value=False, elem_id=f"{tab}_hdr_center") + hdr_channel_shift = gr.Slider(minimum=0.0, maximum=2.0, step=0.1, value=1.0, label='Channel shift', elem_id=f"{tab}_hdr_channel_shift") + hdr_full_shift = gr.Slider(minimum=0.0, maximum=2.0, step=0.1, value=1, label='Full shift', elem_id=f"{tab}_hdr_full_shift") + with FormRow(): + hdr_maximize = gr.Checkbox(label='HDR maximize', value=False, elem_id=f"{tab}_hdr_maximize") + hdr_max_center = gr.Slider(minimum=0.0, maximum=2.0, step=0.1, value=0.6, label='Center', elem_id=f"{tab}_hdr_max_center") + hdr_max_boundry = gr.Slider(minimum=0.5, maximum=2.0, step=0.1, value=1.0, label='Range', elem_id=f"{tab}_hdr_max_boundry") + return cfg_scale, clip_skip, image_cfg_scale, diffusers_guidance_rescale, full_quality, restore_faces, tiling, hdr_clamp, hdr_boundary, hdr_threshold, hdr_center, hdr_channel_shift, hdr_full_shift, hdr_maximize, hdr_max_center, hdr_max_boundry + + +def create_resize_inputs(tab, images): + dummy_component = gr.Number(visible=False, value=0) + with gr.Accordion(open=False, label="Resize", elem_classes=["small-accordion"], elem_id=f"{tab}_resize_group"): + with gr.Row(): + resize_mode = gr.Radio(label="Resize mode", elem_id=f"{tab}_resize_mode", choices=modules.shared.resize_modes, type="index", value="None") + with gr.Row(): + resize_name = gr.Dropdown(label="Resize method", elem_id=f"{tab}_resize_name", choices=[x.name for x in modules.shared.sd_upscalers], value=opts.upscaler_for_img2img) + + with FormRow(visible=True) as _resize_group: + with gr.Column(elem_id=f"{tab}_column_size", scale=4): + selected_scale_tab = gr.State(value=0) # pylint: disable=abstract-class-instantiated + with gr.Tabs(): + with gr.Tab(label="Resize to") as tab_scale_to: + with FormRow(): + with gr.Column(elem_id=f"{tab}_column_size", scale=4): + with FormRow(): + width = gr.Slider(minimum=64, maximum=4096, step=8, label="Width", value=512, elem_id=f"{tab}_width") + height = gr.Slider(minimum=64, maximum=4096, step=8, label="Height", value=512, elem_id=f"{tab}_height") + with gr.Column(elem_id=f"{tab}_column_dim", scale=1, elem_classes="dimensions-tools"): + with FormRow(): + res_switch_btn = ToolButton(value=symbols.switch, elem_id=f"{tab}_res_switch_btn") + res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False) + detect_image_size_btn = ToolButton(value=symbols.detect, elem_id=f"{tab}_detect_image_size_btn") + detect_image_size_btn.click(fn=lambda w, h, _: (w or gr.update(), h or gr.update()), _js="currentImg2imgSourceResolution", inputs=[dummy_component, dummy_component, dummy_component], outputs=[width, height], show_progress=False) + + with gr.Tab(label="Resize by") as tab_scale_by: + scale_by = gr.Slider(minimum=0.05, maximum=4.0, step=0.05, label="Scale", value=1.0, elem_id=f"{tab}_scale") + with FormRow(): + scale_by_html = FormHTML(resize_from_to_html(0, 0, 0.0), elem_id=f"{tab}_scale_resolution_preview") + gr.Slider(label="Unused", elem_id=f"{tab}_unused_scale_by_slider") + button_update_resize_to = gr.Button(visible=False, elem_id=f"{tab}_update_resize_to") + + on_change_args = dict(fn=resize_from_to_html, _js="currentImg2imgSourceResolution", inputs=[dummy_component, dummy_component, scale_by], outputs=scale_by_html, show_progress=False) + scale_by.release(**on_change_args) + button_update_resize_to.click(**on_change_args) + + for component in images: + component.change(fn=lambda: None, _js="updateImg2imgResizeToTextAfterChangingImage", inputs=[], outputs=[], show_progress=False) + + tab_scale_to.select(fn=lambda: 0, inputs=[], outputs=[selected_scale_tab]) + tab_scale_by.select(fn=lambda: 1, inputs=[], outputs=[selected_scale_tab]) + # resize_mode.change(fn=lambda x: gr.update(visible=x != 0), inputs=[resize_mode], outputs=[_resize_group]) + return resize_mode, resize_name, width, height, scale_by, selected_scale_tab def connect_clear_prompt(button): # pylint: disable=unused-argument pass -def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: gr.Textbox, dummy_component, is_subseed): +def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: gr.Textbox, is_subseed): """ 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.""" @@ -202,6 +285,7 @@ def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: log.error(f"Error parsing JSON generation info: {gen_info_string}") return [res, gr_show(False)] + dummy_component = gr.Number(visible=False, value=0) 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]) @@ -255,8 +339,8 @@ def create_toprow(is_img2img: bool = False, id_part: str = None): button_deepbooru = None if is_img2img: with gr.Column(scale=1, elem_classes="interrogate-col"): - button_interrogate = gr.Button('Interrogate\nCLIP', elem_id="interrogate") - button_deepbooru = gr.Button('Interrogate\nDeepBooru', elem_id="deepbooru") + button_interrogate = gr.Button('Interrogate\nCLIP', elem_id=f"{id_part}_interrogate") + button_deepbooru = gr.Button('Interrogate\nDeepBooru', elem_id=f"{id_part}_deepbooru") with gr.Column(scale=1, elem_id=f"{id_part}_actions_column"): with gr.Row(elem_id=f"{id_part}_generate_box"): submit = gr.Button('Generate', elem_id=f"{id_part}_generate", variant='primary') @@ -278,14 +362,14 @@ def create_toprow(is_img2img: bool = False, id_part: str = None): negative_token_counter = gr.HTML(value="0/75", elem_id=f"{id_part}_negative_token_counter", elem_classes=["token-counter"]) negative_token_button = gr.Button(visible=False, elem_id=f"{id_part}_negative_token_button") with gr.Row(elem_id=f"{id_part}_styles_row"): - prompt_styles = gr.Dropdown(label="Styles", elem_id=f"{id_part}_styles", choices=[style.name for style in modules.shared.prompt_styles.styles.values()], value=[], multiselect=True) - prompt_styles_btn_refresh = ToolButton(symbols.refresh, elem_id=f"{id_part}_styles_refresh", visible=True) - prompt_styles_btn_refresh.click(fn=lambda: gr.update(choices=[style.name for style in modules.shared.prompt_styles.styles.values()]), inputs=[], outputs=[prompt_styles]) - prompt_styles_btn_select = gr.Button('Select', elem_id=f"{id_part}_styles_select", visible=False) - prompt_styles_btn_select.click(_js="applyStyles", fn=parse_style, inputs=[prompt_styles], outputs=[prompt_styles]) - prompt_styles_btn_apply = ToolButton(symbols.apply, elem_id=f"{id_part}_extra_apply", visible=False) - prompt_styles_btn_apply.click(fn=apply_styles, inputs=[prompt, negative_prompt, prompt_styles], outputs=[prompt, negative_prompt, prompt_styles]) - return prompt, prompt_styles, negative_prompt, submit, button_interrogate, button_deepbooru, button_paste, button_extra, token_counter, token_button, negative_token_counter, negative_token_button + styles = gr.Dropdown(label="Styles", elem_id=f"{id_part}_styles", choices=[style.name for style in modules.shared.prompt_styles.styles.values()], value=[], multiselect=True) + styles_btn_refresh = ToolButton(symbols.refresh, elem_id=f"{id_part}_styles_refresh", visible=True) + styles_btn_refresh.click(fn=lambda: gr.update(choices=[style.name for style in modules.shared.prompt_styles.styles.values()]), inputs=[], outputs=[styles]) + styles_btn_select = gr.Button('Select', elem_id=f"{id_part}_styles_select", visible=False) + styles_btn_select.click(_js="applyStyles", fn=parse_style, inputs=[styles], outputs=[styles]) + styles_btn_apply = ToolButton(symbols.apply, elem_id=f"{id_part}_extra_apply", visible=False) + styles_btn_apply.click(fn=apply_styles, inputs=[prompt, negative_prompt, styles], outputs=[prompt, negative_prompt, styles]) + return prompt, styles, negative_prompt, submit, button_interrogate, button_deepbooru, button_paste, button_extra, token_counter, token_button, negative_token_counter, negative_token_button def setup_progressbar(*args, **kwargs): # pylint: disable=unused-argument @@ -362,6 +446,44 @@ def create_sampler_and_steps_selection(choices, tabname): return steps, sampler_index +def create_sampler_inputs(tab): + with gr.Accordion(open=False, label="Sampler", elem_id=f"{tab}_sampler", elem_classes=["small-accordion"]): + with FormRow(elem_id=f"{tab}_row_sampler"): + modules.sd_samplers.set_samplers() + steps, sampler_index = create_sampler_and_steps_selection(modules.sd_samplers.samplers, tab) + return steps, sampler_index + + +def create_hires_inputs(tab): + with gr.Accordion(open=False, label="Second pass", elem_id=f"{tab}_second_pass", elem_classes=["small-accordion"]): + with FormGroup(): + with FormRow(elem_id=f"{tab}_hires_row1"): + enable_hr = gr.Checkbox(label='Enable second pass', value=False, elem_id=f"{tab}_enable_hr") + with FormRow(elem_id=f"{tab}_hires_row2"): + latent_index = gr.Dropdown(label='Secondary sampler', elem_id=f"{tab}_sampling_alt", choices=[x.name for x in modules.sd_samplers.samplers], value='Default', type="index") + denoising_strength = gr.Slider(minimum=0.0, maximum=0.99, step=0.01, label='Denoising strength', value=0.5, elem_id=f"{tab}_denoising_strength") + with FormRow(elem_id=f"{tab}_hires_finalres", variant="compact"): + hr_final_resolution = FormHTML(value="", elem_id=f"{tab}_hr_finalres", label="Upscaled resolution", interactive=False) + with FormRow(elem_id=f"{tab}_hires_fix_row1", variant="compact"): + hr_upscaler = gr.Dropdown(label="Upscaler", elem_id=f"{tab}_hr_upscaler", choices=[*modules.shared.latent_upscale_modes, *[x.name for x in modules.shared.sd_upscalers]], value=modules.shared.latent_upscale_default_mode) + hr_force = gr.Checkbox(label='Force Hires', value=False, elem_id=f"{tab}_hr_force") + with FormRow(elem_id=f"{tab}_hires_fix_row2", variant="compact"): + hr_second_pass_steps = gr.Slider(minimum=0, maximum=99, step=1, label='Hires steps', elem_id=f"{tab}_steps_alt", value=20) + hr_scale = gr.Slider(minimum=1.0, maximum=4.0, step=0.05, label="Upscale by", value=2.0, elem_id=f"{tab}_hr_scale") + with FormRow(elem_id=f"{tab}_hires_fix_row3", variant="compact"): + hr_resize_x = gr.Slider(minimum=0, maximum=4096, step=8, label="Resize width to", value=0, elem_id=f"{tab}_hr_resize_x") + hr_resize_y = gr.Slider(minimum=0, maximum=4096, step=8, label="Resize height to", value=0, elem_id=f"{tab}_hr_resize_y") + with FormGroup(visible=modules.shared.backend == modules.shared.Backend.DIFFUSERS): + with FormRow(elem_id=f"{tab}_refiner_row1", variant="compact"): + refiner_start = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Refiner start', value=0.8, elem_id=f"{tab}_refiner_start") + refiner_steps = gr.Slider(minimum=0, maximum=99, step=1, label="Refiner steps", elem_id=f"{tab}_refiner_steps", value=5) + with FormRow(elem_id=f"{tab}_refiner_row3", variant="compact"): + refiner_prompt = gr.Textbox(value='', label='Secondary Prompt', elem_id=f"{tab}_refiner_prompt") + with FormRow(elem_id="txt2img_refiner_row4", variant="compact"): + refiner_negative = gr.Textbox(value='', label='Secondary negative prompt', elem_id=f"{tab}_refiner_neg_prompt") + return enable_hr, latent_index, denoising_strength, hr_final_resolution, hr_upscaler, hr_force, hr_second_pass_steps, hr_scale, hr_resize_x, hr_resize_y, refiner_steps, refiner_start, refiner_prompt, refiner_negative + + def get_value_for_setting(key): value = getattr(opts, key) info = opts.data_labels[key] @@ -371,13 +493,14 @@ def get_value_for_setting(key): def ordered_ui_categories(): - return ['dimensions', 'sampler', 'seed', 'denoising', 'cfg', 'checkboxes', 'accordions', 'override_settings', 'scripts'] # TODO: a1111 compatibility item, not implemented + return ['dimensions', 'sampler', 'seed', 'denoising', 'cfg', 'checkboxes', 'accordions', 'override_settings', 'scripts'] # a1111 compatibility item, not implemented -def create_override_settings_dropdown(tabname, row): # pylint: disable=unused-argument - dropdown = gr.Dropdown([], label="Override settings", visible=False, elem_id=f"{tabname}_override_settings", multiselect=True) - dropdown.change(fn=lambda x: gr.Dropdown.update(visible=len(x) > 0), inputs=[dropdown], outputs=[dropdown]) - return dropdown +def create_override_inputs(tab): # pylint: disable=unused-argument + with FormRow(elem_id=f"{tab}_override_settings_row"): + override_settings = gr.Dropdown([], value=None, label="Override settings", visible=False, elem_id=f"{tab}_override_settings", multiselect=True) + override_settings.change(fn=lambda x: gr.Dropdown.update(visible=len(x) > 0), inputs=[override_settings], outputs=[override_settings]) + return override_settings def create_ui(startup_timer = None): @@ -390,12 +513,14 @@ def create_ui(startup_timer = None): modules.scripts.scripts_current = modules.scripts.scripts_txt2img modules.scripts.scripts_txt2img.initialize_scripts(is_img2img=False) with gr.Blocks(analytics_enabled=False) as txt2img_interface: - txt2img_prompt, txt2img_prompt_styles, txt2img_negative_prompt, submit, _interrogate, _deepbooru, txt2img_paste, extra_networks_button, token_counter, token_button, negative_token_counter, negative_token_button = create_toprow(is_img2img=False) - dummy_component = gr.Label(visible=False) + txt2img_prompt, txt2img_prompt_styles, txt2img_negative_prompt, txt2img_submit, _interrogate, _deepbooru, txt2img_paste, txt2img_extra_networks_button, txt2img_token_counter, txt2img_token_button, txt2img_negative_token_counter, txt2img_negative_token_button = create_toprow(is_img2img=False, id_part="txt2img") + txt_prompt_img = gr.File(label="", elem_id="txt2img_prompt_image", file_count="single", type="binary", visible=False) + txt_prompt_img.change(fn=modules.images.image_data, inputs=[txt_prompt_img], outputs=[txt2img_prompt, txt_prompt_img]) + with FormRow(variant='compact', elem_id="txt2img_extra_networks", visible=False) as extra_networks_ui: from modules import ui_extra_networks - extra_networks_ui = ui_extra_networks.create_ui(extra_networks_ui, extra_networks_button, 'txt2img', skip_indexing=opts.extra_network_skip_indexing) + extra_networks_ui = ui_extra_networks.create_ui(extra_networks_ui, txt2img_extra_networks_button, 'txt2img', skip_indexing=opts.extra_network_skip_indexing) timer.startup.record('ui-extra-networks') with gr.Row(elem_id="txt2img_interface", equal_height=False): @@ -405,79 +530,18 @@ def create_ui(startup_timer = None): width = gr.Slider(minimum=64, maximum=4096, step=8, label="Width", value=512, elem_id="txt2img_width") height = gr.Slider(minimum=64, maximum=4096, step=8, label="Height", value=512, elem_id="txt2img_height") res_switch_btn = ToolButton(value=symbols.switch, elem_id="txt2img_res_switch_btn", label="Switch dims") + res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False) with FormGroup(elem_classes="settings-accordion"): - with gr.Accordion(open=False, label="Sampler", elem_id="txt2img_sampler", elem_classes=["small-accordion"]): - with FormRow(elem_id="txt2img_row_sampler"): - modules.sd_samplers.set_samplers() - steps, sampler_index = create_sampler_and_steps_selection(modules.sd_samplers.samplers, "txt2img") - - with gr.Accordion(open=False, label="Batch", elem_id="txt2img_batch", elem_classes=["small-accordion"]): - with FormRow(elem_id="txt2img_row_batch"): - batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id="txt2img_batch_count") - batch_size = gr.Slider(minimum=1, maximum=32, step=1, label='Batch size', value=1, elem_id="txt2img_batch_size") - batch_switch_btn = ToolButton(value=symbols.switch, elem_id="txt2img_batch_switch_btn", label="Switch dims") + steps, sampler_index = create_sampler_inputs('txt2img') + batch_count, batch_size = create_batch_inputs('txt2img') seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = create_seed_inputs('txt2img') + cfg_scale, clip_skip, image_cfg_scale, diffusers_guidance_rescale, full_quality, restore_faces, tiling, hdr_clamp, hdr_boundary, hdr_threshold, hdr_center, hdr_channel_shift, hdr_full_shift, hdr_maximize, hdr_max_center, hdr_max_boundry = create_advanced_inputs('txt2img') + enable_hr, latent_index, denoising_strength, hr_final_resolution, hr_upscaler, hr_force, hr_second_pass_steps, hr_scale, hr_resize_x, hr_resize_y, refiner_steps, refiner_start, refiner_prompt, refiner_negative = create_hires_inputs('txt2img') + override_settings = create_override_inputs('txt2img') - with gr.Accordion(open=False, label="Advanced", elem_id="txt2img_advanced", elem_classes=["small-accordion"]): - with gr.Group(): - with FormRow(): - cfg_scale = gr.Slider(minimum=0.0, maximum=30.0, step=0.1, label='CFG scale', value=6.0, elem_id="txt2img_cfg_scale") - clip_skip = gr.Slider(label='CLIP skip', value=1, minimum=1, maximum=14, step=1, elem_id='txt2img_clip_skip', interactive=True) - with FormRow(): - image_cfg_scale = gr.Slider(minimum=0.0, maximum=30.0, step=0.1, label='Secondary CFG scale', value=6.0, elem_id="txt2img_image_cfg_scale") - diffusers_guidance_rescale = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Guidance rescale', value=0.7, elem_id="txt2img_image_cfg_rescale") - with gr.Group(): - with FormRow(): - full_quality = gr.Checkbox(label='Full quality', value=True, elem_id="txt2img_full_quality") - restore_faces = gr.Checkbox(label='Face restore', value=False, visible=len(modules.shared.face_restorers) > 1, elem_id="txt2img_restore_faces") - tiling = gr.Checkbox(label='Tiling', value=False, elem_id="txt2img_tiling") - with gr.Group(): - with FormRow(): - hdr_clamp = gr.Checkbox(label='HDR clamp', value=False, elem_id="txt2img_hdr_clamp") - hdr_boundary = gr.Slider(minimum=0.0, maximum=10.0, step=0.1, value=4.0, label='Range', elem_id="txt2img_hdr_boundary") - hdr_threshold = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, value=0.95, label='Threshold', elem_id="txt2img_hdr_threshold") - with FormRow(): - hdr_center = gr.Checkbox(label='HDR center', value=False, elem_id="txt2img_hdr_center") - hdr_channel_shift = gr.Slider(minimum=0.0, maximum=2.0, step=0.1, value=1.0, label='Channel shift', elem_id="txt2img_hdr_channel_shift") - hdr_full_shift = gr.Slider(minimum=0.0, maximum=2.0, step=0.1, value=1, label='Full shift', elem_id="txt2img_hdr_full_shift") - with FormRow(): - hdr_maximize = gr.Checkbox(label='HDR maximize', value=False, elem_id="txt2img_hdr_maximize") - hdr_max_center = gr.Slider(minimum=0.0, maximum=2.0, step=0.1, value=0.6, label='Center', elem_id="txt2img_hdr_max_center") - hdr_max_boundry = gr.Slider(minimum=0.5, maximum=2.0, step=0.1, value=1.0, label='Range', elem_id="txt2img_hdr_max_boundry") - - with gr.Accordion(open=False, label="Second pass", elem_id="txt2img_second_pass", elem_classes=["small-accordion"]): - with FormGroup(): - with FormRow(elem_id="sampler_selection_txt2img_alt_row1"): - enable_hr = gr.Checkbox(label='Enable second pass', value=False, elem_id="txt2img_enable_hr") - with FormRow(elem_id="sampler_selection_txt2img_alt_row1"): - latent_index = gr.Dropdown(label='Secondary sampler', elem_id="txt2img_sampling_alt", choices=[x.name for x in modules.sd_samplers.samplers], value='Default', type="index") - denoising_strength = gr.Slider(minimum=0.0, maximum=0.99, step=0.01, label='Denoising strength', value=0.5, elem_id="txt2img_denoising_strength") - with FormRow(elem_id="txt2img_hires_finalres", variant="compact"): - hr_final_resolution = FormHTML(value="", elem_id="txtimg_hr_finalres", label="Upscaled resolution", interactive=False) - with FormRow(elem_id="txt2img_hires_fix_row1", variant="compact"): - hr_upscaler = gr.Dropdown(label="Upscaler", elem_id="txt2img_hr_upscaler", choices=[*modules.shared.latent_upscale_modes, *[x.name for x in modules.shared.sd_upscalers]], value=modules.shared.latent_upscale_default_mode) - hr_force = gr.Checkbox(label='Force Hires', value=False, elem_id="txt2img_hr_force") - with FormRow(elem_id="txt2img_hires_fix_row2", variant="compact"): - hr_second_pass_steps = gr.Slider(minimum=0, maximum=99, step=1, label='Hires steps', elem_id="txt2img_steps_alt", value=20) - hr_scale = gr.Slider(minimum=1.0, maximum=4.0, step=0.05, label="Upscale by", value=2.0, elem_id="txt2img_hr_scale") - with FormRow(elem_id="txt2img_hires_fix_row3", variant="compact"): - hr_resize_x = gr.Slider(minimum=0, maximum=4096, step=8, label="Resize width to", value=0, elem_id="txt2img_hr_resize_x") - hr_resize_y = gr.Slider(minimum=0, maximum=4096, step=8, label="Resize height to", value=0, elem_id="txt2img_hr_resize_y") - with FormGroup(visible=modules.shared.backend == modules.shared.Backend.DIFFUSERS): - with FormRow(elem_id="txt2img_refiner_row1", variant="compact"): - refiner_start = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Refiner start', value=0.8, elem_id="txt2img_refiner_start") - refiner_steps = gr.Slider(minimum=0, maximum=99, step=1, label="Refiner steps", elem_id="txt2img_refiner_steps", value=5) - with FormRow(elem_id="txt2img_refiner_row3", variant="compact"): - refiner_prompt = gr.Textbox(value='', label='Secondary Prompt') - with FormRow(elem_id="txt2img_refiner_row4", variant="compact"): - refiner_negative = gr.Textbox(value='', label='Secondary negative prompt') - - with FormRow(elem_id="txt2img_override_settings_row") as row: - override_settings = create_override_settings_dropdown('txt2img', row) - - custom_inputs = modules.scripts.scripts_txt2img.setup_ui() + txt2img_script_inputs = modules.scripts.scripts_txt2img.setup_ui() hr_resolution_preview_inputs = [width, height, hr_scale, hr_resize_x, hr_resize_y, hr_upscaler] for preview_input in hr_resolution_preview_inputs: @@ -489,21 +553,19 @@ def create_ui(startup_timer = None): show_progress=False, ) - txt2img_gallery, generation_info, html_info, _html_info_formatted, html_log = ui_common.create_output_panel("txt2img") - connect_reuse_seed(seed, reuse_seed, generation_info, dummy_component, is_subseed=False) - connect_reuse_seed(subseed, reuse_subseed, generation_info, dummy_component, is_subseed=True) + txt2img_gallery, txt2img_generation_info, txt2img_html_info, _txt2img_html_info_formatted, txt2img_html_log = ui_common.create_output_panel("txt2img") + connect_reuse_seed(seed, reuse_seed, txt2img_generation_info, is_subseed=False) + connect_reuse_seed(subseed, reuse_subseed, txt2img_generation_info, is_subseed=True) global txt2img_args # pylint: disable=global-statement + dummy_component = gr.Textbox(visible=False, value='dummy') txt2img_args = [ dummy_component, - txt2img_prompt, txt2img_negative_prompt, - txt2img_prompt_styles, - steps, - sampler_index, latent_index, + txt2img_prompt, txt2img_negative_prompt, txt2img_prompt_styles, + steps, sampler_index, latent_index, full_quality, restore_faces, tiling, batch_count, batch_size, - cfg_scale, image_cfg_scale, - diffusers_guidance_rescale, + cfg_scale, image_cfg_scale, diffusers_guidance_rescale, clip_skip, seed, subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, height, width, @@ -516,21 +578,17 @@ def create_ui(startup_timer = None): txt2img_dict = dict( fn=wrap_gradio_gpu_call(modules.txt2img.txt2img, extra_outputs=[None, '', '']), _js="submit_txt2img", - inputs=txt2img_args + custom_inputs, + inputs=txt2img_args + txt2img_script_inputs, outputs=[ txt2img_gallery, - generation_info, - html_info, - html_log, + txt2img_generation_info, + txt2img_html_info, + txt2img_html_log, ], show_progress=False, ) txt2img_prompt.submit(**txt2img_dict) - submit.click(**txt2img_dict) - - res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False) - batch_switch_btn.click(lambda w, h: (h, w), inputs=[batch_count, batch_size], outputs=[batch_count, batch_size], show_progress=False) - txt_prompt_img.change(fn=modules.images.image_data, inputs=[txt_prompt_img], outputs=[txt2img_prompt, txt_prompt_img]) + txt2img_submit.click(**txt2img_dict) global txt2img_paste_fields # pylint: disable=global-statement txt2img_paste_fields = [ @@ -581,8 +639,8 @@ def create_ui(startup_timer = None): parameters_copypaste.add_paste_fields("txt2img", None, txt2img_paste_fields, override_settings) parameters_copypaste.register_paste_params_button(parameters_copypaste.ParamBinding(paste_button=txt2img_paste, tabname="txt2img", source_text_component=txt2img_prompt, source_image_component=None)) - token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[txt2img_prompt, steps], outputs=[token_counter]) - negative_token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[txt2img_negative_prompt, steps], outputs=[negative_token_counter]) + txt2img_token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[txt2img_prompt, steps], outputs=[txt2img_token_counter]) + txt2img_negative_token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[txt2img_negative_prompt, steps], outputs=[txt2img_negative_token_counter]) ui_extra_networks.setup_ui(extra_networks_ui, txt2img_gallery) @@ -592,12 +650,12 @@ def create_ui(startup_timer = None): modules.scripts.scripts_current = modules.scripts.scripts_img2img modules.scripts.scripts_img2img.initialize_scripts(is_img2img=True) with gr.Blocks(analytics_enabled=False) as img2img_interface: - img2img_prompt, img2img_prompt_styles, img2img_negative_prompt, submit, img2img_interrogate, img2img_deepbooru, img2img_paste, extra_networks_button, token_counter, token_button, negative_token_counter, negative_token_button = create_toprow(is_img2img=True) + img2img_prompt, img2img_prompt_styles, img2img_negative_prompt, submit, img2img_interrogate, img2img_deepbooru, img2img_paste, img2img_extra_networks_button, img2img_token_counter, img2img_token_button, img2img_negative_token_counter, img2img_negative_token_button = create_toprow(is_img2img=True, id_part="img2img") img2img_prompt_img = gr.File(label="", elem_id="img2img_prompt_image", file_count="single", type="binary", visible=False) with FormRow(variant='compact', elem_id="img2img_extra_networks", visible=False) as extra_networks_ui: from modules import ui_extra_networks - extra_networks_ui_img2img = ui_extra_networks.create_ui(extra_networks_ui, extra_networks_button, 'img2img', skip_indexing=opts.extra_network_skip_indexing) + extra_networks_ui_img2img = ui_extra_networks.create_ui(extra_networks_ui, img2img_extra_networks_button, 'img2img', skip_indexing=opts.extra_network_skip_indexing) with FormRow(elem_id="img2img_interface", equal_height=False): with gr.Column(variant='compact', elem_id="img2img_settings"): @@ -671,78 +729,18 @@ def create_ui(startup_timer = None): button.click(fn=lambda: None, _js=f"switch_to_{name.replace(' ', '_')}", inputs=[], outputs=[]) with FormGroup(elem_classes="settings-accordion"): - with gr.Accordion(open=False, label="Sampler", elem_classes=["small-accordion"], elem_id="img2img_sampling_group"): - modules.sd_samplers.set_samplers() - steps, sampler_index = create_sampler_and_steps_selection(modules.sd_samplers.samplers_for_img2img, "img2img") - - with gr.Accordion(open=False, label="Resize", elem_classes=["small-accordion"], elem_id="img2img_resize_group"): - with gr.Row(): - resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", choices=["None", "Resize fixed", "Crop and resize", "Resize and fill", "Latent upscale"], type="index", value="None") - - with FormRow(): - with gr.Column(elem_id="img2img_column_size", scale=4): - selected_scale_tab = gr.State(value=0) # pylint: disable=abstract-class-instantiated - - with gr.Tabs(): - with gr.Tab(label="Resize to") as tab_scale_to: - with FormRow(): - with gr.Column(elem_id="img2img_column_size", scale=4): - with FormRow(): - width = gr.Slider(minimum=64, maximum=4096, step=8, label="Width", value=512, elem_id="img2img_width") - height = gr.Slider(minimum=64, maximum=4096, step=8, label="Height", value=512, elem_id="img2img_height") - with gr.Column(elem_id="img2img_column_dim", scale=1, elem_classes="dimensions-tools"): - with FormRow(): - res_switch_btn = ToolButton(value=symbols.switch, elem_id="img2img_res_switch_btn") - detect_image_size_btn = ToolButton(value=symbols.detect, elem_id="img2img_detect_image_size_btn") - - with gr.Tab(label="Resize by") as tab_scale_by: - scale_by = gr.Slider(minimum=0.05, maximum=4.0, step=0.05, label="Scale", value=1.0, elem_id="img2img_scale") - - with FormRow(): - scale_by_html = FormHTML(resize_from_to_html(0, 0, 0.0), elem_id="img2img_scale_resolution_preview") - gr.Slider(label="Unused", elem_id="img2img_unused_scale_by_slider") - button_update_resize_to = gr.Button(visible=False, elem_id="img2img_update_resize_to") - - on_change_args = dict( - fn=resize_from_to_html, - _js="currentImg2imgSourceResolution", - inputs=[dummy_component, dummy_component, scale_by], - outputs=scale_by_html, - show_progress=False, - ) - - scale_by.release(**on_change_args) - button_update_resize_to.click(**on_change_args) - - for component in [init_img, sketch]: - component.change(fn=lambda: None, _js="updateImg2imgResizeToTextAfterChangingImage", inputs=[], outputs=[], show_progress=False) - - tab_scale_to.select(fn=lambda: 0, inputs=[], outputs=[selected_scale_tab]) - tab_scale_by.select(fn=lambda: 1, inputs=[], outputs=[selected_scale_tab]) - - with gr.Accordion(open=False, label="Batch", elem_classes=["small-accordion"], elem_id="img2img_batch_group"): - with FormRow(elem_id="img2img_column_batch"): - batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id="img2img_batch_count") - batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size', value=1, elem_id="img2img_batch_size") + steps, sampler_index = create_sampler_inputs('img2img') + resize_mode, resize_name, width, height, scale_by, selected_scale_tab = create_resize_inputs('img2img', [init_img, sketch]) + batch_count, batch_size = create_batch_inputs('img2img') seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = create_seed_inputs('img2img') with gr.Accordion(open=False, label="Denoise", elem_classes=["small-accordion"], elem_id="img2img_denoise_group"): with FormRow(): - denoising_strength = gr.Slider(minimum=0.0, maximum=0.99, step=0.01, label='Denoising strength', value=0.75, elem_id="img2img_denoising_strength") + denoising_strength = gr.Slider(minimum=0.0, maximum=0.99, step=0.01, label='Denoising strength', value=0.50, elem_id="img2img_denoising_strength") refiner_start = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Denoise start', value=0.0, elem_id="img2img_refiner_start") - with gr.Accordion(open=False, label="Advanced", elem_classes=["small-accordion"], elem_id="img2img_advanced_group"): - with FormRow(): - cfg_scale = gr.Slider(minimum=0.0, maximum=30.0, step=0.1, label='CFG scale', value=6.0, elem_id="img2img_cfg_scale") - image_cfg_scale = gr.Slider(minimum=0.0, maximum=30.0, step=0.15, label='Image CFG scale', value=1.5, elem_id="img2img_image_cfg_scale") - with FormRow(): - clip_skip = gr.Slider(label='CLIP skip', value=1, minimum=1, maximum=4, step=1, elem_id='img2img_clip_skip', interactive=True) - diffusers_guidance_rescale = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Guidance rescale', value=0.7, elem_id="txt2img_image_cfg_rescale") - with FormRow(elem_classes="img2img_checkboxes_row", variant="compact"): - full_quality = gr.Checkbox(label='Full quality', value=True, elem_id="img2img_full_quality") - restore_faces = gr.Checkbox(label='Restore faces', value=False, visible=len(modules.shared.face_restorers) > 1, elem_id="img2img_restore_faces") - tiling = gr.Checkbox(label='Tiling', value=False, elem_id="img2img_tiling") + cfg_scale, clip_skip, image_cfg_scale, diffusers_guidance_rescale, full_quality, restore_faces, tiling, hdr_clamp, hdr_boundary, hdr_threshold, hdr_center, hdr_channel_shift, hdr_full_shift, hdr_maximize, hdr_max_center, hdr_max_boundry = create_advanced_inputs('img2img') with FormGroup(elem_id="inpaint_controls", visible=False) as inpaint_controls: with FormRow(): @@ -765,32 +763,23 @@ def create_ui(startup_timer = None): for i, elem in enumerate(img2img_tabs): elem.select(fn=lambda tab=i: select_img2img_tab(tab), inputs=[], outputs=[inpaint_controls, mask_alpha]) # pylint: disable=cell-var-from-loop - with FormRow(elem_id="img2img_override_settings_row") as row: - override_settings = create_override_settings_dropdown('img2img', row) + override_settings = create_override_inputs('img2img') with FormGroup(elem_id="img2img_script_container"): - custom_inputs = modules.scripts.scripts_img2img.setup_ui() + img2img_script_inputs = modules.scripts.scripts_img2img.setup_ui() - img2img_gallery, generation_info, html_info, _html_info_formatted, html_log = ui_common.create_output_panel("img2img") + img2img_gallery, img2img_generation_info, img2img_html_info, _img2img_html_info_formatted, img2img_html_log = ui_common.create_output_panel("img2img") - connect_reuse_seed(seed, reuse_seed, generation_info, dummy_component, is_subseed=False) - connect_reuse_seed(subseed, reuse_subseed, generation_info, dummy_component, is_subseed=True) + connect_reuse_seed(seed, reuse_seed, img2img_generation_info, is_subseed=False) + connect_reuse_seed(subseed, reuse_subseed, img2img_generation_info, is_subseed=True) - img2img_prompt_img.change( - fn=modules.images.image_data, - inputs=[ - img2img_prompt_img - ], - outputs=[ - img2img_prompt, - img2img_prompt_img - ] - ) + 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') + dummy_component2 = gr.Number(visible=False, value=0) global img2img_args # pylint: disable=global-statement img2img_args = [ - dummy_component, dummy_component, - img2img_prompt, img2img_negative_prompt, - img2img_prompt_styles, + dummy_component1, dummy_component2, + img2img_prompt, img2img_negative_prompt, img2img_prompt_styles, init_img, sketch, init_img_with_mask, @@ -814,25 +803,27 @@ def create_ui(startup_timer = None): selected_scale_tab, height, width, scale_by, - resize_mode, + resize_mode, resize_name, inpaint_full_res, inpaint_full_res_padding, inpainting_mask_invert, img2img_batch_files, img2img_batch_input_dir, img2img_batch_output_dir, img2img_batch_inpaint_mask_dir, + hdr_clamp, hdr_boundary, hdr_threshold, hdr_center, hdr_channel_shift, hdr_full_shift, hdr_maximize, hdr_max_center, hdr_max_boundry, override_settings, ] img2img_dict = dict( fn=wrap_gradio_gpu_call(modules.img2img.img2img, extra_outputs=[None, '', '']), _js="submit_img2img", - inputs= img2img_args + custom_inputs, + inputs= img2img_args + img2img_script_inputs, outputs=[ img2img_gallery, - generation_info, - html_info, - html_log, + img2img_generation_info, + img2img_html_info, + img2img_html_log, ], show_progress=False, ) img2img_prompt.submit(**img2img_dict) submit.click(**img2img_dict) + dummy_component = gr.Textbox(visible=False, value='dummy') interrogate_args = dict( _js="get_img2img_tab_index", @@ -852,18 +843,8 @@ def create_ui(startup_timer = None): img2img_interrogate.click(fn=lambda *args: process_interrogate(interrogate, *args), **interrogate_args) img2img_deepbooru.click(fn=lambda *args: process_interrogate(interrogate_deepbooru, *args), **interrogate_args) - res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False) - - detect_image_size_btn.click( - fn=lambda w, h, _: (w or gr.update(), h or gr.update()), - _js="currentImg2imgSourceResolution", - inputs=[dummy_component, dummy_component, dummy_component], - outputs=[width, height], - show_progress=False, - ) - - token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[img2img_prompt, steps], outputs=[token_counter]) - negative_token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[img2img_negative_prompt, steps], outputs=[negative_token_counter]) + img2img_token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[img2img_prompt, steps], outputs=[img2img_token_counter]) + img2img_negative_token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[img2img_negative_prompt, steps], outputs=[img2img_negative_token_counter]) ui_extra_networks.setup_ui(extra_networks_ui_img2img, img2img_gallery) global img2img_paste_fields # pylint: disable=global-statement @@ -999,6 +980,7 @@ def create_ui(startup_timer = None): components = [] component_dict = {} modules.shared.settings_components = component_dict + dummy_component1 = gr.Label(visible=False) script_callbacks.ui_settings_callback() opts.reorder() diff --git a/webui.py b/webui.py index 1e53b7d47..350d0d1c6 100644 --- a/webui.py +++ b/webui.py @@ -266,8 +266,7 @@ def start_ui(): favicon_path='html/logo.ico', allowed_paths=[os.path.dirname(__file__), cmd_opts.data_dir], app_kwargs=fastapi_args, - # Workaround for issues with Gradio Network check timeouts (edge-case, but no other workound) - _frontend=not os.environ.get('SD_DISABLE_GRADIO_FRONTEND_CHECK', None), + _frontend=not cmd_opts.share, ) if cmd_opts.data_dir is not None: ui_tempdir.register_tmp_file(shared.demo, os.path.join(cmd_opts.data_dir, 'x'))