add detailer expert mode

Signed-off-by: Vladimir Mandic <mandic00@live.com>
pull/4058/head
Vladimir Mandic 2025-07-21 10:39:19 -04:00
parent 287c3600d7
commit a852d1cea9
8 changed files with 60 additions and 25 deletions

View File

@ -1,8 +1,8 @@
# Change Log for SD.Next
## Update for 2025-07-20
## Update for 2025-07-21
### Highlights for 2025-07-20
### Highlights for 2025-07-21
Feature highlights include:
- **ModernUI** layout redesign which should make it more user friendly and easier to navigate
@ -26,7 +26,7 @@ Although upgrades and existing installations are tested and should work fine!
[ReadMe](https://github.com/vladmandic/automatic/blob/master/README.md) | [ChangeLog](https://github.com/vladmandic/automatic/blob/master/CHANGELOG.md) | [Docs](https://vladmandic.github.io/sdnext-docs/) | [WiKi](https://github.com/vladmandic/automatic/wiki) | [Discord](https://discord.com/invite/sd-next-federal-batch-inspectors-1101998836328697867)
### Details for 2025-07-20
### Details for 2025-07-21
- **License**
- SD.Next [license](https://github.com/vladmandic/sdnext/blob/dev/LICENSE.txt) switched from **aGPL-v3.0** to **Apache-v2.0**
@ -79,7 +79,7 @@ Although upgrades and existing installations are tested and should work fine!
- new [Parameters](https://vladmandic.github.io/sdnext-docs/Parameters/) page that lists and explains all generation parameters
massive thanks to @CalamitousFelicitousness for bringing this to life!
- updated *Models, Video, LTX, FramePack, Styles*, etc.
- **Compute**
- **Compute**
- support for [SageAttention2++](https://github.com/thu-ml/SageAttention)
provides 10-15% performance improvement over default SDPA for transformer-based models!
enable in *settings -> compute settings -> sdp options*
@ -107,6 +107,8 @@ Although upgrades and existing installations are tested and should work fine!
- **Batch** warn on unprocessable images and skip operations on errors so that other images can still be processed
- **Metadata** improved parsing and detect foreign metadata
detect ComfyUI images
- **Detailer** add `expert` mode where list of detailer models can be converted to textbox for manual editing
see [docs](https://vladmandic.github.io/sdnext-docs/Detailer/) for more information
- **SDNQ**
- use inference context during quantization
- use static compile

@ -1 +1 @@
Subproject commit c12e8cda4e45a1d5f20659a30e329c7cc7e69339
Subproject commit 716b1ee7dc8042ba2a62460425930cf3ab472919

View File

@ -5,7 +5,7 @@ from copy import copy
import numpy as np
import gradio as gr
from PIL import Image, ImageDraw
from modules import shared, processing, devices, processing_class, ui_common
from modules import shared, processing, devices, processing_class, ui_common, ui_components, ui_symbols
from modules.detailer import Detailer
@ -46,6 +46,7 @@ class YoloRestorer(Detailer):
super().__init__()
self.models = {} # cache loaded models
self.list = {}
self.ui_mode = True
self.enumerate()
def name(self):
@ -202,16 +203,28 @@ class YoloRestorer(Detailer):
p.detailer_active = 0
if np_image is None or p.detailer_active >= p.batch_size * p.n_iter:
return np_image
if len(shared.opts.detailer_models) == 0:
models = [m.strip() for m in shared.opts.detailer_args.split(',')]
if len(models) == 0:
models = shared.opts.detailer_models
if len(models) == 0:
shared.log.warning('Detailer: model=None')
return np_image
models_used = []
shared.log.debug(f'Detailer: models={models}')
# create backups
orig_apply_overlay = shared.opts.mask_apply_overlay
orig_p = p.__dict__.copy()
orig_cls = p.__class__
models_used = []
for i, model_val in enumerate(models):
if ':' in model_val:
model_name, model_args = model_val.split(':', 1)
else:
model_name, model_args = model_val, ''
model_args = [m.strip() for m in model_args.split(':')]
model_args = {k.strip(): v.strip() for k, v in (arg.split('=') for arg in model_args if '=' in arg)}
for i, model_name in enumerate(shared.opts.detailer_models):
name, model = self.load(model_name)
if model is None:
shared.log.warning(f'Detailer: model="{name}" not loaded')
@ -267,6 +280,7 @@ class YoloRestorer(Detailer):
'height': resolution,
'vae_type': orig_p.get('vae_type', 'Full'),
}
args.update(model_args)
if args['denoising_strength'] == 0:
shared.log.debug(f'Detailer: model="{name}" strength=0 skip')
return np_image
@ -286,7 +300,7 @@ class YoloRestorer(Detailer):
p.steps = orig_p.get('steps', 0)
report = [{'label': i.label, 'score': i.score, 'size': f'{i.width}x{i.height}' } for i in items]
shared.log.info(f'Detailer: model="{name}" items={report} args={items[0].args} strength={p.detailer_strength} blur={p.mask_blur} width={p.width} height={p.height} padding={p.inpaint_full_res_padding}')
shared.log.info(f'Detailer: model="{name}" items={report} args={args}')
models_used.append(name)
mask_all = []
@ -338,9 +352,19 @@ class YoloRestorer(Detailer):
return np_image
def change_mode(self, dropdown, text):
self.ui_mode = not self.ui_mode
if self.ui_mode:
value = [val.split(':')[0].strip() for val in text.split(',')]
return gr.update(visible=True, value=value), gr.update(visible=False), gr.update(visible=True)
else:
value = ', '.join(dropdown)
return gr.update(visible=False), gr.update(visible=True, value=value), gr.update(visible=False)
def ui(self, tab: str):
def ui_settings_change(detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end):
def ui_settings_change(detailers, text, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end):
shared.opts.detailer_models = detailers
shared.opts.detailer_args = text if not self.ui_mode else ''
shared.opts.detailer_classes = classes
shared.opts.detailer_padding = padding
shared.opts.detailer_blur = blur
@ -358,8 +382,11 @@ class YoloRestorer(Detailer):
with gr.Row():
enabled = gr.Checkbox(label="Enable detailer pass", elem_id=f"{tab}_detailer_enabled", value=False)
with gr.Row():
detailers = gr.Dropdown(label="Detailer models", elem_id=f"{tab}_detailers", choices=self.list, value=shared.opts.detailer_models, multiselect=True)
ui_common.create_refresh_button(detailers, self.enumerate, {}, elem_id=f"{tab}_detailers_refresh")
detailers = gr.Dropdown(label="Detailer models", elem_id=f"{tab}_detailers", choices=self.list, value=shared.opts.detailer_models, multiselect=True, visible=True)
detailers_text = gr.Textbox(label="Detailer models", elem_id=f"{tab}_detailers_text", placeholder="Comma separated list of detailer models", lines=2, visible=False, interactive=True)
refresh_btn = ui_common.create_refresh_button(detailers, self.enumerate, {}, elem_id=f"{tab}_detailers_refresh")
ui_mode = ui_components.ToolButton(value=ui_symbols.view)
ui_mode.click(fn=self.change_mode, inputs=[detailers, detailers_text], outputs=[detailers, detailers_text, refresh_btn])
with gr.Row():
classes = gr.Textbox(label="Detailer classes", placeholder="Classes", elem_id=f"{tab}_detailer_classes")
with gr.Row():
@ -385,15 +412,16 @@ class YoloRestorer(Detailer):
with gr.Row(elem_classes=['flex-break']):
renoise_value = gr.Slider(minimum=0.5, maximum=1.5, step=0.01, label='Renoise', value=shared.opts.detailer_sigma_adjust, elem_id=f"{tab}_detailer_renoise")
renoise_end = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Renoise end', value=shared.opts.detailer_sigma_adjust_max, elem_id=f"{tab}_detailer_renoise_end")
detailers.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
classes.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
padding.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
blur.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
min_confidence.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
max_detected.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
min_size.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
max_size.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
iou.change(fn=ui_settings_change, inputs=[detailers, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
detailers.change(fn=ui_settings_change, inputs=[detailers, detailers_text, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
detailers_text.change(fn=ui_settings_change, inputs=[detailers, detailers_text, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
classes.change(fn=ui_settings_change, inputs=[detailers, detailers_text, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
padding.change(fn=ui_settings_change, inputs=[detailers, detailers_text, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
blur.change(fn=ui_settings_change, inputs=[detailers, detailers_text, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
min_confidence.change(fn=ui_settings_change, inputs=[detailers, detailers_text, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
max_detected.change(fn=ui_settings_change, inputs=[detailers, detailers_text, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
min_size.change(fn=ui_settings_change, inputs=[detailers, detailers_text, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
max_size.change(fn=ui_settings_change, inputs=[detailers, detailers_text, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
iou.change(fn=ui_settings_change, inputs=[detailers, detailers_text, classes, strength, padding, blur, min_confidence, max_detected, min_size, max_size, iou, steps, renoise_value, renoise_end], outputs=[])
return enabled, prompt, negative, steps, strength

View File

@ -582,5 +582,9 @@ def switch_class(p: StableDiffusionProcessing, new_class: type, dct: dict = None
if dct is not None: # post init set additional values
for k, v in dct.items():
if hasattr(p, k):
setattr(p, k, v)
valtype = type(getattr(p, k, None))
if valtype in [int, float, str]:
setattr(p, k, valtype(v))
else:
setattr(p, k, v)
return p

View File

@ -141,7 +141,7 @@ def create_infotext(p: StableDiffusionProcessing, all_prompts=None, all_seeds=No
args['Resize name mask'] = p.resize_name_mask
args['Resize scale mask'] = p.scale_by_mask
if 'detailer' in p.ops:
args["Detailer"] = ', '.join(shared.opts.detailer_models)
args["Detailer"] = ', '.join(shared.opts.detailer_models) if len(shared.opts.detailer_args) == 0 else shared.opts.detailer_args
args["Detailer steps"] = p.detailer_steps
args["Detailer strength"] = p.detailer_strength
args["Detailer prompt"] = p.detailer_prompt if len(p.detailer_prompt) > 0 else None

View File

@ -623,6 +623,7 @@ options_templates.update(options_section(('postprocessing', "Postprocessing"), {
"detailer_padding": OptionInfo(20, "Item padding", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1, "visible": False}),
"detailer_blur": OptionInfo(10, "Item edge blur", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1, "visible": False}),
"detailer_models": OptionInfo(['face-yolo8n'], "Detailer models", gr.Dropdown, lambda: {"multiselect":True, "choices": list(yolo.list), "visible": False}),
"detailer_args": OptionInfo("", "Detailer args", gr.Textbox, { "visible": False}),
"detailer_unload": OptionInfo(False, "Move detailer model to CPU when complete"),
"detailer_augment": OptionInfo(True, "Detailer use model augment"),

View File

@ -19,6 +19,7 @@
"venv": ". venv/bin/activate",
"start": ". venv/bin/activate; python launch.py --debug",
"localize": "node cli/localize.js",
"update": ". venv/bin/activate && pip install --upgrade transformers accelerate huggingface_hub safetensors tokenizers peft compel pytorch_lightning",
"eslint": "eslint . javascript/ extensions-builtin/sdnext-modernui/javascript/",
"ruff": ". venv/bin/activate && ruff check",
"pylint": ". venv/bin/activate && pylint *.py modules/ pipelines/ scripts/ extensions-builtin/ | grep -v '^*'",

View File

@ -263,7 +263,6 @@ axis_options = [
AxisOption("[Control] Strength", float, apply_control('control_strength')),
AxisOption("[Control] Start", float, apply_control('control_start')),
AxisOption("[Control] End", float, apply_control('control_end')),
AxisOption("[HiDiffusion] T1", float, apply_override('hidiffusion_t1')),
AxisOption("[HiDiffusion] T2", float, apply_override('hidiffusion_t2')),
AxisOption("[HiDiffusion] Agression step", float, apply_field('hidiffusion_steps')),