mirror of
https://github.com/vladmandic/automatic
synced 2026-09-19 17:24:32 +02:00
lora working versions
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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]
|
||||
|
||||
|
||||
Submodule extensions-builtin/sd-dynamic-thresholding updated: 681a5ffe7b...6c76a48a1f
Submodule extensions-builtin/seed_travel updated: 8f818f18f2...185ae54e93
Submodule extensions-builtin/stable-diffusion-webui-images-browser updated: 0416810adf...c25e8a73f3
@@ -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
@@ -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"),
|
||||
]
|
||||
|
||||
|
||||
@@ -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>
|
||||
"""
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
@@ -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": ""
|
||||
}
|
||||
Reference in New Issue
Block a user