| from __future__ import annotations |
|
|
| import importlib |
| from functools import lru_cache |
| from pathlib import Path |
| import re |
|
|
| from modules import 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") |
| is_in_builtin = False |
| controlnet_exists = False |
| controlnet_enabled_models = { |
| 'inpaint': 'inpaint_global_harmonious', |
| 'scribble': 't2ia_sketch_pidi', |
| 'lineart': 'lineart_coarse', |
| 'openpose': 'openpose_full', |
| 'tile': None, |
| } |
| controlnet_model_regex = re.compile(r'.*('+('|'.join(controlnet_enabled_models.keys()))+').*') |
|
|
| if ext_path.exists(): |
| controlnet_exists = any( |
| p.name == "sd-webui-controlnet" for p in ext_path.iterdir() if p.is_dir() |
| ) |
|
|
| if not controlnet_exists and ext_builtin_path.exists(): |
| controlnet_exists = any( |
| p.name == "sd-webui-controlnet" |
| for p in ext_builtin_path.iterdir() |
| if p.is_dir() |
| ) |
|
|
| if controlnet_exists: |
| is_in_builtin = True |
|
|
|
|
| class ControlNetExt: |
| def __init__(self): |
| self.cn_models = ["None"] |
| self.cn_available = False |
| self.external_cn = None |
|
|
| def init_controlnet(self): |
| if is_in_builtin: |
| import_path = "extensions-builtin.sd-webui-controlnet.scripts.external_code" |
| else: |
| import_path = "extensions.sd-webui-controlnet.scripts.external_code" |
|
|
| self.external_cn = importlib.import_module(import_path, "external_code") |
| self.cn_available = True |
| models = self.external_cn.get_models() |
| self.cn_models.extend(m for m in models if controlnet_model_regex.match(m)) |
|
|
| def _update_scripts_args(self, p, model: str, weight: float, guidance_end: float): |
| module = None |
| for m, v in controlnet_enabled_models.items(): |
| if m in model: |
| module = v |
| break |
|
|
| cn_units = [ |
| self.external_cn.ControlNetUnit( |
| model=model, |
| weight=weight, |
| control_mode=self.external_cn.ControlMode.BALANCED, |
| module=module, |
| guidance_end=guidance_end, |
| pixel_perfect=True, |
| ) |
| ] |
|
|
| self.external_cn.update_cn_script_in_processing(p, cn_units) |
|
|
| def update_scripts_args(self, p, model: str, weight: float, guidance_end: float): |
| if self.cn_available and model != "None": |
| self._update_scripts_args(p, model, weight, guidance_end) |
|
|
|
|
| def get_cn_model_dirs() -> list[Path]: |
| cn_model_dir = Path(models_path, "ControlNet") |
| if is_in_builtin: |
| cn_model_dir_old = Path(ext_builtin_path, "sd-webui-controlnet", "models") |
| else: |
| cn_model_dir_old = Path(ext_path, "sd-webui-controlnet", "models") |
| ext_dir1 = shared.opts.data.get("control_net_models_path", "") |
| ext_dir2 = shared.opts.data.get("controlnet_dir", "") |
|
|
| dirs = [cn_model_dir, cn_model_dir_old] |
| for ext_dir in [ext_dir1, ext_dir2]: |
| if ext_dir: |
| dirs.append(Path(ext_dir)) |
|
|
| return dirs |
|
|
|
|
| @lru_cache |
| def _get_cn_inpaint_models() -> list[str]: |
| """ |
| Since we can't import ControlNet, we use a function that does something like |
| controlnet's `list(global_state.cn_models_names.values())`. |
| """ |
| cn_model_exts = (".pt", ".pth", ".ckpt", ".safetensors") |
| dirs = get_cn_model_dirs() |
| name_filter = shared.opts.data.get("control_net_models_name_filter", "") |
| name_filter = name_filter.strip(" ").lower() |
|
|
| model_paths = [] |
|
|
| for base in dirs: |
| if not base.exists(): |
| continue |
|
|
| for p in base.rglob("*"): |
| if p.is_file() and p.suffix in cn_model_exts and controlnet_model_regex.match(p.name): |
| if name_filter and name_filter not in p.name.lower(): |
| continue |
| model_paths.append(p) |
| model_paths.sort(key=lambda p: p.name) |
|
|
| models = [] |
| for p in model_paths: |
| model_hash = sd_models.model_hash(p) |
| name = f"{p.stem} [{model_hash}]" |
| models.append(name) |
| return models |
|
|
|
|
| def get_cn_inpaint_models() -> list[str]: |
| if controlnet_exists: |
| return _get_cn_inpaint_models() |
| return [] |
|
|