fix masking

This commit is contained in:
Vladimir Mandic
2024-02-04 21:15:12 -05:00
parent c463edb0c1
commit e2e6743fbc
9 changed files with 65 additions and 61 deletions
+4 -2
View File
@@ -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)
+7 -2
View File
@@ -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)
+2 -1
View File
@@ -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
+16 -15
View File
@@ -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:
+2 -4
View File
@@ -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
+13 -19
View File
@@ -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():
+1
View File
@@ -631,6 +631,7 @@ options_templates.update(options_section(('postprocessing', "Postprocessing"), {
"postprocessing_sep_img2img": OptionInfo("<h2>Img2Img & Inpainting</h2>", "", gr.HTML),
"img2img_color_correction": OptionInfo(False, "Apply color correction"),
"mask_apply_overlay": OptionInfo(True, "Apply mask as overlay"),
"img2img_fix_steps": OptionInfo(False, "For image processing do exact number of steps as specified", gr.Checkbox, { "visible": False }),
"img2img_background_color": OptionInfo("#ffffff", "Image transparent color fill", gr.ColorPicker, {}),
"inpainting_mask_weight": OptionInfo(1.0, "Inpainting conditioning mask strength", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
+2 -1
View File
@@ -127,7 +127,8 @@ def select_input(input_mode, input_image, init_image, init_type, input_resize, i
status = f'Control input | Image | Size {selected_input.width}x{selected_input.height} | Mode {selected_input.mode}'
res = [gr.Tabs.update(selected='out-gallery'), status]
elif isinstance(selected_input, dict): # inpaint -> dict image+mask
input_mask = masking.run_mask(input_image=selected_input['image'], input_mask=selected_input['mask'], return_type='Grayscale')
# input_mask = masking.run_mask(input_image=selected_input['image'], input_mask=selected_input['mask'], return_type='Grayscale')
input_mask = selected_input['mask']
selected_input = selected_input['image']
input_source = [selected_input]
input_type = 'PIL.Image'
+18 -17
View File
@@ -1,5 +1,22 @@
[tool.ruff]
target-version = "py39"
exclude = [
"/usr/lib",
"extensions",
"extensions-builtin",
"modules/lora",
"modules/dml",
"modules/k-diffusion",
"repositories/ldm",
"repositories/taming",
"repositories/blip",
"repositories/codeformer",
"modules/control/proc/normalbae/nets/submodules/efficientnet_repo/geffnet",
"modules/control/units/*_model.py",
"modules/control/units/*_pipe.py",
"modules/pipelines/*.py",
]
[tool.ruff.lint]
select = [
"F",
"E",
@@ -28,22 +45,6 @@ select = [
# "S",
# "PL",
]
exclude = [
"/usr/lib",
"extensions",
"extensions-builtin",
"modules/lora",
"modules/dml",
"modules/k-diffusion",
"repositories/ldm",
"repositories/taming",
"repositories/blip",
"repositories/codeformer",
"modules/control/proc/normalbae/nets/submodules/efficientnet_repo/geffnet",
"modules/control/units/*_model.py",
"modules/control/units/*_pipe.py",
"modules/pipelines/*.py",
]
ignore = [
"A003", # Class attirbute shadowing builtin
"C901", # Function is too complex
@@ -66,7 +67,7 @@ ignore = [
"TID252", # Relative imports from parent modules
]
[tool.ruff.flake8-bugbear]
[tool.ruff.lint.flake8-bugbear]
extend-immutable-calls = ["fastapi.Depends", "fastapi.security.HTTPBasic"]
[tool.pytest.ini_options]