added controlnet models to frontend; backend needs to be done

This commit is contained in:
Lincoln Stein 2023-05-30 00:38:37 -04:00
parent dc54cbb1fc
commit c9ee42450e
5 changed files with 240 additions and 164 deletions

View File

@ -359,6 +359,7 @@ setting environment variables INVOKEAI_<setting>.
conf_path : Path = Field(default='configs/models.yaml', description='Path to models definition file', category='Paths') conf_path : Path = Field(default='configs/models.yaml', description='Path to models definition file', category='Paths')
embedding_dir : Path = Field(default='embeddings', description='Path to InvokeAI textual inversion aembeddings directory', category='Paths') embedding_dir : Path = Field(default='embeddings', description='Path to InvokeAI textual inversion aembeddings directory', category='Paths')
gfpgan_model_dir : Path = Field(default="./models/gfpgan/GFPGANv1.4.pth", description='Path to GFPGAN models directory.', category='Paths') gfpgan_model_dir : Path = Field(default="./models/gfpgan/GFPGANv1.4.pth", description='Path to GFPGAN models directory.', category='Paths')
controlnet_dir : Path = Field(default="controlnet", description='Path to directory of ControlNet models.', category='Paths')
legacy_conf_dir : Path = Field(default='configs/stable-diffusion', description='Path to directory of legacy checkpoint config files', category='Paths') legacy_conf_dir : Path = Field(default='configs/stable-diffusion', description='Path to directory of legacy checkpoint config files', category='Paths')
lora_dir : Path = Field(default='loras', description='Path to InvokeAI LoRA model directory', category='Paths') lora_dir : Path = Field(default='loras', description='Path to InvokeAI LoRA model directory', category='Paths')
outdir : Path = Field(default='outputs', description='Default folder for output images', category='Paths') outdir : Path = Field(default='outputs', description='Default folder for output images', category='Paths')
@ -465,6 +466,13 @@ setting environment variables INVOKEAI_<setting>.
''' '''
return self._resolve(self.lora_dir) if self.lora_dir else None return self._resolve(self.lora_dir) if self.lora_dir else None
@property
def controlnet_path(self)->Path:
'''
Path to the controlnet models directory.
'''
return self._resolve(self.controlnet_dir) if self.controlnet_dir else None
@property @property
def autoconvert_path(self)->Path: def autoconvert_path(self)->Path:
''' '''

View File

@ -12,7 +12,6 @@ print("Loading Python libraries...\n",file=sys.stderr)
import argparse import argparse
import io import io
import os import os
import re
import shutil import shutil
import traceback import traceback
import warnings import warnings
@ -67,14 +66,9 @@ config = get_invokeai_config()
Model_dir = "models" Model_dir = "models"
Weights_dir = "ldm/stable-diffusion-v1/" Weights_dir = "ldm/stable-diffusion-v1/"
# the initial "configs" dir is now bundled in the `invokeai.configs` package
Dataset_path = Path(configs.__path__[0]) / "INITIAL_MODELS.yaml"
Default_config_file = config.model_conf_path Default_config_file = config.model_conf_path
SD_Configs = config.legacy_conf_path SD_Configs = config.legacy_conf_path
Datasets = OmegaConf.load(Dataset_path)
# minimum size for the UI # minimum size for the UI
MIN_COLS = 135 MIN_COLS = 135
MIN_LINES = 45 MIN_LINES = 45

View File

