lora working versions

This commit is contained in:
Vladimir Mandic
2023-02-13 11:44:25 -05:00
parent 16aeb09143
commit c9db3f2f6b
18 changed files with 190 additions and 160 deletions
+15 -8
View File
@@ -23,12 +23,9 @@ Simplified start script: `automatic.sh`
> ./automatic.sh
- Start in default mode with optimizations enabled
> ./automatic.sh env
- Print env info and exit
Example:
- Start in default mode with optimizations enabled
Additionally print environment info during startup
Example:
Version: c07487a Tue Jan 24 08:04:31 2023 -0500
Platform: Ubuntu 22.04.1 LTS 5.15.79.1-microsoft-standard-WSL2 x86_64
@@ -81,10 +78,20 @@ For some Torch optimizations notes, see Wiki
Fork is compatible with regular **PyTorch 1.13** as well as pre-release of **PyTorch 2.0**
See [Wiki](https://github.com/vladmandic/automatic/wiki) for **Torch** optimization notes
<br>
## Scripts
This repository comes with a large collection of scripts that can be used to process inputs, train, generate, and benchmark models
As well as number of auxiliary scripts that do not rely on **WebUI**, but can be used for end-to-end solutions such as extract frames from videos, etc.
For full details see [Docs](cli/README.md)
<br>
## Docs
Everything is in [Wiki](https://github.com/vladmandic/automatic/wiki)
Except my current [TODO](TODO.md)
- Scripts are in [Scripts](cli/README.md)
- Everything else is in [Wiki](https://github.com/vladmandic/automatic/wiki)
- Except my current [TODO](TODO.md)
+1 -45
View File
@@ -72,6 +72,7 @@ Tech that can be integrated as part of the core workflow...
- [Seed travel](https://github.com/yownas/seed_travel)
- [Google frame interpolation](https://github.com/google-research/frame-interpolation)
- [Prompt fusion](https://github.com/ljleb/prompt-fusion-extension)
- [ControlNet](https://github.com/lllyasviel/ControlNet)
## Experimental
@@ -80,49 +81,4 @@ Cool stuff that is not integrated anywhere...
- [TensorRT](https://www.photoroom.com/tech/stable-diffusion-25-percent-faster-and-save-seconds/)
- [GIT](https://huggingface.co/microsoft/git-large-textcaps)
- Bunch of stuff:<https://pharmapsychotic.com/tools.html>
- Prevalent colors to interrogate
- Auto-Sort inputs by face recognition
## Updates
- core library updates:
- must run `./automatic.sh install`
- note: this is quite a big one so some testing is reccomended after upgrade
- non-trivial ui updates
- renamed scripts in `cli/modules` to be more descriptive
if you're using old script names, update them
for example, `ffmpeg.py` is now `video-extract.py`
also possible that there are some bugs due to broken import paths, so testing is welcome
- updated script `process.py`
- new **brightness dynamic range** check
- new **preview** mode to run all checks but without saving images plus print a summary at the end
- updated scripts `models-preview.py`
- can generate **lora** previews, note that trigger keywords are inferred from model name so name models carefully
- can generate **hypernetwork** previews
- new script: `image-watermark.py`
- optionally strip exif from images
- add invisible watermark to images which persists even if user modifies image so we can always track it
- new script: `palette-extract.py`
- creates color palette wheel from image(s)
- new script: `extract-lora.py`
- extract lora from fine-tuned model
- updated `embedding-preview.py`
- skip existing previews or overwrite them
- expose **variation seed** in main ui
- integrated **seed travel** functionality into core
- integrated `pix2pix` functionality to standard `img2img` workflow
- note: requires **pix2pix** model to be loaded
- integrated large `cfg scale` values fix
- integrated **model converter**
- tested `aesthetic gradients` training, not worth it
- updated `image browser`
was broken for a while and maintainer is gone
- initial work on **queue management** allowing to submit multiple requests to server
- initial work on `lora` integration
can render loras without extensions
can extract lora from fine-tuned model
training prototype in place in `train-lora.py`, not optimized or integrated
- initial work on `custom diffusion` integration
no testing so far
- spent quite some time making stable-diffusion compatible with upcomming `pytorch` 2.0 release
and testing `dynamo` torch dynamic optimizer and `triton` script compiler
+14 -14
View File
@@ -1,4 +1,4 @@
#/bin/env bash
#!/bin/env bash
TF_CPP_MIN_LOG_LEVEL=2
FORCE_CUDA="1"
@@ -11,7 +11,7 @@ CUDA_AUTO_BOOST=1
CUDA_DEVICE_DEFAULT_PERSISTING_L2_CACHE_PERCENTAGE_LIMIT=0
if [ "$PYTHON" == "" ]; then
PYTHON=`which python`
PYTHON=$(which python)
fi
CMD="launch.py --api --xformers --disable-console-progressbars --gradio-queue --skip-version-check --cors-allow-origins=http://127.0.0.1:7860"
@@ -43,27 +43,27 @@ done
echo "SD server: $MODE"
VER=`git log -1 --pretty=format:"%h %ad"`
LSB=`lsb_release -ds 2>/dev/null`
UN=`uname -rm 2>/dev/null`
VER=$(git log -1 --pretty=format:"%h %ad")
LSB=$(lsb_release -ds 2>/dev/null)
UNAME=$(uname -rm 2>/dev/null)
echo "Version: $VER"
echo "Platform: $LSB $UN"
$PYTHON -c 'import torch; import platform; print("Python:", platform.python_version(), "Torch:", torch.__version__, "CUDA:", torch.version.cuda, "cuDNN:", torch.backends.cudnn.version(), "GPU:", torch.cuda.get_device_name(torch.cuda.current_device()), "Arch:", torch.cuda.get_device_capability());'
echo "Platform: $LSB $UNAME"
"$PYTHON" -c 'import torch; import platform; print("Python:", platform.python_version(), "Torch:", torch.__version__, "CUDA:", torch.version.cuda, "cuDNN:", torch.backends.cudnn.version(), "GPU:", torch.cuda.get_device_name(torch.cuda.current_device()), "Arch:", torch.cuda.get_device_capability());'
if [ $MODE == install ]; then
$PYTHON -m pip --version
if [ "$MODE" == install ]; then
"$PYTHON" -m pip --version
echo "Installing general requirements"
$PYTHON -m pip install --disable-pip-version-check --quiet --no-warn-conflicts --requirement requirements.txt
"$PYTHON" -m pip install --disable-pip-version-check --quiet --no-warn-conflicts --requirement requirements.txt
echo "Installing versioned requirements"
$PYTHON -m pip install --disable-pip-version-check --quiet --no-warn-conflicts --requirement requirements_versions.txt
"$PYTHON" -m pip install --disable-pip-version-check --quiet --no-warn-conflicts --requirement requirements_versions.txt
echo "Updating submodules"
git submodule update --rebase --remote
exit 0
fi
if [ $MODE == clean ]; then
if [ "$MODE" == clean ]; then
CMD="--disable-opt-split-attention --disable-console-progressbars --api"
$PYTHON launch.py $CMD
"$PYTHON" launch.py $CMD
exit 0
fi
@@ -75,4 +75,4 @@ if [ $MODE == optimized ]; then
CMD="$CMD"
fi
exec accelerate launch --no_python --quiet --num_cpu_threads_per_process=6 $PYTHON $CMD
exec accelerate launch --no_python --quiet --num_cpu_threads_per_process=6 "$PYTHON" $CMD
+13 -9
View File
@@ -15,7 +15,7 @@ from generate import sd, generate
default = 'sd-v15-runwayml.ckpt [cc6cb27103]'
embeddings = ['blonde', 'bruntette', 'sexy', 'naked', 'mia', 'lin', 'kelly', 'hanna', 'rreid-random-v0']
embeddings = ['blonde', 'bruntette', 'sexy', 'naked', 'ti-mia', 'ti-lin', 'ti-kelly', 'ti-hanna', 'ti-rreid-random']
exclude = ['sd-v20', 'sd-v21', 'inpainting', 'pix2pix']
prompt = "photo of <keyword> <embedding>, photograph, posing, pose, high detailed, intricate, elegant, sharp focus, skin texture, looking forward, facing camera, 135mm, shot on dslr, canon 5d, 4k, modelshoot style, cinematic lighting"
options = Map({
@@ -32,6 +32,7 @@ options = Map({
'width': 512,
'height': 512,
},
'format': '.jpg',
'paths': {
"root": "/mnt/c/Users/mandi/OneDrive/Generative/Generate",
"generate": "image",
@@ -43,11 +44,11 @@ options = Map({
"sd_vae": "vae-ft-mse-840000-ema-pruned.ckpt",
},
'lora': {
'strength': 0.8,
'strength': 0.9,
},
'hypernetwork': {
'keyword': 'beautiful sexy woman',
'strength': 1.0,
'strength': 0.9,
},
})
@@ -91,7 +92,7 @@ async def models(params):
log.info({ 'total jobs': len(models) * len(embeddings) * options.generate.batch_size, 'per-model': len(embeddings) * options.generate.batch_size })
log.info(json.dumps(options, indent=2))
for model in models:
fn = os.path.join(dir, model + '.png')
fn = os.path.join(dir, model + options.format)
if os.path.exists(fn) and len(params.input) == 0: # if model preview exists and not manually included
log.info({ 'model preview exists': model })
continue
@@ -139,15 +140,18 @@ async def lora(params):
models = [f.stem for f in models1 + models2]
log.info({ 'loras': len(models) })
for model in models:
fn = os.path.join(dir, model + '.png')
fn = os.path.join(dir, model + options.format)
if os.path.exists(fn) and len(params.input) == 0: # if model preview exists and not manually included
log.info({ 'lora preview exists': model })
continue
images = []
labels = []
t0 = time.time()
keyword = model.replace('-', ' ')
options.generate.prompt = prompt.replace('<keyword>', f'\"{keyword}\"')
import re
keywords = re.sub('\d', '', model)
keywords = keywords.replace('-v', ' ').replace('-', ' ').strip().split(' ')
keyword = '\"' + '\" \"'.join(keywords) + '\"'
options.generate.prompt = prompt.replace('<keyword>', keyword)
options.generate.prompt = options.generate.prompt.replace('<embedding>', '')
options.generate.prompt += f' <lora:{model}:{options.lora.strength}>'
log.info({ 'lora generating': model, 'keyword': keyword, 'prompt': options.generate.prompt })
@@ -175,7 +179,7 @@ async def hypernetwork(params):
models = [f.stem for f in Path(dir).glob('*.pt')]
log.info({ 'loras': len(models) })
for model in models:
fn = os.path.join(dir, model + '.png')
fn = os.path.join(dir, model + options.format)
if os.path.exists(fn) and len(params.input) == 0: # if model preview exists and not manually included
log.info({ 'hypernetwork preview exists': model })
continue
@@ -186,7 +190,7 @@ async def hypernetwork(params):
options.generate.prompt = prompt.replace('<keyword>', options.hypernetwork.keyword)
options.generate.prompt = options.generate.prompt.replace('<embedding>', '')
options.generate.prompt = f' <hypernet:{model}:{options.hypernetwork.strength}> ' + options.generate.prompt
log.info({ 'lora generating': model, 'keyword': keyword, 'prompt': options.generate.prompt })
log.info({ 'hypernetwork generating': model, 'keyword': keyword, 'prompt': options.generate.prompt })
data = await generate(options = options, quiet=True)
if 'image' in data:
for img in data['image']:
+29 -17
View File
@@ -7,6 +7,7 @@ process people images
- visible: is face or body detected
- in frame: for face based on box, for body based on number of visible keypoints
- resolution: is cropped image still of sufficient resolution
- optionaly upsample and restore face quality
- blur: is image sharp enough
- dynamic range: is image bright enough
- similarity: compares image to all previously processed images to see if its unique enough
@@ -40,20 +41,29 @@ from sdapi import postsync
params = Map({
# general settings, do not modify
'src': '', # source folder
'dst': '', # destination folder
'format': '.jpg', # image format
'extract_face': True, # extract face from image
'extract_body': True, # extract face from image
'clear_dst': True, # remove all files from destination at the start
'format': '.jpg', # image format
'target_size': 512, # target resolution
'square_images': True, # should output images be squared
'segmentation_model': 0, # segmentation model 0/general 1/landscape
'segmentation_background': (192, 192, 192), # segmentation background color
'blur_samplesize': 60, # sample size to use for blur detection
'similarity_size': 64, # base similarity detection on reduced images
# face processing settings
'extract_face': True, # extract face from image
'face_score': 0.7, # min face detection score
'face_pad': 0.2, # pad face image percentage
'face_model': 1, # which face model to use 0/close-up 1/standard
'face_blur_score': 1.5, # max score for face blur detection
'face_range_score': 0.15, # min score for face dynamic range detection
'face_restore': True, # attempt to restore face quality
'face_upscale': True, # attempt to scale small faces
'face_segmentation': False, # segmentation enabled
# body processing settings
'extract_body': True, # extract face from image
'body_score': 0.9, # min body detection score
'body_visibility': 0.5, # min visibility score for each detected body part
'body_parts': 15, # min number of detected body parts with sufficient visibility
@@ -61,16 +71,13 @@ params = Map({
'body_model': 2, # body model to use 0/low 1/medium 2/high
'body_blur_score': 1.8, # max score for body blur detection
'body_range_score': 0.15, # min score for body dynamic range detection
'segmentation_face': False, # segmentation enabled
'segmentation_body': False, # segmentation enabled
'segmentation_model': 0, # segmentation model 0/general 1/landscape
'segmentation_background': (192, 192, 192), # segmentation background color
'body_segmentation': False, # segmentation enabled
# similarity detection settings
'similarity_score': 0.8, # maximum similarity score before image is discarded
'similarity_size': 64, # base similarity detection on reduced images
'interrogate_model': ['clip', 'deepdanbooru'], # interrogate model
# interrogate settings
'interrogate_model': ['clip', 'deepdanbooru'], # interrogate models
'interrogate_captions': True, # write captions to file
'tag_limit': 5, # number of tags to extract
'face_restore': True, # attempt to restore face quality
'face_upscale': True, # attempt to scale small faces
})
face_model = None
body_model = None
@@ -181,6 +188,9 @@ def extract_face(img):
})
original = [cropped.size[0], cropped.size[1]]
res = postsync('/sdapi/v1/extra-single-image', kwargs)
if 'image' not in res:
log.error({ 'process face': 'upscale failed' })
raise ValueError('upscale failed')
cropped = Image.open(io.BytesIO(base64.b64decode(res['image'])))
kwargs.image = [cropped.size[0], cropped.size[1]]
upscaled = [cropped.size[0], cropped.size[1]]
@@ -195,7 +205,7 @@ def extract_face(img):
if params.square_images:
squared = Image.new('RGB', (params.target_size, params.target_size))
squared.paste(cropped, (0, 0))
if params.segmentation_face:
if params.face_segmentation:
squared = segmentation(squared)
else:
squared = cropped
@@ -258,7 +268,7 @@ def extract_body(img):
if params.square_images:
squared = Image.new('RGB', (params.target_size, params.target_size))
squared.paste(cropped, (0, 0))
if params.segmentation_body:
if params.body_segmentation:
squared = segmentation(squared)
else:
squared = cropped
@@ -293,7 +303,7 @@ def encode(img):
return encoded
def interrogate(img, fn, txt):
def interrogate(img, fn):
if len(params.interrogate_model) == 0:
return
caption = ''
@@ -308,7 +318,7 @@ def interrogate(img, fn, txt):
tag = res.caption if 'caption' in res else ''
tags = tag.split(',')
tags = [t.replace('(', '').replace(')', '').split(':')[0].strip() for t in tags]
if txt:
if params.interrogate_captions:
file = fn.replace(params.format, '.txt')
f = open(file, 'w')
f.write(caption)
@@ -323,7 +333,7 @@ def interrogate(img, fn, txt):
i = {}
metadata = Map({})
def process_file(f: str, dst: str = None, preview: bool = False, offline: bool = False, txt: bool = True):
def process_file(f: str, dst: str = None, preview: bool = False, offline: bool = False, txt = None):
def save(img, f, what):
i[what] = i.get(what, 0) + 1
@@ -338,7 +348,7 @@ def process_file(f: str, dst: str = None, preview: bool = False, offline: bool =
if not preview:
img.save(fn)
if not offline:
caption, tags = interrogate(img, fn, txt)
caption, tags = interrogate(img, fn)
metadata[fn] = { 'caption': caption, 'tags': tags }
return fn
@@ -350,6 +360,8 @@ def process_file(f: str, dst: str = None, preview: bool = False, offline: bool =
return 0, {}
image = ImageOps.exif_transpose(image) # rotate image according to EXIF orientation
if txt is not None:
params.interrogate_captions = txt
if image.width < 512 or image.height < 512:
log.info({ 'process skip': 'low resolution', 'resolution': [image.width, image.height] })
+5 -1
View File
@@ -182,12 +182,16 @@ if __name__ == '__main__':
if not args.noprocess:
# preprocess
for f in files:
res, metadata = modules.process.process_file(f = f, dst = dir, preview = False, offline = args.offline, txt = False)
try:
res, metadata = modules.process.process_file(f = f, dst = dir, preview = False, offline = args.offline, txt = False)
except ValueError as e:
exit(1)
modules.process.unload_models()
mem_stats()
if args.tag is not None:
for name, item in metadata.items():
item['tags'].insert(0, args.tag)
item['tags'] = ', '.join(item['tags'])
with open(json_file, "w") as outfile:
outfile.write(json.dumps(metadata, indent=2))
log.info({ 'processed': res, 'inputs': len(files), 'metadata': json_file, 'path': dir })
+1 -1
View File
@@ -34,7 +34,7 @@
"eta_ddim": 0.0,
"eta_noise_seed_delta": 0,
"export_for_4chan": false,
"extra_networks_default_multiplier": 1,
"extra_networks_default_multiplier": 0.9,
"extra_networks_default_view": "cards",
"face_restoration_model": "CodeFormer",
"face_restoration_unload": false,
@@ -15,7 +15,7 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage):
def list_items(self):
for name, lora_on_disk in lora.available_loras.items():
path, ext = os.path.splitext(lora_on_disk.filename)
previews = [path + ".png", path + ".preview.png"]
previews = [path + ".png", path + ".preview.png", path + ".jpg", path + ".preview.jpg", path + ".jpeg", path + ".preview.jpeg", path + ".webp", path + ".preview.webp"]
preview = None
for file in previews:
@@ -28,10 +28,9 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage):
"filename": path,
"preview": preview,
"search_term": self.search_terms_from_path(lora_on_disk.filename),
"prompt": json.dumps(f"<lora:{name}:") + " + opts.extra_networks_default_multiplier + " + json.dumps(">"),
"local_preview": path + ".png",
"prompt": json.dumps(f"<lora:{name}:{str(shared.opts.extra_networks_default_multiplier)}>"),
"local_preview": f"{path}.{shared.opts.samples_format}",
}
def allowed_directories_for_previews(self):
return [shared.cmd_opts.lora_dir]
+6
View File
@@ -2,6 +2,7 @@ import os
import sys
import traceback
import time
import git
from modules import paths, shared
@@ -25,6 +26,7 @@ class Extension:
self.status = ''
self.can_update = False
self.is_builtin = is_builtin
self.version = ''
repo = None
try:
@@ -40,6 +42,10 @@ class Extension:
try:
self.remote = next(repo.remote().urls, None)
self.status = 'unknown'
head = repo.head.commit
ts = time.asctime(time.gmtime(repo.head.commit.committed_date))
self.version = f'{head.hexsha[:7]} ({ts})'
except Exception:
self.remote = None
+44 -43
View File
@@ -1012,48 +1012,6 @@ def create_ui():
}
return interp_descriptions[value]
with gr.Blocks(analytics_enabled=False) as modelmerger_interface:
with gr.Row().style(equal_height=False):
with gr.Column(variant='compact'):
interp_description = gr.HTML(value=update_interp_description("Weighted sum"), elem_id="modelmerger_interp_description")
with FormRow(elem_id="modelmerger_models"):
primary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_primary_model_name", label="Primary model (A)")
create_refresh_button(primary_model_name, modules.sd_models.list_models, lambda: {"choices": modules.sd_models.checkpoint_tiles()}, "refresh_checkpoint_A")
secondary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_secondary_model_name", label="Secondary model (B)")
create_refresh_button(secondary_model_name, modules.sd_models.list_models, lambda: {"choices": modules.sd_models.checkpoint_tiles()}, "refresh_checkpoint_B")
tertiary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_tertiary_model_name", label="Tertiary model (C)")
create_refresh_button(tertiary_model_name, modules.sd_models.list_models, lambda: {"choices": modules.sd_models.checkpoint_tiles()}, "refresh_checkpoint_C")
custom_name = gr.Textbox(label="Custom Name (Optional)", elem_id="modelmerger_custom_name")
interp_amount = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Multiplier (M) - set to 0 to get model A', value=0.3, elem_id="modelmerger_interp_amount")
interp_method = gr.Radio(choices=["No interpolation", "Weighted sum", "Add difference"], value="Weighted sum", label="Interpolation Method", elem_id="modelmerger_interp_method")
interp_method.change(fn=update_interp_description, inputs=[interp_method], outputs=[interp_description])
with FormRow():
checkpoint_format = gr.Radio(choices=["ckpt", "safetensors"], value="ckpt", label="Checkpoint format", elem_id="modelmerger_checkpoint_format")
save_as_half = gr.Checkbox(value=False, label="Save as float16", elem_id="modelmerger_save_as_half")
with FormRow():
with gr.Column():
config_source = gr.Radio(choices=["A, B or C", "B", "C", "Don't"], value="A, B or C", label="Copy config from", type="index", elem_id="modelmerger_config_method")
with gr.Column():
with FormRow():
bake_in_vae = gr.Dropdown(choices=["None"] + list(sd_vae.vae_dict), value="None", label="Bake in VAE", elem_id="modelmerger_bake_in_vae")
create_refresh_button(bake_in_vae, sd_vae.refresh_vae_list, lambda: {"choices": ["None"] + list(sd_vae.vae_dict)}, "modelmerger_refresh_bake_in_vae")
with FormRow():
discard_weights = gr.Textbox(value="", label="Discard weights with matching name", elem_id="modelmerger_discard_weights")
with gr.Row():
modelmerger_merge = gr.Button(elem_id="modelmerger_merge", value="Merge", variant='primary')
with gr.Column(variant='compact', elem_id="modelmerger_results_container"):
with gr.Group(elem_id="modelmerger_results_panel"):
modelmerger_result = gr.HTML(elem_id="modelmerger_result", show_label=False)
with gr.Blocks(analytics_enabled=False) as train_interface:
with gr.Row().style(equal_height=False):
@@ -1062,6 +1020,49 @@ def create_ui():
with gr.Row(variant="compact").style(equal_height=False):
with gr.Tabs(elem_id="train_tabs"):
with gr.Tab(label="Merge models") as modelmerger_interface:
with gr.Row().style(equal_height=False):
with gr.Column(variant='compact'):
interp_description = gr.HTML(value=update_interp_description("Weighted sum"), elem_id="modelmerger_interp_description")
with FormRow(elem_id="modelmerger_models"):
primary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_primary_model_name", label="Primary model (A)")
create_refresh_button(primary_model_name, modules.sd_models.list_models, lambda: {"choices": modules.sd_models.checkpoint_tiles()}, "refresh_checkpoint_A")
secondary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_secondary_model_name", label="Secondary model (B)")
create_refresh_button(secondary_model_name, modules.sd_models.list_models, lambda: {"choices": modules.sd_models.checkpoint_tiles()}, "refresh_checkpoint_B")
tertiary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_tertiary_model_name", label="Tertiary model (C)")
create_refresh_button(tertiary_model_name, modules.sd_models.list_models, lambda: {"choices": modules.sd_models.checkpoint_tiles()}, "refresh_checkpoint_C")
custom_name = gr.Textbox(label="Custom Name (Optional)", elem_id="modelmerger_custom_name")
interp_amount = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Multiplier (M) - set to 0 to get model A', value=0.3, elem_id="modelmerger_interp_amount")
interp_method = gr.Radio(choices=["No interpolation", "Weighted sum", "Add difference"], value="Weighted sum", label="Interpolation Method", elem_id="modelmerger_interp_method")
interp_method.change(fn=update_interp_description, inputs=[interp_method], outputs=[interp_description])
with FormRow():
checkpoint_format = gr.Radio(choices=["ckpt", "safetensors"], value="ckpt", label="Checkpoint format", elem_id="modelmerger_checkpoint_format")
save_as_half = gr.Checkbox(value=False, label="Save as float16", elem_id="modelmerger_save_as_half")
with FormRow():
with gr.Column():
config_source = gr.Radio(choices=["A, B or C", "B", "C", "Don't"], value="A, B or C", label="Copy config from", type="index", elem_id="modelmerger_config_method")
with gr.Column():
with FormRow():
bake_in_vae = gr.Dropdown(choices=["None"] + list(sd_vae.vae_dict), value="None", label="Bake in VAE", elem_id="modelmerger_bake_in_vae")
create_refresh_button(bake_in_vae, sd_vae.refresh_vae_list, lambda: {"choices": ["None"] + list(sd_vae.vae_dict)}, "modelmerger_refresh_bake_in_vae")
with FormRow():
discard_weights = gr.Textbox(value="", label="Discard weights with matching name", elem_id="modelmerger_discard_weights")
with gr.Row():
modelmerger_merge = gr.Button(elem_id="modelmerger_merge", value="Merge", variant='primary')
with gr.Column(variant='compact', elem_id="modelmerger_results_container"):
with gr.Group(elem_id="modelmerger_results_panel"):
modelmerger_result = gr.HTML(elem_id="modelmerger_result", show_label=False)
with gr.Tab(label="Create embedding"):
new_embedding_name = gr.Textbox(label="Name", elem_id="train_new_embedding_name")
initialization_text = gr.Textbox(label="Initialization text", value="*", elem_id="train_initialization_text")
@@ -1539,7 +1540,7 @@ def create_ui():
(img2img_interface, "From Image", "img2img"),
(extras_interface, "Process", "extras"),
(pnginfo_interface, "Image Info", "pnginfo"),
(modelmerger_interface, "Checkpoint Merger", "modelmerger"),
# (modelmerger_interface, "Checkpoint Merger", "modelmerger"),
(train_interface, "Train", "ti"),
]
+3 -5
View File
@@ -80,6 +80,7 @@ def extension_table():
<tr>
<th><abbr title="Use checkbox to enable the extension; it will be enabled or disabled when you click apply button">Extension</abbr></th>
<th>URL</th>
<th><abbr title="Extension version">Version</abbr></th>
<th><abbr title="Use checkbox to mark the extension for update; it will be updated when you click apply button">Update</abbr></th>
</tr>
</thead>
@@ -87,11 +88,7 @@ def extension_table():
"""
for ext in extensions.extensions:
remote = ""
if ext.is_builtin:
remote = "built-in"
elif ext.remote:
remote = f"""<a href="{html.escape(ext.remote or '')}" target="_blank">{html.escape("built-in" if ext.is_builtin else ext.remote or '')}</a>"""
remote = f"""<a href="{html.escape(ext.remote or '')}" target="_blank">{html.escape("built-in" if ext.is_builtin else ext.remote or '')}</a>"""
if ext.can_update:
ext_status = f"""<label><input class="gr-check-radio gr-checkbox" name="update_{html.escape(ext.name)}" checked="checked" type="checkbox">{html.escape(ext.status)}</label>"""
@@ -102,6 +99,7 @@ def extension_table():
<tr>
<td><label><input class="gr-check-radio gr-checkbox" name="enable_{html.escape(ext.name)}" type="checkbox" {'checked="checked"' if ext.enabled else ''}>{html.escape(ext.name)}</label></td>
<td>{remote}</td>
<td>{ext.version}</td>
<td{' class="extension_status"' if ext.remote is not None else ''}>{ext_status}</td>
</tr>
"""
+3 -2
View File
@@ -17,7 +17,7 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage):
checkpoint: sd_models.CheckpointInfo
for name, checkpoint in sd_models.checkpoints_list.items():
path, ext = os.path.splitext(checkpoint.filename)
previews = [path + ".png", path + ".preview.png"]
previews = [path + ".png", path + ".preview.png", path + ".jpg", path + ".preview.jpg", path + ".jpeg", path + ".preview.jpeg", path + ".webp", path + ".preview.webp"]
preview = None
for file in previews:
@@ -31,7 +31,8 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage):
"preview": preview,
"search_term": self.search_terms_from_path(checkpoint.filename) + " " + (checkpoint.sha256 or ""),
"onclick": '"' + html.escape(f"""return selectCheckpoint({json.dumps(name)})""") + '"',
"local_preview": path + ".png",
"local_preview": f"{path}.{shared.opts.samples_format}",
}
def allowed_directories_for_previews(self):
+3 -3
View File
@@ -14,7 +14,7 @@ class ExtraNetworksPageHypernetworks(ui_extra_networks.ExtraNetworksPage):
def list_items(self):
for name, path in shared.hypernetworks.items():
path, ext = os.path.splitext(path)
previews = [path + ".png", path + ".preview.png"]
previews = [path + ".png", path + ".preview.png", path + ".jpg", path + ".preview.jpg", path + ".jpeg", path + ".preview.jpeg", path + ".webp", path + ".preview.webp"]
preview = None
for file in previews:
@@ -27,8 +27,8 @@ class ExtraNetworksPageHypernetworks(ui_extra_networks.ExtraNetworksPage):
"filename": path,
"preview": preview,
"search_term": self.search_terms_from_path(path),
"prompt": json.dumps(f"<hypernet:{name}:") + " + opts.extra_networks_default_multiplier + " + json.dumps(">"),
"local_preview": path + ".png",
"prompt": json.dumps(f"<hypernet:{name}:{str(shared.opts.extra_networks_default_multiplier)}>"),
"local_preview": f"{path}.{shared.opts.samples_format}",
}
def allowed_directories_for_previews(self):
@@ -1,7 +1,7 @@
import json
import os
from modules import ui_extra_networks, sd_hijack
from modules import ui_extra_networks, sd_hijack, shared
class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage):
@@ -16,10 +16,13 @@ class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage):
for embedding in sd_hijack.model_hijack.embedding_db.word_embeddings.values():
path, ext = os.path.splitext(embedding.filename)
preview_file = path + ".preview.png"
previews = [path + ".preview.png", path + ".preview.jpg", path + ".preview.jpeg", path + ".preview.webp"]
preview = None
if os.path.isfile(preview_file):
preview = self.link_preview(preview_file)
for file in previews:
if os.path.isfile(file):
preview = self.link_preview(file)
break
yield {
"name": embedding.name,
@@ -27,7 +30,7 @@ class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage):
"preview": preview,
"search_term": self.search_terms_from_path(embedding.filename),
"prompt": json.dumps(embedding.name),
"local_preview": path + ".preview.png",
"local_preview": f"{path}.preview.{shared.opts.samples_format}",
}
def allowed_directories_for_previews(self):
+40 -1
View File
@@ -1130,5 +1130,44 @@
"customscript/dynamic_thresholding.py/img2img/Power Scheduler Value/value": 4.0,
"customscript/dynamic_thresholding.py/img2img/Power Scheduler Value/minimum": 0.0,
"customscript/dynamic_thresholding.py/img2img/Power Scheduler Value/maximum": 15.0,
"customscript/dynamic_thresholding.py/img2img/Power Scheduler Value/step": 0.5
"customscript/dynamic_thresholding.py/img2img/Power Scheduler Value/step": 0.5,
"customscript/seed_travel.py/txt2img/Interpolation rate/visible": true,
"customscript/seed_travel.py/txt2img/Interpolation rate/value": "Linear",
"customscript/seed_travel.py/txt2img/Rate strength/visible": true,
"customscript/seed_travel.py/txt2img/Rate strength/value": 3,
"customscript/seed_travel.py/txt2img/Rate strength/minimum": 0.0,
"customscript/seed_travel.py/txt2img/Rate strength/maximum": 10.0,
"customscript/seed_travel.py/txt2img/Rate strength/step": 0.1,
"customscript/seed_travel.py/img2img/Interpolation rate/visible": true,
"customscript/seed_travel.py/img2img/Interpolation rate/value": "Linear",
"customscript/seed_travel.py/img2img/Rate strength/visible": true,
"customscript/seed_travel.py/img2img/Rate strength/value": 3,
"customscript/seed_travel.py/img2img/Rate strength/minimum": 0.0,
"customscript/seed_travel.py/img2img/Rate strength/maximum": 10.0,
"customscript/seed_travel.py/img2img/Rate strength/step": 0.1,
"train/Primary model (A)/visible": true,
"train/Primary model (A)/value": null,
"train/Secondary model (B)/visible": true,
"train/Secondary model (B)/value": null,
"train/Tertiary model (C)/visible": true,
"train/Tertiary model (C)/value": null,
"train/Custom Name (Optional)/visible": true,
"train/Custom Name (Optional)/value": "",
"train/Multiplier (M) - set to 0 to get model A/visible": true,
"train/Multiplier (M) - set to 0 to get model A/value": 0.3,
"train/Multiplier (M) - set to 0 to get model A/minimum": 0.0,
"train/Multiplier (M) - set to 0 to get model A/maximum": 1.0,
"train/Multiplier (M) - set to 0 to get model A/step": 0.05,
"train/Interpolation Method/visible": true,
"train/Interpolation Method/value": "Weighted sum",
"train/Checkpoint format/visible": true,
"train/Checkpoint format/value": "ckpt",
"train/Save as float16/visible": true,
"train/Save as float16/value": true,
"train/Copy config from/visible": true,
"train/Copy config from/value": "A, B or C",
"train/Bake in VAE/visible": true,
"train/Bake in VAE/value": "None",
"train/Discard weights with matching name/visible": true,
"train/Discard weights with matching name/value": ""
}