From 8031a5f1e4b72825cbcff7427519a35f58b5b96e Mon Sep 17 00:00:00 2001 From: Bingsu Date: Sat, 13 May 2023 16:17:10 +0900 Subject: [PATCH 1/8] feat: enable check with pydantic --- adetailer/__init__.py | 4 ++-- adetailer/args.py | 26 +++++++++++--------------- scripts/!adetailer.py | 7 +++++-- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/adetailer/__init__.py b/adetailer/__init__.py index 44a2b14..56564c4 100644 --- a/adetailer/__init__.py +++ b/adetailer/__init__.py @@ -1,5 +1,5 @@ from .__version__ import __version__ -from .args import AD_ENABLE, ALL_ARGS, ADetailerArgs, enable_check +from .args import AD_ENABLE, ALL_ARGS, ADetailerArgs, EnableChecker from .common import PredictOutput, get_models from .mediapipe import mediapipe_predict from .ultralytics import ultralytics_predict @@ -9,8 +9,8 @@ __all__ = [ "AD_ENABLE", "ADetailerArgs", "ALL_ARGS", + "EnableChecker", "PredictOutput", - "enable_check", "get_models", "mediapipe_predict", "ultralytics_predict", diff --git a/adetailer/args.py b/adetailer/args.py index e8ef28a..cf622d1 100644 --- a/adetailer/args.py +++ b/adetailer/args.py @@ -1,7 +1,6 @@ from collections import UserList -from collections.abc import Mapping from functools import cached_property -from typing import Any, NamedTuple +from typing import Any, NamedTuple, Optional, Union import pydantic from pydantic import ( @@ -107,20 +106,17 @@ class ADetailerArgs(BaseModel, extra=Extra.forbid): return params -def enable_check(*args: Any) -> bool: - if not args: - return False - a0: bool | Mapping = args[0] - ad_model = ALL_ARGS[0].attr +class EnableChecker(BaseModel): + a0: Union[bool, dict] + a1: Optional[dict] - if isinstance(a0, Mapping): - return a0.get(ad_model, "None") != "None" - if len(args) == 1: - return False - - a1 = args[1] - a1_model = a1.get(ad_model, "None") - return a0 and a1_model != "None" + def is_enabled(self) -> bool: + ad_model = ALL_ARGS[0].attr + if isinstance(self.a0, dict): + return self.a0.get(ad_model, "None") != "None" + if self.a1 is None: + return False + return self.a0 and self.a1.get(ad_model, "None") != "None" _all_args = [ diff --git a/scripts/!adetailer.py b/scripts/!adetailer.py index 7841f2c..98f508c 100644 --- a/scripts/!adetailer.py +++ b/scripts/!adetailer.py @@ -18,8 +18,8 @@ from adetailer import ( AD_ENABLE, ALL_ARGS, ADetailerArgs, + EnableChecker, __version__, - enable_check, get_models, mediapipe_predict, ultralytics_predict, @@ -403,7 +403,10 @@ class AfterDetailerScript(scripts.Script): input: {args_!r} """ raise ValueError(dedent(message)) - return enable_check(*args_) + a0 = args_[0] + a1 = args_[1] if len(args_) > 1 else None + checker = EnableChecker(a0=a0, a1=a1) + return checker.is_enabled() def get_args(self, *args_) -> list[ADetailerArgs]: """ From b8d6366234554d4169bab26dd59c4f58b4305f01 Mon Sep 17 00:00:00 2001 From: Bingsu Date: Sun, 14 May 2023 19:04:17 +0900 Subject: [PATCH 2/8] refactor: move ui to adetailer folder --- adetailer/__init__.py | 3 + adetailer/ui.py | 319 ++++++++++++++++++++++++++++++++++++++++++ scripts/!adetailer.py | 299 ++------------------------------------- 3 files changed, 333 insertions(+), 288 deletions(-) create mode 100644 adetailer/ui.py diff --git a/adetailer/__init__.py b/adetailer/__init__.py index 56564c4..82e96df 100644 --- a/adetailer/__init__.py +++ b/adetailer/__init__.py @@ -4,10 +4,13 @@ from .common import PredictOutput, get_models from .mediapipe import mediapipe_predict from .ultralytics import ultralytics_predict +AFTER_DETAILER = "After Detailer" + __all__ = [ "__version__", "AD_ENABLE", "ADetailerArgs", + "AFTER_DETAILER", "ALL_ARGS", "EnableChecker", "PredictOutput", diff --git a/adetailer/ui.py b/adetailer/ui.py new file mode 100644 index 0000000..51007d2 --- /dev/null +++ b/adetailer/ui.py @@ -0,0 +1,319 @@ +from __future__ import annotations + +from functools import partial +from typing import Any + +import gradio as gr + +from adetailer import AFTER_DETAILER +from adetailer.args import AD_ENABLE, ALL_ARGS +from controlnet_ext import controlnet_exists, get_cn_inpaint_models + + +class Widgets: + def tolist(self): + return [getattr(self, attr) for attr in ALL_ARGS.attrs] + + +def gr_interactive(value: bool = True): + return gr.update(interactive=value) + + +def ordinal(n: int) -> str: + d = {1: "st", 2: "nd", 3: "rd"} + return str(n) + ("th" if 11 <= n % 100 <= 13 else d.get(n % 10, "th")) + + +def suffix(n: int, c: str = " ") -> str: + return "" if n == 0 else c + ordinal(n + 1) + + +def on_widget_change(state: dict, value: Any, *, attr: str): + state[attr] = value + return state + + +def on_generate_click(state: dict, *values: Any): + for attr, value in zip(ALL_ARGS.attrs, values): + state[attr] = value + return state + + +def adui( + num_models: int, + is_img2img: bool, + model_list: list[str], + t2i_button: gr.Button, + i2i_button: gr.Button, +): + widgets = [] + states = [] + infotext_fields = [] + + with gr.Accordion(AFTER_DETAILER, open=False, elem_id="AD_main_acc"): + with gr.Row(): + ad_enable = gr.Checkbox( + label="Enable ADetailer", + value=False, + visible=True, + ) + + infotext_fields.append((ad_enable, AD_ENABLE.name)) + + with gr.Group(), gr.Tabs(): + for n in range(num_models): + with gr.Tab(ordinal(n + 1)): + w, state, infofields = one_ui_group( + n=n, + is_img2img=is_img2img, + model_list=model_list, + t2i_button=t2i_button, + i2i_button=i2i_button, + ) + + widgets.append(w) + states.append(state) + infotext_fields.extend(infofields) + + # components: [bool, dict, dict, ...] + components = [ad_enable] + states + return components, infotext_fields + + +def one_ui_group( + n: int, + is_img2img: bool, + model_list: list[str], + t2i_button: gr.Button, + i2i_button: gr.Button, +): + w = Widgets() + state = gr.State({}) + + with gr.Row(): + model_choices = model_list if n == 0 else ["None"] + model_list + + w.ad_model = gr.Dropdown( + label="ADetailer model" + suffix(n), + choices=model_choices, + value=model_choices[0], + visible=True, + type="value", + ) + + with gr.Group(): + with gr.Row(elem_id="AD_toprow_prompt" + suffix(n, "_")): + w.ad_prompt = gr.Textbox( + label="ad_prompt" + suffix(n), + show_label=False, + lines=3, + placeholder="ADetailer prompt" + suffix(n), + elem_id="AD_prompt" + suffix(n, "_"), + ) + + with gr.Row(elem_id="AD_toprow_negative_prompt" + suffix(n, "_")): + w.ad_negative_prompt = gr.Textbox( + label="ad_negative_prompt" + suffix(n), + show_label=False, + lines=2, + placeholder="ADetailer negative prompt" + suffix(n), + elem_id="AD_negative_prompt" + suffix(n, "_"), + ) + + with gr.Group(): + with gr.Row(): + w.ad_conf = gr.Slider( + label="Detection model confidence threshold %" + suffix(n), + minimum=0, + maximum=100, + step=1, + value=30, + visible=True, + ) + w.ad_dilate_erode = gr.Slider( + label="Mask erosion (-) / dilation (+)" + suffix(n), + minimum=-128, + maximum=128, + step=4, + value=32, + visible=True, + ) + + with gr.Row(): + w.ad_x_offset = gr.Slider( + label="Mask x(→) offset" + suffix(n), + minimum=-200, + maximum=200, + step=1, + value=0, + visible=True, + ) + w.ad_y_offset = gr.Slider( + label="Mask y(↑) offset" + suffix(n), + minimum=-200, + maximum=200, + step=1, + value=0, + visible=True, + ) + + with gr.Row(): + w.ad_mask_blur = gr.Slider( + label="Inpaint mask blur" + suffix(n), + minimum=0, + maximum=64, + step=1, + value=4, + visible=True, + ) + + w.ad_denoising_strength = gr.Slider( + label="Inpaint denoising strength" + suffix(n), + minimum=0.0, + maximum=1.0, + step=0.01, + value=0.4, + visible=True, + ) + + with gr.Group(): + with gr.Row(): + with gr.Column(variant="compact"): + w.ad_inpaint_full_res = gr.Checkbox( + label="Inpaint at full resolution " + suffix(n), + value=True, + visible=True, + ) + w.ad_inpaint_full_res_padding = gr.Slider( + label="Inpaint at full resolution padding, pixels " + suffix(n), + minimum=0, + maximum=256, + step=4, + value=0, + visible=True, + ) + + w.ad_inpaint_full_res.change( + gr_interactive, + inputs=w.ad_inpaint_full_res, + outputs=w.ad_inpaint_full_res_padding, + queue=False, + ) + + with gr.Column(variant="compact"): + w.ad_use_inpaint_width_height = gr.Checkbox( + label="Use separate width/height" + suffix(n), + value=False, + visible=True, + ) + + w.ad_inpaint_width = gr.Slider( + label="inpaint width" + suffix(n), + minimum=64, + maximum=2048, + step=4, + value=512, + visible=True, + ) + + w.ad_inpaint_height = gr.Slider( + label="inpaint height" + suffix(n), + minimum=64, + maximum=2048, + step=4, + value=512, + visible=True, + ) + + w.ad_use_inpaint_width_height.change( + lambda value: (gr_interactive(value), gr_interactive(value)), + inputs=w.ad_use_inpaint_width_height, + outputs=[w.ad_inpaint_width, w.ad_inpaint_height], + queue=False, + ) + + with gr.Row(): + with gr.Column(variant="compact"): + w.ad_use_steps = gr.Checkbox( + label="Use separate steps" + suffix(n), + value=False, + visible=True, + ) + + w.ad_steps = gr.Slider( + label="ADetailer steps" + suffix(n), + minimum=1, + maximum=150, + step=1, + value=28, + visible=True, + ) + + w.ad_use_steps.change( + gr_interactive, + inputs=w.ad_use_steps, + outputs=w.ad_steps, + queue=False, + ) + + with gr.Column(variant="compact"): + w.ad_use_cfg_scale = gr.Checkbox( + label="Use separate CFG scale" + suffix(n), + value=False, + visible=True, + ) + + w.ad_cfg_scale = gr.Slider( + label="ADetailer CFG scale" + suffix(n), + minimum=0.0, + maximum=30.0, + step=0.5, + value=7.0, + visible=True, + ) + + w.ad_use_cfg_scale.change( + gr_interactive, + inputs=w.ad_use_cfg_scale, + outputs=w.ad_cfg_scale, + queue=False, + ) + + with gr.Group(), gr.Row(variant="panel"): + cn_inpaint_models = ["None"] + get_cn_inpaint_models() + + w.ad_controlnet_model = gr.Dropdown( + label="ControlNet model" + suffix(n), + choices=cn_inpaint_models, + value="None", + visible=True, + type="value", + interactive=controlnet_exists, + ) + + w.ad_controlnet_weight = gr.Slider( + label="ControlNet weight" + suffix(n), + minimum=0.0, + maximum=1.0, + step=0.05, + value=1.0, + visible=True, + interactive=controlnet_exists, + ) + + for attr in ALL_ARGS.attrs: + widget = getattr(w, attr) + on_change = partial(on_widget_change, attr=attr) + widget.change( + fn=on_change, inputs=[state, widget], outputs=[state], queue=False + ) + + all_inputs = [state] + w.tolist() + target_button = i2i_button if is_img2img else t2i_button + target_button.click( + fn=on_generate_click, inputs=all_inputs, outputs=state, queue=False + ) + + infotext_fields = [(getattr(w, attr), name + suffix(n)) for attr, name in ALL_ARGS] + + return w, state, infotext_fields diff --git a/scripts/!adetailer.py b/scripts/!adetailer.py index 98f508c..d64b873 100644 --- a/scripts/!adetailer.py +++ b/scripts/!adetailer.py @@ -15,7 +15,7 @@ import torch import modules # noqa: F401 from adetailer import ( - AD_ENABLE, + AFTER_DETAILER, ALL_ARGS, ADetailerArgs, EnableChecker, @@ -25,7 +25,8 @@ from adetailer import ( ultralytics_predict, ) from adetailer.common import mask_preprocess -from controlnet_ext import ControlNetExt, controlnet_exists, get_cn_inpaint_models +from adetailer.ui import adui, ordinal, suffix +from controlnet_ext import ControlNetExt, controlnet_exists from modules import images, safe, script_callbacks, scripts, shared from modules.paths import data_path, models_path from modules.processing import ( @@ -43,7 +44,6 @@ try: except Exception: pass -AFTER_DETAILER = "After Detailer" no_huggingface = getattr(cmd_opts, "ad_no_huggingface", False) adetailer_dir = Path(models_path, "adetailer") model_mapping = get_models(adetailer_dir, huggingface=not no_huggingface) @@ -55,11 +55,6 @@ print( ) -class Widgets: - def tolist(self): - return [getattr(self, attr) for attr in ALL_ARGS.attrs] - - class ChangeTorchLoad: def __enter__(self): self.orig = torch.load @@ -69,30 +64,6 @@ class ChangeTorchLoad: torch.load = self.orig -def gr_interactive(value: bool = True): - return gr.update(interactive=value) - - -def ordinal(n: int) -> str: - d = {1: "st", 2: "nd", 3: "rd"} - return str(n) + ("th" if 11 <= n % 100 <= 13 else d.get(n % 10, "th")) - - -def suffix(n: int, c: str = " ") -> str: - return "" if n == 0 else c + ordinal(n + 1) - - -def on_widget_change(state: dict, value: Any, *, attr: str): - state[attr] = value - return state - - -def on_generate_click(state: dict, *values: Any): - for attr, value in zip(ALL_ARGS.attrs, values): - state[attr] = value - return state - - class AfterDetailerScript(scripts.Script): def __init__(self): super().__init__() @@ -107,266 +78,18 @@ class AfterDetailerScript(scripts.Script): def ui(self, is_img2img): num_models = opts.data.get("ad_max_models", 2) - widgets = [] - states = [] - self.infotext_fields = [] - - with gr.Accordion(AFTER_DETAILER, open=False, elem_id="AD_main_acc"): - with gr.Row(): - ad_enable = gr.Checkbox( - label="Enable ADetailer", - value=False, - visible=True, - ) - - self.infotext_fields.append((ad_enable, AD_ENABLE.name)) - - with gr.Group(), gr.Tabs(): - for n in range(num_models): - with gr.Tab(ordinal(n + 1)): - w, state, infofields = self.one_ui_group(n, is_img2img) - - widgets.append(w) - states.append(state) - self.infotext_fields.extend(infofields) - - # return: [bool, dict, dict, ...] - return [ad_enable] + states - - def one_ui_group(self, n: int, is_img2img: bool): model_list = list(model_mapping.keys()) - w = Widgets() - state = gr.State({}) - with gr.Row(): - model_choices = model_list if n == 0 else ["None"] + model_list - - w.ad_model = gr.Dropdown( - label="ADetailer model" + suffix(n), - choices=model_choices, - value=model_choices[0], - visible=True, - type="value", - ) - - with gr.Group(): - with gr.Row(elem_id="AD_toprow_prompt" + suffix(n, "_")): - w.ad_prompt = gr.Textbox( - label="ad_prompt" + suffix(n), - show_label=False, - lines=3, - placeholder="ADetailer prompt" + suffix(n), - elem_id="AD_prompt" + suffix(n, "_"), - ) - - with gr.Row(elem_id="AD_toprow_negative_prompt" + suffix(n, "_")): - w.ad_negative_prompt = gr.Textbox( - label="ad_negative_prompt" + suffix(n), - show_label=False, - lines=2, - placeholder="ADetailer negative prompt" + suffix(n), - elem_id="AD_negative_prompt" + suffix(n, "_"), - ) - - with gr.Group(): - with gr.Row(): - w.ad_conf = gr.Slider( - label="Detection model confidence threshold %" + suffix(n), - minimum=0, - maximum=100, - step=1, - value=30, - visible=True, - ) - w.ad_dilate_erode = gr.Slider( - label="Mask erosion (-) / dilation (+)" + suffix(n), - minimum=-128, - maximum=128, - step=4, - value=32, - visible=True, - ) - - with gr.Row(): - w.ad_x_offset = gr.Slider( - label="Mask x(→) offset" + suffix(n), - minimum=-200, - maximum=200, - step=1, - value=0, - visible=True, - ) - w.ad_y_offset = gr.Slider( - label="Mask y(↑) offset" + suffix(n), - minimum=-200, - maximum=200, - step=1, - value=0, - visible=True, - ) - - with gr.Row(): - w.ad_mask_blur = gr.Slider( - label="Inpaint mask blur" + suffix(n), - minimum=0, - maximum=64, - step=1, - value=4, - visible=True, - ) - - w.ad_denoising_strength = gr.Slider( - label="Inpaint denoising strength" + suffix(n), - minimum=0.0, - maximum=1.0, - step=0.01, - value=0.4, - visible=True, - ) - - with gr.Group(): - with gr.Row(): - with gr.Column(variant="compact"): - w.ad_inpaint_full_res = gr.Checkbox( - label="Inpaint at full resolution " + suffix(n), - value=True, - visible=True, - ) - w.ad_inpaint_full_res_padding = gr.Slider( - label="Inpaint at full resolution padding, pixels " + suffix(n), - minimum=0, - maximum=256, - step=4, - value=0, - visible=True, - ) - - w.ad_inpaint_full_res.change( - gr_interactive, - inputs=w.ad_inpaint_full_res, - outputs=w.ad_inpaint_full_res_padding, - queue=False, - ) - - with gr.Column(variant="compact"): - w.ad_use_inpaint_width_height = gr.Checkbox( - label="Use separate width/height" + suffix(n), - value=False, - visible=True, - ) - - w.ad_inpaint_width = gr.Slider( - label="inpaint width" + suffix(n), - minimum=64, - maximum=2048, - step=4, - value=512, - visible=True, - ) - - w.ad_inpaint_height = gr.Slider( - label="inpaint height" + suffix(n), - minimum=64, - maximum=2048, - step=4, - value=512, - visible=True, - ) - - w.ad_use_inpaint_width_height.change( - lambda value: (gr_interactive(value), gr_interactive(value)), - inputs=w.ad_use_inpaint_width_height, - outputs=[w.ad_inpaint_width, w.ad_inpaint_height], - queue=False, - ) - - with gr.Row(): - with gr.Column(variant="compact"): - w.ad_use_steps = gr.Checkbox( - label="Use separate steps" + suffix(n), - value=False, - visible=True, - ) - - w.ad_steps = gr.Slider( - label="ADetailer steps" + suffix(n), - minimum=1, - maximum=150, - step=1, - value=28, - visible=True, - ) - - w.ad_use_steps.change( - gr_interactive, - inputs=w.ad_use_steps, - outputs=w.ad_steps, - queue=False, - ) - - with gr.Column(variant="compact"): - w.ad_use_cfg_scale = gr.Checkbox( - label="Use separate CFG scale" + suffix(n), - value=False, - visible=True, - ) - - w.ad_cfg_scale = gr.Slider( - label="ADetailer CFG scale" + suffix(n), - minimum=0.0, - maximum=30.0, - step=0.5, - value=7.0, - visible=True, - ) - - w.ad_use_cfg_scale.change( - gr_interactive, - inputs=w.ad_use_cfg_scale, - outputs=w.ad_cfg_scale, - queue=False, - ) - - with gr.Group(), gr.Row(variant="panel"): - cn_inpaint_models = ["None"] + get_cn_inpaint_models() - - w.ad_controlnet_model = gr.Dropdown( - label="ControlNet model" + suffix(n), - choices=cn_inpaint_models, - value="None", - visible=True, - type="value", - interactive=controlnet_exists, - ) - - w.ad_controlnet_weight = gr.Slider( - label="ControlNet weight" + suffix(n), - minimum=0.0, - maximum=1.0, - step=0.05, - value=1.0, - visible=True, - interactive=controlnet_exists, - ) - - for attr in ALL_ARGS.attrs: - widget = getattr(w, attr) - on_change = partial(on_widget_change, attr=attr) - widget.change( - fn=on_change, inputs=[state, widget], outputs=[state], queue=False - ) - - all_inputs = [state] + w.tolist() - target_button = img2img_submit_button if is_img2img else txt2img_submit_button - target_button.click( - fn=on_generate_click, inputs=all_inputs, outputs=state, queue=False + components, infotext_fields = adui( + num_models, + is_img2img, + model_list, + txt2img_submit_button, + img2img_submit_button, ) - infotext_fields = [ - (getattr(w, attr), name + suffix(n)) for attr, name in ALL_ARGS - ] - - return w, state, infotext_fields + self.infotext_fields = infotext_fields + return components def init_controlnet_ext(self) -> None: if self.controlnet_ext is not None: From 48e75351153630f12e2733f006d9dde1fb5447fc Mon Sep 17 00:00:00 2001 From: Bingsu Date: Sun, 14 May 2023 20:06:22 +0900 Subject: [PATCH 3/8] fix: disable controlnet units --- adetailer/__version__.py | 2 +- scripts/!adetailer.py | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/adetailer/__version__.py b/adetailer/__version__.py index e2e33d4..e74b40b 100644 --- a/adetailer/__version__.py +++ b/adetailer/__version__.py @@ -1 +1 @@ -__version__ = "23.5.12" +__version__ = "23.5.13.dev1" diff --git a/scripts/!adetailer.py b/scripts/!adetailer.py index d64b873..8093e93 100644 --- a/scripts/!adetailer.py +++ b/scripts/!adetailer.py @@ -3,9 +3,7 @@ from __future__ import annotations import platform import sys import traceback -from collections.abc import Mapping from copy import copy, deepcopy -from functools import partial from pathlib import Path from textwrap import dedent from typing import Any @@ -257,10 +255,11 @@ class AfterDetailerScript(scripts.Script): def script_filter(self, p, args: ADetailerArgs): script_runner = copy(p.scripts) + script_args = deepcopy(p.script_args) ad_only_seleted_scripts = opts.data.get("ad_only_seleted_scripts", True) if not ad_only_seleted_scripts: - return script_runner + return script_runner, script_args default = "dynamic_prompting,dynamic_thresholding,wildcards,wildcard_recursive" ad_script_names = opts.data.get("ad_script_names", default) @@ -270,6 +269,7 @@ class AfterDetailerScript(scripts.Script): for name in (script_name, script_name.strip()) } if args.ad_controlnet_model != "None": + self.disable_controlnet_units(script_args) script_names_set.add("controlnet") filtered_alwayson = [] @@ -280,7 +280,14 @@ class AfterDetailerScript(scripts.Script): filtered_alwayson.append(script_object) script_runner.alwayson_scripts = filtered_alwayson - return script_runner + return script_runner, script_args + + def disable_controlnet_units(self, script_args: list[Any]) -> None: + for obj in script_args: + if "controlnet" in obj.__class__.__name__.lower() and hasattr( + obj, "enabled" + ): + obj.enabled = False def get_i2i_p(self, p, args: ADetailerArgs, image): prompt, negative_prompt = self.get_prompt(p, args) @@ -327,8 +334,7 @@ class AfterDetailerScript(scripts.Script): do_not_save_grid=True, ) - i2i.scripts = self.script_filter(p, args) - i2i.script_args = deepcopy(p.script_args) + i2i.scripts, i2i.script_args = self.script_filter(p, args) i2i._disable_adetailer = True if args.ad_controlnet_model != "None": From a5b82742c8e5850b4881878d0c2ade96ee6aba99 Mon Sep 17 00:00:00 2001 From: Bingsu Date: Sun, 14 May 2023 22:13:00 +0900 Subject: [PATCH 4/8] refactor: get_prompt --- scripts/!adetailer.py | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/scripts/!adetailer.py b/scripts/!adetailer.py index 8093e93..deb9d99 100644 --- a/scripts/!adetailer.py +++ b/scripts/!adetailer.py @@ -177,28 +177,23 @@ class AfterDetailerScript(scripts.Script): return device + def _get_prompt(self, ad_prompt: str, all_prompts: list[str], i: int, default: str): + if ad_prompt: + return ad_prompt + if not all_prompts: + return default + if i < len(all_prompts): + return all_prompts[i] + j = i % len(all_prompts) + return all_prompts[j] + def get_prompt(self, p, args: ADetailerArgs) -> tuple[str, str]: i = p._idx - if args.ad_prompt: - prompt = args.ad_prompt - elif not p.all_prompts: - prompt = p.prompt - elif i < len(p.all_prompts): - prompt = p.all_prompts[i] - else: - j = i % len(p.all_prompts) - prompt = p.all_prompts[j] - - if args.ad_negative_prompt: - negative_prompt = args.ad_negative_prompt - elif not p.all_negative_prompts: - negative_prompt = p.negative_prompt - elif i < len(p.all_negative_prompts): - negative_prompt = p.all_negative_prompts[i] - else: - j = i % len(p.all_negative_prompts) - negative_prompt = p.all_negative_prompts[j] + prompt = self._get_prompt(args.ad_prompt, p.all_prompts, i, p.prompt) + negative_prompt = self._get_prompt( + args.ad_negative_prompt, p.all_negative_prompts, i, p.negative_prompt + ) return prompt, negative_prompt From e0b947eb0a58acf9d77c77242b08b0a4b63295b0 Mon Sep 17 00:00:00 2001 From: Bingsu Date: Sun, 14 May 2023 22:42:13 +0900 Subject: [PATCH 5/8] feat: split ad_prompt with [SEP] --- scripts/!adetailer.py | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/scripts/!adetailer.py b/scripts/!adetailer.py index deb9d99..1617ffc 100644 --- a/scripts/!adetailer.py +++ b/scripts/!adetailer.py @@ -1,6 +1,7 @@ from __future__ import annotations import platform +import re import sys import traceback from copy import copy, deepcopy @@ -177,9 +178,9 @@ class AfterDetailerScript(scripts.Script): return device - def _get_prompt(self, ad_prompt: str, all_prompts: list[str], i: int, default: str): - if ad_prompt: - return ad_prompt + def prompt_blank_replacement( + self, all_prompts: list[str], i: int, default: str + ) -> str: if not all_prompts: return default if i < len(all_prompts): @@ -187,7 +188,17 @@ class AfterDetailerScript(scripts.Script): j = i % len(all_prompts) return all_prompts[j] - def get_prompt(self, p, args: ADetailerArgs) -> tuple[str, str]: + def _get_prompt( + self, ad_prompt: str, all_prompts: list[str], i: int, default: str + ) -> list[str]: + prompts = re.split(r"\s*\[SEP\]\s*", ad_prompt) + blank_replacement = self.prompt_blank_replacement(all_prompts, i, default) + for n in range(len(prompts)): + if not prompts[n]: + prompts[n] = blank_replacement + return prompts + + def get_prompt(self, p, args: ADetailerArgs) -> tuple[list[str], list[str]]: i = p._idx prompt = self._get_prompt(args.ad_prompt, p.all_prompts, i, p.prompt) @@ -285,7 +296,6 @@ class AfterDetailerScript(scripts.Script): obj.enabled = False def get_i2i_p(self, p, args: ADetailerArgs, image): - prompt, negative_prompt = self.get_prompt(p, args) seed, subseed = self.get_seed(p) width, height = self.get_width_height(p, args) steps = self.get_steps(p, args) @@ -308,8 +318,8 @@ class AfterDetailerScript(scripts.Script): sd_model=p.sd_model, outpath_samples=p.outpath_samples, outpath_grids=p.outpath_grids, - prompt=prompt, - negative_prompt=negative_prompt, + prompt="", # replace later + negative_prompt="", styles=p.styles, seed=seed, subseed=subseed, @@ -359,6 +369,16 @@ class AfterDetailerScript(scripts.Script): raise ValueError(msg) return model_mapping[name] + def i2i_prompts_replace( + self, i2i, prompts: list[str], negative_prompts: list[str], j: int + ): + i1 = j % len(prompts) + i2 = j % len(negative_prompts) + prompt = prompts[i1] + negative_prompt = negative_prompts[i2] + i2i.prompt = prompt + i2i.negative_prompt = negative_prompt + def process(self, p, *args_): if getattr(p, "_disable_adetailer", False): return @@ -380,6 +400,7 @@ class AfterDetailerScript(scripts.Script): i2i = self.get_i2i_p(p, args, pp.image) seed, subseed = self.get_seed(p) + ad_prompts, ad_negatives = self.get_prompt(p, args) is_mediapipe = args.ad_model.lower().startswith("mediapipe") @@ -424,6 +445,7 @@ class AfterDetailerScript(scripts.Script): p2 = copy(i2i) for j in range(steps): p2.image_mask = masks[j] + self.i2i_prompts_replace(p2, ad_prompts, ad_negatives, j) processed = process_images(p2) p2 = copy(i2i) From 80f8975d4e68ac87331d890867f6df8aef535dad Mon Sep 17 00:00:00 2001 From: Bingsu Date: Mon, 15 May 2023 12:29:34 +0900 Subject: [PATCH 6/8] fix: prompt repeat -> use last --- scripts/!adetailer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/!adetailer.py b/scripts/!adetailer.py index 1617ffc..80b010a 100644 --- a/scripts/!adetailer.py +++ b/scripts/!adetailer.py @@ -372,8 +372,8 @@ class AfterDetailerScript(scripts.Script): def i2i_prompts_replace( self, i2i, prompts: list[str], negative_prompts: list[str], j: int ): - i1 = j % len(prompts) - i2 = j % len(negative_prompts) + i1 = min(j, len(prompts) - 1) + i2 = min(j, len(negative_prompts) - 1) prompt = prompts[i1] negative_prompt = negative_prompts[i2] i2i.prompt = prompt From 74484acd292e150c5c9fa336e3d1260ee71f2387 Mon Sep 17 00:00:00 2001 From: Bingsu Date: Mon, 15 May 2023 14:03:49 +0900 Subject: [PATCH 7/8] feat: make adetailer folder --- scripts/!adetailer.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/!adetailer.py b/scripts/!adetailer.py index 80b010a..355c738 100644 --- a/scripts/!adetailer.py +++ b/scripts/!adetailer.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os import platform import re import sys @@ -48,6 +49,12 @@ adetailer_dir = Path(models_path, "adetailer") model_mapping = get_models(adetailer_dir, huggingface=not no_huggingface) txt2img_submit_button = img2img_submit_button = None +if ( + not adetailer_dir.exists() + and adetailer_dir.parent.exists() + and os.access(adetailer_dir.parent, os.W_OK) +): + adetailer_dir.mkdir() print( f"[-] ADetailer initialized. version: {__version__}, num models: {len(model_mapping)}" From cde0acd38f0737b9849937689e875ecd43ca4488 Mon Sep 17 00:00:00 2001 From: Bingsu Date: Mon, 15 May 2023 14:19:35 +0900 Subject: [PATCH 8/8] chore: v23.5.13 --- CHANGELOG.md | 9 +++++++++ adetailer/__version__.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 346ae1d..8622e99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +### 2023-05-15 + +- v23.5.13 +- `[SEP]`으로 ad prompt를 분리하여 적용하는 기능 추가 +- enable checker를 다시 pydantic으로 변경함 +- ui 관련 함수를 adetailer.ui 폴더로 분리함 +- controlnet을 사용할 때 모든 controlnet unit 비활성화 +- adetailer 폴더가 없으면 만들게 함 + ### 2023-05-13 - v23.5.12 diff --git a/adetailer/__version__.py b/adetailer/__version__.py index e74b40b..5a20257 100644 --- a/adetailer/__version__.py +++ b/adetailer/__version__.py @@ -1 +1 @@ -__version__ = "23.5.13.dev1" +__version__ = "23.5.13"