Skip to content

Commit

Permalink
Merge branch 'main' into update/ci/fix-test-invoke-pip
Browse files Browse the repository at this point in the history
  • Loading branch information
lstein committed Feb 6, 2023
2 parents bc7a061 + 05bb9e4 commit 2935244
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 84 deletions.
28 changes: 10 additions & 18 deletions invokeai/frontend/README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,20 @@
# Stable Diffusion Web UI
# InvokeAI UI dev setup

## Run
The UI is in `invokeai/frontend`.

- `python scripts/dream.py --web` serves both frontend and backend at
http://localhost:9090
## Environment set up

## Evironment

Install [node](https://nodejs.org/en/download/) (includes npm) and optionally
Install [node](https://nodejs.org/en/download/) (includes npm) and
[yarn](https://yarnpkg.com/getting-started/install).

From `frontend/` run `npm install` / `yarn install` to install the frontend
packages.
From `invokeai/frontend/` run `yarn install` to get everything set up.

## Dev

1. From `frontend/`, run `npm dev` / `yarn dev` to start the dev server.
2. Run `python scripts/dream.py --web`.
3. Navigate to the dev server address e.g. `http://localhost:5173/`.

To build for dev: `npm build-dev` / `yarn build-dev`

To build for production: `npm build` / `yarn build`
1. Start the dev server: `yarn dev`
2. Start the InvokeAI UI per usual: `invokeai --web`
3. Point your browser to the dev server address e.g. `http://localhost:5173/`

## TODO
To build for dev: `yarn build-dev`

- Search repo for "TODO"
To build for production: `yarn build`
140 changes: 82 additions & 58 deletions ldm/invoke/merge_diffusers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,18 @@
from typing import List, Union

import npyscreen
from diffusers import DiffusionPipeline, logging as dlogging
from diffusers import DiffusionPipeline
from diffusers import logging as dlogging
from npyscreen import widget
from omegaconf import OmegaConf

from ldm.invoke.globals import (
Globals,
global_cache_dir,
global_config_file,
global_models_dir,
global_set_root,
)
from ldm.invoke.globals import (Globals, global_cache_dir, global_config_file,
global_models_dir, global_set_root)
from ldm.invoke.model_manager import ModelManager

DEST_MERGED_MODEL_DIR = "merged_models"


def merge_diffusion_models(
model_ids_or_paths: List[Union[str, Path]],
alpha: float = 0.5,
Expand All @@ -48,10 +46,10 @@ def merge_diffusion_models(
cache_dir, resume_download, force_download, proxies, local_files_only, use_auth_token, revision, torch_dtype, device_map
"""
with warnings.catch_warnings():
warnings.simplefilter('ignore')
warnings.simplefilter("ignore")
verbosity = dlogging.get_verbosity()
dlogging.set_verbosity_error()

pipe = DiffusionPipeline.from_pretrained(
model_ids_or_paths[0],
cache_dir=kwargs.get("cache_dir", global_cache_dir()),
Expand Down Expand Up @@ -188,13 +186,12 @@ class FloatTitleSlider(npyscreen.TitleText):


class mergeModelsForm(npyscreen.FormMultiPageAction):

interpolations = ["weighted_sum", "sigmoid", "inv_sigmoid", "add_difference"]

def __init__(self, parentApp, name):
self.parentApp = parentApp
self.ALLOW_RESIZE=True
self.FIX_MINIMUM_SIZE_WHEN_CREATED=False
self.ALLOW_RESIZE = True
self.FIX_MINIMUM_SIZE_WHEN_CREATED = False
super().__init__(parentApp, name)

@property
Expand All @@ -205,29 +202,29 @@ def afterEditing(self):
self.parentApp.setNextForm(None)

def create(self):
window_height,window_width=curses.initscr().getmaxyx()
window_height, window_width = curses.initscr().getmaxyx()

self.model_names = self.get_model_names()
max_width = max([len(x) for x in self.model_names])
max_width += 6
horizontal_layout = max_width*3 < window_width
horizontal_layout = max_width * 3 < window_width

self.add_widget_intelligent(
npyscreen.FixedText,
color='CONTROL',
color="CONTROL",
value=f"Select two models to merge and optionally a third.",
editable=False,
)
self.add_widget_intelligent(
npyscreen.FixedText,
color='CONTROL',
color="CONTROL",
value=f"Use up and down arrows to move, <space> to select an item, <tab> and <shift-tab> to move from one field to the next.",
editable=False,
)
self.add_widget_intelligent(
npyscreen.FixedText,
value='MODEL 1',
color='GOOD',
value="MODEL 1",
color="GOOD",
editable=False,
rely=4 if horizontal_layout else None,
)
Expand All @@ -242,57 +239,57 @@ def create(self):
)
self.add_widget_intelligent(
npyscreen.FixedText,
value='MODEL 2',
color='GOOD',
value="MODEL 2",
color="GOOD",
editable=False,
relx=max_width+3 if horizontal_layout else None,
relx=max_width + 3 if horizontal_layout else None,
rely=4 if horizontal_layout else None,
)
self.model2 = self.add_widget_intelligent(
npyscreen.SelectOne,
name='(2)',
name="(2)",
values=self.model_names,
value=1,
max_height=len(self.model_names),
max_width=max_width,
relx=max_width+3 if horizontal_layout else None,
relx=max_width + 3 if horizontal_layout else None,
rely=5 if horizontal_layout else None,
scroll_exit=True,
)
self.add_widget_intelligent(
npyscreen.FixedText,
value='MODEL 3',
color='GOOD',
value="MODEL 3",
color="GOOD",
editable=False,
relx=max_width*2+3 if horizontal_layout else None,
relx=max_width * 2 + 3 if horizontal_layout else None,
rely=4 if horizontal_layout else None,
)
models_plus_none = self.model_names.copy()
models_plus_none.insert(0,'None')
models_plus_none.insert(0, "None")
self.model3 = self.add_widget_intelligent(
npyscreen.SelectOne,
name='(3)',
name="(3)",
values=models_plus_none,
value=0,
max_height=len(self.model_names)+1,
max_height=len(self.model_names) + 1,
max_width=max_width,
scroll_exit=True,
relx=max_width*2+3 if horizontal_layout else None,
relx=max_width * 2 + 3 if horizontal_layout else None,
rely=5 if horizontal_layout else None,
)
for m in [self.model1,self.model2,self.model3]:
for m in [self.model1, self.model2, self.model3]:
m.when_value_edited = self.models_changed
self.merged_model_name = self.add_widget_intelligent(
npyscreen.TitleText,
name="Name for merged model:",
labelColor='CONTROL',
labelColor="CONTROL",
value="",
scroll_exit=True,
)
self.force = self.add_widget_intelligent(
npyscreen.Checkbox,
name="Force merge of incompatible models",
labelColor='CONTROL',
labelColor="CONTROL",
value=False,
scroll_exit=True,
)
Expand All @@ -301,7 +298,7 @@ def create(self):
name="Merge Method:",
values=self.interpolations,
value=0,
labelColor='CONTROL',
labelColor="CONTROL",
max_height=len(self.interpolations) + 1,
scroll_exit=True,
)
Expand All @@ -312,7 +309,7 @@ def create(self):
step=0.05,
lowest=0,
value=0.5,
labelColor='CONTROL',
labelColor="CONTROL",
scroll_exit=True,
)
self.model1.editing = True
Expand All @@ -322,43 +319,43 @@ def models_changed(self):
selected_model1 = self.model1.value[0]
selected_model2 = self.model2.value[0]
selected_model3 = self.model3.value[0]
merged_model_name = f'{models[selected_model1]}+{models[selected_model2]}'
merged_model_name = f"{models[selected_model1]}+{models[selected_model2]}"
self.merged_model_name.value = merged_model_name

if selected_model3 > 0:
self.merge_method.values=['add_difference'],
self.merged_model_name.value += f'+{models[selected_model3]}'
self.merge_method.values = (["add_difference"],)
self.merged_model_name.value += f"+{models[selected_model3]}"
else:
self.merge_method.values=self.interpolations
self.merge_method.value=0
self.merge_method.values = self.interpolations
self.merge_method.value = 0

def on_ok(self):
if self.validate_field_values() and self.check_for_overwrite():
self.parentApp.setNextForm(None)
self.editing = False
self.parentApp.merge_arguments = self.marshall_arguments()
npyscreen.notify('Starting the merge...')
npyscreen.notify("Starting the merge...")
else:
self.editing = True

def on_cancel(self):
sys.exit(0)

def marshall_arguments(self)->dict:
def marshall_arguments(self) -> dict:
model_names = self.model_names
models = [
model_names[self.model1.value[0]],
model_names[self.model2.value[0]],
]
]
if self.model3.value[0] > 0:
models.append(model_names[self.model3.value[0]-1])
models.append(model_names[self.model3.value[0] - 1])

args = dict(
models=models,
alpha = self.alpha.value,
interp = self.interpolations[self.merge_method.value[0]],
force = self.force.value,
merged_model_name = self.merged_model_name.value,
alpha=self.alpha.value,
interp=self.interpolations[self.merge_method.value[0]],
force=self.force.value,
merged_model_name=self.merged_model_name.value,
)
return args

Expand All @@ -371,18 +368,22 @@ def check_for_overwrite(self) -> bool:
f"The chosen merged model destination, {model_out}, is already in use. Overwrite?"
)

def validate_field_values(self)->bool:
def validate_field_values(self) -> bool:
bad_fields = []
model_names = self.model_names
selected_models = set((model_names[self.model1.value[0]],model_names[self.model2.value[0]]))
selected_models = set(
(model_names[self.model1.value[0]], model_names[self.model2.value[0]])
)
if self.model3.value[0] > 0:
selected_models.add(model_names[self.model3.value[0]-1])
selected_models.add(model_names[self.model3.value[0] - 1])
if len(selected_models) < 2:
bad_fields.append(f'Please select two or three DIFFERENT models to compare. You selected {selected_models}')
bad_fields.append(
f"Please select two or three DIFFERENT models to compare. You selected {selected_models}"
)
if len(bad_fields) > 0:
message = 'The following problems were detected and must be corrected:'
message = "The following problems were detected and must be corrected:"
for problem in bad_fields:
message += f'\n* {problem}'
message += f"\n* {problem}"
npyscreen.notify_confirm(message)
return False
else:
Expand Down Expand Up @@ -410,6 +411,7 @@ def onStart(self):
npyscreen.setTheme(npyscreen.Themes.ElegantTheme)
self.main = self.addForm("MAIN", mergeModelsForm, name="Merge Models Settings")


def run_gui(args: Namespace):
mergeapp = Mergeapp()
mergeapp.run()
Expand Down Expand Up @@ -450,5 +452,27 @@ def main():
] = cache_dir # because not clear the merge pipeline is honoring cache_dir
args.cache_dir = cache_dir

try:
if args.front_end:
run_gui(args)
else:
run_cli(args)
print(f">> Conversion successful. New model is named {args.merged_model_name}")
except widget.NotEnoughSpaceForWidget as e:
if str(e).startswith("Height of 1 allocated"):
print(
"** You need to have at least two diffusers models defined in models.yaml in order to merge"
)
else:
print(f"** A layout error has occurred: {str(e)}")
sys.exit(-1)
except Exception as e:
print(">> An error occurred:")
traceback.print_exc()
sys.exit(-1)
except KeyboardInterrupt:
sys.exit(-1)


if __name__ == "__main__":
main()
12 changes: 11 additions & 1 deletion ldm/invoke/training/textual_inversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from typing import List, Tuple

import npyscreen
from npyscreen import widget
from omegaconf import OmegaConf

from ldm.invoke.globals import Globals, global_set_root
Expand Down Expand Up @@ -295,7 +296,7 @@ def get_model_names(self) -> Tuple[List[str], int]:
for idx in range(len(model_names))
if "default" in conf[model_names[idx]]
]
default = defaults[0] if len(defaults)>0 else 0
default = defaults[0] if len(defaults) > 0 else 0
return (model_names, default)

def marshall_arguments(self) -> dict:
Expand Down Expand Up @@ -438,11 +439,20 @@ def main():
do_front_end(args)
else:
do_textual_inversion_training(**vars(args))
except widget.NotEnoughSpaceForWidget as e:
if str(e).startswith("Height of 1 allocated"):
print(
"** You need to have at least one diffusers models defined in models.yaml in order to train"
)
else:
print(f"** A layout error has occurred: {str(e)}")
sys.exit(-1)
except AssertionError as e:
print(str(e))
sys.exit(-1)
except KeyboardInterrupt:
pass


if __name__ == "__main__":
main()
Loading

0 comments on commit 2935244

Please sign in to comment.