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("

Watermarking

", "", gr.HTML), @@ -552,8 +550,6 @@ def create_settings(cmd_opts): "images_sep_ui": OptionInfo("

Outputs & Images

", "", gr.HTML), "return_grid": OptionInfo(True, "Show grid in results"), - "return_mask": OptionInfo(False, "Inpainting include greyscale mask in results"), - "return_mask_composite": OptionInfo(False, "Inpainting include masked composite in results"), "send_seed": OptionInfo(True, "Send seed when sending prompt or image to other interface", gr.Checkbox, {"visible": False}), "send_size": OptionInfo(False, "Send size when sending prompt or image to another interface", gr.Checkbox, {"visible": False}), }))