diff --git a/CHANGELOG.md b/CHANGELOG.md
index 796cedf97..6a9023fa1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,7 @@
- [CircleStone Anima 1.0](https://huggingface.co/circlestone-labs/Anima) in *Base* and *Turbo* (distilled) variants
in both original precision and SDNQ-4bit quantiztion
- **Features**
- - **SDNQ** additional quantization algorithm: *Hadamard Rotations*
+ - **SDNQ** new quantization algorithm: *Hadamard Rotations*
much higher quality than base SDNQ, but runs slightly slower
still faster than SVD and can be combined together with SVD for combined benefits
- **Metadata**
@@ -15,6 +15,8 @@
- **Captioning** new feature: analyze existing images for prompt adherence
*tip*: image analysis requires larger VLM model to produce quality output
new api endpoint: `/sdapi/v1/analyze`
+ - **Masking** updated interface and capabilities
+ you can now also select mask type instead of focing alpha mask with all models
- **HF download** use `XET` by default
see *settings -> huggingface -> download method* for options
- **Nunchaku** consider DEV builds when auto-installing
@@ -46,6 +48,7 @@
- improve `settings` search
- `nunchaku` z-image loader
- `ui` server log monitor
+ - `kanvas` enable toolbar on *send-to* action
## Update for 2026-05-13
diff --git a/extensions-builtin/sdnext-kanvas b/extensions-builtin/sdnext-kanvas
index 4fa1f5e38..343d75019 160000
--- a/extensions-builtin/sdnext-kanvas
+++ b/extensions-builtin/sdnext-kanvas
@@ -1 +1 @@
-Subproject commit 4fa1f5e38da5d292d3c42570d2d0bbf524fa3afe
+Subproject commit 343d75019c548701f2da5b523010e7d5193cec34
diff --git a/html/locale_ar.json b/html/locale_ar.json
index b1c1d3cb5..9e6cea5fe 100644
--- a/html/locale_ar.json
+++ b/html/locale_ar.json
@@ -4957,7 +4957,7 @@
},
{
"id": 33,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "التلوين للمناطق المقنعة فقط",
"reload": "",
"hint": ""
diff --git a/html/locale_bn.json b/html/locale_bn.json
index 252670494..523d24265 100644
--- a/html/locale_bn.json
+++ b/html/locale_bn.json
@@ -4957,7 +4957,7 @@
},
{
"id": 33,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "শুধুমাত্র মাস্কড অংশ ইনপেইন্ট করুন",
"reload": "",
"hint": ""
diff --git a/html/locale_de.json b/html/locale_de.json
index 9fa94e636..fcbf19095 100644
--- a/html/locale_de.json
+++ b/html/locale_de.json
@@ -4957,7 +4957,7 @@
},
{
"id": 34,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "Nur maskiertes Inpaint",
"reload": "",
"hint": "Nur maskiertes Inpaint"
diff --git a/html/locale_en.json b/html/locale_en.json
index e31678ded..5cefb34ec 100644
--- a/html/locale_en.json
+++ b/html/locale_en.json
@@ -730,7 +730,7 @@
{"id":"","label":"Include images","localized":"","hint":"","ui":"script_xyz_grid_script"},
{"id":"","label":"invert","localized":"","hint":"","ui":"img2img"},
{"id":"","label":"Init image same as control","localized":"","hint":"Will additionally treat any image placed into the Control input window as a source for img2img type tasks, an image to modify for example.","ui":"control"},
- {"id":"","label":"Inpaint masked only","localized":"","hint":"Crop the masked region, denoise it at full resolution, then paste the result back into the original image.
Best for small detail edits where you want maximum quality on the masked area without spending compute denoising the rest of the image. Detail in unmasked regions stays untouched.
Tradeoff: the model only sees the cropped region, so it loses global context. The inpainted content may not match the surrounding scene's lighting, perspective, or style, and visible seams can appear at the crop boundary. Mitigate with Dilate + Blur on the mask, or disable this option to denoise the full image together.
When off, the whole image is denoised at the generation resolution and the unmasked area is restored from the original via the mask blend, which preserves global coherence at the cost of detail in the masked region.
Default off.","ui":"control"},
+ {"id":"","label":"Focus mask","localized":"","hint":"Crop the masked region, denoise it at full resolution, then paste the result back into the original image.
Best for small detail edits where you want maximum quality on the masked area without spending compute denoising the rest of the image. Detail in unmasked regions stays untouched.
Tradeoff: the model only sees the cropped region, so it loses global context. The inpainted content may not match the surrounding scene's lighting, perspective, or style, and visible seams can appear at the crop boundary. Mitigate with Dilate + Blur on the mask, or disable this option to denoise the full image together.
When off, the whole image is denoised at the generation resolution and the unmasked area is restored from the original via the mask blend, which preserves global coherence at the cost of detail in the masked region.
Default off.","ui":"control"},
{"id":"","label":"Invert mask","localized":"","hint":"Swaps which area is treated as masked.
Useful when you have painted the region to preserve instead of the region to modify: enable this to flip the interpretation without redoing the mask.
Default off.","ui":"control"},
{"id":"","label":"IOU","localized":"","hint":"","ui":"control"},
{"id":"","label":"Init strength","localized":"","hint":"","ui":"video"},
diff --git a/html/locale_es.json b/html/locale_es.json
index 94d82fbe0..c2993e42d 100644
--- a/html/locale_es.json
+++ b/html/locale_es.json
@@ -4957,7 +4957,7 @@
},
{
"id": 33,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "Inpaint solo en máscara",
"reload": "",
"hint": ""
diff --git a/html/locale_fr.json b/html/locale_fr.json
index 5b2c37935..d1aa13ad5 100644
--- a/html/locale_fr.json
+++ b/html/locale_fr.json
@@ -4957,7 +4957,7 @@
},
{
"id": 33,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "Inpaint uniquement masqué",
"reload": "",
"hint": ""
diff --git a/html/locale_he.json b/html/locale_he.json
index 27a37076b..a1a2b25e0 100644
--- a/html/locale_he.json
+++ b/html/locale_he.json
@@ -4957,7 +4957,7 @@
},
{
"id": 33,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "מילוי (Inpaint) של האזור המסומן בלבד",
"reload": "",
"hint": ""
diff --git a/html/locale_hi.json b/html/locale_hi.json
index 8aa989b29..3687cdcac 100644
--- a/html/locale_hi.json
+++ b/html/locale_hi.json
@@ -4957,7 +4957,7 @@
},
{
"id": 33,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "केवल मास्क किया गया इनपेंट करें",
"reload": "n/a",
"hint": ""
diff --git a/html/locale_hr.json b/html/locale_hr.json
index efa35485f..1c570722e 100644
--- a/html/locale_hr.json
+++ b/html/locale_hr.json
@@ -4964,7 +4964,7 @@
},
{
"id": 0,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "Inpaintaj samo maskirano",
"reload": "",
"hint": ""
diff --git a/html/locale_id.json b/html/locale_id.json
index 7450256f8..b826015de 100644
--- a/html/locale_id.json
+++ b/html/locale_id.json
@@ -4957,7 +4957,7 @@
},
{
"id": 33,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "Inpaint hanya bagian yang ditopeng",
"reload": "n/a",
"hint": ""
diff --git a/html/locale_it.json b/html/locale_it.json
index 034654b91..e1470704f 100644
--- a/html/locale_it.json
+++ b/html/locale_it.json
@@ -4957,7 +4957,7 @@
},
{
"id": 34,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "Inpaint solo maschera",
"reload": "n/a",
"hint": "Esegui il processo di inpainting solo sull'area mascherata"
diff --git a/html/locale_ja.json b/html/locale_ja.json
index e30dba4f0..bba85dd39 100644
--- a/html/locale_ja.json
+++ b/html/locale_ja.json
@@ -4957,9 +4957,9 @@
},
{
"id": 33,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "マスクされた部分のみインペイント",
- "reload": "Inpaint masked only",
+ "reload": "Focus mask",
"hint": ""
},
{
diff --git a/html/locale_ko.json b/html/locale_ko.json
index a5918ee95..b870a2078 100644
--- a/html/locale_ko.json
+++ b/html/locale_ko.json
@@ -4957,7 +4957,7 @@
},
{
"id": 33,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "마스크된 영역만 인페인트",
"reload": "n/a",
"hint": ""
diff --git a/html/locale_nb.json b/html/locale_nb.json
index 89e39b0f7..5ddb2a2aa 100644
--- a/html/locale_nb.json
+++ b/html/locale_nb.json
@@ -4957,8 +4957,8 @@
},
{
"id": 33,
- "label": "Inpaint masked only",
- "localized": "Inpaint masked only",
+ "label": "Focus mask",
+ "localized": "Focus mask",
"reload": "",
"hint": "Only change the area you have painted over."
},
diff --git a/html/locale_po.json b/html/locale_po.json
index b391db6af..dc1475477 100644
--- a/html/locale_po.json
+++ b/html/locale_po.json
@@ -4957,7 +4957,7 @@
},
{
"id": 33,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "Inpaint tylko zamaskowanego",
"reload": "",
"hint": "Wypełniaj tylko zamaskowany obszar"
diff --git a/html/locale_pt.json b/html/locale_pt.json
index 065887bf9..ab04d0b89 100644
--- a/html/locale_pt.json
+++ b/html/locale_pt.json
@@ -4957,7 +4957,7 @@
},
{
"id": 33,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "Inpaint apenas na máscara",
"reload": "",
"hint": ""
diff --git a/html/locale_qq.json b/html/locale_qq.json
index 1d351b192..bfe83278a 100644
--- a/html/locale_qq.json
+++ b/html/locale_qq.json
@@ -4957,7 +4957,7 @@
},
{
"id": 0,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "Inpaint solum obductum",
"reload": "n/a",
"hint": ""
diff --git a/html/locale_ru.json b/html/locale_ru.json
index 318e7754f..8f8c14bcf 100644
--- a/html/locale_ru.json
+++ b/html/locale_ru.json
@@ -4957,7 +4957,7 @@
},
{
"id": 34,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "Закрашивать только маску",
"reload": "",
"hint": "Ограничить Inpaint только замаскированной областью"
diff --git a/html/locale_sr.json b/html/locale_sr.json
index 2cbccaada..40eee9573 100644
--- a/html/locale_sr.json
+++ b/html/locale_sr.json
@@ -4957,7 +4957,7 @@
},
{
"id": 34,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "Inpaint samo maskiranog",
"reload": "",
"hint": ""
diff --git a/html/locale_tb.json b/html/locale_tb.json
index bb09b9850..ff2c6a38d 100644
--- a/html/locale_tb.json
+++ b/html/locale_tb.json
@@ -4957,7 +4957,7 @@
},
{
"id": 33,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "Isolated Patching",
"reload": "",
"hint": "Restrict processing vectors strictly to masked coordinate zones"
diff --git a/html/locale_tlh.json b/html/locale_tlh.json
index a0447e4c8..56a7e67ad 100644
--- a/html/locale_tlh.json
+++ b/html/locale_tlh.json
@@ -4957,7 +4957,7 @@
},
{
"id": 34,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "Inpaint masked",
"reload": "",
"hint": "Inpaint masked"
diff --git a/html/locale_tr.json b/html/locale_tr.json
index 2a0ddfd06..07d57b6f7 100644
--- a/html/locale_tr.json
+++ b/html/locale_tr.json
@@ -4957,7 +4957,7 @@
},
{
"id": 34,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "Sadece maskeli alanı inpaint et",
"reload": "",
"hint": ""
diff --git a/html/locale_ur.json b/html/locale_ur.json
index 4edfe260a..4bcb202ff 100644
--- a/html/locale_ur.json
+++ b/html/locale_ur.json
@@ -4957,7 +4957,7 @@
},
{
"id": 33,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "صرف ماسک شدہ حصہ ان پینٹ کریں",
"reload": "",
"hint": ""
diff --git a/html/locale_vi.json b/html/locale_vi.json
index 06187acbd..94087501b 100644
--- a/html/locale_vi.json
+++ b/html/locale_vi.json
@@ -4957,7 +4957,7 @@
},
{
"id": 33,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "Chỉ inpaint vùng được che",
"reload": "",
"hint": "Chỉ thực hiện vẽ lại trên vùng mặt nạ được chỉ định"
diff --git a/html/locale_xx.json b/html/locale_xx.json
index 79d813296..2e14fdc8d 100644
--- a/html/locale_xx.json
+++ b/html/locale_xx.json
@@ -4957,7 +4957,7 @@
},
{
"id": 33,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "Inpaint nur maskitan",
"reload": "",
"hint": "Inpaint nur maskitan"
diff --git a/html/locale_zh.json b/html/locale_zh.json
index 700de6f4e..4f143809f 100644
--- a/html/locale_zh.json
+++ b/html/locale_zh.json
@@ -4957,7 +4957,7 @@
},
{
"id": 33,
- "label": "Inpaint masked only",
+ "label": "Focus mask",
"localized": "仅重绘蒙版区域",
"reload": "",
"hint": ""
diff --git a/modules/control/processor.py b/modules/control/processor.py
index 56fa66f66..4395c4173 100644
--- a/modules/control/processor.py
+++ b/modules/control/processor.py
@@ -110,7 +110,7 @@ def preprocess_image(
if input_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
- p.extra_generation_params["Mask invert"] = masking.opts.invert if masking.opts.invert else None
+ p.extra_generation_params["Mask invert"] = masking.opts.mask_invert if masking.opts.mask_invert else None
p.extra_generation_params["Mask blur"] = masking.opts.mask_blur if masking.opts.mask_blur > 0 else None
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
diff --git a/modules/control/run.py b/modules/control/run.py
index 6b3ac43c4..665afedba 100644
--- a/modules/control/run.py
+++ b/modules/control/run.py
@@ -382,8 +382,6 @@ def control_run(state: str = '', # pylint: disable=keyword-arg-before-vararg
save_images_before_highres_fix: bool | None = None, save_images_before_refiner: bool | None = None,
save_images_before_detailer: bool | None = None, save_images_before_color_correction: bool | None = None,
grid_save: bool | None = None, grid_format: str | None = None, return_grid: bool | None = None,
- save_mask: bool | None = None, save_mask_composite: bool | None = None,
- return_mask: bool | None = None, return_mask_composite: bool | None = None,
keep_incomplete: bool | None = None, image_metadata: bool | None = None, jpeg_quality: int | None = None,
# scheduler/noise overrides
schedulers_prediction_type: str | None = None, schedulers_beta_schedule: str | None = None, schedulers_timesteps: str | None = None,
@@ -545,12 +543,10 @@ def control_run(state: str = '', # pylint: disable=keyword-arg-before-vararg
save_images_before_highres_fix=save_images_before_highres_fix, save_images_before_refiner=save_images_before_refiner,
save_images_before_detailer=save_images_before_detailer, save_images_before_color_correction=save_images_before_color_correction,
grid_save=grid_save, grid_format=grid_format, return_grid=return_grid,
- save_mask=save_mask, save_mask_composite=save_mask_composite,
- return_mask=return_mask, return_mask_composite=return_mask_composite,
keep_incomplete=keep_incomplete, image_metadata=image_metadata, jpeg_quality=jpeg_quality,
# inpaint
inpaint_full_res = masking.opts.mask_only,
- inpainting_mask_invert = 1 if masking.opts.invert else 0,
+ inpainting_mask_invert = 1 if masking.opts.mask_invert else 0,
# hdr
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_boundary=hdr_max_boundary, hdr_color_picker=hdr_color_picker, hdr_tint_ratio=hdr_tint_ratio, hdr_apply_hires=hdr_apply_hires,
diff --git a/modules/masking.py b/modules/masking.py
index 0accc7eef..ca8db1040 100644
--- a/modules/masking.py
+++ b/modules/masking.py
@@ -142,7 +142,7 @@ MODELS = {
'Rembg ISNet anime': 'isnet-anime',
}
COLORMAP = ['autumn', 'bone', 'jet', 'winter', 'rainbow', 'ocean', 'summer', 'spring', 'cool', 'hsv', 'pink', 'hot', 'parula', 'magma', 'inferno', 'plasma', 'viridis', 'cividis', 'twilight', 'shifted', 'turbo', 'deepgreen']
-TYPES = ['None', 'Opaque', 'Binary', 'Masked', 'Grayscale', 'Color', 'Composite']
+TYPES = ['Exact', 'Opaque', 'Binary', 'Masked', 'Grayscale', 'Color', 'Composite']
cache_dir = 'models/control/segment'
generator: MaskGenerationPipeline = None
busy = False
@@ -151,13 +151,16 @@ btn_lama = None
lama_model = None
controls = []
opts = SimpleNamespace(**{
- 'model': None,
- 'auto_mask': 'None',
- 'auto_segment': 'None',
- 'mask_only': False,
'mask_blur': 0,
'mask_erode': 0,
'mask_dilate': 0,
+ 'mask_only': False,
+ 'mask_invert': False,
+ 'mask_return': False,
+ 'mask_type': 'Grayscale',
+ 'model': None,
+ 'auto_mask': 'None',
+ 'auto_segment': 'None',
'seg_iou_thresh': 0.5,
'seg_score_thresh': 0.8,
'seg_nms_thresh': 0.5,
@@ -165,12 +168,10 @@ opts = SimpleNamespace(**{
'seg_points_per_batch': 64,
'seg_topK': 50,
'seg_colormap': 'pink',
- 'preview_type': 'Composite',
'seg_live': True,
'weight_original': 0.5,
'weight_mask': 0.5,
- 'kernel_iterations': 1,
- 'invert': False
+ 'kernel_iterations': 1
})
@@ -408,7 +409,7 @@ def run_mask(input_image: Image.Image, input_mask: Image.Image | None = None, re
except Exception:
return input_mask
if invert is not None:
- opts.invert = invert
+ opts.mask_invert = invert
# set legacy mask args
if mask_blur is not None or mask_padding is not None:
@@ -461,12 +462,12 @@ def run_mask(input_image: Image.Image, input_mask: Image.Image | None = None, re
debug(f'Mask blur={opts.mask_blur:.3f} x={sigmax} y={sigmay} mask={mask.shape}')
except Exception as e:
log.error(f'Mask blur: {e}')
- if opts.invert:
+ if opts.mask_invert:
mask = np.invert(mask)
- return_type = return_type or opts.preview_type
+ return_type = return_type or opts.mask_type
- if return_type == 'None':
+ if return_type == 'Exact':
return input_mask
elif return_type == 'Opaque':
binary_mask = cv2.threshold(mask, 0, 255, cv2.THRESH_BINARY)[1]
@@ -520,59 +521,41 @@ def run_lama(input_image: gr.Image, input_mask: gr.Image = None):
return result
-def run_mask_live(input_image: gr.Image):
- global busy # pylint: disable=global-statement
- if opts.seg_live:
- if not busy:
- busy = True
- res = run_mask(input_image)
- busy = False
- return res
- return None
-
-
def create_segment_ui():
def update_opts(*args):
- opts.seg_live = args[0]
- opts.mask_only = args[1]
- opts.invert = args[2]
- opts.mask_dilate = args[3]
- opts.mask_erode = args[4]
- opts.mask_blur = args[5]
- opts.seg_score_thresh = args[6]
- opts.auto_segment = args[7]
- opts.auto_mask = args[8]
- opts.seg_iou_thresh = args[9]
- opts.seg_nms_thresh = args[10]
- opts.preview_type = args[11]
- opts.seg_colormap = args[12]
+ # opts.seg_live
+ opts.mask_dilate, opts.mask_erode, opts.mask_blur, \
+ opts.mask_only, opts.mask_invert, opts.mask_return, \
+ opts.mask_type, opts.mask_colormap, \
+ opts.auto_segment, opts.auto_mask, opts.seg_score_thresh, opts.seg_iou_thresh, opts.seg_nms_thresh = args
+ debug(f'Update mask opts: {args}')
global btn_mask, btn_lama # pylint: disable=global-statement
with gr.Accordion(open=False, label="Mask", elem_id="control_mask", elem_classes=["small-accordion"]):
controls.clear()
- with gr.Row():
- controls.append(gr.Checkbox(label="Live update", value=False, visible=False, elem_id="control_mask_live_update"))
- with gr.Row():
- controls.append(gr.Checkbox(label="Inpaint masked only", value=False, elem_id="control_mask_only", ))
- controls.append(gr.Checkbox(label="Invert mask", value=False, elem_id="control_mask_invert"))
with gr.Row():
controls.append(gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Dilate', value=0, elem_id="control_mask_dilate"))
controls.append(gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Erode', value=0, elem_id="control_mask_erode"))
- with gr.Row():
controls.append(gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Blur', value=0, elem_id="control_mask_blur"))
- controls.append(gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Auto min score', value=0.8, elem_id="control_mask_score"))
with gr.Row():
- controls.append(gr.Dropdown(label="Auto-segment", choices=MODELS.keys(), value='None', elem_id="control_mask_segment"))
- controls.append(gr.Dropdown(label="Auto-mask", choices=['None', 'Threshold', 'Edge', 'Grayscale'], value='None', elem_id="control_mask_auto"))
+ controls.append(gr.Checkbox(label="Focus mask", value=False, elem_id="control_mask_only", ))
+ controls.append(gr.Checkbox(label="Invert mask", value=False, elem_id="control_mask_invert"))
+ controls.append(gr.Checkbox(label="Include mask", value=False, elem_id="control_mask_return"))
with gr.Row():
- controls.append(gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='IOU', value=0.5, visible=False, elem_id="control_mask_iou"))
- controls.append(gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='NMS', value=0.5, visible=False, elem_id="control_mask_nms"))
+ controls.append(gr.Dropdown(label="Mask type", choices=TYPES, value='Grayscale', elem_id="control_mask_preview"))
+ controls.append(gr.Dropdown(label="Mask Colormap", choices=COLORMAP, value='pink', elem_id="control_mask_colormap", visible=False))
+ with gr.Accordion(open=False, label="Auto masking",elem_classes=["small-accordion"]):
+ with gr.Row():
+ controls.append(gr.Dropdown(label="Auto-segment", choices=MODELS.keys(), value='None', elem_id="control_mask_segment"))
+ controls.append(gr.Dropdown(label="Auto-mask", choices=['None', 'Threshold', 'Edge', 'Grayscale'], value='None', elem_id="control_mask_auto"))
+ controls.append(gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Auto min score', value=0.8, elem_id="control_mask_score"))
+ with gr.Row():
+ btn_lama = gr.Button("LaMa remove", elem_id="control_mask_remove")
+ with gr.Row():
+ controls.append(gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='IOU', value=0.5, visible=False, elem_id="control_mask_iou"))
+ controls.append(gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='NMS', value=0.5, visible=False, elem_id="control_mask_nms"))
with gr.Row():
- controls.append(gr.Dropdown(label="Preview", choices=['None', 'Masked', 'Binary', 'Grayscale', 'Color', 'Composite'], value='Composite', elem_id="control_mask_preview"))
- controls.append(gr.Dropdown(label="Colormap", choices=COLORMAP, value='pink', elem_id="control_mask_colormap"))
- with gr.Row():
- btn_mask = gr.Button("Run Preview", elem_id="control_mask_refresh", )
- btn_lama = gr.Button("LaMa Remove", elem_id="control_mask_remove")
+ btn_mask = gr.Button("Run preview", elem_id="control_mask_refresh", )
for control in controls:
control.change(fn=update_opts, inputs=controls, outputs=[])
@@ -583,9 +566,6 @@ def bind_controls(image_controls: list[gr.Image], output_image: gr.Image):
for image_control in image_controls:
btn_mask.click(run_mask, inputs=[image_control], outputs=[output_image])
btn_lama.click(run_lama, inputs=[image_control], outputs=[output_image])
- image_control.edit(fn=run_mask_live, inputs=[image_control], outputs=[output_image])
- for control in controls:
- control.change(fn=run_mask_live, inputs=[image_control], outputs=[output_image])
def process_kanvas(kanvas_data):
diff --git a/modules/processing.py b/modules/processing.py
index 8c49c1b83..56d6da3f1 100644
--- a/modules/processing.py
+++ b/modules/processing.py
@@ -3,7 +3,7 @@ import json
import time
import numpy as np
from PIL import Image, ImageOps
-from modules import shared, devices, errors, images, scripts_manager, memstats, script_callbacks, extra_networks, detailer, sd_models, sd_checkpoint, sd_vae, processing_helpers, processing_grading, timer
+from modules import shared, devices, errors, images, scripts_manager, memstats, script_callbacks, extra_networks, detailer, sd_models, sd_checkpoint, sd_vae, processing_helpers, processing_grading, timer, masking
from modules.logger import log
from modules.sd_hijack_hypertile import context_hypertile_vae, context_hypertile_unet
from modules.processing_class import ( # pylint: disable=unused-import
@@ -373,28 +373,12 @@ def process_samples(p: StableDiffusionProcessing, samples):
if _overlay:
image = apply_overlay(image, p.paste_to, i, p.overlay_images)
- _save_mask = get_opt(p, 'save_mask')
- _save_mask_composite = get_opt(p, 'save_mask_composite')
- _return_mask = get_opt(p, 'return_mask')
- _return_mask_composite = get_opt(p, 'return_mask_composite')
- if hasattr(p, 'mask_for_overlay') and p.mask_for_overlay and any([_save_mask, _save_mask_composite, _return_mask, _return_mask_composite]):
- image_mask = p.mask_for_overlay.convert('RGB')
- image1 = image.convert('RGBA').convert('RGBa')
- image2 = Image.new('RGBa', image.size)
- mask = images.resize_image(3, p.mask_for_overlay, image.width, image.height).convert('L')
- image_mask_composite = Image.composite(image1, image2, mask).convert('RGBA')
- info = create_infotext(p, p.prompts, p.seeds, p.subseeds, index=i)
- _fmt = get_opt(p, 'samples_format')
- if _save_mask:
- images.save_image(image_mask, p.outpath_samples, "", p.seeds[i], p.prompts[i], _fmt, info=info, p=p, suffix="-mask")
- if _save_mask_composite:
- images.save_image(image_mask_composite, p.outpath_samples, "", p.seeds[i], p.prompts[i], _fmt, info=info, p=p, suffix="-mask-composite")
- if _return_mask:
- out_infotexts.append(info)
- out_images.append(image_mask)
- if _return_mask_composite:
- out_infotexts.append(info)
- out_images.append(image_mask_composite)
+ if masking.opts.mask_return and p.image_mask is not None:
+ out_infotexts.append(create_infotext(p, all_prompts=['Mask']))
+ if p.mask_for_overlay is not None:
+ out_images.append(p.mask_for_overlay.convert('RGB'))
+ else:
+ out_images.append(p.image_mask.convert('RGB'))
_inc_mask = getattr(p, 'include_mask', None)
if _inc_mask is None:
@@ -426,6 +410,7 @@ def process_samples(p: StableDiffusionProcessing, samples):
image.info["parameters"] = info
out_infotexts.append(info)
out_images.append(image)
+
shared.history.add(None, info=out_infotexts, ops=p.ops, images=out_images)
return out_images, out_infotexts
diff --git a/modules/processing_class.py b/modules/processing_class.py
index 093da2bdd..1c68b42e9 100644
--- a/modules/processing_class.py
+++ b/modules/processing_class.py
@@ -106,10 +106,6 @@ class StableDiffusionProcessing:
grid_save: bool | None = None,
grid_format: str | None = None,
return_grid: bool | None = None,
- save_mask: bool | None = None,
- save_mask_composite: bool | None = None,
- return_mask: bool | None = None,
- return_mask_composite: bool | None = None,
keep_incomplete: bool | None = None,
image_metadata: bool | None = None,
jpeg_quality: int | None = None,
@@ -390,10 +386,6 @@ class StableDiffusionProcessing:
self.grid_save = grid_save
self.grid_format = grid_format
self.return_grid = return_grid
- self.save_mask = save_mask
- self.save_mask_composite = save_mask_composite
- self.return_mask = return_mask
- self.return_mask_composite = return_mask_composite
self.keep_incomplete = keep_incomplete
self.image_metadata = image_metadata
self.jpeg_quality = jpeg_quality
@@ -778,9 +770,9 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
if type(self.image_mask) == list:
self.image_mask = self.image_mask[0]
if 'Control' in self.__class__.__name__:
- self.image_mask = masking.run_mask(input_image=self.init_images, input_mask=self.image_mask, return_type='Grayscale', invert=self.inpainting_mask_invert==1) # blur/padding are handled in masking module
+ self.image_mask = masking.run_mask(input_image=self.init_images, input_mask=self.image_mask, invert=self.inpainting_mask_invert==1) # blur/padding are handled in masking module
elif self.image_mask is not None:
- self.image_mask = masking.run_mask(input_image=self.init_images, input_mask=self.image_mask, return_type='Grayscale', invert=self.inpainting_mask_invert==1, mask_blur=self.mask_blur, mask_padding=self.inpaint_full_res_padding) # old img2img
+ self.image_mask = masking.run_mask(input_image=self.init_images, input_mask=self.image_mask, invert=self.inpainting_mask_invert==1, mask_blur=self.mask_blur, mask_padding=self.inpaint_full_res_padding) # old img2img
if self.inpaint_full_res and self.image_mask is not None: # mask only inpaint
self.mask_for_overlay = self.image_mask
mask = self.image_mask.convert('L')
diff --git a/modules/ui_control_helpers.py b/modules/ui_control_helpers.py
index 45e933f76..d345a665c 100644
--- a/modules/ui_control_helpers.py
+++ b/modules/ui_control_helpers.py
@@ -163,7 +163,7 @@ def select_input(input_mode, init_type, input_video, input_batch, input_folder,
# control inputs
if isinstance(selected_input, Image.Image): # image via upload -> image
if input_mode == 'Outpaint':
- masking.opts.invert = True
+ masking.opts.mask_invert = True
selected_input, input_mask = masking.outpaint(input_image=selected_input)
input_source = [selected_input]
input_type = 'PIL.Image'
diff --git a/modules/ui_definitions.py b/modules/ui_definitions.py
index af990c0a3..86f192c48 100644
--- a/modules/ui_definitions.py
+++ b/modules/ui_definitions.py
@@ -450,8 +450,6 @@ def create_settings(cmd_opts):
"save_images_before_refiner": OptionInfo(False, "Save image before refiner"),
"save_images_before_detailer": OptionInfo(False, "Save image before detailer"),
"save_images_before_color_correction": OptionInfo(False, "Save image before color correction"),
- "save_mask": OptionInfo(False, "Save inpainting mask"),
- "save_mask_composite": OptionInfo(False, "Save inpainting masked composite"),
"gradio_skip_video": OptionInfo(False, "Do not display video output in UI"),
"image_sep_watermark": OptionInfo("