Merge branch 'dev'

This commit is contained in:
Dowon
2024-06-16 09:32:49 +09:00
14 changed files with 103 additions and 89 deletions

View File

@@ -17,12 +17,12 @@ repos:
- id: mixed-line-ending
- repo: https://github.com/rbubley/mirrors-prettier
rev: v3.2.5
rev: v3.3.2
hooks:
- id: prettier
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.4
rev: v0.4.9
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]

View File

@@ -1,5 +1,13 @@
# Changelog
## 2024-06-16
- v24.6.0
- webui 1.6.0 미만 버전을 위한 기능들을 제거하고, 최소 버전을 1.6.0으로 올림
- 허깅페이스 연결을 체크하는데 1초만 소요되도록 함
- 허깅페이스 미러 (hf-mirror.com)도 체크함 (합쳐서 2초)
- InputAccordion을 적용함
## 2024-05-20
- v24.5.1

View File

@@ -1,19 +1,21 @@
from __future__ import annotations
from PIL import Image
from rich import print
msg = "[-] ADetailer: WebUI versions below 1.6.0 are not supported."
try:
from modules.processing import create_binary_mask
except ImportError:
msg = "[-] ADetailer: Support for webui versions below 1.6.0 will be discontinued."
print(msg)
from modules.processing import create_binary_mask # noqa: F401
except ImportError as e:
raise RuntimeError(msg) from e
def create_binary_mask(image: Image.Image):
return image.convert("L")
try:
from modules.ui_components import InputAccordion # noqa: F401
except ImportError as e:
raise RuntimeError(msg) from e
try:
from modules.sd_schedulers import schedulers
except ImportError:
# webui < 1.9.0
schedulers = []

View File

@@ -3,13 +3,15 @@ from __future__ import annotations
import io
import platform
import sys
from collections.abc import Callable
from importlib.metadata import version
from typing import Any, Callable
from typing import Any, TypeVar
from rich.console import Console, Group
from rich.panel import Panel
from rich.table import Table
from rich.traceback import Traceback
from typing_extensions import ParamSpec
from adetailer.__version__ import __version__
from adetailer.args import ADetailerArgs
@@ -137,7 +139,11 @@ def get_table(title: str, data: dict[str, Any]) -> Table:
return table
def rich_traceback(func: Callable) -> Callable:
P = ParamSpec("P")
T = TypeVar("T")
def rich_traceback(func: Callable[P, T]) -> Callable[P, T]:
def wrapper(*args, **kwargs):
string = io.StringIO()
width = Console().width

View File

