diff --git a/TODO.md b/TODO.md index 03fe28160..dc2b5b49c 100644 --- a/TODO.md +++ b/TODO.md @@ -47,6 +47,8 @@ Tech that can be integrated as part of the core workflow... - [LORA](https://github.com/cloneofsimo/lora) - - + - + - - [Hypernetworks](https://civitai.com/models/4086/luisap-tutorial-hypernetwork-monkeypatch-method) - - @@ -65,6 +67,7 @@ Tech that can be integrated as part of the core workflow... - [Video killed the radio star](https://colab.research.google.com/github/dmarx/video-killed-the-radio-star/blob/main/Video_Killed_The_Radio_Star_Defusion.ipynb) - [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) ## Experimental diff --git a/cli/generate.py b/cli/generate.py index 921debbe4..83fcdccb2 100755 --- a/cli/generate.py +++ b/cli/generate.py @@ -33,7 +33,7 @@ from PIL.TiffImagePlugin import ImageFileDirectory_v2 sys.path.append(os.path.join(os.path.dirname(__file__), 'modules')) from modules.sdapi import close, get, interrupt, post, session -from modules.util import Map, log +from modules.util import Map, log, safestring sd = {} @@ -51,7 +51,8 @@ def grid(data): for i, img in enumerate(data.image): image.paste(img, box=(i % cols * w, i // cols * h)) short = data.info.prompt[:min(len(data.info.prompt), 96)] # limit prompt part of filename to 96 chars - name = '{seed:0>9}-{short}.jpg'.format(short = short, seed = data.info.all_seeds[0]) # pylint: disable=consider-using-f-string + name = '{seed:0>9} {short}'.format(short = short, seed = data.info.all_seeds[0]) # pylint: disable=consider-using-f-string + name = safestring(name) + '.jpg' f = os.path.join(sd.paths.root, sd.paths.grid, name) log.info({ 'grid': { 'name': f, 'size': image.size, 'images': len(data.image) } }) image.save(f, 'JPEG', exif = exif(data.info, None, 'grid'), optimize = True, quality = 70) @@ -118,7 +119,10 @@ def sampler(params, options): # find sampler return sd.generate.sampler_name -async def generate(prompt = None): # pylint: disable=redefined-outer-name +async def generate(prompt = None, options = None): # pylint: disable=redefined-outer-name + global sd + if options: + sd = Map(options) if prompt is not None: sd.generate.prompt = prompt log.info({ 'generate': sd.generate }) @@ -137,7 +141,8 @@ async def generate(prompt = None): # pylint: disable=redefined-outer-name for i in range(len(images)): b64s.append(images[i]) images[i] = Image.open(io.BytesIO(base64.b64decode(images[i].split(',',1)[0]))) - name = '{seed:0>9}-{short}.jpg'.format(short = short, seed = info.all_seeds[i]) # pylint: disable=consider-using-f-string + name = '{seed:0>9} {short}'.format(short = short, seed = info.all_seeds[i]) # pylint: disable=consider-using-f-string + name = safestring(name) + '.jpg' f = os.path.join(sd.paths.root, sd.paths.generate, name) names.append(f) log.info({ 'image': { 'name': f, 'size': images[i].size } }) diff --git a/cli/models.py b/cli/models.py new file mode 100755 index 000000000..a105a2a32 --- /dev/null +++ b/cli/models.py @@ -0,0 +1,94 @@ +#!/bin/env python +import os +import sys +import json +import asyncio +import argparse + +sys.path.append(os.path.join(os.path.dirname(__file__), 'modules')) +from modules.util import Map, log +from modules.sdapi import get, post, close +from generate import sd, generate +from modules.grid import grid + + +embeddings = ['blonde', 'bruntette', 'sexy', 'mia', 'lin', 'kelly', 'hanna', 'rreid-random-v0'] +exclude = ['sd-v20', 'sd-v21', 'inpainting'] +prompt = "photo of beautiful woman , 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({ + 'generate': { + 'restore_faces': True, + 'prompt': '', + 'negative_prompt': 'digital art, cgi, render, foggy, blurry, blurred, duplicate, ugly, mutilated, mutation, mutated, out of frame, bad anatomy, disfigured, deformed, censored, low res, low resolution, watermark, text, poorly drawn face, poorly drawn hands, signature', + 'steps': 30, + 'batch_size': 4, + 'n_iter': 1, + 'seed': -1, + 'sampler_name': 'DPM2 Karras', + 'cfg_scale': 7, + 'width': 512, + 'height': 512 + }, + 'paths': { + "root": "/mnt/c/Users/mandi/OneDrive/Generative/Generate", + "generate": "image", + "upscale": "upscale", + "grid": "grid" + }, + 'options': { + "sd_model_checkpoint": "sd-v15-runwayml", + "sd_vae": "vae-ft-mse-840000-ema-pruned.ckpt" + } +}) + + +async def models(params): + global sd + data = await get('/sdapi/v1/sd-models') + all = [m['title'] for m in data] + models = [] + excluded = [] + for m in all: + ok = True + for e in exclude: + if e in m: + excluded.append(m) + ok = False + break + if ok: + models.append(m) + log.info({ 'models preview' }) + log.info({ 'models': len(models), 'excluded': len(excluded) }) + log.info({ 'embeddings': len(embeddings) }) + log.info({ 'batch size': options.generate.batch_size }) + log.info({ 'total jobs': len(models) * len(embeddings) * options.generate.batch_size }) + log.info(json.dumps(options, indent=2)) + models = ['sd-v15-runwayml.ckpt [cc6cb27103]'] + for model in models: + opt = await get('/sdapi/v1/options') + opt['sd_model_checkpoint'] = model + await post('/sdapi/v1/options', opt) + images = [] + labels = [] + for embedding in embeddings: + options.generate.prompt = prompt.replace('', f'\"{embedding}\"') + log.info({ 'embedding': embedding, 'prompt': options.generate.prompt }) + data = await generate(options = options) + if 'image' in data: + for img in data['image']: + images.append(img) + labels.append(embedding) + else: + log.error({ 'model': model, 'embedding': embedding, 'error': data }) + image = grid(images = images, labels = labels, border = 8) + fn = os.path.join(params.output, model + '.jpg') + image.save(fn) + log.info({ 'file': fn, 'model': model, 'images': len(images), 'grid': [image.width, image.height] }) + await close() + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description = 'generate model previews') + parser.add_argument('--output', type = str, default = '', required = False, help = 'output directory') + params = parser.parse_args() + asyncio.run(models(params)) diff --git a/cli/modules/util.py b/cli/modules/util.py index e246edfff..6c8c4fa1e 100755 --- a/cli/modules/util.py +++ b/cli/modules/util.py @@ -3,6 +3,7 @@ generic helper methods """ +import string import logging log_format = '%(asctime)s %(levelname)s: %(message)s' @@ -19,6 +20,14 @@ def set_logfile(logfile): log.info({ 'log file': logfile }) +def safestring(text: str): + lines = [] + for line in text.splitlines(): + lines.append(line.translate(str.maketrans('', '', string.punctuation)).strip()) + res = ', '.join(lines) + return res[:1000] + + class Map(dict): def __init__(self, *args, **kwargs): super(Map, self).__init__(*args, **kwargs) diff --git a/config.json b/config.json index f93ffade8..32ca35979 100644 --- a/config.json +++ b/config.json @@ -177,5 +177,7 @@ "upscaling_max_images_in_cache": 5, "upcast_attn": false, "lora_apply_to_outputs": false, - "outdir_ip2p_samples": "outputs/ip2p-images" + "outdir_ip2p_samples": "outputs/ip2p-images", + "postprocessing_enable_in_main_ui": [], + "postprocessing_operation_order": [] } \ No newline at end of file