mirror of
https://github.com/vladmandic/automatic
synced 2026-09-19 09:14:35 +02:00
fix masking
This commit is contained in:
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -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
@@ -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():
|
||||
|
||||
@@ -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}),
|
||||
|
||||
@@ -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
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user