Merge branch 'dev'

This commit is contained in:
Dowon
2024-05-20 20:37:45 +09:00
8 changed files with 73 additions and 57 deletions

View File

@@ -1,5 +1,12 @@
# Changelog # Changelog
## 2024-05-20
- v24.5.1
- uv를 사용하지 않게 함
- 모든 허깅페이스 모델을 동시에 다운로드 시도함
- 기본 탭 수를 2에서 4로 변경
## 2024-05-19 ## 2024-05-19
- v24.5.0 - v24.5.0

View File

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

View File

@@ -2,9 +2,10 @@ from __future__ import annotations
import os import os
from collections import OrderedDict from collections import OrderedDict
from concurrent.futures import ThreadPoolExecutor
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
from typing import Any, Optional from typing import Any, Generic, Optional, TypeVar
from huggingface_hub import hf_hub_download from huggingface_hub import hf_hub_download
from PIL import Image, ImageDraw from PIL import Image, ImageDraw
@@ -12,29 +13,24 @@ from rich import print
from torchvision.transforms.functional import to_pil_image from torchvision.transforms.functional import to_pil_image
REPO_ID = "Bingsu/adetailer" REPO_ID = "Bingsu/adetailer"
_download_failed = False
T = TypeVar("T", int, float)
@dataclass @dataclass
class PredictOutput: class PredictOutput(Generic[T]):
bboxes: list[list[int | float]] = field(default_factory=list) bboxes: list[list[T]] = field(default_factory=list)
masks: list[Image.Image] = field(default_factory=list) masks: list[Image.Image] = field(default_factory=list)
preview: Optional[Image.Image] = None preview: Optional[Image.Image] = None
def hf_download(file: str, repo_id: str = REPO_ID) -> str | None: def hf_download(file: str, repo_id: str = REPO_ID) -> str:
global _download_failed
if _download_failed:
return "INVALID"
try: try:
path = hf_hub_download(repo_id, file) path = hf_hub_download(repo_id, file)
except Exception: except Exception:
msg = f"[-] ADetailer: Failed to load model {file!r} from huggingface" msg = f"[-] ADetailer: Failed to load model {file!r} from huggingface"
print(msg) print(msg)
path = "INVALID" path = "INVALID"
_download_failed = True
return path return path
@@ -50,6 +46,19 @@ def scan_model_dir(path: Path) -> list[Path]:
return [p for p in path.rglob("*") if p.is_file() and p.suffix == ".pt"] return [p for p in path.rglob("*") if p.is_file() and p.suffix == ".pt"]
def download_models(*names: str) -> 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"
)
else:
models[name] = executor.submit(hf_download, name)
return {name: future.result() for name, future in models.items()}
def get_models( def get_models(
*dirs: str | os.PathLike[str], huggingface: bool = True *dirs: str | os.PathLike[str], huggingface: bool = True
) -> OrderedDict[str, str]: ) -> OrderedDict[str, str]:
@@ -62,18 +71,16 @@ def get_models(
models = OrderedDict() models = OrderedDict()
if huggingface: if huggingface:
models.update( to_download = [
{ "face_yolov8n.pt",
"face_yolov8n.pt": hf_download("face_yolov8n.pt"), "face_yolov8s.pt",
"face_yolov8s.pt": hf_download("face_yolov8s.pt"), "hand_yolov8n.pt",
"hand_yolov8n.pt": hf_download("hand_yolov8n.pt"), "person_yolov8n-seg.pt",
"person_yolov8n-seg.pt": hf_download("person_yolov8n-seg.pt"), "person_yolov8s-seg.pt",
"person_yolov8s-seg.pt": hf_download("person_yolov8s-seg.pt"), "yolov8x-worldv2.pt",
"yolov8x-worldv2.pt": hf_download( ]
"yolov8x-worldv2.pt", repo_id="Bingsu/yolo-world-mirror" models.update(download_models(*to_download))
),
}
)
models.update( models.update(
{ {
"mediapipe_face_full": "mediapipe_face_full", "mediapipe_face_full": "mediapipe_face_full",

View File

@@ -3,7 +3,7 @@ from __future__ import annotations
from enum import IntEnum from enum import IntEnum
from functools import partial, reduce from functools import partial, reduce
from math import dist from math import dist
from typing import Any from typing import Any, TypeVar
import cv2 import cv2
import numpy as np import numpy as np
@@ -26,6 +26,9 @@ class MergeInvert(IntEnum):
MERGE_INVERT = 2 MERGE_INVERT = 2
T = TypeVar("T", int, float)
def _dilate(arr: np.ndarray, value: int) -> np.ndarray: def _dilate(arr: np.ndarray, value: int) -> np.ndarray:
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (value, value)) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (value, value))
return cv2.dilate(arr, kernel, iterations=1) return cv2.dilate(arr, kernel, iterations=1)
@@ -96,7 +99,7 @@ def has_intersection(im1: Any, im2: Any) -> bool:
return not is_all_black(cv2.bitwise_and(arr1, arr2)) return not is_all_black(cv2.bitwise_and(arr1, arr2))
def bbox_area(bbox: list[float]) -> float: def bbox_area(bbox: list[T]) -> T:
return (bbox[2] - bbox[0]) * (bbox[3] - bbox[1]) return (bbox[2] - bbox[0]) * (bbox[3] - bbox[1])
@@ -141,25 +144,25 @@ def mask_preprocess(
# Bbox sorting # Bbox sorting
def _key_left_to_right(bbox: list[float]) -> float: def _key_left_to_right(bbox: list[T]) -> T:
""" """
Left to right Left to right
Parameters Parameters
---------- ----------
bbox: list[float] bbox: list[int] | list[float]
list of [x1, y1, x2, y2] list of [x1, y1, x2, y2]
""" """
return bbox[0] return bbox[0]
def _key_center_to_edge(bbox: list[float], *, center: tuple[float, float]) -> float: def _key_center_to_edge(bbox: list[T], *, center: tuple[float, float]) -> float:
""" """
Center to edge Center to edge
Parameters Parameters
---------- ----------
bbox: list[float] bbox: list[int] | list[float]
list of [x1, y1, x2, y2] list of [x1, y1, x2, y2]
image: Image.Image image: Image.Image
the image the image
@@ -168,21 +171,21 @@ def _key_center_to_edge(bbox: list[float], *, center: tuple[float, float]) -> fl
return dist(center, bbox_center) return dist(center, bbox_center)
def _key_area(bbox: list[float]) -> float: def _key_area(bbox: list[T]) -> T:
""" """
Large to small Large to small
Parameters Parameters
---------- ----------
bbox: list[float] bbox: list[int] | list[float]
list of [x1, y1, x2, y2] list of [x1, y1, x2, y2]
""" """
return -bbox_area(bbox) return -bbox_area(bbox)
def sort_bboxes( def sort_bboxes(
pred: PredictOutput, order: int | SortBy = SortBy.NONE pred: PredictOutput[T], order: int | SortBy = SortBy.NONE
) -> PredictOutput: ) -> PredictOutput[T]:
if order == SortBy.NONE or len(pred.bboxes) <= 1: if order == SortBy.NONE or len(pred.bboxes) <= 1:
return pred return pred
@@ -205,12 +208,14 @@ def sort_bboxes(
# Filter by ratio # Filter by ratio
def is_in_ratio(bbox: list[float], low: float, high: float, orig_area: int) -> bool: def is_in_ratio(bbox: list[T], low: float, high: float, orig_area: int) -> bool:
area = bbox_area(bbox) area = bbox_area(bbox)
return low <= area / orig_area <= high return low <= area / orig_area <= high
def filter_by_ratio(pred: PredictOutput, low: float, high: float) -> PredictOutput: def filter_by_ratio(
pred: PredictOutput[T], low: float, high: float
) -> PredictOutput[T]:
if not pred.bboxes: if not pred.bboxes:
return pred return pred
@@ -223,7 +228,7 @@ def filter_by_ratio(pred: PredictOutput, low: float, high: float) -> PredictOutp
return pred return pred
def filter_k_largest(pred: PredictOutput, k: int = 0) -> PredictOutput: def filter_k_largest(pred: PredictOutput[T], k: int = 0) -> PredictOutput[T]:
if not pred.bboxes or k == 0: if not pred.bboxes or k == 0:
return pred return pred
areas = [bbox_area(bbox) for bbox in pred.bboxes] areas = [bbox_area(bbox) for bbox in pred.bboxes]

View File

@@ -28,7 +28,7 @@ def mediapipe_predict(
def mediapipe_face_detection( def mediapipe_face_detection(
model_type: int, image: Image.Image, confidence: float = 0.3 model_type: int, image: Image.Image, confidence: float = 0.3
) -> PredictOutput: ) -> PredictOutput[float]:
import mediapipe as mp import mediapipe as mp
img_width, img_height = image.size img_width, img_height = image.size
@@ -68,7 +68,9 @@ def mediapipe_face_detection(
return PredictOutput(bboxes=bboxes, masks=masks, preview=preview) return PredictOutput(bboxes=bboxes, masks=masks, preview=preview)
def mediapipe_face_mesh(image: Image.Image, confidence: float = 0.3) -> PredictOutput: def mediapipe_face_mesh(
image: Image.Image, confidence: float = 0.3
) -> PredictOutput[int]:
import mediapipe as mp import mediapipe as mp
mp_face_mesh = mp.solutions.face_mesh mp_face_mesh = mp.solutions.face_mesh
@@ -98,7 +100,9 @@ def mediapipe_face_mesh(image: Image.Image, confidence: float = 0.3) -> PredictO
connection_drawing_spec=drawing_styles.get_default_face_mesh_tesselation_style(), connection_drawing_spec=drawing_styles.get_default_face_mesh_tesselation_style(),
) )
points = np.intp([(land.x * w, land.y * h) for land in landmarks.landmark]) points = np.array(
[[land.x * w, land.y * h] for land in landmarks.landmark], dtype=int
)
outline = cv2.convexHull(points).reshape(-1).tolist() outline = cv2.convexHull(points).reshape(-1).tolist()
mask = Image.new("L", image.size, "black") mask = Image.new("L", image.size, "black")
@@ -113,7 +117,7 @@ def mediapipe_face_mesh(image: Image.Image, confidence: float = 0.3) -> PredictO
def mediapipe_face_mesh_eyes_only( def mediapipe_face_mesh_eyes_only(
image: Image.Image, confidence: float = 0.3 image: Image.Image, confidence: float = 0.3
) -> PredictOutput: ) -> PredictOutput[int]:
import mediapipe as mp import mediapipe as mp
mp_face_mesh = mp.solutions.face_mesh mp_face_mesh = mp.solutions.face_mesh
@@ -136,7 +140,9 @@ def mediapipe_face_mesh_eyes_only(
masks = [] masks = []
for landmarks in pred.multi_face_landmarks: for landmarks in pred.multi_face_landmarks:
points = np.intp([(land.x * w, land.y * h) for land in landmarks.landmark]) points = np.array(
[[land.x * w, land.y * h] for land in landmarks.landmark], dtype=int
)
left_eyes = points[left_idx] left_eyes = points[left_idx]
right_eyes = points[right_idx] right_eyes = points[right_idx]
left_outline = cv2.convexHull(left_eyes).reshape(-1).tolist() left_outline = cv2.convexHull(left_eyes).reshape(-1).tolist()

View File

@@ -21,7 +21,7 @@ def ultralytics_predict(
confidence: float = 0.3, confidence: float = 0.3,
device: str = "", device: str = "",
classes: str = "", classes: str = "",
) -> PredictOutput: ) -> PredictOutput[float]:
from ultralytics import YOLO from ultralytics import YOLO
model = YOLO(model_path) model = YOLO(model_path)

View File

@@ -1,7 +1,6 @@
from __future__ import annotations from __future__ import annotations
import importlib.util import importlib.util
import os
import subprocess import subprocess
import sys import sys
from importlib.metadata import version # python >= 3.8 from importlib.metadata import version # python >= 3.8
@@ -39,11 +38,7 @@ def is_installed(
def run_pip(*args): def run_pip(*args):
subprocess.run([sys.executable, "-m", "pip", "install", *args], check=False) subprocess.run([sys.executable, "-m", "pip", "install", *args], check=True)
def run_uv_pip(*args):
subprocess.run([sys.executable, "-m", "uv", "pip", "install", *args], check=False)
def install(): def install():
@@ -56,11 +51,6 @@ def install():
("protobuf", "4.25.3", "4.9999"), ("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 = [] pkgs = []
for pkg, low, high in deps: for pkg, low, high in deps:
if not is_installed(pkg, low, high): if not is_installed(pkg, low, high):
@@ -74,7 +64,8 @@ def install():
cmd = pkg cmd = pkg
pkgs.append(cmd) pkgs.append(cmd)
run_uv_pip(*pkgs) if pkgs:
run_pip(*pkgs)
try: try:

View File

@@ -853,10 +853,10 @@ def on_ui_settings():
shared.opts.add_option( shared.opts.add_option(
"ad_max_models", "ad_max_models",
shared.OptionInfo( shared.OptionInfo(
default=2, default=4,
label="Max models", label="Max models",
component=gr.Slider, component=gr.Slider,
component_args={"minimum": 1, "maximum": 10, "step": 1}, component_args={"minimum": 1, "maximum": 15, "step": 1},
section=section, section=section,
), ),
) )