mirror of
https://github.com/muerrilla/sd-webui-detail-daemon.git
synced 2026-05-02 04:01:20 +00:00
Update detail_daemon.py
Major overhaul (added multiple daemons, etc.)
This commit is contained in:
@@ -8,30 +8,66 @@ matplotlib.use('Agg')
|
|||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
import modules.scripts as scripts
|
import modules.scripts as scripts
|
||||||
from modules.script_callbacks import on_cfg_denoiser, remove_callbacks_for_function, on_infotext_pasted
|
from modules.script_callbacks import on_cfg_denoiser, remove_callbacks_for_function, on_infotext_pasted, on_ui_settings
|
||||||
from modules.ui_components import InputAccordion
|
from modules.ui_components import InputAccordion
|
||||||
|
from modules import shared
|
||||||
|
|
||||||
|
try:
|
||||||
|
import modules_forge.forge_version
|
||||||
|
is_forge = True
|
||||||
|
except:
|
||||||
|
is_forge = False
|
||||||
|
|
||||||
|
def add_settings():
|
||||||
|
section = ('detail_daemon', "Detail Daemon")
|
||||||
|
shared.opts.add_option("detail_daemon_count", shared.OptionInfo(
|
||||||
|
6, "Daemon count", gr.Slider, {"minimum": 1, "maximum": 10, "step": 1}, section=section).needs_reload_ui())
|
||||||
|
shared.opts.add_option("detail_daemon_verbose", shared.OptionInfo(
|
||||||
|
False, "Verbose", gr.Checkbox, {"interactive": True}, section=section))
|
||||||
|
|
||||||
|
on_ui_settings(add_settings)
|
||||||
|
|
||||||
def parse_infotext(infotext, params):
|
def parse_infotext(infotext, params):
|
||||||
try:
|
try:
|
||||||
|
raw = params['Detail Daemon']
|
||||||
|
if raw.startswith("D"):
|
||||||
|
daemons = raw.split(";")
|
||||||
|
if len(daemons) > shared.opts.data.get("detail_daemon_count", 6):
|
||||||
|
tqdm.write(f"\033[31mDetail Daemon:\033[0m Need more daemons! Go to 'Settings > Uncategorized > Detail Daemon' and increase count to {len(daemons)}.")
|
||||||
|
dd_dict = {}
|
||||||
|
for idx, daemon in enumerate(daemons):
|
||||||
|
tag, values = daemon.split(":")
|
||||||
|
vals = values.split(",")
|
||||||
|
dd_dict[f"active{idx + 1 if idx > 0 else ''}"] = bool(int(vals[0]))
|
||||||
|
dd_dict[f"hr{idx + 1 if idx > 0 else ''}"] = bool(int(vals[1]))
|
||||||
|
dd_dict[f"mode{idx + 1 if idx > 0 else ''}"] = vals[2]
|
||||||
|
dd_dict[f"amount{idx + 1 if idx > 0 else ''}"] = float(vals[3])
|
||||||
|
dd_dict[f"st{idx + 1 if idx > 0 else ''}"] = float(vals[4])
|
||||||
|
dd_dict[f"ed{idx + 1 if idx > 0 else ''}"] = float(vals[5])
|
||||||
|
dd_dict[f"bias{idx + 1 if idx > 0 else ''}"] = float(vals[6])
|
||||||
|
dd_dict[f"exp{idx + 1 if idx > 0 else ''}"] = float(vals[7])
|
||||||
|
dd_dict[f"st_offset{idx + 1 if idx > 0 else ''}"] = float(vals[8])
|
||||||
|
dd_dict[f"ed_offset{idx + 1 if idx > 0 else ''}"] = float(vals[9])
|
||||||
|
dd_dict[f"fade{idx + 1 if idx > 0 else ''}"] = float(vals[10])
|
||||||
|
dd_dict[f"smooth{idx + 1 if idx > 0 else ''}"] = bool(int(vals[11]))
|
||||||
|
params['Detail Daemon'] = dd_dict
|
||||||
|
else:
|
||||||
|
# fallback to old format for backward compatibility
|
||||||
d = {}
|
d = {}
|
||||||
for s in params['Detail Daemon'].split(','):
|
for s in raw.split(','):
|
||||||
k, _, v = s.partition(':')
|
k, _, v = s.partition(':')
|
||||||
d[k.strip()] = v.strip()
|
d[k.strip()] = v.strip()
|
||||||
params['Detail Daemon'] = d
|
params['Detail Daemon'] = d
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
on_infotext_pasted(parse_infotext)
|
on_infotext_pasted(parse_infotext)
|
||||||
|
|
||||||
|
|
||||||
class Script(scripts.Script):
|
class Script(scripts.Script):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
self.tab_param_count = 0
|
||||||
self.schedule_params: dict[str, float] = None
|
|
||||||
self.schedule = None
|
|
||||||
|
|
||||||
def title(self):
|
def title(self):
|
||||||
return "Detail Daemon"
|
return "Detail Daemon"
|
||||||
@@ -40,34 +76,58 @@ class Script(scripts.Script):
|
|||||||
return scripts.AlwaysVisible
|
return scripts.AlwaysVisible
|
||||||
|
|
||||||
def ui(self, is_img2img):
|
def ui(self, is_img2img):
|
||||||
|
|
||||||
|
def extract_infotext(d: dict, key, old_key=None):
|
||||||
|
if 'Detail Daemon' in d:
|
||||||
|
return d['Detail Daemon'].get(key)
|
||||||
|
return d.get(old_key)
|
||||||
|
|
||||||
|
daemon_count = shared.opts.data.get("detail_daemon_count", 6)
|
||||||
|
all_params = []
|
||||||
|
|
||||||
with InputAccordion(False, label="Detail Daemon", elem_id=self.elem_id('detail-daemon')) as gr_enabled:
|
with InputAccordion(False, label="Detail Daemon", elem_id=self.elem_id('detail-daemon')) as gr_enabled:
|
||||||
|
all_params.append(gr_enabled)
|
||||||
|
self.infotext_fields = [(gr_enabled, lambda d: 'Detail Daemon' in d or 'DD_enabled' in d)]
|
||||||
|
thumbs = []
|
||||||
|
with gr.Group():
|
||||||
|
with gr.Row(elem_classes=['detail-daemon-thumb-group']):
|
||||||
|
for i in range(daemon_count):
|
||||||
|
_, empty = self.visualize(False, 0, 1, 0.5, 0, 0, 0, 0, 0, False, 'both', False)
|
||||||
|
gr_thumb = gr.Plot(value=empty, elem_classes=['detail-daemon-thumb'], show_label=False)
|
||||||
|
thumbs.append(gr_thumb)
|
||||||
|
with gr.Group(elem_classes=['detail-daemon-tab-group']):
|
||||||
|
for i in range(daemon_count):
|
||||||
|
with gr.Tab(f'{["I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"][i]}', elem_classes=['detail-daemon-tab']):
|
||||||
|
params_set = []
|
||||||
with gr.Row():
|
with gr.Row():
|
||||||
with gr.Column(scale=2):
|
gr_active = gr.Checkbox(label="Active", value=False, min_width=60, elem_classes=['detail-daemon-active'])
|
||||||
|
gr_hires = gr.Checkbox(label="Hires Pass", value=False, min_width=60, elem_classes=['detail-daemon-hires'])
|
||||||
|
with gr.Row():
|
||||||
|
with gr.Column(scale=2, elem_classes=['detail-daemon-params']):
|
||||||
gr_amount_slider = gr.Slider(minimum=-1.00, maximum=1.00, step=.01, value=0.10, label="Detail Amount")
|
gr_amount_slider = gr.Slider(minimum=-1.00, maximum=1.00, step=.01, value=0.10, label="Detail Amount")
|
||||||
gr_start = gr.Slider(minimum=0.0, maximum=1.0, step=.01, value=0.2, label="Start")
|
|
||||||
gr_end = gr.Slider(minimum=0.0, maximum=1.0, step=.01, value=0.8, label="End")
|
|
||||||
gr_bias = gr.Slider(minimum=0.0, maximum=1.0, step=.01, value=0.5, label="Bias")
|
|
||||||
with gr.Column(scale=1, min_width=275):
|
|
||||||
preview = self.visualize(False, 0.2, 0.8, 0.5, 0.1, 1, 0, 0, 0, True)
|
|
||||||
gr_vis = gr.Plot(value=preview, elem_classes=['detail-daemon-vis'], show_label=False)
|
|
||||||
with gr.Accordion("More Knobs:", elem_classes=['detail-daemon-more-accordion'], open=False):
|
|
||||||
with gr.Row():
|
with gr.Row():
|
||||||
with gr.Column(scale=2):
|
gr_start = gr.Slider(minimum=0.0, maximum=1.0, step=.01, value=0.2, label="Start", min_width=60)
|
||||||
|
gr_end = gr.Slider(minimum=0.0, maximum=1.0, step=.01, value=0.8, label="End", min_width=60)
|
||||||
with gr.Row():
|
with gr.Row():
|
||||||
gr_start_offset_slider = gr.Slider(minimum=-1.00, maximum=1.00, step=.01, value=0.00, label="Start Offset", min_width=60)
|
gr_start_offset_slider = gr.Slider(minimum=-1.00, maximum=1.00, step=.01, value=0.00, label="Start Offset", min_width=60)
|
||||||
gr_end_offset_slider = gr.Slider(minimum=-1.00, maximum=1.00, step=.01, value=0.00, label="End Offset", min_width=60)
|
gr_end_offset_slider = gr.Slider(minimum=-1.00, maximum=1.00, step=.01, value=0.00, label="End Offset", min_width=60)
|
||||||
with gr.Row():
|
with gr.Row():
|
||||||
|
gr_bias = gr.Slider(minimum=0.0, maximum=1.0, step=.01, value=0.5, label="Bias", min_width=60)
|
||||||
gr_exponent = gr.Slider(minimum=0.0, maximum=10.0, step=.05, value=1.0, label="Exponent", min_width=60)
|
gr_exponent = gr.Slider(minimum=0.0, maximum=10.0, step=.05, value=1.0, label="Exponent", min_width=60)
|
||||||
gr_fade = gr.Slider(minimum=0.0, maximum=1.0, step=.05, value=0.0, label="Fade", min_width=60)
|
gr_fade = gr.Slider(minimum=0.0, maximum=1.0, step=.05, value=0.0, label="Fade", min_width=60)
|
||||||
# Because the slider max and min are sometimes too limiting:
|
with gr.Column(scale=1, min_width=275):
|
||||||
|
preview, _ = self.visualize(False, 0.2, 0.8, 0.5, 0.1, 1, 0, 0, 0, True, 'both', False)
|
||||||
|
gr_vis = gr.Plot(value=preview, elem_classes=['detail-daemon-vis'], show_label=False)
|
||||||
|
gr_smooth = gr.Checkbox(label="Smooth", value=True, min_width=60, elem_classes=['detail-daemon-smooth'])
|
||||||
|
with gr.Accordion("More Knobs:", elem_classes=['detail-daemon-more-accordion'], open=False):
|
||||||
with gr.Row():
|
with gr.Row():
|
||||||
|
with gr.Column(scale=2):
|
||||||
|
with gr.Row():
|
||||||
|
# Because the slider max and min are sometimes too limiting:
|
||||||
gr_amount = gr.Number(value=0.10, precision=4, step=.01, label="Amount", min_width=60)
|
gr_amount = gr.Number(value=0.10, precision=4, step=.01, label="Amount", min_width=60)
|
||||||
gr_start_offset = gr.Number(value=0.0, precision=4, step=.01, label="Start Offset", min_width=60)
|
gr_start_offset = gr.Number(value=0.0, precision=4, step=.01, label="Start Offset", min_width=60)
|
||||||
gr_end_offset = gr.Number(value=0.0, precision=4, step=.01, label="End Offset", min_width=60)
|
gr_end_offset = gr.Number(value=0.0, precision=4, step=.01, label="End Offset", min_width=60)
|
||||||
with gr.Column(scale=1, min_width=275):
|
gr_mode = gr.Dropdown(["both", "cond", "uncond"], value="both", label="Mode", show_label=True, min_width=60, elem_classes=['detail-daemon-mode'])
|
||||||
gr_mode = gr.Dropdown(["both", "cond", "uncond"], value="uncond", label="Mode", show_label=True, min_width=60, elem_classes=['detail-daemon-mode'])
|
|
||||||
gr_smooth = gr.Checkbox(label="Smooth", value=True, min_width=60, elem_classes=['detail-daemon-smooth'])
|
|
||||||
gr.Markdown("## [Ⓗ Help](https://github.com/muerrilla/sd-webui-detail-daemon)", elem_classes=['detail-daemon-help'])
|
|
||||||
|
|
||||||
gr_amount_slider.release(None, gr_amount_slider, gr_amount, _js="(x) => x")
|
gr_amount_slider.release(None, gr_amount_slider, gr_amount, _js="(x) => x")
|
||||||
gr_amount.change(None, gr_amount, gr_amount_slider, _js="(x) => x")
|
gr_amount.change(None, gr_amount, gr_amount_slider, _js="(x) => x")
|
||||||
@@ -78,20 +138,26 @@ class Script(scripts.Script):
|
|||||||
gr_end_offset_slider.release(None, gr_end_offset_slider, gr_end_offset, _js="(x) => x")
|
gr_end_offset_slider.release(None, gr_end_offset_slider, gr_end_offset, _js="(x) => x")
|
||||||
gr_end_offset.change(None, gr_end_offset, gr_end_offset_slider, _js="(x) => x")
|
gr_end_offset.change(None, gr_end_offset, gr_end_offset_slider, _js="(x) => x")
|
||||||
|
|
||||||
vis_args = [gr_enabled, gr_start, gr_end, gr_bias, gr_amount, gr_exponent, gr_start_offset, gr_end_offset, gr_fade, gr_smooth]
|
vis_args = [gr_active, gr_start, gr_end, gr_bias, gr_amount, gr_exponent, gr_start_offset, gr_end_offset, gr_fade, gr_smooth, gr_mode, gr_hires]
|
||||||
for vis_arg in vis_args:
|
for vis_arg in vis_args:
|
||||||
if isinstance(vis_arg, gr.components.Slider):
|
if isinstance(vis_arg, gr.components.Slider):
|
||||||
vis_arg.release(fn=self.visualize, show_progress=False, inputs=vis_args, outputs=[gr_vis])
|
vis_arg.release(fn=self.visualize, show_progress=False, inputs=vis_args, outputs=[gr_vis, thumbs[i]])
|
||||||
else:
|
else:
|
||||||
vis_arg.change(fn=self.visualize, show_progress=False, inputs=vis_args, outputs=[gr_vis])
|
vis_arg.change(fn=self.visualize, show_progress=False, inputs=vis_args, outputs=[gr_vis, thumbs[i]])
|
||||||
|
|
||||||
def extract_infotext(d: dict, key, old_key):
|
params_set = [
|
||||||
if 'Detail Daemon' in d:
|
gr_active, gr_hires, gr_mode, gr_start, gr_end, gr_bias, gr_amount,
|
||||||
return d['Detail Daemon'].get(key)
|
gr_exponent, gr_start_offset, gr_end_offset, gr_fade, gr_smooth
|
||||||
return d.get(old_key)
|
]
|
||||||
|
all_params.extend(params_set)
|
||||||
|
|
||||||
self.infotext_fields = [
|
# First tab should be backward compatible with the older single daemon Detail Daemon, hence the variable names without numbers
|
||||||
(gr_enabled, lambda d: True if ('Detail Daemon' in d or 'DD_enabled' in d) else False),
|
# older single daemon DD was backward compatible with yet older DD which had infotext with the DD_stuff format, so that's handled here too
|
||||||
|
if i == 0 :
|
||||||
|
self.tab_param_count = len(params_set)
|
||||||
|
self.infotext_fields.extend([
|
||||||
|
(gr_active, lambda d: extract_infotext(d, 'active') or 'Detail Daemon' in d or 'DD_enabled' in d),
|
||||||
|
(gr_hires, lambda d: extract_infotext(d, 'hr') or False),
|
||||||
(gr_mode, lambda d: extract_infotext(d, 'mode', 'DD_mode')),
|
(gr_mode, lambda d: extract_infotext(d, 'mode', 'DD_mode')),
|
||||||
(gr_amount, lambda d: extract_infotext(d, 'amount', 'DD_amount')),
|
(gr_amount, lambda d: extract_infotext(d, 'amount', 'DD_amount')),
|
||||||
(gr_start, lambda d: extract_infotext(d, 'st', 'DD_start')),
|
(gr_start, lambda d: extract_infotext(d, 'st', 'DD_start')),
|
||||||
@@ -102,12 +168,49 @@ class Script(scripts.Script):
|
|||||||
(gr_end_offset, lambda d: extract_infotext(d, 'ed_offset', 'DD_end_offset')),
|
(gr_end_offset, lambda d: extract_infotext(d, 'ed_offset', 'DD_end_offset')),
|
||||||
(gr_fade, lambda d: extract_infotext(d, 'fade', 'DD_fade')),
|
(gr_fade, lambda d: extract_infotext(d, 'fade', 'DD_fade')),
|
||||||
(gr_smooth, lambda d: extract_infotext(d, 'smooth', 'DD_smooth')),
|
(gr_smooth, lambda d: extract_infotext(d, 'smooth', 'DD_smooth')),
|
||||||
]
|
])
|
||||||
return [gr_enabled, gr_mode, gr_start, gr_end, gr_bias, gr_amount, gr_exponent, gr_start_offset, gr_end_offset, gr_fade, gr_smooth]
|
else:
|
||||||
|
tab_tag = i + 1
|
||||||
|
self.infotext_fields.extend([
|
||||||
|
(gr_active, lambda d, key=f'active{tab_tag}': extract_infotext(d, key) or False),
|
||||||
|
(gr_hires, lambda d, key=f'hr{tab_tag}': extract_infotext(d, key)),
|
||||||
|
(gr_mode, lambda d, key=f'mode{tab_tag}': extract_infotext(d, key)),
|
||||||
|
(gr_amount, lambda d, key=f'amount{tab_tag}': extract_infotext(d, key)),
|
||||||
|
(gr_start, lambda d, key=f'st{tab_tag}': extract_infotext(d, key)),
|
||||||
|
(gr_end, lambda d, key=f'ed{tab_tag}': extract_infotext(d, key)),
|
||||||
|
(gr_bias, lambda d, key=f'bias{tab_tag}': extract_infotext(d, key)),
|
||||||
|
(gr_exponent, lambda d, key=f'exp{tab_tag}': extract_infotext(d, key)),
|
||||||
|
(gr_start_offset, lambda d, key=f'st_offset{tab_tag}': extract_infotext(d, key)),
|
||||||
|
(gr_end_offset, lambda d, key=f'ed_offset{tab_tag}': extract_infotext(d, key)),
|
||||||
|
(gr_fade, lambda d, key=f'fade{tab_tag}': extract_infotext(d, key)),
|
||||||
|
(gr_smooth, lambda d, key=f'smooth{tab_tag}': extract_infotext(d, key)),
|
||||||
|
])
|
||||||
|
return all_params
|
||||||
|
|
||||||
def process(self, p, enabled, mode, start, end, bias, amount, exponent, start_offset, end_offset, fade, smooth):
|
def process(self, p, enabled, *all_daemon_args):
|
||||||
|
if not enabled:
|
||||||
|
if hasattr(self, 'callback_added'):
|
||||||
|
remove_callbacks_for_function(self.denoiser_callback)
|
||||||
|
delattr(self, 'callback_added')
|
||||||
|
return
|
||||||
|
|
||||||
enabled = getattr(p, "DD_enabled", enabled)
|
if p.sampler_name in ["DPM adaptive", "HeunPP2"]:
|
||||||
|
tqdm.write(f'\033[31mDetail Daemon:\033[0m Selected sampler ({p.sampler_name}) is not supported.')
|
||||||
|
return
|
||||||
|
|
||||||
|
self.daemon_data = []
|
||||||
|
extra_gen_texts = []
|
||||||
|
num_daemons = len(all_daemon_args) // self.tab_param_count
|
||||||
|
|
||||||
|
for i in range(num_daemons):
|
||||||
|
start_idx = i * self.tab_param_count
|
||||||
|
end_idx = start_idx + self.tab_param_count
|
||||||
|
daemon_args = all_daemon_args[start_idx:end_idx]
|
||||||
|
|
||||||
|
active, hires, mode, start, end, bias, amount, exponent, start_offset, end_offset, fade, smooth = daemon_args
|
||||||
|
|
||||||
|
# TODO? XYZ support for other channels
|
||||||
|
if (i == 0) :
|
||||||
mode = getattr(p, "DD_mode", mode)
|
mode = getattr(p, "DD_mode", mode)
|
||||||
amount = getattr(p, "DD_amount", amount)
|
amount = getattr(p, "DD_amount", amount)
|
||||||
start = getattr(p, "DD_start", start)
|
start = getattr(p, "DD_start", start)
|
||||||
@@ -119,12 +222,8 @@ class Script(scripts.Script):
|
|||||||
fade = getattr(p, "DD_fade", fade)
|
fade = getattr(p, "DD_fade", fade)
|
||||||
smooth = getattr(p, "DD_smooth", smooth)
|
smooth = getattr(p, "DD_smooth", smooth)
|
||||||
|
|
||||||
if enabled:
|
if active:
|
||||||
if p.sampler_name in ["DPM adaptive", "HeunPP2"]:
|
daemon_schedule_params = {
|
||||||
tqdm.write(f'\033[33mWARNING:\033[0m Detail Daemon does not work with {p.sampler_name}')
|
|
||||||
return
|
|
||||||
|
|
||||||
self.schedule_params = {
|
|
||||||
"start": start,
|
"start": start,
|
||||||
"end": end,
|
"end": end,
|
||||||
"bias": bias,
|
"bias": bias,
|
||||||
@@ -135,52 +234,66 @@ class Script(scripts.Script):
|
|||||||
"fade": fade,
|
"fade": fade,
|
||||||
"smooth": smooth
|
"smooth": smooth
|
||||||
}
|
}
|
||||||
self.mode = mode
|
|
||||||
self.cfg_scale = p.cfg_scale
|
self.daemon_data.append({
|
||||||
self.batch_size = p.batch_size
|
'name': f'Daemon {i+1}',
|
||||||
|
'mode': mode,
|
||||||
|
'schedule': None,
|
||||||
|
'schedule_params': daemon_schedule_params,
|
||||||
|
'hires': hires,
|
||||||
|
'multiplier': .1 # Add slider for this?
|
||||||
|
})
|
||||||
|
|
||||||
|
text = ",".join([
|
||||||
|
str(int(active)), str(int(hires)), mode, f"{amount}", f"{start}", f"{end}", f"{bias}",
|
||||||
|
f"{exponent}", f"{start_offset}", f"{end_offset}", f"{fade:}", str(int(smooth))
|
||||||
|
])
|
||||||
|
extra_gen_texts.append(f"D{i+1}:{text}")
|
||||||
|
|
||||||
|
if extra_gen_texts:
|
||||||
|
p.extra_generation_params['Detail Daemon'] = ";".join(extra_gen_texts)
|
||||||
|
|
||||||
|
if not hasattr(self, 'callback_added'):
|
||||||
on_cfg_denoiser(self.denoiser_callback)
|
on_cfg_denoiser(self.denoiser_callback)
|
||||||
self.callback_added = True
|
self.callback_added = True
|
||||||
p.extra_generation_params['Detail Daemon'] = f'mode:{mode},amount:{amount},st:{start},ed:{end},bias:{bias},exp:{exponent},st_offset:{start_offset},ed_offset:{end_offset},fade:{fade},smooth:{1 if smooth else 0}'
|
self.cfg_scale = p.cfg_scale
|
||||||
tqdm.write('\033[32mINFO:\033[0m Detail Daemon is enabled')
|
self.batch_size = p.batch_size
|
||||||
else:
|
self.is_hires_pass = False
|
||||||
if hasattr(self, 'callback_added'):
|
|
||||||
remove_callbacks_for_function(self.denoiser_callback)
|
|
||||||
delattr(self, 'callback_added')
|
|
||||||
self.schedule = None
|
|
||||||
# tqdm.write('\033[90mINFO: Detail Daemon callback removed\033[0m')
|
|
||||||
|
|
||||||
def before_process_batch(self, p, *args, **kwargs):
|
def before_process_batch(self, p, *args, **kwargs):
|
||||||
self.is_hires = False
|
self.is_hires_pass = False
|
||||||
|
|
||||||
|
def before_hr(self, p, *args):
|
||||||
|
self.is_hires_pass = True
|
||||||
|
|
||||||
def postprocess(self, p, processed, *args):
|
def postprocess(self, p, processed, *args):
|
||||||
if hasattr(self, 'callback_added'):
|
if hasattr(self, 'callback_added'):
|
||||||
remove_callbacks_for_function(self.denoiser_callback)
|
remove_callbacks_for_function(self.denoiser_callback)
|
||||||
delattr(self, 'callback_added')
|
delattr(self, 'callback_added')
|
||||||
self.schedule = None
|
|
||||||
# tqdm.write('\033[90mINFO: Detail Daemon callback removed\033[0m')
|
|
||||||
|
|
||||||
def before_hr(self, p, *args):
|
|
||||||
self.is_hires = True
|
|
||||||
enabled = args[0]
|
|
||||||
if enabled:
|
|
||||||
tqdm.write(f'\033[33mINFO:\033[0m Detail Daemon does not work during Hires Fix')
|
|
||||||
|
|
||||||
def denoiser_callback(self, params):
|
def denoiser_callback(self, params):
|
||||||
if self.is_hires:
|
for daemon in self.daemon_data:
|
||||||
return
|
if daemon['hires'] != self.is_hires_pass:
|
||||||
step = max(params.sampling_step, params.denoiser.step)
|
continue
|
||||||
total_steps = max(params.total_sampling_steps, params.denoiser.total_steps)
|
|
||||||
corrected_step_count = total_steps - max(total_steps // params.denoiser.steps - 1, 0)
|
|
||||||
if self.schedule is None:
|
|
||||||
self.schedule = self.make_schedule(corrected_step_count, **self.schedule_params)
|
|
||||||
|
|
||||||
idx = min(step, corrected_step_count - 1)
|
name = daemon['name']
|
||||||
multiplier = self.schedule[idx] * .1
|
mode = daemon['mode']
|
||||||
mode = self.mode
|
step = max(params.sampling_step, params.denoiser.step)
|
||||||
if params.sigma.size(0) == 1 and mode != "both":
|
steps = max(params.total_sampling_steps, params.denoiser.total_steps)
|
||||||
|
actual_steps = steps - max(steps // params.denoiser.steps - 1, 0)
|
||||||
|
idx = min(step, actual_steps - 1)
|
||||||
|
|
||||||
|
if daemon['schedule'] is None:
|
||||||
|
daemon['schedule'] = self.make_schedule(actual_steps, **daemon['schedule_params'])
|
||||||
|
|
||||||
|
schedule = daemon['schedule']
|
||||||
|
multiplier = schedule[idx] * daemon['multiplier']
|
||||||
|
|
||||||
|
if is_forge:
|
||||||
|
if idx == 0 and mode != "both":
|
||||||
|
tqdm.write(f'\033[33mDetail Daemon:\033[0m Forge does not support `cond` and `uncond` modes, using `both` instead')
|
||||||
mode = "both"
|
mode = "both"
|
||||||
if idx == 0:
|
|
||||||
tqdm.write(f'\033[33mWARNING:\033[0m Forge does not support `cond` and `uncond` modes, using `both` instead')
|
|
||||||
if mode == "cond":
|
if mode == "cond":
|
||||||
for i in range(self.batch_size):
|
for i in range(self.batch_size):
|
||||||
params.sigma[i] *= 1 - multiplier
|
params.sigma[i] *= 1 - multiplier
|
||||||
@@ -190,6 +303,9 @@ class Script(scripts.Script):
|
|||||||
else:
|
else:
|
||||||
params.sigma *= 1 - multiplier * self.cfg_scale
|
params.sigma *= 1 - multiplier * self.cfg_scale
|
||||||
|
|
||||||
|
if shared.opts.data.get("detail_daemon_verbose", False):
|
||||||
|
tqdm.write(f'\033[32mDetail Daemon:\033[0m {name} | sigma: {params.sigma} | step: {idx}/{actual_steps - 1} | multiplier: {multiplier:.4f}')
|
||||||
|
|
||||||
def make_schedule(self, steps, start, end, bias, amount, exponent, start_offset, end_offset, fade, smooth):
|
def make_schedule(self, steps, start, end, bias, amount, exponent, start_offset, end_offset, fade, smooth):
|
||||||
start = min(start, end)
|
start = min(start, end)
|
||||||
mid = start + bias * (end - start)
|
mid = start + bias * (end - start)
|
||||||
@@ -221,7 +337,7 @@ class Script(scripts.Script):
|
|||||||
|
|
||||||
return multipliers
|
return multipliers
|
||||||
|
|
||||||
def visualize(self, enabled, start, end, bias, amount, exponent, start_offset, end_offset, fade, smooth):
|
def visualize(self, enabled, start, end, bias, amount, exponent, start_offset, end_offset, fade, smooth, mode, hires):
|
||||||
try:
|
try:
|
||||||
steps = 50
|
steps = 50
|
||||||
values = self.make_schedule(steps, start, end, bias, amount, exponent, start_offset, end_offset, fade, smooth)
|
values = self.make_schedule(steps, start, end, bias, amount, exponent, start_offset, end_offset, fade, smooth)
|
||||||
@@ -232,6 +348,7 @@ class Script(scripts.Script):
|
|||||||
mid = start + bias * (end - start)
|
mid = start + bias * (end - start)
|
||||||
opacity = .1 + (1 - fade) * 0.7
|
opacity = .1 + (1 - fade) * 0.7
|
||||||
plot_color = (0.5, 0.5, 0.5, opacity) if not enabled else ((1 - peak)**2, 1, 0, opacity) if mean >= 0 else (1, (1 - peak)**2, 0, opacity)
|
plot_color = (0.5, 0.5, 0.5, opacity) if not enabled else ((1 - peak)**2, 1, 0, opacity) if mean >= 0 else (1, (1 - peak)**2, 0, opacity)
|
||||||
|
|
||||||
plt.rcParams.update({
|
plt.rcParams.update({
|
||||||
"text.color": plot_color,
|
"text.color": plot_color,
|
||||||
"axes.labelcolor": plot_color,
|
"axes.labelcolor": plot_color,
|
||||||
@@ -242,23 +359,47 @@ class Script(scripts.Script):
|
|||||||
"ytick.labelcolor": plot_color,
|
"ytick.labelcolor": plot_color,
|
||||||
"ytick.color": plot_color,
|
"ytick.color": plot_color,
|
||||||
})
|
})
|
||||||
fig, ax = plt.subplots(figsize=(2.15, 2.00), layout="constrained")
|
|
||||||
ax.plot(range(steps), values, color=plot_color)
|
|
||||||
ax.axhline(y=0, color=plot_color, linestyle='dotted')
|
|
||||||
ax.axvline(x=mid * (steps - 1), color=plot_color, linestyle='dotted')
|
|
||||||
ax.tick_params(right=False, color=plot_color)
|
|
||||||
ax.set_xticks([i * (steps - 1) / 10 for i in range(10)][1:])
|
|
||||||
ax.set_xticklabels([])
|
|
||||||
ax.set_ylim([-1, 1])
|
|
||||||
ax.set_xlim([0, steps-1])
|
|
||||||
plt.close()
|
|
||||||
self.last_vis = fig
|
|
||||||
return fig
|
|
||||||
except Exception:
|
|
||||||
if self.last_vis is not None:
|
|
||||||
return self.last_vis
|
|
||||||
return
|
|
||||||
|
|
||||||
|
fig_main, ax_main = plt.subplots(figsize=(2.15, 2.00), layout="constrained")
|
||||||
|
ax_main.plot(range(steps), values, color=plot_color, linewidth=1.5, linestyle="dashed" if hires else "solid")
|
||||||
|
ax_main.axhline(y=0, color=plot_color, linestyle='dotted')
|
||||||
|
ax_main.axvline(x=mid * (steps - 1), color=plot_color, linestyle='dotted')
|
||||||
|
ax_main.tick_params(right=False, color=plot_color)
|
||||||
|
ax_main.set_xticks([i * (steps - 1) / 10 for i in range(10)][1:])
|
||||||
|
ax_main.set_xticklabels([])
|
||||||
|
ax_main.set_ylim([-1, 1])
|
||||||
|
ax_main.set_xlim([0, steps - 1])
|
||||||
|
plt.close(fig_main)
|
||||||
|
|
||||||
|
plot_color = (0.5, 0.5, 0.5, .1) if not enabled else (0.75, 0.75, 0.75, opacity)
|
||||||
|
plt.rcParams.update({
|
||||||
|
"text.color": plot_color,
|
||||||
|
"axes.labelcolor": plot_color,
|
||||||
|
"axes.edgecolor": plot_color,
|
||||||
|
})
|
||||||
|
|
||||||
|
fig_thumb, ax_thumb = plt.subplots(figsize=(0.85, 0.85), layout="constrained")
|
||||||
|
ax_thumb.plot(range(steps), values, color=plot_color, linewidth=1.5, linestyle="dashed" if hires else "solid")
|
||||||
|
ax_thumb.set_xticks([])
|
||||||
|
ax_thumb.set_yticks([])
|
||||||
|
ax_thumb.set_ylim([-1, 1])
|
||||||
|
ax_thumb.set_xlim([0, steps - 1])
|
||||||
|
if (mode != "both"):
|
||||||
|
ax_thumb.text(
|
||||||
|
0.98, 0.96, mode.upper(),
|
||||||
|
transform=ax_thumb.transAxes,
|
||||||
|
fontsize=8, fontweight='bold', color=plot_color,
|
||||||
|
ha='right', va='top'
|
||||||
|
)
|
||||||
|
plt.close(fig_thumb)
|
||||||
|
|
||||||
|
self.last_vis = fig_main
|
||||||
|
self.last_thumb = fig_thumb
|
||||||
|
return [fig_main, fig_thumb]
|
||||||
|
except Exception:
|
||||||
|
if self.last_vis is not None and self.last_thumb is not None:
|
||||||
|
return [self.last_vis, self.last_thumb]
|
||||||
|
return
|
||||||
|
|
||||||
def xyz_support():
|
def xyz_support():
|
||||||
for scriptDataTuple in scripts.scripts_data:
|
for scriptDataTuple in scripts.scripts_data:
|
||||||
@@ -315,6 +456,11 @@ def xyz_support():
|
|||||||
float,
|
float,
|
||||||
xy_grid.apply_field('DD_fade')
|
xy_grid.apply_field('DD_fade')
|
||||||
)
|
)
|
||||||
|
smooth = xy_grid.AxisOption(
|
||||||
|
'[Detail Daemon] Smooth',
|
||||||
|
bool,
|
||||||
|
xy_grid.apply_field('DD_smooth')
|
||||||
|
)
|
||||||
xy_grid.axis_options.extend([
|
xy_grid.axis_options.extend([
|
||||||
mode,
|
mode,
|
||||||
amount,
|
amount,
|
||||||
@@ -325,10 +471,11 @@ def xyz_support():
|
|||||||
start_offset,
|
start_offset,
|
||||||
end_offset,
|
end_offset,
|
||||||
fade,
|
fade,
|
||||||
|
smooth,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
xyz_support()
|
xyz_support()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'Error trying to add XYZ plot options for Detail Daemon', e)
|
tqdm.write(f'f"\033[31mDetail Daemon:\033[0m Error trying to add XYZ plot options for Detail Daemon: {e}')
|
||||||
|
|||||||
Reference in New Issue
Block a user