diff --git a/modules/control/run.py b/modules/control/run.py index 33c9b5293..d8322a5c7 100644 --- a/modules/control/run.py +++ b/modules/control/run.py @@ -86,6 +86,7 @@ def control_run(units: List[unit.Unit], inputs, inits, mask, unit_type: str, is_ batch_size = batch_size, inpaint_full_res = masking.opts.mask_only, inpaint_full_res_padding = masking.opts.mask_padding, + inpainting_mask_invert = 1 if masking.opts.invert else 0, inpainting_fill = 1, hdr_mode=hdr_mode, hdr_brightness=hdr_brightness, hdr_color=hdr_color, hdr_sharpen=hdr_sharpen, hdr_clamp=hdr_clamp, hdr_boundary=hdr_boundary, hdr_threshold=hdr_threshold, hdr_maximize=hdr_maximize, hdr_max_center=hdr_max_center, hdr_max_boundry=hdr_max_boundry, @@ -340,7 +341,6 @@ def control_run(units: List[unit.Unit], inputs, inits, mask, unit_type: str, is_ debug(f'Control: input image={input_image}') processed_images = [] - masked_image = masking.run_mask(input_image=input_image, input_mask=mask, return_type='Masked') if mask is not None else input_image if mask is not None: p.extra_generation_params["Mask only"] = masking.opts.mask_only if masking.opts.mask_only else None p.extra_generation_params["Mask auto"] = masking.opts.auto_mask if masking.opts.auto_mask != 'None' else None @@ -349,6 +349,8 @@ def control_run(units: List[unit.Unit], inputs, inits, mask, unit_type: str, is_ p.extra_generation_params["Mask erode"] = masking.opts.mask_erode if masking.opts.mask_erode > 0 else None p.extra_generation_params["Mask dilate"] = masking.opts.mask_dilate if masking.opts.mask_dilate > 0 else None p.extra_generation_params["Mask model"] = masking.opts.model if masking.opts.model is not None else None + if len(active_process) > 0: + masked_image = masking.run_mask(input_image=input_image, input_mask=mask, return_type='Masked', invert=p.inpainting_mask_invert==1) if mask is not None else input_image for i, process in enumerate(active_process): # list[image] image_mode = 'L' if unit_type == 't2i adapter' and len(active_model) > i and ('Canny' in active_model[i].model_id or 'Sketch' in active_model[i].model_id) else 'RGB' # t2iadapter canny and sketch work in grayscale only debug(f'Control: i={i+1} process="{process.processor_id}" input={masked_image} override={process.override}') @@ -468,7 +470,7 @@ def control_run(units: List[unit.Unit], inputs, inits, mask, unit_type: str, is_ if pipe is not None: # run new pipeline debug(f'Control exec pipeline: task={sd_models.get_diffusers_task(pipe)} class={pipe.__class__}') debug(f'Control exec pipeline: p={vars(p)}') - debug(f'Control exec pipeline: args={p.task_args} image={p.task_args.get("image", None)} control={p.task_args.get("control_image", None)} mask={p.task_args.get("mask_image", None)} ref={p.task_args.get("ref_image", None)}') + debug(f'Control exec pipeline: args={p.task_args} image={p.task_args.get("image", None)} control={p.task_args.get("control_image", None)} mask={p.task_args.get("mask_image", None) or p.image_mask} ref={p.task_args.get("ref_image", None)}') p.scripts = scripts.scripts_control p.script_args = input_script_args processed = p.scripts.run(p, *input_script_args) diff --git a/modules/masking.py b/modules/masking.py index a15f881ed..c31f3d27d 100644 --- a/modules/masking.py +++ b/modules/masking.py @@ -1,6 +1,7 @@ from types import SimpleNamespace from typing import List import os +import sys import time import gradio as gr import numpy as np @@ -315,7 +316,9 @@ def get_mask(input_image: gr.Image, input_mask: gr.Image): return output_mask -def run_mask(input_image: gr.Image, input_mask: gr.Image = None, return_type: str = None, mask_blur: int = None, mask_padding: int = None, segment_enable=True, invert=False): +def run_mask(input_image: gr.Image, input_mask: gr.Image = None, return_type: str = None, mask_blur: int = None, mask_padding: int = None, segment_enable=True, invert=None): + debug(f'Run mask: function={sys._getframe(1).f_code.co_name}') # pylint: disable=protected-access + if input_image is None: return input_mask if isinstance(input_image, list): @@ -331,6 +334,8 @@ def run_mask(input_image: gr.Image, input_mask: gr.Image = None, return_type: st if input_mask is None: return None + if invert is not None: + opts.invert = invert if mask_blur is not None: # compatibility with old img2img values which have different range opts.mask_blur = mask_blur / min(input_image.width, input_image.height) if mask_padding is not None: @@ -373,7 +378,7 @@ def run_mask(input_image: gr.Image, input_mask: gr.Image = None, return_type: st debug(f'Segment blur={opts.mask_blur} x={sigmax} y={sigmay} mask={mask.shape}') except Exception as e: shared.log.error(f'Segment blur: {e}') - if invert or opts.invert: + if opts.invert: mask = np.invert(mask) mask_size = np.count_nonzero(mask) diff --git a/modules/processing.py b/modules/processing.py index 11aebf361..51d97333c 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -349,7 +349,8 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: images.save_image(image_without_cc, path=p.outpath_samples, basename="", seed=p.seeds[i], prompt=p.prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-color-correct") p.ops.append('color') image = apply_color_correction(p.color_corrections[i], image) - image = apply_overlay(image, p.paste_to, i, p.overlay_images) + if shared.opts.mask_apply_overlay: + image = apply_overlay(image, p.paste_to, i, p.overlay_images) text = infotext(i) infotexts.append(text) image.info["parameters"] = text diff --git a/modules/processing_class.py b/modules/processing_class.py index de5357048..22fc27a79 100644 --- a/modules/processing_class.py +++ b/modules/processing_class.py @@ -309,9 +309,10 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): if self.sampler_name == "PLMS": self.sampler_name = 'UniPC' - self.sampler = sd_samplers.create_sampler(self.sampler_name, self.sd_model) - if hasattr(self.sampler, "initialize"): - self.sampler.initialize(self) + if shared.backend == shared.Backend.ORIGINAL: + self.sampler = sd_samplers.create_sampler(self.sampler_name, self.sd_model) + if hasattr(self.sampler, "initialize"): + self.sampler.initialize(self) if self.image_mask is not None: self.ops.append('inpaint') @@ -332,7 +333,8 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): np_mask = cv2.GaussianBlur(np_mask, (kernel_size, 1), self.mask_blur) np_mask = cv2.GaussianBlur(np_mask, (1, kernel_size), self.mask_blur) self.image_mask = Image.fromarray(np_mask) - else: + """ + else: # handled in processing_diffusers if hasattr(self, 'init_images'): self.image_mask = masking.run_mask( input_image=self.init_images, @@ -341,8 +343,9 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): mask_blur=self.mask_blur, mask_padding=self.inpaint_full_res_padding, segment_enable=False, - invert=self.inpainting_mask_invert, + invert=self.inpainting_mask_invert==1, ) + """ if self.inpaint_full_res: # mask only inpaint self.mask_for_overlay = self.image_mask mask = self.image_mask.convert('L') @@ -386,16 +389,14 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): image = images.resize_image(self.resize_mode, image, self.width, self.height, self.resize_name) self.width = image.width self.height = image.height - if self.image_mask is not None: - try: - 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: - shared.log.error(f"Failed to apply mask to image: {e}") + if self.image_mask is not None and shared.opts.mask_apply_overlay: + 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) + image_masked = image_masked.convert('RGBA') + self.overlay_images.append(image_masked) if crop_region is not None: # crop_region is not None if we are doing inpaint full res image = image.crop(crop_region) if image.width != self.width or image.height != self.height: diff --git a/modules/processing_diffusers.py b/modules/processing_diffusers.py index 43dfd2813..b7ab23a2a 100644 --- a/modules/processing_diffusers.py +++ b/modules/processing_diffusers.py @@ -114,11 +114,9 @@ def process_diffusers(p: processing.StableDiffusionProcessing): elif (sd_models.get_diffusers_task(model) == sd_models.DiffusersTaskType.INPAINTING or is_img2img_model) and len(getattr(p, 'init_images' ,[])) > 0: p.ops.append('inpaint') if p.task_args.get('mask_image', None) is not None: # provided as override by a module - p.mask = masking.run_mask(input_image=p.init_images, input_mask=p.task_args['mask_image'], return_type='Grayscale') - if p.task_args.get('mask_image', None) is not None: # provided as override by a module - p.mask = masking.run_mask(input_image=p.init_images, input_mask=p.task_args['mask_image'], return_type='Grayscale') + p.mask = masking.run_mask(input_image=p.init_images, input_mask=p.task_args['mask_image'], return_type='Grayscale', invert=p.inpainting_mask_invert==1) elif getattr(p, 'image_mask', None) is not None: # standard - p.mask = masking.run_mask(input_image=p.init_images, input_mask=p.image_mask, return_type='Grayscale') + p.mask = masking.run_mask(input_image=p.init_images, input_mask=p.image_mask, return_type='Grayscale', invert=p.inpainting_mask_invert==1) elif getattr(p, 'mask', None) is not None: # backward compatibility pass else: # fallback diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py index cf80d346c..20e58fbb9 100644 --- a/modules/sd_samplers.py +++ b/modules/sd_samplers.py @@ -11,7 +11,6 @@ samplers = all_samplers samplers_for_img2img = all_samplers samplers_map = {} loaded_config = None -loaded_sampler = None def list_samplers(backend_name = shared.backend): @@ -47,7 +46,6 @@ def visible_sampler_names(): def create_sampler(name, model): - global loaded_config, loaded_sampler # pylint: disable=global-statement if name == 'Default' and hasattr(model, 'scheduler'): config = {k: v for k, v in model.scheduler.config.items() if not k.startswith('_')} shared.log.debug(f'Sampler default {type(model.scheduler).__name__}: {config}') @@ -56,26 +54,22 @@ def create_sampler(name, model): if config is None: shared.log.error(f'Attempting to use unknown sampler: {name}') config = all_samplers[0] - sampler = loaded_sampler if shared.backend == shared.Backend.ORIGINAL: - if config != loaded_config: - sampler = config.constructor(model) - sampler.config = config - sampler.name = name - loaded_config = config - shared.log.debug(f'Sampler: sampler="{name}" config={config.options}') + sampler = config.constructor(model) + sampler.config = config + sampler.name = name sampler.initialize(p=None) - loaded_sampler = sampler + shared.log.debug(f'Sampler: sampler="{name}" config={config.options}') + return sampler elif shared.backend == shared.Backend.DIFFUSERS: - if config != loaded_config: - sampler = config.constructor(model) - loaded_config = config - if not hasattr(model, 'scheduler_config'): - model.scheduler_config = sampler.sampler.config.copy() - shared.log.debug(f'Sampler: sampler="{sampler.name}" config={sampler.config}') - loaded_sampler = sampler.sampler - model.scheduler = loaded_sampler - return loaded_sampler + sampler = config.constructor(model) + if not hasattr(model, 'scheduler_config'): + model.scheduler_config = sampler.sampler.config.copy() + model.scheduler = sampler.sampler + shared.log.debug(f'Sampler: sampler="{sampler.name}" config={sampler.config}') + return sampler.sampler + else: + return None def set_samplers(): diff --git a/modules/shared.py b/modules/shared.py index cdd515220..866bd0b95 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -631,6 +631,7 @@ options_templates.update(options_section(('postprocessing', "Postprocessing"), { "postprocessing_sep_img2img": OptionInfo("