From 4d374b7742d84db7f3e790c7c1ce6ea63fbd9719 Mon Sep 17 00:00:00 2001 From: Dowon Date: Wed, 17 Apr 2024 19:59:38 +0900 Subject: [PATCH 01/19] fix(install): ultralytics protobuf version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mediapipe 다음 버전 대기중 --- adetailer/__version__.py | 2 +- install.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/adetailer/__version__.py b/adetailer/__version__.py index 5068bd4..45e0f9b 100644 --- a/adetailer/__version__.py +++ b/adetailer/__version__.py @@ -1 +1 @@ -__version__ = "24.4.2" +__version__ = "24.4.3-dev.0" diff --git a/install.py b/install.py index 10efe7b..f1e0443 100644 --- a/install.py +++ b/install.py @@ -44,11 +44,11 @@ def run_pip(*args): def install(): deps = [ # requirements - ("ultralytics", "8.1.29", None), - ("mediapipe", "0.10.9", None), + ("ultralytics", "8.2.0", None), + ("mediapipe", "0.10.12", None), ("rich", "13.0.0", None), # mediapipe - ("protobuf", "3.20", "3.9999"), + ("protobuf", "4.25.3", "4.9999"), ] for pkg, low, high in deps: From 1526d180c202e1695eb2ee40a56888c1d136708a Mon Sep 17 00:00:00 2001 From: Dowon Date: Sat, 4 May 2024 17:29:13 +0900 Subject: [PATCH 02/19] fix(install): mediapipe, protobuf version google --- install.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/install.py b/install.py index f1e0443..e43210c 100644 --- a/install.py +++ b/install.py @@ -45,10 +45,12 @@ def install(): deps = [ # requirements ("ultralytics", "8.2.0", None), - ("mediapipe", "0.10.12", None), + ("mediapipe", "0.10.13", None), ("rich", "13.0.0", None), # mediapipe ("protobuf", "4.25.3", "4.9999"), + # protobuf + ("transformers", "4.31.0", None), ] for pkg, low, high in deps: From 1e4268d6e4472cd94a18fe4dc54d2bad864afd96 Mon Sep 17 00:00:00 2001 From: Dowon Date: Sat, 4 May 2024 17:38:34 +0900 Subject: [PATCH 03/19] chore(misc): versions --- .gitignore | 1 + pyproject.toml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index ce19e6c..aefb818 100644 --- a/.gitignore +++ b/.gitignore @@ -194,3 +194,4 @@ pyrightconfig.json # End of https://www.toptal.com/developers/gitignore/api/python,visualstudiocode *.ipynb +node_modules diff --git a/pyproject.toml b/pyproject.toml index 66edde1..3c55fed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,11 +2,11 @@ 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,<3.13" +requires-python = ">=3.8" readme = "README.md" license = { text = "AGPL-3.0" } dependencies = [ - "ultralytics>=8.1", + "ultralytics>=8.2", "mediapipe>=0.10", "pydantic<3", "rich>=13", From 196d7c354e73a2f553ad00edb06d5c492de44fa8 Mon Sep 17 00:00:00 2001 From: Dowon Date: Sat, 4 May 2024 17:39:26 +0900 Subject: [PATCH 04/19] misc: trivial --- .github/workflows/{lint.yml => lgtm.yml} | 1 + 1 file changed, 1 insertion(+) rename .github/workflows/{lint.yml => lgtm.yml} (95%) diff --git a/.github/workflows/lint.yml b/.github/workflows/lgtm.yml similarity index 95% rename from .github/workflows/lint.yml rename to .github/workflows/lgtm.yml index ffa7ba6..08c0fad 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lgtm.yml @@ -8,6 +8,7 @@ on: jobs: lint: permissions: + issues: write pull-requests: write runs-on: ubuntu-latest From 87739bed8553cfdeaa3fcb3d785d2cc627256844 Mon Sep 17 00:00:00 2001 From: Dowon Date: Sat, 4 May 2024 17:42:55 +0900 Subject: [PATCH 05/19] chore(ci): fix pre-commit --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 20cd974..fcdf0a8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,13 +16,13 @@ repos: - id: end-of-file-fixer - id: mixed-line-ending - - repo: https://github.com/pre-commit/mirrors-prettier - rev: "v4.0.0-alpha.8" + - repo: https://github.com/rbubley/mirrors-prettier + rev: v3.2.5 hooks: - id: prettier - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.3.7 + rev: v0.4.3 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] From dc451caaa27ea22d70c0c1b4b603a2c013d15884 Mon Sep 17 00:00:00 2001 From: Natans <52584161+Natans8@users.noreply.github.com> Date: Mon, 6 May 2024 10:02:14 +0300 Subject: [PATCH 06/19] Added option to add multiple extra models dirs separated by colons (#596) * Added option to add multiple extra models dirs separated by colons * Added "(requires Reload UI)" tag to the extra models path setting * Changed colon seperator to in favor of vertical bar. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- scripts/!adetailer.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/!adetailer.py b/scripts/!adetailer.py index b3fb1c6..0d43264 100644 --- a/scripts/!adetailer.py +++ b/scripts/!adetailer.py @@ -76,10 +76,10 @@ adetailer_dir = Path(paths.models_path, "adetailer") safe_mkdir(adetailer_dir) -extra_models_dir = shared.opts.data.get("ad_extra_models_dir", "") +extra_models_dirs = shared.opts.data.get("ad_extra_models_dir", "") model_mapping = get_models( adetailer_dir, - extra_models_dir, + *extra_models_dirs.split("|"), huggingface=not no_huggingface, ) @@ -849,10 +849,12 @@ def on_ui_settings(): "ad_extra_models_dir", shared.OptionInfo( default="", - label="Extra path to scan adetailer models", + label="Extra paths to scan adetailer models seperated by vertical bars", component=gr.Textbox, section=section, - ), + ) + .info("eg. path/to/models|another/path/to/models|different/path/to/models") + .needs_reload_ui(), ) shared.opts.add_option( From 72752138480327203b4a1760f39f77483d2b4f80 Mon Sep 17 00:00:00 2001 From: Dowon Date: Mon, 6 May 2024 16:04:34 +0900 Subject: [PATCH 07/19] fix(scripts): fix extra models dir option label, example --- scripts/!adetailer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/!adetailer.py b/scripts/!adetailer.py index 0d43264..d0fe926 100644 --- a/scripts/!adetailer.py +++ b/scripts/!adetailer.py @@ -849,11 +849,11 @@ def on_ui_settings(): "ad_extra_models_dir", shared.OptionInfo( default="", - label="Extra paths to scan adetailer models seperated by vertical bars", + label="Extra paths to scan adetailer models seperated by vertical bars(|)", component=gr.Textbox, section=section, ) - .info("eg. path/to/models|another/path/to/models|different/path/to/models") + .info("eg. path\\to\\models|C:\\path\\to\\models|another/path/to/models") .needs_reload_ui(), ) From 0a0606fc017ef0c09e93d31c4dc419129a6dbb9b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 15 May 2024 21:42:55 +0900 Subject: [PATCH 08/19] [pre-commit.ci] pre-commit autoupdate (#592) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.4.3 → v0.4.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.3...v0.4.4) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fcdf0a8..765a14f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: - id: prettier - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.3 + rev: v0.4.4 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] From 972158abee9107900fd8e8bd1efb36067f5097b9 Mon Sep 17 00:00:00 2001 From: Dowon Date: Wed, 15 May 2024 21:53:32 +0900 Subject: [PATCH 09/19] chore: update tool --- Taskfile.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index 355de2f..03a4e8a 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -10,6 +10,7 @@ tasks: cmds: - echo "$PYTHON" - echo "$WEBUI" + - echo "$UV_PYTHON" silent: true launch: @@ -24,8 +25,8 @@ tasks: update: cmds: - - "{{.PYTHON}} -m pip install -U ultralytics mediapipe ruff pre-commit black devtools pytest" + - "{{.PYTHON}} -m uv pip install -U ultralytics mediapipe ruff pre-commit black devtools pytest" update-torch: cmds: - - "{{.PYTHON}} -m pip install -U torch torchvision torchaudio -f https://download.pytorch.org/whl/torch_stable.html" + - "{{.PYTHON}} -m uv pip install -U torch torchvision torchaudio -f https://download.pytorch.org/whl/torch_stable.html" From e05104a220fbf85419e37e9b450fa8a55de685a4 Mon Sep 17 00:00:00 2001 From: Dowon Date: Wed, 15 May 2024 21:55:33 +0900 Subject: [PATCH 10/19] fix: move traceback.py, ui.py --- {adetailer => aaaaaa}/traceback.py | 0 {adetailer => aaaaaa}/ui.py | 0 scripts/!adetailer.py | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) rename {adetailer => aaaaaa}/traceback.py (100%) rename {adetailer => aaaaaa}/ui.py (100%) diff --git a/adetailer/traceback.py b/aaaaaa/traceback.py similarity index 100% rename from adetailer/traceback.py rename to aaaaaa/traceback.py diff --git a/adetailer/ui.py b/aaaaaa/ui.py similarity index 100% rename from adetailer/ui.py rename to aaaaaa/ui.py diff --git a/scripts/!adetailer.py b/scripts/!adetailer.py index d0fe926..d2c40a9 100644 --- a/scripts/!adetailer.py +++ b/scripts/!adetailer.py @@ -29,6 +29,8 @@ need_call_postprocess, need_call_process, ) +from aaaaaa.traceback import rich_traceback +from aaaaaa.ui import WebuiInfo, adui, ordinal, suffix from adetailer import ( AFTER_DETAILER, __version__, @@ -46,8 +48,6 @@ mask_preprocess, sort_bboxes, ) -from adetailer.traceback import rich_traceback -from adetailer.ui import WebuiInfo, adui, ordinal, suffix from controlnet_ext import ( CNHijackRestore, ControlNetExt, From f12f66c298561a93bb2885c3d6c48e61fc5da086 Mon Sep 17 00:00:00 2001 From: Dowon Date: Wed, 15 May 2024 22:13:06 +0900 Subject: [PATCH 11/19] refactor: refactor some functions --- aaaaaa/p_method.py | 4 ++++ adetailer/args.py | 6 ++++++ scripts/!adetailer.py | 11 ++++++----- tests/test_args.py | 20 ++++++++++++++++++++ 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/aaaaaa/p_method.py b/aaaaaa/p_method.py index 9a87e7c..a9dfd14 100644 --- a/aaaaaa/p_method.py +++ b/aaaaaa/p_method.py @@ -28,3 +28,7 @@ def get_i(p) -> int: bs = p.batch_size i = p.batch_index return it * bs + i + + +def is_skip_img2img(p) -> bool: + return getattr(p, "_ad_skip_img2img", False) diff --git a/adetailer/args.py b/adetailer/args.py index a54ac6c..e6d0b4e 100644 --- a/adetailer/args.py +++ b/adetailer/args.py @@ -200,6 +200,12 @@ def extra_params(self, suffix: str = "") -> dict[str, Any]: return p + def is_mediapipe(self) -> bool: + return self.ad_model.lower().startswith("mediapipe") + + def need_skip(self) -> bool: + return self.ad_model == "None" + _all_args = [ ("ad_model", "ADetailer model"), diff --git a/scripts/!adetailer.py b/scripts/!adetailer.py index d2c40a9..696c605 100644 --- a/scripts/!adetailer.py +++ b/scripts/!adetailer.py @@ -26,6 +26,7 @@ get_i, is_img2img_inpaint, is_inpaint_only_masked, + is_skip_img2img, need_call_postprocess, need_call_process, ) @@ -625,7 +626,7 @@ def compare_prompt(p, extra_params: dict[str, Any], processed, n: int = 0): @staticmethod def get_i2i_init_image(p, pp): - if getattr(p, "_ad_skip_img2img", False): + if is_skip_img2img(p): return p.init_images[0] return pp.image @@ -649,7 +650,7 @@ def get_image_mask(p) -> Image.Image: mask = ImageChops.invert(mask) mask = create_binary_mask(mask) - if getattr(p, "_ad_skip_img2img", False): + if is_skip_img2img(p): if hasattr(p, "init_images") and p.init_images: width, height = p.init_images[0].size else: @@ -712,7 +713,7 @@ def _postprocess_image_inner( seed, subseed = self.get_seed(p) ad_prompts, ad_negatives = self.get_prompt(p, args) - is_mediapipe = args.ad_model.lower().startswith("mediapipe") + is_mediapipe = args.is_mediapipe() kwargs = {} if is_mediapipe: @@ -800,11 +801,11 @@ def postprocess_image(self, p, pp, *args_): is_processed = False with CNHijackRestore(), pause_total_tqdm(), cn_allow_script_control(): for n, args in enumerate(arg_list): - if args.ad_model == "None": + if args.need_skip(): continue is_processed |= self._postprocess_image_inner(p, pp, args, n=n) - if is_processed and not getattr(p, "_ad_skip_img2img", False): + if is_processed and not is_skip_img2img(p): self.save_image( p, init_image, condition="ad_save_images_before", suffix="-ad-before" ) diff --git a/tests/test_args.py b/tests/test_args.py index 89b0c2b..eb96330 100644 --- a/tests/test_args.py +++ b/tests/test_args.py @@ -1,5 +1,7 @@ from __future__ import annotations +import pytest + from adetailer.args import ALL_ARGS, ADetailerArgs @@ -12,3 +14,21 @@ def test_all_args() -> None: if attr == "is_api": continue assert attr in ALL_ARGS.attrs, attr + + +@pytest.mark.parametrize( + ("ad_model", "expect"), + [("mediapipe_face_full", True), ("face_yolov8n.pt", False)], +) +def test_is_mediapipe(ad_model: str, expect: bool) -> None: + args = ADetailerArgs(ad_model=ad_model) + assert args.is_mediapipe() is expect + + +@pytest.mark.parametrize( + ("ad_model", "expect"), + [("mediapipe_face_full", False), ("face_yolov8n.pt", False), ("None", True)], +) +def test_need_skip(ad_model: str, expect: bool) -> None: + args = ADetailerArgs(ad_model=ad_model) + assert args.need_skip() is expect From 9672046eeb9fcde3b0e4df0d791d792e6185b705 Mon Sep 17 00:00:00 2001 From: Dowon Date: Fri, 17 May 2024 14:16:16 +0900 Subject: [PATCH 12/19] fix: builtin scripts --- adetailer/args.py | 4 +++- scripts/!adetailer.py | 13 ++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/adetailer/args.py b/adetailer/args.py index e6d0b4e..8ae74e0 100644 --- a/adetailer/args.py +++ b/adetailer/args.py @@ -268,6 +268,8 @@ def need_skip(self) -> bool: "wildcards", "lora_block_weight", "negpip", - "soft_inpainting", ) SCRIPT_DEFAULT = ",".join(sorted(_script_default)) + +_builtin_script = ("soft_inpainting", "hypertile_script") +BUILTIN_SCRIPT = ",".join(sorted(_builtin_script)) diff --git a/scripts/!adetailer.py b/scripts/!adetailer.py index 696c605..7f8328d 100644 --- a/scripts/!adetailer.py +++ b/scripts/!adetailer.py @@ -39,7 +39,13 @@ mediapipe_predict, ultralytics_predict, ) -from adetailer.args import BBOX_SORTBY, SCRIPT_DEFAULT, ADetailerArgs, SkipImg2ImgOrig +from adetailer.args import ( + BBOX_SORTBY, + BUILTIN_SCRIPT, + SCRIPT_DEFAULT, + ADetailerArgs, + SkipImg2ImgOrig, +) from adetailer.common import PredictOutput, ensure_pil_image, safe_mkdir from adetailer.mask import ( filter_by_ratio, @@ -442,10 +448,11 @@ def script_filter(self, p, args: ADetailerArgs): if not ad_only_seleted_scripts: return script_runner, script_args - ad_script_names = opts.data.get("ad_script_names", SCRIPT_DEFAULT) + ad_script_names_string: str = opts.data.get("ad_script_names", SCRIPT_DEFAULT) + ad_script_names = ad_script_names_string.split(",") + BUILTIN_SCRIPT.split(",") script_names_set = { name - for script_name in ad_script_names.split(",") + for script_name in ad_script_names for name in (script_name, script_name.strip()) } From ef7d413814aa36adb4a603ad9fefc1f5d817e218 Mon Sep 17 00:00:00 2001 From: Dowon Date: Fri, 17 May 2024 14:41:47 +0900 Subject: [PATCH 13/19] fix: always reset cached_c, uc --- scripts/!adetailer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/!adetailer.py b/scripts/!adetailer.py index 7f8328d..b12637d 100644 --- a/scripts/!adetailer.py +++ b/scripts/!adetailer.py @@ -770,6 +770,8 @@ def _postprocess_image_inner( p2.seed = self.get_each_tap_seed(seed, j) p2.subseed = self.get_each_tap_seed(subseed, j) + p2.cached_c = [None, None] + p2.cached_uc = [None, None] try: processed = process_images(p2) except NansException as e: From af3418728dc27831592365d4c1ce2e77dad1ef84 Mon Sep 17 00:00:00 2001 From: Dowon Date: Sun, 19 May 2024 15:54:38 +0900 Subject: [PATCH 14/19] feat: add gradio info --- aaaaaa/ui.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/aaaaaa/ui.py b/aaaaaa/ui.py index f17c52c..e7e2ec6 100644 --- a/aaaaaa/ui.py +++ b/aaaaaa/ui.py @@ -180,20 +180,22 @@ def one_ui_group(n: int, is_img2img: bool, webui_info: WebuiInfo): ) w.ad_model = gr.Dropdown( - label="ADetailer model" + suffix(n), + label="ADetailer detector" + suffix(n), choices=model_choices, value=model_choices[0], visible=True, type="value", elem_id=eid("ad_model"), + info="Select a model to use for detection.", ) with gr.Row(): w.ad_model_classes = gr.Textbox( - label="ADetailer model classes" + suffix(n), + label="ADetailer detector classes" + suffix(n), value="", visible=False, elem_id=eid("ad_classes"), + info="Comma separated class names to detect, ex: 'girl,cat'.", ) w.ad_model.change( @@ -280,6 +282,7 @@ def detection(w: Widgets, n: int, is_img2img: bool): value=0.3, visible=True, elem_id=eid("ad_confidence"), + info="Confidence threshold for detection model.", ) w.ad_mask_k_largest = gr.Slider( label="Mask only the top k largest (0 to disable)" + suffix(n), @@ -289,6 +292,7 @@ def detection(w: Widgets, n: int, is_img2img: bool): value=0, visible=True, elem_id=eid("ad_mask_k_largest"), + info="Use only the top k largest objects for masking.", ) with gr.Column(variant="compact"): @@ -300,6 +304,7 @@ def detection(w: Widgets, n: int, is_img2img: bool): value=0.0, visible=True, elem_id=eid("ad_mask_min_ratio"), + info="Minimum area ratio for masking.", ) w.ad_mask_max_ratio = gr.Slider( label="Mask max area ratio" + suffix(n), @@ -309,6 +314,7 @@ def detection(w: Widgets, n: int, is_img2img: bool): value=1.0, visible=True, elem_id=eid("ad_mask_max_ratio"), + info="Maximum area ratio for masking.", ) @@ -326,6 +332,7 @@ def mask_preprocessing(w: Widgets, n: int, is_img2img: bool): value=0, visible=True, elem_id=eid("ad_x_offset"), + info="Mask x offset (→) in pixels.", ) w.ad_y_offset = gr.Slider( label="Mask y(↑) offset" + suffix(n), @@ -335,6 +342,7 @@ def mask_preprocessing(w: Widgets, n: int, is_img2img: bool): value=0, visible=True, elem_id=eid("ad_y_offset"), + info="Mask y offset (↑) in pixels.", ) with gr.Column(variant="compact"): @@ -346,6 +354,7 @@ def mask_preprocessing(w: Widgets, n: int, is_img2img: bool): value=4, visible=True, elem_id=eid("ad_dilate_erode"), + info="Mask erosion (-) / dilation (+) in pixels.", ) with gr.Row(): @@ -354,6 +363,7 @@ def mask_preprocessing(w: Widgets, n: int, is_img2img: bool): choices=MASK_MERGE_INVERT, value="None", elem_id=eid("ad_mask_merge_invert"), + info="Mask merge mode. (None: do nothing, Merge: merge masks, Merge and Invert: merge all masks and invert)", ) @@ -622,6 +632,7 @@ def inpainting(w: Widgets, n: int, is_img2img: bool, webui_info: WebuiInfo): label="Restore faces after ADetailer" + suffix(n), value=False, elem_id=eid("ad_restore_face"), + info="Apply face restoration after ADetailer.", ) From 2ace2759b8c330dd0cdfa67198fc5dcccad018ed Mon Sep 17 00:00:00 2001 From: Dowon Date: Sun, 19 May 2024 16:25:06 +0900 Subject: [PATCH 15/19] feat(scripts): enable each tap --- aaaaaa/traceback.py | 30 +++++++++++++++++++----------- aaaaaa/ui.py | 20 ++++++++++++++------ adetailer/args.py | 7 +++++-- scripts/!adetailer.py | 8 +++++++- tests/test_args.py | 14 ++++++++++++++ 5 files changed, 59 insertions(+), 20 deletions(-) diff --git a/aaaaaa/traceback.py b/aaaaaa/traceback.py index 74d1848..f4f653d 100644 --- a/aaaaaa/traceback.py +++ b/aaaaaa/traceback.py @@ -12,6 +12,7 @@ from rich.traceback import Traceback from adetailer.__version__ import __version__ +from adetailer.args import ADetailerArgs def processing(*args: Any) -> dict[str, Any]: @@ -66,23 +67,30 @@ def sd_models() -> dict[str, str]: def ad_args(*args: Any) -> dict[str, Any]: - ad_args = [ - arg - for arg in args - if isinstance(arg, dict) and arg.get("ad_model", "None") != "None" - ] + ad_args = [] + for arg in args: + if not isinstance(arg, dict): + continue + + try: + a = ADetailerArgs(**arg) + except ValueError: + continue + + if not a.need_skip(): + ad_args.append(a) + if not ad_args: return {} arg0 = ad_args[0] - is_api = arg0.get("is_api", True) return { "version": __version__, - "ad_model": arg0["ad_model"], - "ad_prompt": arg0.get("ad_prompt", ""), - "ad_negative_prompt": arg0.get("ad_negative_prompt", ""), - "ad_controlnet_model": arg0.get("ad_controlnet_model", "None"), - "is_api": type(is_api) is not tuple, + "ad_model": arg0.ad_model, + "ad_prompt": arg0.ad_prompt, + "ad_negative_prompt": arg0.ad_negative_prompt, + "ad_controlnet_model": arg0.ad_controlnet_model, + "is_api": arg0.is_api, } diff --git a/aaaaaa/ui.py b/aaaaaa/ui.py index e7e2ec6..b63760e 100644 --- a/aaaaaa/ui.py +++ b/aaaaaa/ui.py @@ -162,7 +162,7 @@ def adui( states.append(state) infotext_fields.extend(infofields) - # components: [bool, dict, dict, ...] + # components: [bool, bool, dict, dict, ...] components = [ad_enable, ad_skip_img2img, *states] return components, infotext_fields @@ -171,14 +171,22 @@ def one_ui_group(n: int, is_img2img: bool, webui_info: WebuiInfo): w = Widgets() eid = partial(elem_id, n=n, is_img2img=is_img2img) + model_choices = ( + [*webui_info.ad_model_list, "None"] + if n == 0 + else ["None", *webui_info.ad_model_list] + ) + with gr.Group(): - with gr.Row(): - model_choices = ( - [*webui_info.ad_model_list, "None"] - if n == 0 - else ["None", *webui_info.ad_model_list] + with gr.Row(variant="compact"): + w.ad_tap_enable = gr.Checkbox( + label=f"Enable this tap ({ordinal(n + 1)})", + value=True, + visible=True, + elem_id=eid("ad_tap_enable"), ) + with gr.Row(): w.ad_model = gr.Dropdown( label="ADetailer detector" + suffix(n), choices=model_choices, diff --git a/adetailer/args.py b/adetailer/args.py index 8ae74e0..ba0808c 100644 --- a/adetailer/args.py +++ b/adetailer/args.py @@ -55,6 +55,7 @@ def names(self) -> tuple[str, ...]: class ADetailerArgs(BaseModel, extra=Extra.forbid): ad_model: str = "None" ad_model_classes: str = "" + ad_tap_enable: bool = True ad_prompt: str = "" ad_negative_prompt: str = "" ad_confidence: confloat(ge=0.0, le=1.0) = 0.3 @@ -119,7 +120,7 @@ def ppop( p.pop(k, None) def extra_params(self, suffix: str = "") -> dict[str, Any]: - if self.ad_model == "None": + if self.need_skip(): return {} p = {name: getattr(self, attr) for attr, name in ALL_ARGS} @@ -128,6 +129,7 @@ def extra_params(self, suffix: str = "") -> dict[str, Any]: ppop("ADetailer model classes") ppop("ADetailer prompt") ppop("ADetailer negative prompt") + p.pop("ADetailer tap 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) @@ -204,12 +206,13 @@ def is_mediapipe(self) -> bool: return self.ad_model.lower().startswith("mediapipe") def need_skip(self) -> bool: - return self.ad_model == "None" + return self.ad_model == "None" or self.ad_tap_enable is False _all_args = [ ("ad_model", "ADetailer model"), ("ad_model_classes", "ADetailer model classes"), + ("ad_tap_enable", "ADetailer tap enable"), ("ad_prompt", "ADetailer prompt"), ("ad_negative_prompt", "ADetailer negative prompt"), ("ad_confidence", "ADetailer confidence"), diff --git a/scripts/!adetailer.py b/scripts/!adetailer.py index b12637d..c5a5f16 100644 --- a/scripts/!adetailer.py +++ b/scripts/!adetailer.py @@ -187,7 +187,13 @@ def is_ad_enabled(self, *args_) -> bool: return False ad_enabled = args_[0] if isinstance(args_[0], bool) else True - not_none = any(arg.get("ad_model", "None") != "None" for arg in arg_list) + pydantic_args = [] + for arg in arg_list: + try: + pydantic_args.append(ADetailerArgs(**arg)) + except ValueError: # noqa: PERF203 + continue + not_none = not all(arg.need_skip() for arg in pydantic_args) return ad_enabled and not_none def set_skip_img2img(self, p, *args_) -> None: diff --git a/tests/test_args.py b/tests/test_args.py index eb96330..19db115 100644 --- a/tests/test_args.py +++ b/tests/test_args.py @@ -32,3 +32,17 @@ def test_is_mediapipe(ad_model: str, expect: bool) -> None: def test_need_skip(ad_model: str, expect: bool) -> None: args = ADetailerArgs(ad_model=ad_model) assert args.need_skip() is expect + + +@pytest.mark.parametrize( + ("ad_model", "ad_tap_enable", "expect"), + [ + ("face_yolov8n.pt", False, True), + ("mediapipe_face_full", False, True), + ("None", True, True), + ("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) + assert args.need_skip() is expect From 8a4d546566ed466affe6dcced1719061728ee87d Mon Sep 17 00:00:00 2001 From: Dowon Date: Sun, 19 May 2024 16:32:56 +0900 Subject: [PATCH 16/19] fix(ui): remove most info --- aaaaaa/ui.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/aaaaaa/ui.py b/aaaaaa/ui.py index b63760e..7bc923d 100644 --- a/aaaaaa/ui.py +++ b/aaaaaa/ui.py @@ -203,7 +203,6 @@ def one_ui_group(n: int, is_img2img: bool, webui_info: WebuiInfo): value="", visible=False, elem_id=eid("ad_classes"), - info="Comma separated class names to detect, ex: 'girl,cat'.", ) w.ad_model.change( @@ -290,7 +289,6 @@ def detection(w: Widgets, n: int, is_img2img: bool): value=0.3, visible=True, elem_id=eid("ad_confidence"), - info="Confidence threshold for detection model.", ) w.ad_mask_k_largest = gr.Slider( label="Mask only the top k largest (0 to disable)" + suffix(n), @@ -300,7 +298,6 @@ def detection(w: Widgets, n: int, is_img2img: bool): value=0, visible=True, elem_id=eid("ad_mask_k_largest"), - info="Use only the top k largest objects for masking.", ) with gr.Column(variant="compact"): @@ -312,7 +309,6 @@ def detection(w: Widgets, n: int, is_img2img: bool): value=0.0, visible=True, elem_id=eid("ad_mask_min_ratio"), - info="Minimum area ratio for masking.", ) w.ad_mask_max_ratio = gr.Slider( label="Mask max area ratio" + suffix(n), @@ -322,7 +318,6 @@ def detection(w: Widgets, n: int, is_img2img: bool): value=1.0, visible=True, elem_id=eid("ad_mask_max_ratio"), - info="Maximum area ratio for masking.", ) @@ -340,7 +335,6 @@ def mask_preprocessing(w: Widgets, n: int, is_img2img: bool): value=0, visible=True, elem_id=eid("ad_x_offset"), - info="Mask x offset (→) in pixels.", ) w.ad_y_offset = gr.Slider( label="Mask y(↑) offset" + suffix(n), @@ -350,7 +344,6 @@ def mask_preprocessing(w: Widgets, n: int, is_img2img: bool): value=0, visible=True, elem_id=eid("ad_y_offset"), - info="Mask y offset (↑) in pixels.", ) with gr.Column(variant="compact"): @@ -362,7 +355,6 @@ def mask_preprocessing(w: Widgets, n: int, is_img2img: bool): value=4, visible=True, elem_id=eid("ad_dilate_erode"), - info="Mask erosion (-) / dilation (+) in pixels.", ) with gr.Row(): @@ -371,7 +363,7 @@ def mask_preprocessing(w: Widgets, n: int, is_img2img: bool): choices=MASK_MERGE_INVERT, value="None", elem_id=eid("ad_mask_merge_invert"), - info="Mask merge mode. (None: do nothing, Merge: merge masks, Merge and Invert: merge all masks and invert)", + info="None: do nothing, Merge: merge masks, Merge and Invert: merge all masks and invert", ) @@ -640,7 +632,6 @@ def inpainting(w: Widgets, n: int, is_img2img: bool, webui_info: WebuiInfo): label="Restore faces after ADetailer" + suffix(n), value=False, elem_id=eid("ad_restore_face"), - info="Apply face restoration after ADetailer.", ) From 1bcc00c3b9982f11fdd062694c80f0db0505203d Mon Sep 17 00:00:00 2001 From: Dowon Date: Sun, 19 May 2024 16:34:22 +0900 Subject: [PATCH 17/19] fix(install): do not update transformers --- install.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/install.py b/install.py index e43210c..28660cc 100644 --- a/install.py +++ b/install.py @@ -49,8 +49,6 @@ def install(): ("rich", "13.0.0", None), # mediapipe ("protobuf", "4.25.3", "4.9999"), - # protobuf - ("transformers", "4.31.0", None), ] for pkg, low, high in deps: From e44872e775ade46b90c42d013992642823930ea4 Mon Sep 17 00:00:00 2001 From: Dowon Date: Sun, 19 May 2024 16:54:51 +0900 Subject: [PATCH 18/19] feat(install): use uv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 한국인이 원하는 속도감 --- install.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/install.py b/install.py index 28660cc..7a6d4d0 100644 --- a/install.py +++ b/install.py @@ -1,6 +1,7 @@ from __future__ import annotations import importlib.util +import os import subprocess import sys from importlib.metadata import version # python >= 3.8 @@ -38,7 +39,11 @@ def is_installed( def run_pip(*args): - subprocess.run([sys.executable, "-m", "pip", "install", *args]) + subprocess.run([sys.executable, "-m", "pip", "install", *args], check=False) + + +def run_uv_pip(*args): + subprocess.run([sys.executable, "-m", "uv", "pip", "install", *args], check=False) def install(): @@ -51,6 +56,12 @@ def install(): ("protobuf", "4.25.3", "4.9999"), ] + if not is_installed("uv", "0.1.44", None): + run_pip("uv>=0.1.44") + + os.environ["UV_PYTHON"] = sys.executable + + pkgs = [] for pkg, low, high in deps: if not is_installed(pkg, low, high): if low and high: @@ -61,8 +72,9 @@ def install(): cmd = f"{pkg}<={high}" else: cmd = pkg + pkgs.append(cmd) - run_pip("-U", cmd) + run_uv_pip(*pkgs) try: From fd6cab8e381649464f12c1aa874ed8c601772346 Mon Sep 17 00:00:00 2001 From: Dowon Date: Sun, 19 May 2024 17:03:35 +0900 Subject: [PATCH 19/19] chore: v24.5.0 --- CHANGELOG.md | 10 ++++++++++ adetailer/__version__.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8481da1..1093ca8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 2024-05-19 + +- v24.5.0 +- 개별 탭 활성화/비활성화 체크박스 추가 +- ad_extra_model_dir 옵션에 |로 구분된 여러 디렉토리를 추가할 수 있게 함 (PR #596) +- `hypertile` 빌트인 확장이 지원되도록 함 +- 항상 cond 캐시를 비움 +- 설치 스크립트에 uv를 사용함 +- mediapipe 최소 버전을 올려 protobuf 버전 4를 사용하게 함 + ## 2024-04-17 - v24.4.2 diff --git a/adetailer/__version__.py b/adetailer/__version__.py index 45e0f9b..7ad6f1b 100644 --- a/adetailer/__version__.py +++ b/adetailer/__version__.py @@ -1 +1 @@ -__version__ = "24.4.3-dev.0" +__version__ = "24.5.0"