@ -49,9 +49,9 @@ Config_preamble = """
def default_config_file(): def default_config_file():
print(config.root_dir)
return config.model_conf_path return config.model_conf_path
def sd_configs(): def sd_configs():
return config.legacy_conf_path return config.legacy_conf_path
@ -59,8 +59,7 @@ def initial_models():
global Datasets global Datasets
if Datasets: if Datasets:
return Datasets return Datasets
return (Datasets := OmegaConf.load(Dataset_path)) return (Datasets := OmegaConf.load(Dataset_path)['diffusers'])
def install_requested_models( def install_requested_models(
install_initial_models: List[str] = None, install_initial_models: List[str] = None,
@ -79,7 +78,7 @@ def install_requested_models(
if not config_file_path.exists(): if not config_file_path.exists():
open(config_file_path, "w") open(config_file_path, "w")
model_manager = ModelManager(OmegaConf.load(config_file_path), precision=precision) model_manager = ModelManager(OmegaConf.load(config_file_path)['diffusers'], precision=precision)
if remove_models and len(remove_models) > 0: if remove_models and len(remove_models) > 0:
print("== DELETING UNCHECKED STARTER MODELS ==") print("== DELETING UNCHECKED STARTER MODELS ==")

View File

@ -1,83 +1,98 @@
stable-diffusion-1.5: diffusers:
description: Stable Diffusion version 1.5 diffusers model (4.27 GB) stable-diffusion-1.5:
repo_id: runwayml/stable-diffusion-v1-5 description: Stable Diffusion version 1.5 diffusers model (4.27 GB)
format: diffusers repo_id: runwayml/stable-diffusion-v1-5
vae: format: diffusers
repo_id: stabilityai/sd-vae-ft-mse vae:
recommended: True repo_id: stabilityai/sd-vae-ft-mse
default: True recommended: True
sd-inpainting-1.5: default: True
description: RunwayML SD 1.5 model optimized for inpainting, diffusers version (4.27 GB) sd-inpainting-1.5:
repo_id: runwayml/stable-diffusion-inpainting description: RunwayML SD 1.5 model optimized for inpainting, diffusers version (4.27 GB)
format: diffusers repo_id: runwayml/stable-diffusion-inpainting
vae: format: diffusers
repo_id: stabilityai/sd-vae-ft-mse vae:
recommended: True repo_id: stabilityai/sd-vae-ft-mse
stable-diffusion-2.1: recommended: True
description: Stable Diffusion version 2.1 diffusers model, trained on 768 pixel images (5.21 GB) stable-diffusion-2.1:
repo_id: stabilityai/stable-diffusion-2-1 description: Stable Diffusion version 2.1 diffusers model, trained on 768 pixel images (5.21 GB)
format: diffusers repo_id: stabilityai/stable-diffusion-2-1
recommended: True format: diffusers
sd-inpainting-2.0: recommended: True
description: Stable Diffusion version 2.0 inpainting model (5.21 GB) sd-inpainting-2.0:
repo_id: stabilityai/stable-diffusion-2-inpainting description: Stable Diffusion version 2.0 inpainting model (5.21 GB)
format: diffusers repo_id: stabilityai/stable-diffusion-2-inpainting
recommended: False format: diffusers
analog-diffusion-1.0: recommended: False
description: An SD-1.5 model trained on diverse analog photographs (2.13 GB) analog-diffusion-1.0:
repo_id: wavymulder/Analog-Diffusion description: An SD-1.5 model trained on diverse analog photographs (2.13 GB)
format: diffusers repo_id: wavymulder/Analog-Diffusion
recommended: false format: diffusers
deliberate-1.0: recommended: false
description: Versatile model that produces detailed images up to 768px (4.27 GB) deliberate-1.0:
format: diffusers description: Versatile model that produces detailed images up to 768px (4.27 GB)
repo_id: XpucT/Deliberate format: diffusers
recommended: False repo_id: XpucT/Deliberate
d&d-diffusion-1.0: recommended: False
description: Dungeons & Dragons characters (2.13 GB) d&d-diffusion-1.0:
format: diffusers description: Dungeons & Dragons characters (2.13 GB)
repo_id: 0xJustin/Dungeons-and-Diffusion format: diffusers
recommended: False repo_id: 0xJustin/Dungeons-and-Diffusion
dreamlike-photoreal-2.0: recommended: False
description: A photorealistic model trained on 768 pixel images based on SD 1.5 (2.13 GB) dreamlike-photoreal-2.0:
format: diffusers description: A photorealistic model trained on 768 pixel images based on SD 1.5 (2.13 GB)
repo_id: dreamlike-art/dreamlike-photoreal-2.0 format: diffusers
recommended: False repo_id: dreamlike-art/dreamlike-photoreal-2.0
inkpunk-1.0: recommended: False
description: Stylized illustrations inspired by Gorillaz, FLCL and Shinkawa; prompt with "nvinkpunk" (4.27 GB) inkpunk-1.0:
format: diffusers description: Stylized illustrations inspired by Gorillaz, FLCL and Shinkawa; prompt with "nvinkpunk" (4.27 GB)
repo_id: Envvi/Inkpunk-Diffusion format: diffusers
recommended: False repo_id: Envvi/Inkpunk-Diffusion
openjourney-4.0: recommended: False
description: An SD 1.5 model fine tuned on Midjourney; prompt with "mdjrny-v4 style" (2.13 GB) openjourney-4.0:
format: diffusers description: An SD 1.5 model fine tuned on Midjourney; prompt with "mdjrny-v4 style" (2.13 GB)
repo_id: prompthero/openjourney format: diffusers
vae: repo_id: prompthero/openjourney
repo_id: stabilityai/sd-vae-ft-mse vae:
recommended: False repo_id: stabilityai/sd-vae-ft-mse
portrait-plus-1.0: recommended: False
description: An SD-1.5 model trained on close range portraits of people; prompt with "portrait+" (2.13 GB) portrait-plus-1.0:
format: diffusers description: An SD-1.5 model trained on close range portraits of people; prompt with "portrait+" (2.13 GB)
repo_id: wavymulder/portraitplus format: diffusers
recommended: False repo_id: wavymulder/portraitplus
seek-art-mega-1.0: recommended: False
description: A general use SD-1.5 "anything" model that supports multiple styles (2.1 GB) seek-art-mega-1.0:
repo_id: coreco/seek.art_MEGA description: A general use SD-1.5 "anything" model that supports multiple styles (2.1 GB)
format: diffusers repo_id: coreco/seek.art_MEGA
vae: format: diffusers
repo_id: stabilityai/sd-vae-ft-mse vae:
recommended: False repo_id: stabilityai/sd-vae-ft-mse
trinart-2.0: recommended: False
description: An SD-1.5 model finetuned with ~40K assorted high resolution manga/anime-style images (2.13 GB) trinart-2.0:
repo_id: naclbit/trinart_stable_diffusion_v2 description: An SD-1.5 model finetuned with ~40K assorted high resolution manga/anime-style images (2.13 GB)
format: diffusers repo_id: naclbit/trinart_stable_diffusion_v2
vae: format: diffusers
repo_id: stabilityai/sd-vae-ft-mse vae:
recommended: False repo_id: stabilityai/sd-vae-ft-mse
waifu-diffusion-1.4: recommended: False
description: An SD-1.5 model trained on 680k anime/manga-style images (2.13 GB) waifu-diffusion-1.4:
repo_id: hakurei/waifu-diffusion description: An SD-1.5 model trained on 680k anime/manga-style images (2.13 GB)
format: diffusers repo_id: hakurei/waifu-diffusion
vae: format: diffusers
repo_id: stabilityai/sd-vae-ft-mse vae:
recommended: False repo_id: stabilityai/sd-vae-ft-mse
recommended: False
controlnet:
canny: lllyasviel/control_v11p_sd15_canny
inpaint: lllyasviel/control_v11p_sd15_inpaint
mlsd: lllyasviel/control_v11p_sd15_mlsd
depth: lllyasviel/control_v11f1p_sd15_depth
normal_bae: lllyasviel/control_v11p_sd15_normalbae
seg: lllyasviel/control_v11p_sd15_seg
lineart: lllyasviel/control_v11p_sd15_lineart
lineart_anime: lllyasviel/control_v11p_sd15s2_lineart_anime
scribble: lllyasviel/control_v11p_sd15_scribble
softedge: lllyasviel/control_v11p_sd15_softedge
shuffle: lllyasviel/control_v11e_sd15_shuffle
tile: lllyasviel/control_v11f1e_sd15_tile
ip2p: lllyasviel/control_v11e_sd15_ip2p

View File

@ -53,14 +53,17 @@ class addModelsForm(npyscreen.FormMultiPage):
def __init__(self, parentApp, name, multipage=False, *args, **keywords): def __init__(self, parentApp, name, multipage=False, *args, **keywords):
self.multipage = multipage self.multipage = multipage
self.initial_models = OmegaConf.load(Dataset_path) self.initial_models = OmegaConf.load(Dataset_path)['diffusers']
self.control_net_models = OmegaConf.load(Dataset_path)['controlnet']
self.installed_cn_models = self._get_installed_cn_models()
try: try:
self.existing_models = OmegaConf.load(default_config_file()) self.existing_models = OmegaConf.load(default_config_file())
except: except:
self.existing_models = dict() self.existing_models = dict()
self.starter_model_list = [ # self.starter_model_list = [
x for x in list(self.initial_models.keys()) if x not in self.existing_models # x for x in list(self.initial_models.keys()) if x not in self.existing_models
] # ]
self.starter_model_list = list(self.initial_models.keys())
self.installed_models = dict() self.installed_models = dict()
super().__init__(parentApp=parentApp, name=name, *args, **keywords) super().__init__(parentApp=parentApp, name=name, *args, **keywords)
@ -75,6 +78,9 @@ class addModelsForm(npyscreen.FormMultiPage):
self.installed_models = sorted( self.installed_models = sorted(
[x for x in list(self.initial_models.keys()) if x in self.existing_models] [x for x in list(self.initial_models.keys()) if x in self.existing_models]
) )
cn_model_list = sorted(self.control_net_models.keys())
self.nextrely -= 1 self.nextrely -= 1
self.add_widget_intelligent( self.add_widget_intelligent(
npyscreen.FixedText, npyscreen.FixedText,
@ -89,44 +95,44 @@ class addModelsForm(npyscreen.FormMultiPage):
color="CAUTION", color="CAUTION",
) )
self.nextrely += 1 self.nextrely += 1
if len(self.installed_models) > 0: # if len(self.installed_models) > 0:
self.add_widget_intelligent( # self.add_widget_intelligent(
CenteredTitleText, # CenteredTitleText,
name="== INSTALLED STARTER MODELS ==", # name="== INSTALLED STARTER MODELS ==",
editable=False, # editable=False,
color="CONTROL", # color="CONTROL",
) # )
self.nextrely -= 1 # self.nextrely -= 1
self.add_widget_intelligent( # self.add_widget_intelligent(
CenteredTitleText, # CenteredTitleText,
name="Currently installed starter models. Uncheck to delete:", # name="Currently installed starter models. Uncheck to delete:",
editable=False, # editable=False,
labelColor="CAUTION", # labelColor="CAUTION",
) # )
self.nextrely -= 1 # self.nextrely -= 1
columns = self._get_columns() # columns = self._get_columns()
self.previously_installed_models = self.add_widget_intelligent( # self.previously_installed_models = self.add_widget_intelligent(
MultiSelectColumns, # MultiSelectColumns,
columns=columns, # columns=columns,
values=self.installed_models, # values=self.installed_models,
value=[x for x in range(0, len(self.installed_models))], # value=[x for x in range(0, len(self.installed_models))],
max_height=1 + len(self.installed_models) // columns, # max_height=1 + len(self.installed_models) // columns,
relx=4, # relx=4,
slow_scroll=True, # slow_scroll=True,
scroll_exit=True, # scroll_exit=True,
) # )
self.purge_deleted = self.add_widget_intelligent( # self.purge_deleted = self.add_widget_intelligent(
npyscreen.Checkbox, # npyscreen.Checkbox,
name="Purge deleted models from disk", # name="Purge deleted models from disk",
value=False, # value=False,
scroll_exit=True, # scroll_exit=True,
relx=4, # relx=4,
) # )
self.nextrely += 1 # self.nextrely += 1
if len(self.starter_model_list) > 0: if len(self.starter_model_list) > 0:
self.add_widget_intelligent( self.add_widget_intelligent(
CenteredTitleText, CenteredTitleText,
name="== STARTER MODELS (recommended ones selected) ==", name="== DIFFUSERS MODELS (recommended ones selected) ==",
editable=False, editable=False,
color="CONTROL", color="CONTROL",
) )
@ -148,12 +154,42 @@ class addModelsForm(npyscreen.FormMultiPage):
value=[ value=[
self.starter_model_list.index(x) self.starter_model_list.index(x)
for x in self.starter_model_list for x in self.starter_model_list
if show_recommended and x in recommended_models if (show_recommended and x in recommended_models)\
or (x in self.existing_models)
], ],
max_height=len(starter_model_labels) + 1, max_height=len(starter_model_labels) + 1,
relx=4, relx=4,
scroll_exit=True, scroll_exit=True,
) )
self.add_widget_intelligent(
CenteredTitleText,
name="== CONTROLNET MODELS ==",
editable=False,
color="CONTROL",
)
columns=6
self.cn_models_selected = self.add_widget_intelligent(
MultiSelectColumns,
columns=columns,
name="Install ControlNet Models",
values=cn_model_list,
value=[
cn_model_list.index(x)
for x in cn_model_list
if x in self.installed_cn_models
],
max_height=len(cn_model_list)//columns + 1,
relx=4,
scroll_exit=True,
)
self.nextrely += 1
self.purge_deleted = self.add_widget_intelligent(
npyscreen.Checkbox,
name="Purge unchecked models from disk",
value=False,
scroll_exit=True,
relx=4,
)
self.add_widget_intelligent( self.add_widget_intelligent(
CenteredTitleText, CenteredTitleText,
name="== IMPORT LOCAL AND REMOTE MODELS ==", name="== IMPORT LOCAL AND REMOTE MODELS ==",
@ -263,6 +299,21 @@ class addModelsForm(npyscreen.FormMultiPage):
for x in range(0, len(names)) for x in range(0, len(names))
] ]
def _get_installed_cn_models(self)->list[str]:
with open('log.txt','w') as file:
cn_dir = config.controlnet_path
file.write(f'cn_dir={cn_dir}\n')
installed_cn_models = set()
for root, dirs, files in os.walk(cn_dir):
for name in dirs:
file.write(f'{root}/{name}/config.json\n')
if Path(root, name, 'config.json').exists():
installed_cn_models.add(name)
inverse_dict = {name.split('/')[1]: key for key, name in self.control_net_models.items()}
file.write(f'inverse={inverse_dict}')
return [inverse_dict[x] for x in installed_cn_models]
def _get_columns(self) -> int: def _get_columns(self) -> int:
window_width, window_height = get_terminal_size() window_width, window_height = get_terminal_size()
cols = ( cols = (
@ -318,16 +369,20 @@ class addModelsForm(npyscreen.FormMultiPage):
) )
else: else:
starter_models = dict() starter_models = dict()
selections.purge_deleted_models = False selections.purge_deleted_models = self.purge_deleted.value
if hasattr(self, "previously_installed_models"):
unchecked = [ selections.install_models = [x for x in starter_models if x not in self.existing_models]
self.previously_installed_models.values[x] selections.remove_models = [x for x in self.starter_model_list if x in self.existing_models and x not in starter_models]
for x in range(0, len(self.previously_installed_models.values))
if x not in self.previously_installed_models.value selections.install_cn_models = [self.control_net_models[self.cn_models_selected.values[x]]
] for x in self.cn_models_selected.value
starter_models.update(map(lambda x: (x, False), unchecked)) if self.cn_models_selected.values[x] not in self.installed_cn_models
selections.purge_deleted_models = self.purge_deleted.value ]
selections.starter_models = starter_models selections.remove_cn_models = [self.control_net_models[x]
for x in self.cn_models_selected.values
if x in self.installed_cn_models
and self.cn_models_selected.values.index(x) not in self.cn_models_selected.value
]
# load directory and whether to scan on startup # load directory and whether to scan on startup
if self.show_directory_fields.value: if self.show_directory_fields.value:
@ -346,8 +401,11 @@ class AddModelApplication(npyscreen.NPSAppManaged):
super().__init__() super().__init__()
self.user_cancelled = False self.user_cancelled = False
self.user_selections = Namespace( self.user_selections = Namespace(
starter_models=None, install_models=None,
remove_models=None,
purge_deleted_models=False, purge_deleted_models=False,
install_cn_models = None,
remove_cn_models = None,
scan_directory=None, scan_directory=None,
autoscan_on_startup=None, autoscan_on_startup=None,
import_model_paths=None, import_model_paths=None,
@ -362,28 +420,29 @@ class AddModelApplication(npyscreen.NPSAppManaged):
# -------------------------------------------------------- # --------------------------------------------------------
def process_and_execute(opt: Namespace, selections: Namespace): def process_and_execute(opt: Namespace, selections: Namespace):
models_to_remove = [ models_to_install = selections.install_models
x for x in selections.starter_models if not selections.starter_models[x] models_to_remove = selections.remove_models
]
models_to_install = [
x for x in selections.starter_models if selections.starter_models[x]
]
directory_to_scan = selections.scan_directory directory_to_scan = selections.scan_directory
scan_at_startup = selections.autoscan_on_startup scan_at_startup = selections.autoscan_on_startup
potential_models_to_install = selections.import_model_paths potential_models_to_install = selections.import_model_paths
install_requested_models( print('NOT INSTALLING MODELS DURING DEBUGGING')
install_initial_models=models_to_install, print('models to install:',models_to_install)
remove_models=models_to_remove, print('models to remove:',models_to_remove)
scan_directory=Path(directory_to_scan) if directory_to_scan else None, print('CN models to install:',selections.install_cn_models)
external_models=potential_models_to_install, print('CN models to remove:',selections.remove_cn_models)
scan_at_startup=scan_at_startup, # install_requested_models(
precision="float32" # install_initial_models=models_to_install,
if opt.full_precision # remove_models=models_to_remove,
else choose_precision(torch.device(choose_torch_device())), # scan_directory=Path(directory_to_scan) if directory_to_scan else None,
purge_deleted=selections.purge_deleted_models, # external_models=potential_models_to_install,
config_file_path=Path(opt.config_file) if opt.config_file else None, # scan_at_startup=scan_at_startup,
) # precision="float32"
# if opt.full_precision
# else choose_precision(torch.device(choose_torch_device())),
# purge_deleted=selections.purge_deleted_models,
# config_file_path=Path(opt.config_file) if opt.config_file else None,
# )
# -------------------------------------------------------- # --------------------------------------------------------
@ -453,8 +512,9 @@ def main():
opt = parser.parse_args() opt = parser.parse_args()
# setting a global here # setting a global here
config.root = Path(opt.root or '') if opt.root and Path(opt.root).exists():
config.root = Path(opt.root)
if not (config.root_dir / config.conf_path.parent).exists(): if not (config.root_dir / config.conf_path.parent).exists():
logger.info( logger.info(
"Your InvokeAI root directory is not set up. Calling invokeai-configure." "Your InvokeAI root directory is not set up. Calling invokeai-configure."