1
0
mirror of https://github.com/vladmandic/sdnext.git synced 2026-01-27 15:02:48 +03:00
Files
sdnext/scripts/automatic_color_inpaint.py
Vladimir Mandic e8b5ea3847 major refactor: remove backend original
Signed-off-by: Vladimir Mandic <mandic00@live.com>
2025-07-05 13:16:46 -04:00

135 lines
4.7 KiB
Python

import gradio as gr
from PIL import Image
import numpy as np
from modules import shared, scripts_manager, processing, masking
"""
Automatic Color Inpaint Script for SD.NEXT - SD & SDXL Support
Author: Artheriax
Credits: SD.NEXT team for script template
Version: v1
Contributions: A new script to automatically inpaint colors in images using Stable Diffusion, Stable Diffusion XL or Flux.
"""
## Config
# script title
supported_models = ['sd','sdxl', 'flux']
title = 'Automatic Color Inpaint'
# is script available in txt2img tab
txt2img = False
# is script available in img2img tab
img2img = True
### Script definition
class Script(scripts_manager.Script):
def title(self):
return title
def show(self, is_img2img):
return img2img if is_img2img else txt2img
# Define UI for pipeline
def ui(self, _is_img2img):
with gr.Row():
gr.HTML("&nbsp ACI: Automatic Color Inpaint<br>")
with gr.Row():
color_picker = gr.ColorPicker(
label="ACI: Color to Mask",
value="#04F404", # Default to green screen green
# info="Pick the color you want to mask and inpaint."
)
tolerance_slider = gr.Slider(
minimum=0,
maximum=100,
step=1,
value=65,
label="ACI: Color tolerance",
)
denoising_slider = gr.Slider(
minimum=0.01,
maximum=1,
step=0.01,
value=0.9,
label="ACI: Denoising strength",
)
with gr.Row():
dilate_slider = gr.Slider(
minimum=0,
maximum=1,
step=0.01,
value=0.0,
label="ACI: Mask dilate",
# info="(Recommended value = 2 to remove leftovers at edges)"
)
erode_slider = gr.Slider(
minimum=0,
maximum=1,
step=0.01,
value=0,
label="ACI: Mask erode",
# info="(Recommended value = 0 for sharpness)"
)
blur_slider = gr.Slider(
minimum=0,
maximum=1,
step=0.01,
value=0.15,
label="ACI: Mask blur",
# info="(Recommended value = 0 for sharpness)"
)
return [color_picker, tolerance_slider, dilate_slider, erode_slider, blur_slider, denoising_slider]
# Run pipeline
def run(self, p: processing.StableDiffusionProcessing, *args): # pylint: disable=arguments-differ
if shared.sd_model_type not in supported_models:
shared.log.warning(f'MoD: class={shared.sd_model.__class__.__name__} model={shared.sd_model_type} required={supported_models}')
return None
if not hasattr(p, 'init_images') or p.init_images is None or len(p.init_images) == 0:
return None
color_to_mask_hex, mask_tolerance, mask_dilate, mask_erode, mask_blur, inpaint_denoising_strength = args
# Convert hex color to RGB tuple (0-255)
color_to_mask_rgb = tuple(int(color_to_mask_hex[i:i+2], 16) for i in (1, 3, 5))
shared.log.debug(f'ACI: rgb={color_to_mask_rgb} tolerance={mask_tolerance} dilate={mask_dilate} erode={mask_erode} blur={mask_blur} denoise={inpaint_denoising_strength}')
# Create Color Mask using vectorized operations
init_image = p.init_images[0].convert("RGB")
image_np = np.array(init_image)
# Calculate Euclidean distance for all pixels at once
diff = np.linalg.norm(image_np.astype(np.int16) - np.array(color_to_mask_rgb, dtype=np.int16), axis=2)
calc_tolerance = (diff.max() - diff.min()) * mask_tolerance/100
mask_np = (diff <= calc_tolerance).astype(np.uint8) * 255
mask_image = Image.fromarray(mask_np).convert("L")
# If an inpaint mask is already provided from the UI, combine it with the color mask
if p.image_mask:
combined_mask = Image.composite(
Image.new("L", mask_image.size, "white"),
p.image_mask.convert("L"),
mask_image
)
p.image_mask = combined_mask
else:
p.image_mask = mask_image
# override inpaint parameters
p.inpaint_full_res = False # always use full res
p.denoising_strength = inpaint_denoising_strength
p.mask_blur = None # do not use legacy mask blur
p.inpaint_full_res_padding = None # do not use legacy mask blur
masking.opts.mask_blur = mask_blur # new masking params triggers masking.py:run_mask
masking.opts.mask_erode = mask_erode
masking.opts.mask_dilate = mask_dilate
return None