@@ -7,7 +7,8 @@ from typing import Any
import gradio as gr
from adetailer import AFTER_DETAILER, __version__
from aaaaaa.conditional import InputAccordion
from adetailer import ADETAILER, __version__
from adetailer.args import ALL_ARGS, MASK_MERGE_INVERT
from controlnet_ext import controlnet_exists, controlnet_type, get_cn_models
@@ -105,9 +106,9 @@ def on_cn_model_update(cn_model_name: str):
def elem_id(item_id: str, n: int, is_img2img: bool) -> str:
tap = "img2img" if is_img2img else "txt2img"
tab = "img2img" if is_img2img else "txt2img"
suf = suffix(n, "_")
return f"script_{tap}_adetailer_{item_id}{suf}"
return f"script_{tab}_adetailer_{item_id}{suf}"
def state_init(w: Widgets) -> dict[str, Any]:
@@ -123,17 +124,14 @@ def adui(
infotext_fields = []
eid = partial(elem_id, n=0, is_img2img=is_img2img)
with gr.Accordion(AFTER_DETAILER, open=False, elem_id=eid("ad_main_accordion")):
with InputAccordion(
value=False,
elem_id=eid("ad_main_accordion"),
label=ADETAILER,
visible=True,
) as ad_enable:
with gr.Row():
with gr.Column(scale=6):
ad_enable = gr.Checkbox(
label="Enable ADetailer",
value=False,
visible=True,
elem_id=eid("ad_enable"),
)
with gr.Column(scale=6):
with gr.Column(scale=8):
ad_skip_img2img = gr.Checkbox(
label="Skip img2img",
value=False,
@@ -179,11 +177,11 @@ def one_ui_group(n: int, is_img2img: bool, webui_info: WebuiInfo):
with gr.Group():
with gr.Row(variant="compact"):
w.ad_tap_enable = gr.Checkbox(
label=f"Enable this tap ({ordinal(n + 1)})",
w.ad_tab_enable = gr.Checkbox(
label=f"Enable this tab ({ordinal(n + 1)})",
value=True,
visible=True,
elem_id=eid("ad_tap_enable"),
elem_id=eid("ad_tab_enable"),
)
with gr.Row():

View File

@@ -4,12 +4,12 @@ from .common import PredictOutput, get_models
from .mediapipe import mediapipe_predict
from .ultralytics import ultralytics_predict
AFTER_DETAILER = "ADetailer"
ADETAILER = "ADetailer"
__all__ = [
"__version__",
"ADetailerArgs",
"AFTER_DETAILER",
"ADETAILER",
"ALL_ARGS",
"PredictOutput",
"get_models",

View File

@@ -1 +1 @@
__version__ = "24.5.1"
__version__ = "24.6.0"

View File

@@ -55,7 +55,7 @@ class ArgsList(UserList):
class ADetailerArgs(BaseModel, extra=Extra.forbid):
ad_model: str = "None"
ad_model_classes: str = ""
ad_tap_enable: bool = True
ad_tab_enable: bool = True
ad_prompt: str = ""
ad_negative_prompt: str = ""
ad_confidence: confloat(ge=0.0, le=1.0) = 0.3
@@ -129,7 +129,7 @@ class ADetailerArgs(BaseModel, extra=Extra.forbid):
ppop("ADetailer model classes")
ppop("ADetailer prompt")
ppop("ADetailer negative prompt")
p.pop("ADetailer tap enable", None) # always pop
p.pop("ADetailer tab enable", None) # always pop
ppop("ADetailer mask only top k largest", cond=0)
ppop("ADetailer mask min ratio", cond=0.0)
ppop("ADetailer mask max ratio", cond=1.0)
@@ -206,13 +206,13 @@ class ADetailerArgs(BaseModel, extra=Extra.forbid):
return self.ad_model.lower().startswith("mediapipe")
def need_skip(self) -> bool:
return self.ad_model == "None" or self.ad_tap_enable is False
return self.ad_model == "None" or self.ad_tab_enable is False
_all_args = [
("ad_model", "ADetailer model"),
("ad_model_classes", "ADetailer model classes"),
("ad_tap_enable", "ADetailer tap enable"),
("ad_tab_enable", "ADetailer tab enable"),
("ad_prompt", "ADetailer prompt"),
("ad_negative_prompt", "ADetailer negative prompt"),
("ad_confidence", "ADetailer confidence"),

View File

@@ -3,6 +3,7 @@ from __future__ import annotations
import os
from collections import OrderedDict
from concurrent.futures import ThreadPoolExecutor
from contextlib import suppress
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Generic, Optional, TypeVar
@@ -24,14 +25,22 @@ class PredictOutput(Generic[T]):
preview: Optional[Image.Image] = None
def hf_download(file: str, repo_id: str = REPO_ID) -> str:
try:
path = hf_hub_download(repo_id, file)
except Exception:
msg = f"[-] ADetailer: Failed to load model {file!r} from huggingface"
print(msg)
path = "INVALID"
return path
def hf_download(file: str, repo_id: str = REPO_ID, check_remote: bool = True) -> str:
if check_remote:
with suppress(Exception):
return hf_hub_download(repo_id, file, etag_timeout=1)
with suppress(Exception):
return hf_hub_download(
repo_id, file, etag_timeout=1, endpoint="https://hf-mirror.com"
)
with suppress(Exception):
return hf_hub_download(repo_id, file, local_files_only=True)
msg = f"[-] ADetailer: Failed to load model {file!r} from huggingface"
print(msg)
return "INVALID"
def safe_mkdir(path: str | os.PathLike[str]) -> None:
@@ -46,16 +55,23 @@ def scan_model_dir(path: Path) -> list[Path]:
return [p for p in path.rglob("*") if p.is_file() and p.suffix == ".pt"]
def download_models(*names: str) -> dict[str, str]:
def download_models(*names: str, check_remote: bool = True) -> dict[str, str]:
models = OrderedDict()
with ThreadPoolExecutor() as executor:
for name in names:
if "-world" in name:
models[name] = executor.submit(
hf_download, name, repo_id="Bingsu/yolo-world-mirror"
hf_download,
name,
repo_id="Bingsu/yolo-world-mirror",
check_remote=check_remote,
)
else:
models[name] = executor.submit(hf_download, name)
models[name] = executor.submit(
hf_download,
name,
check_remote=check_remote,
)
return {name: future.result() for name, future in models.items()}
@@ -70,16 +86,15 @@ def get_models(
model_paths.extend(scan_model_dir(Path(dir_)))
models = OrderedDict()
if huggingface:
to_download = [
"face_yolov8n.pt",
"face_yolov8s.pt",
"hand_yolov8n.pt",
"person_yolov8n-seg.pt",
"person_yolov8s-seg.pt",
"yolov8x-worldv2.pt",
]
models.update(download_models(*to_download))
to_download = [
"face_yolov8n.pt",
"face_yolov8s.pt",
"hand_yolov8n.pt",
"person_yolov8n-seg.pt",
"person_yolov8s-seg.pt",
"yolov8x-worldv2.pt",
]
models.update(download_models(*to_download, check_remote=huggingface))
models.update(
{

View File

@@ -4,22 +4,12 @@ import importlib
import sys
from functools import lru_cache
from pathlib import Path
from textwrap import dedent
from modules import extensions, sd_models, shared
from modules.paths import extensions_builtin_dir, extensions_dir, models_path
from .common import cn_model_module, cn_model_regex
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

View File

@@ -47,8 +47,6 @@ def install():
("ultralytics", "8.2.0", None),
("mediapipe", "0.10.13", None),
("rich", "13.0.0", None),
# mediapipe
("protobuf", "4.25.3", "4.9999"),
]
pkgs = []

View File

@@ -2,12 +2,12 @@
name = "adetailer"
description = "An object detection and auto-mask extension for stable diffusion webui."
authors = [{ name = "dowon", email = "ks2515@naver.com" }]
requires-python = ">=3.8"
requires-python = ">=3.9"
readme = "README.md"
license = { text = "AGPL-3.0" }
dependencies = [
"ultralytics>=8.2",
"mediapipe>=0.10",
"mediapipe>=0.10.13",
"pydantic<3",
"rich>=13",
"huggingface_hub",
@@ -39,7 +39,7 @@ profile = "black"
known_first_party = ["launch", "modules"]
[tool.ruff]
target-version = "py38"
target-version = "py39"
[tool.ruff.lint]
select = [

View File

@@ -33,7 +33,7 @@ from aaaaaa.p_method import (
from aaaaaa.traceback import rich_traceback
from aaaaaa.ui import WebuiInfo, adui, ordinal, suffix
from adetailer import (
AFTER_DETAILER,
ADETAILER,
__version__,
get_models,
mediapipe_predict,
@@ -110,7 +110,7 @@ class AfterDetailerScript(scripts.Script):
return f"{self.__class__.__name__}(version={__version__})"
def title(self):
return AFTER_DETAILER
return ADETAILER
def show(self, is_img2img):
return scripts.AlwaysVisible
@@ -121,10 +121,7 @@ class AfterDetailerScript(scripts.Script):
sampler_names = [sampler.name for sampler in all_samplers]
scheduler_names = [x.label for x in schedulers]
try:
checkpoint_list = modules.sd_models.checkpoint_tiles(use_shorts=True)
except TypeError:
checkpoint_list = modules.sd_models.checkpoint_tiles()
checkpoint_list = modules.sd_models.checkpoint_tiles(use_short=True)
vae_list = modules.shared_items.sd_vae_items()
webui_info = WebuiInfo(
@@ -644,8 +641,8 @@ class AfterDetailerScript(scripts.Script):
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)
def get_each_tab_seed(seed: int, i: int):
use_same_seed = shared.opts.data.get("ad_same_seed_for_each_tab", False)
return seed if use_same_seed else seed + i
@staticmethod
@@ -773,8 +770,8 @@ class AfterDetailerScript(scripts.Script):
if re.match(r"^\s*\[SKIP\]\s*$", p2.prompt):
continue
p2.seed = self.get_each_tap_seed(seed, j)
p2.subseed = self.get_each_tap_seed(subseed, j)
p2.seed = self.get_each_tab_seed(seed, j)
p2.subseed = self.get_each_tab_seed(subseed, j)
p2.cached_c = [None, None]
p2.cached_uc = [None, None]
@@ -849,16 +846,16 @@ def on_after_component(component, **_kwargs):
def on_ui_settings():
section = ("ADetailer", AFTER_DETAILER)
section = ("ADetailer", ADETAILER)
shared.opts.add_option(
"ad_max_models",
shared.OptionInfo(
default=4,
label="Max models",
label="Max tabs",
component=gr.Slider,
component_args={"minimum": 1, "maximum": 15, "step": 1},
section=section,
),
).needs_reload_ui(),
)
shared.opts.add_option(
@@ -918,7 +915,7 @@ def on_ui_settings():
)
shared.opts.add_option(
"ad_same_seed_for_each_tap",
"ad_same_seed_for_each_tab",
shared.OptionInfo(
False, "Use same seed for each tab in adetailer", section=section
),

View File

@@ -35,7 +35,7 @@ def test_need_skip(ad_model: str, expect: bool) -> None:
@pytest.mark.parametrize(
("ad_model", "ad_tap_enable", "expect"),
("ad_model", "ad_tab_enable", "expect"),
[
("face_yolov8n.pt", False, True),
("mediapipe_face_full", False, True),
@@ -43,6 +43,6 @@ def test_need_skip(ad_model: str, expect: bool) -> None:
("ace_yolov8s.pt", True, False),
],
)
def test_need_skip_tap_enable(ad_model: str, ad_tap_enable: bool, expect: bool) -> None:
args = ADetailerArgs(ad_model=ad_model, ad_tap_enable=ad_tap_enable)
def test_need_skip_tab_enable(ad_model: str, ad_tab_enable: bool, expect: bool) -> None:
args = ADetailerArgs(ad_model=ad_model, ad_tab_enable=ad_tab_enable)
assert args.need_skip() is expect