mirror of
https://github.com/Bing-su/adetailer.git
synced 2026-03-06 14:00:11 +00:00
Merge branch 'dev' into main
This commit is contained in:
@@ -8,12 +8,12 @@ repos:
|
||||
- id: mixed-line-ending
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: "v0.0.292"
|
||||
rev: "v0.1.3"
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix, --exit-non-zero-on-fix]
|
||||
|
||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||
rev: 23.9.1
|
||||
rev: 23.10.1
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
13
CHANGELOG.md
13
CHANGELOG.md
@@ -1,5 +1,18 @@
|
||||
# Changelog
|
||||
|
||||
## 2023-10-30
|
||||
|
||||
- v23.11.0
|
||||
- 이미지의 인덱스 계산방법 변경
|
||||
- webui 1.1.0 미만에서 adetailer 실행 불가능하게 함
|
||||
- 컨트롤넷 preprocessor 선택지 늘림
|
||||
- 추가 yolo 모델 디렉터리를 설정할 수 있는 옵션 추가
|
||||
- infotext에 `/`가 있는 항목이 exif에서 복원되지 않는 문제 수정
|
||||
- 이전 버전에 생성된 이미지는 여전히 복원안됨
|
||||
- 같은 탭에서 항상 같은 시드를 적용하게 하는 옵션 추가
|
||||
- 컨트롤넷 1.1.411 (f2aafcf2beb99a03cbdf7db73852228ccd6bd1d6) 버전을 사용중일 경우,
|
||||
webui 버전 1.6.0 미만에서 사용할 수 없다는 메세지 출력
|
||||
|
||||
## 2023-10-15
|
||||
|
||||
- v23.10.1
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "23.10.1"
|
||||
__version__ = "23.11.0"
|
||||
|
||||
@@ -14,11 +14,11 @@ from pydantic import (
|
||||
confloat,
|
||||
conint,
|
||||
constr,
|
||||
root_validator,
|
||||
validator,
|
||||
)
|
||||
|
||||
cn_model_regex = r".*(inpaint|tile|scribble|lineart|openpose).*|^None$"
|
||||
cn_module_regex = r".*(inpaint|tile|pidi|lineart|openpose).*|^None$"
|
||||
|
||||
|
||||
class Arg(NamedTuple):
|
||||
@@ -71,20 +71,12 @@ class ADetailerArgs(BaseModel, extra=Extra.forbid):
|
||||
ad_clip_skip: conint(ge=1, le=12) = 1
|
||||
ad_restore_face: bool = False
|
||||
ad_controlnet_model: constr(regex=cn_model_regex) = "None"
|
||||
ad_controlnet_module: Optional[constr(regex=r".*inpaint.*|^None$")] = None
|
||||
ad_controlnet_module: constr(regex=cn_module_regex) = "None"
|
||||
ad_controlnet_weight: confloat(ge=0.0, le=1.0) = 1.0
|
||||
ad_controlnet_guidance_start: confloat(ge=0.0, le=1.0) = 0.0
|
||||
ad_controlnet_guidance_end: confloat(ge=0.0, le=1.0) = 1.0
|
||||
is_api: bool = True
|
||||
|
||||
@root_validator(skip_on_failure=True)
|
||||
def ad_controlnt_module_validator(cls, values): # noqa: N805
|
||||
cn_model = values.get("ad_controlnet_model", "None")
|
||||
cn_module = values.get("ad_controlnet_module", None)
|
||||
if "inpaint" not in cn_model or cn_module == "None":
|
||||
values["ad_controlnet_module"] = None
|
||||
return values
|
||||
|
||||
@validator("is_api", pre=True)
|
||||
def is_api_validator(cls, v: Any): # noqa: N805
|
||||
"tuple is json serializable but cannot be made with json deserialize."
|
||||
@@ -122,12 +114,12 @@ class ADetailerArgs(BaseModel, extra=Extra.forbid):
|
||||
ppop("ADetailer mask max ratio", cond=1.0)
|
||||
ppop("ADetailer x offset", cond=0)
|
||||
ppop("ADetailer y offset", cond=0)
|
||||
ppop("ADetailer mask merge/invert", cond="None")
|
||||
ppop("ADetailer mask merge invert", cond="None")
|
||||
ppop("ADetailer inpaint only masked", ["ADetailer inpaint padding"])
|
||||
ppop(
|
||||
"ADetailer use inpaint width/height",
|
||||
"ADetailer use inpaint width height",
|
||||
[
|
||||
"ADetailer use inpaint width/height",
|
||||
"ADetailer use inpaint width height",
|
||||
"ADetailer inpaint width",
|
||||
"ADetailer inpaint height",
|
||||
],
|
||||
@@ -174,7 +166,7 @@ class ADetailerArgs(BaseModel, extra=Extra.forbid):
|
||||
],
|
||||
cond="None",
|
||||
)
|
||||
ppop("ADetailer ControlNet module")
|
||||
ppop("ADetailer ControlNet module", cond="None")
|
||||
ppop("ADetailer ControlNet weight", cond=1.0)
|
||||
ppop("ADetailer ControlNet guidance start", cond=0.0)
|
||||
ppop("ADetailer ControlNet guidance end", cond=1.0)
|
||||
@@ -195,13 +187,13 @@ _all_args = [
|
||||
("ad_mask_max_ratio", "ADetailer mask max ratio"),
|
||||
("ad_x_offset", "ADetailer x offset"),
|
||||
("ad_y_offset", "ADetailer y offset"),
|
||||
("ad_dilate_erode", "ADetailer dilate/erode"),
|
||||
("ad_mask_merge_invert", "ADetailer mask merge/invert"),
|
||||
("ad_dilate_erode", "ADetailer dilate erode"),
|
||||
("ad_mask_merge_invert", "ADetailer mask merge invert"),
|
||||
("ad_mask_blur", "ADetailer mask blur"),
|
||||
("ad_denoising_strength", "ADetailer denoising strength"),
|
||||
("ad_inpaint_only_masked", "ADetailer inpaint only masked"),
|
||||
("ad_inpaint_only_masked_padding", "ADetailer inpaint padding"),
|
||||
("ad_use_inpaint_width_height", "ADetailer use inpaint width/height"),
|
||||
("ad_use_inpaint_width_height", "ADetailer use inpaint width height"),
|
||||
("ad_inpaint_width", "ADetailer inpaint width"),
|
||||
("ad_inpaint_height", "ADetailer inpaint height"),
|
||||
("ad_use_steps", "ADetailer use separate steps"),
|
||||
|
||||
@@ -36,18 +36,16 @@ def hf_download(file: str):
|
||||
return path
|
||||
|
||||
|
||||
def scan_model_dir(path_: str | Path) -> list[Path]:
|
||||
if not path_ or not (path := Path(path_)).is_dir():
|
||||
return []
|
||||
return [p for p in path.rglob("*") if p.is_file() and p.suffix in (".pt", ".pth")]
|
||||
|
||||
|
||||
def get_models(
|
||||
model_dir: Union[str, Path], huggingface: bool = True
|
||||
) -> OrderedDict[str, Optional[str]]:
|
||||
model_dir = Path(model_dir)
|
||||
if model_dir.is_dir():
|
||||
model_paths = [
|
||||
p
|
||||
for p in model_dir.rglob("*")
|
||||
if p.is_file() and p.suffix in (".pt", ".pth")
|
||||
]
|
||||
else:
|
||||
model_paths = []
|
||||
model_dir: str | Path, extra_dir: str | Path = "", huggingface: bool = True
|
||||
) -> OrderedDict[str, str | None]:
|
||||
model_paths = [*scan_model_dir(model_dir), *scan_model_dir(extra_dir)]
|
||||
|
||||
models = OrderedDict()
|
||||
if huggingface:
|
||||
|
||||
@@ -220,6 +220,7 @@ def filter_k_largest(pred: PredictOutput, k: int = 0) -> PredictOutput:
|
||||
return pred
|
||||
areas = [bbox_area(bbox) for bbox in pred.bboxes]
|
||||
idx = np.argsort(areas)[-k:]
|
||||
idx = idx[::-1]
|
||||
pred.bboxes = [pred.bboxes[i] for i in idx]
|
||||
pred.masks = [pred.masks[i] for i in idx]
|
||||
return pred
|
||||
|
||||
@@ -92,7 +92,7 @@ def library_version():
|
||||
for lib in libraries:
|
||||
try:
|
||||
d[lib] = version(lib)
|
||||
except Exception:
|
||||
except Exception: # noqa: PERF203
|
||||
d[lib] = "Unknown"
|
||||
return d
|
||||
|
||||
@@ -147,7 +147,7 @@ def rich_traceback(func: Callable) -> Callable:
|
||||
]
|
||||
if data
|
||||
]
|
||||
tables.append(Traceback())
|
||||
tables.append(Traceback(extra_lines=1))
|
||||
|
||||
console.print(Panel(Group(*tables)))
|
||||
output = "\n" + string.getvalue()
|
||||
|
||||
@@ -11,11 +11,22 @@ from adetailer import AFTER_DETAILER, __version__
|
||||
from adetailer.args import ALL_ARGS, MASK_MERGE_INVERT
|
||||
from controlnet_ext import controlnet_exists, get_cn_models
|
||||
|
||||
cn_module_choices = [
|
||||
"inpaint_global_harmonious",
|
||||
"inpaint_only",
|
||||
"inpaint_only+lama",
|
||||
]
|
||||
cn_module_choices = {
|
||||
"inpaint": [
|
||||
"inpaint_global_harmonious",
|
||||
"inpaint_only",
|
||||
"inpaint_only+lama",
|
||||
],
|
||||
"lineart": [
|
||||
"lineart_coarse",
|
||||
"lineart_realistic",
|
||||
"lineart_anime",
|
||||
"lineart_anime_denoise",
|
||||
],
|
||||
"openpose": ["openpose_full", "dw_openpose_full"],
|
||||
"tile": ["tile_resample", "tile_colorfix", "tile_colorfix+sharp"],
|
||||
"scribble": ["t2ia_sketch_pidi"],
|
||||
}
|
||||
|
||||
|
||||
class Widgets(SimpleNamespace):
|
||||
@@ -58,11 +69,11 @@ def on_generate_click(state: dict, *values: Any):
|
||||
return state
|
||||
|
||||
|
||||
def on_cn_model_update(cn_model: str):
|
||||
if "inpaint" in cn_model:
|
||||
return gr.update(
|
||||
visible=True, choices=cn_module_choices, value=cn_module_choices[0]
|
||||
)
|
||||
def on_cn_model_update(cn_model_name: str):
|
||||
for t in cn_module_choices:
|
||||
if t in cn_model_name:
|
||||
choices = cn_module_choices[t]
|
||||
return gr.update(visible=True, choices=choices, value=choices[0])
|
||||
return gr.update(visible=False, choices=["None"], value="None")
|
||||
|
||||
|
||||
@@ -564,8 +575,8 @@ def controlnet(w: Widgets, n: int, is_img2img: bool):
|
||||
|
||||
w.ad_controlnet_module = gr.Dropdown(
|
||||
label="ControlNet module" + suffix(n),
|
||||
choices=cn_module_choices,
|
||||
value="inpaint_global_harmonious",
|
||||
choices=["None"],
|
||||
value="None",
|
||||
visible=False,
|
||||
type="value",
|
||||
interactive=controlnet_exists,
|
||||
|
||||
@@ -4,12 +4,22 @@ import importlib
|
||||
import re
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from textwrap import dedent
|
||||
|
||||
from modules import extensions, sd_models, shared
|
||||
from modules.paths import data_path, models_path, script_path
|
||||
|
||||
ext_path = Path(data_path, "extensions")
|
||||
ext_builtin_path = Path(script_path, "extensions-builtin")
|
||||
try:
|
||||
from modules.paths import extensions_builtin_dir, extensions_dir, models_path
|
||||
except ImportError as e:
|
||||
msg = """
|
||||
[-] ADetailer: `stable-diffusion-webui < 1.1.0` is no longer supported.
|
||||
Please upgrade to stable-diffusion-webui >= 1.1.0.
|
||||
or you can use ADetailer v23.10.1 (https://github.com/Bing-su/adetailer/archive/refs/tags/v23.10.1.zip)
|
||||
"""
|
||||
raise RuntimeError(dedent(msg)) from e
|
||||
|
||||
ext_path = Path(extensions_dir)
|
||||
ext_builtin_path = Path(extensions_builtin_dir)
|
||||
controlnet_exists = False
|
||||
controlnet_path = None
|
||||
cn_base_path = ""
|
||||
@@ -29,7 +39,7 @@ cn_model_module = {
|
||||
"scribble": "t2ia_sketch_pidi",
|
||||
"lineart": "lineart_coarse",
|
||||
"openpose": "openpose_full",
|
||||
"tile": None,
|
||||
"tile": "tile_resample",
|
||||
}
|
||||
cn_model_regex = re.compile("|".join(cn_model_module.keys()))
|
||||
|
||||
@@ -60,11 +70,13 @@ class ControlNetExt:
|
||||
if (not self.cn_available) or model == "None":
|
||||
return
|
||||
|
||||
if module is None:
|
||||
if module is None or module == "None":
|
||||
for m, v in cn_model_module.items():
|
||||
if m in model:
|
||||
module = v
|
||||
break
|
||||
else:
|
||||
module = None
|
||||
|
||||
cn_units = [
|
||||
self.external_cn.ControlNetUnit(
|
||||
@@ -78,7 +90,13 @@ class ControlNetExt:
|
||||
)
|
||||
]
|
||||
|
||||
self.external_cn.update_cn_script_in_processing(p, cn_units)
|
||||
try:
|
||||
self.external_cn.update_cn_script_in_processing(p, cn_units)
|
||||
except AttributeError as e:
|
||||
if "script_args_value" not in str(e):
|
||||
raise
|
||||
msg = "[-] Adetailer: ControlNet option not available in WEBUI version lower than 1.6.0 due to updates in ControlNet"
|
||||
raise RuntimeError(msg) from e
|
||||
|
||||
|
||||
def get_cn_model_dirs() -> list[Path]:
|
||||
@@ -91,9 +109,9 @@ def get_cn_model_dirs() -> list[Path]:
|
||||
ext_dir2 = getattr(shared.cmd_opts, "controlnet_dir", "")
|
||||
|
||||
dirs = [cn_model_dir]
|
||||
for ext_dir in [cn_model_dir_old, ext_dir1, ext_dir2]:
|
||||
if ext_dir:
|
||||
dirs.append(Path(ext_dir))
|
||||
dirs += [
|
||||
Path(ext_dir) for ext_dir in [cn_model_dir_old, ext_dir1, ext_dir2] if ext_dir
|
||||
]
|
||||
|
||||
return dirs
|
||||
|
||||
|
||||
@@ -26,8 +26,10 @@ select = [
|
||||
"I001",
|
||||
"ISC",
|
||||
"N",
|
||||
"PERF",
|
||||
"PIE",
|
||||
"PT",
|
||||
"PTH",
|
||||
"RET",
|
||||
"RUF",
|
||||
"SIM",
|
||||
|
||||
@@ -53,7 +53,10 @@ from modules.shared import cmd_opts, opts, state
|
||||
|
||||
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)
|
||||
extra_models_dir = shared.opts.data.get("ad_extra_models_dir", "")
|
||||
model_mapping = get_models(
|
||||
adetailer_dir, extra_dir=extra_models_dir, huggingface=not no_huggingface
|
||||
)
|
||||
txt2img_submit_button = img2img_submit_button = None
|
||||
SCRIPT_DEFAULT = "dynamic_prompting,dynamic_thresholding,wildcard_recursive,wildcards,lora_block_weight"
|
||||
|
||||
@@ -223,6 +226,13 @@ class AfterDetailerScript(scripts.Script):
|
||||
else:
|
||||
p._ad_skip_img2img = False
|
||||
|
||||
@staticmethod
|
||||
def get_i(p) -> int:
|
||||
it = p.iteration
|
||||
bs = p.batch_size
|
||||
i = p.batch_index
|
||||
return it * bs + i
|
||||
|
||||
def get_args(self, p, *args_) -> list[ADetailerArgs]:
|
||||
"""
|
||||
`args_` is at least 1 in length by `is_ad_enabled` immediately above
|
||||
@@ -308,7 +318,7 @@ class AfterDetailerScript(scripts.Script):
|
||||
return prompts
|
||||
|
||||
def get_prompt(self, p, args: ADetailerArgs) -> tuple[list[str], list[str]]:
|
||||
i = p._ad_idx
|
||||
i = self.get_i(p)
|
||||
prompt_sr = p._ad_xyz_prompt_sr if hasattr(p, "_ad_xyz_prompt_sr") else []
|
||||
|
||||
prompt = self._get_prompt(args.ad_prompt, p.all_prompts, i, p.prompt, prompt_sr)
|
||||
@@ -323,7 +333,7 @@ class AfterDetailerScript(scripts.Script):
|
||||
return prompt, negative_prompt
|
||||
|
||||
def get_seed(self, p) -> tuple[int, int]:
|
||||
i = p._ad_idx
|
||||
i = self.get_i(p)
|
||||
|
||||
if not p.all_seeds:
|
||||
seed = p.seed
|
||||
@@ -401,7 +411,7 @@ class AfterDetailerScript(scripts.Script):
|
||||
)
|
||||
|
||||
def write_params_txt(self, p) -> None:
|
||||
i = p._ad_idx
|
||||
i = self.get_i(p)
|
||||
lenp = len(p.all_prompts)
|
||||
if i % lenp != lenp - 1:
|
||||
return
|
||||
@@ -503,6 +513,7 @@ class AfterDetailerScript(scripts.Script):
|
||||
i2i.cached_uc = [None, None]
|
||||
i2i.scripts, i2i.script_args = self.script_filter(p, args)
|
||||
i2i._ad_disabled = True
|
||||
i2i._ad_inner = True
|
||||
|
||||
if args.ad_controlnet_model != "None":
|
||||
self.update_controlnet_args(i2i, args)
|
||||
@@ -512,7 +523,7 @@ class AfterDetailerScript(scripts.Script):
|
||||
return i2i
|
||||
|
||||
def save_image(self, p, image, *, condition: str, suffix: str) -> None:
|
||||
i = p._ad_idx
|
||||
i = self.get_i(p)
|
||||
if p.all_prompts:
|
||||
i %= len(p.all_prompts)
|
||||
save_prompt = p.all_prompts[i]
|
||||
@@ -591,17 +602,15 @@ class AfterDetailerScript(scripts.Script):
|
||||
def need_call_process(p) -> bool:
|
||||
if p.scripts is None:
|
||||
return False
|
||||
i = p._ad_idx
|
||||
i = p.batch_index
|
||||
bs = p.batch_size
|
||||
return i % bs == bs - 1
|
||||
return i == bs - 1
|
||||
|
||||
@staticmethod
|
||||
def need_call_postprocess(p) -> bool:
|
||||
if p.scripts is None:
|
||||
return False
|
||||
i = p._ad_idx
|
||||
bs = p.batch_size
|
||||
return i % bs == 0
|
||||
return p.batch_index == 0
|
||||
|
||||
@staticmethod
|
||||
def get_i2i_init_image(p, pp):
|
||||
@@ -609,6 +618,11 @@ class AfterDetailerScript(scripts.Script):
|
||||
return p.init_images[0]
|
||||
return pp.image
|
||||
|
||||
@staticmethod
|
||||
def get_each_tap_seed(seed: int, i: int):
|
||||
use_same_seed = shared.opts.data.get("ad_same_seed_for_each_tap", False)
|
||||
return seed if use_same_seed else seed + i
|
||||
|
||||
@rich_traceback
|
||||
def process(self, p, *args_):
|
||||
if getattr(p, "_ad_disabled", False):
|
||||
@@ -635,7 +649,7 @@ class AfterDetailerScript(scripts.Script):
|
||||
if state.interrupted or state.skipped:
|
||||
return False
|
||||
|
||||
i = p._ad_idx
|
||||
i = self.get_i(p)
|
||||
|
||||
pp.image = self.get_i2i_init_image(p, pp)
|
||||
i2i = self.get_i2i_p(p, args, pp.image)
|
||||
@@ -688,8 +702,8 @@ class AfterDetailerScript(scripts.Script):
|
||||
if re.match(r"^\s*\[SKIP\]\s*$", p2.prompt):
|
||||
continue
|
||||
|
||||
p2.seed = seed + j
|
||||
p2.subseed = subseed + j
|
||||
p2.seed = self.get_each_tap_seed(seed, j)
|
||||
p2.subseed = self.get_each_tap_seed(subseed, j)
|
||||
|
||||
try:
|
||||
processed = process_images(p2)
|
||||
@@ -715,7 +729,6 @@ class AfterDetailerScript(scripts.Script):
|
||||
if getattr(p, "_ad_disabled", False) or not self.is_ad_enabled(*args_):
|
||||
return
|
||||
|
||||
p._ad_idx = getattr(p, "_ad_idx", -1) + 1
|
||||
init_image = copy(pp.image)
|
||||
arg_list = self.get_args(p, *args_)
|
||||
|
||||
@@ -738,7 +751,10 @@ class AfterDetailerScript(scripts.Script):
|
||||
|
||||
if self.need_call_process(p):
|
||||
with preseve_prompts(p):
|
||||
p.scripts.process(copy(p))
|
||||
copy_p = copy(p)
|
||||
if hasattr(p.scripts, "before_process"):
|
||||
p.scripts.before_process(copy_p)
|
||||
p.scripts.process(copy_p)
|
||||
|
||||
self.write_params_txt(p)
|
||||
|
||||
@@ -766,6 +782,16 @@ def on_ui_settings():
|
||||
),
|
||||
)
|
||||
|
||||
shared.opts.add_option(
|
||||
"ad_extra_models_dir",
|
||||
shared.OptionInfo(
|
||||
default="",
|
||||
label="Extra path to scan adetailer models",
|
||||
component=gr.Textbox,
|
||||
section=section,
|
||||
),
|
||||
)
|
||||
|
||||
shared.opts.add_option(
|
||||
"ad_save_previews",
|
||||
shared.OptionInfo(False, "Save mask previews", section=section),
|
||||
@@ -810,6 +836,13 @@ def on_ui_settings():
|
||||
),
|
||||
)
|
||||
|
||||
shared.opts.add_option(
|
||||
"ad_same_seed_for_each_tap",
|
||||
shared.OptionInfo(
|
||||
False, "Use same seed for each tab in adetailer", section=section
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
# xyz_grid
|
||||
|
||||
|
||||
Reference in New Issue
Block a user