2022-08-21 23:57:48 +00:00
|
|
|
#!/usr/bin/env python3
|
2022-08-24 13:22:04 +00:00
|
|
|
# Copyright (c) 2022 Lincoln D. Stein (https://github.com/lstein)
|
|
|
|
|
2022-08-17 02:49:47 +00:00
|
|
|
import os
|
2022-08-29 16:54:40 +00:00
|
|
|
import re
|
2022-08-21 15:03:22 +00:00
|
|
|
import sys
|
2022-08-24 16:02:36 +00:00
|
|
|
import copy
|
2022-08-26 05:20:01 +00:00
|
|
|
import warnings
|
2022-08-31 04:33:23 +00:00
|
|
|
import time
|
2022-08-26 02:49:15 +00:00
|
|
|
import ldm.dream.readline
|
2022-09-17 17:28:37 +00:00
|
|
|
from ldm.dream.args import Args, metadata_dumps
|
2022-09-16 17:09:04 +00:00
|
|
|
from ldm.dream.pngwriter import PngWriter
|
2022-08-28 20:37:27 +00:00
|
|
|
from ldm.dream.server import DreamServer, ThreadingDreamServer
|
2022-08-31 04:36:38 +00:00
|
|
|
from ldm.dream.image_util import make_grid
|
2022-09-01 18:04:31 +00:00
|
|
|
from omegaconf import OmegaConf
|
2022-08-18 16:43:59 +00:00
|
|
|
|
2022-09-09 22:49:51 +00:00
|
|
|
# Placeholder to be replaced with proper class that tracks the
|
|
|
|
# outputs and associates with the prompt that generated them.
|
|
|
|
# Just want to get the formatting look right for now.
|
|
|
|
output_cntr = 0
|
|
|
|
|
2022-08-17 01:34:37 +00:00
|
|
|
def main():
|
2022-08-26 07:15:42 +00:00
|
|
|
"""Initialize command-line parsers and the diffusion model"""
|
2022-09-16 17:09:04 +00:00
|
|
|
opt = Args()
|
|
|
|
args = opt.parse_args()
|
|
|
|
if not args:
|
|
|
|
sys.exit(-1)
|
2022-09-11 19:47:12 +00:00
|
|
|
|
2022-09-16 17:09:04 +00:00
|
|
|
if args.laion400m:
|
2022-09-01 19:46:53 +00:00
|
|
|
print('--laion400m flag has been deprecated. Please use --model laion400m instead.')
|
|
|
|
sys.exit(-1)
|
2022-09-16 17:09:04 +00:00
|
|
|
if args.weights:
|
|
|
|
print('--weights argument has been deprecated. Please edit ./configs/models.yaml, and select the weights using --model instead.')
|
2022-09-01 19:46:53 +00:00
|
|
|
sys.exit(-1)
|
2022-09-11 19:47:12 +00:00
|
|
|
|
2022-08-26 07:15:42 +00:00
|
|
|
print('* Initializing, be patient...\n')
|
2022-08-21 15:03:22 +00:00
|
|
|
sys.path.append('.')
|
2022-09-06 00:40:10 +00:00
|
|
|
from ldm.generate import Generate
|
2022-08-26 07:15:42 +00:00
|
|
|
|
2022-08-22 19:33:27 +00:00
|
|
|
# these two lines prevent a horrible warning message from appearing
|
|
|
|
# when the frozen CLIP tokenizer is imported
|
|
|
|
import transformers
|
|
|
|
transformers.logging.set_verbosity_error()
|
2022-08-26 07:15:42 +00:00
|
|
|
|
2022-09-13 17:17:14 +00:00
|
|
|
# Loading Face Restoration and ESRGAN Modules
|
|
|
|
try:
|
|
|
|
gfpgan, codeformer, esrgan = None, None, None
|
2022-09-19 17:59:43 +00:00
|
|
|
from ldm.dream.restoration import Restoration
|
2022-09-13 17:17:14 +00:00
|
|
|
restoration = Restoration(opt.gfpgan_dir, opt.gfpgan_model_path, opt.esrgan_bg_tile)
|
|
|
|
if opt.restore:
|
|
|
|
gfpgan, codeformer = restoration.load_face_restore_models()
|
|
|
|
else:
|
2022-09-19 17:59:43 +00:00
|
|
|
print('>> Face restoration disabled')
|
2022-09-13 17:17:14 +00:00
|
|
|
if opt.esrgan:
|
|
|
|
esrgan = restoration.load_ersgan()
|
|
|
|
else:
|
2022-09-19 17:59:43 +00:00
|
|
|
print('>> Upscaling disabled')
|
2022-09-13 17:17:14 +00:00
|
|
|
except (ModuleNotFoundError, ImportError):
|
|
|
|
import traceback
|
|
|
|
print(traceback.format_exc(), file=sys.stderr)
|
|
|
|
print('>> You may need to install the ESRGAN and/or GFPGAN modules')
|
|
|
|
|
|
|
|
# creating a simple text2image object with a handful of
|
2022-08-17 01:34:37 +00:00
|
|
|
# defaults passed on the command line.
|
|
|
|
# additional parameters will be added (or overriden) during
|
|
|
|
# the user input loop
|
2022-09-14 11:02:31 +00:00
|
|
|
try:
|
|
|
|
gen = Generate(
|
2022-09-16 17:09:04 +00:00
|
|
|
conf = opt.conf,
|
2022-09-14 11:02:31 +00:00
|
|
|
model = opt.model,
|
|
|
|
sampler_name = opt.sampler_name,
|
|
|
|
embedding_path = opt.embedding_path,
|
|
|
|
full_precision = opt.full_precision,
|
2022-09-13 17:17:14 +00:00
|
|
|
gfpgan=gfpgan,
|
|
|
|
codeformer=codeformer,
|
|
|
|
esrgan=esrgan
|
2022-09-14 11:02:31 +00:00
|
|
|
)
|
|
|
|
except (FileNotFoundError, IOError, KeyError) as e:
|
|
|
|
print(f'{e}. Aborting.')
|
|
|
|
sys.exit(-1)
|
2022-08-17 01:34:37 +00:00
|
|
|
|
2022-08-17 02:49:47 +00:00
|
|
|
# make sure the output directory exists
|
|
|
|
if not os.path.exists(opt.outdir):
|
|
|
|
os.makedirs(opt.outdir)
|
2022-08-26 07:15:42 +00:00
|
|
|
|
2022-08-26 11:10:37 +00:00
|
|
|
# load the infile as a list of lines
|
2022-08-23 04:30:06 +00:00
|
|
|
infile = None
|
2022-08-26 11:10:37 +00:00
|
|
|
if opt.infile:
|
2022-08-28 18:20:34 +00:00
|
|
|
try:
|
|
|
|
if os.path.isfile(opt.infile):
|
2022-08-29 02:57:13 +00:00
|
|
|
infile = open(opt.infile, 'r', encoding='utf-8')
|
2022-08-28 20:14:29 +00:00
|
|
|
elif opt.infile == '-': # stdin
|
2022-08-28 18:20:34 +00:00
|
|
|
infile = sys.stdin
|
|
|
|
else:
|
|
|
|
raise FileNotFoundError(f'{opt.infile} not found.')
|
2022-08-28 20:14:29 +00:00
|
|
|
except (FileNotFoundError, IOError) as e:
|
2022-08-28 18:20:34 +00:00
|
|
|
print(f'{e}. Aborting.')
|
|
|
|
sys.exit(-1)
|
2022-08-23 04:30:06 +00:00
|
|
|
|
2022-09-03 13:39:35 +00:00
|
|
|
if opt.seamless:
|
|
|
|
print(">> changed to seamless tiling mode")
|
|
|
|
|
2022-08-17 01:34:37 +00:00
|
|
|
# preload the model
|
2022-09-14 11:02:31 +00:00
|
|
|
gen.load_model()
|
2022-08-26 02:57:30 +00:00
|
|
|
|
2022-08-28 18:20:34 +00:00
|
|
|
if not infile:
|
|
|
|
print(
|
2022-08-28 19:54:12 +00:00
|
|
|
"\n* Initialization done! Awaiting your command (-h for help, 'q' to quit)"
|
2022-08-28 18:20:34 +00:00
|
|
|
)
|
2022-08-17 01:34:37 +00:00
|
|
|
|
2022-09-14 11:02:31 +00:00
|
|
|
# web server loops forever
|
2022-08-29 00:34:55 +00:00
|
|
|
if opt.web:
|
2022-09-13 17:17:14 +00:00
|
|
|
dream_server_loop(gen, opt.host, opt.port, opt.outdir, gfpgan)
|
2022-09-14 11:02:31 +00:00
|
|
|
sys.exit(0)
|
2022-08-17 01:34:37 +00:00
|
|
|
|
2022-09-16 17:09:04 +00:00
|
|
|
main_loop(gen, opt, infile)
|
2022-08-19 03:23:44 +00:00
|
|
|
|
2022-09-14 11:02:31 +00:00
|
|
|
# TODO: main_loop() has gotten busy. Needs to be refactored.
|
2022-09-16 17:09:04 +00:00
|
|
|
def main_loop(gen, opt, infile):
|
2022-08-26 07:15:42 +00:00
|
|
|
"""prompt/read/execute loop"""
|
2022-09-11 19:47:12 +00:00
|
|
|
done = False
|
|
|
|
path_filter = re.compile(r'[<>:"/\\|?*]')
|
2022-09-11 14:52:19 +00:00
|
|
|
last_results = list()
|
2022-09-17 05:14:00 +00:00
|
|
|
model_config = OmegaConf.load(opt.conf)[opt.model]
|
2022-08-29 16:54:40 +00:00
|
|
|
|
|
|
|
# os.pathconf is not available on Windows
|
|
|
|
if hasattr(os, 'pathconf'):
|
2022-09-16 17:09:04 +00:00
|
|
|
path_max = os.pathconf(opt.outdir, 'PC_PATH_MAX')
|
|
|
|
name_max = os.pathconf(opt.outdir, 'PC_NAME_MAX')
|
2022-08-29 16:54:40 +00:00
|
|
|
else:
|
|
|
|
path_max = 260
|
|
|
|
name_max = 255
|
2022-08-26 07:15:42 +00:00
|
|
|
|
2022-08-19 03:03:22 +00:00
|
|
|
while not done:
|
add ability to post-process images from the CLI
- supports gfpgan, esrgan, codeformer and embiggen
- To use:
dream> !fix ./outputs/img-samples/000056.292144555.png -ft gfpgan -U2 -G0.8
dream> !fix ./outputs/img-samples/000056.292144555.png -ft codeformer -G 0.8
dream> !fix ./outputs/img-samples/000056.29214455.png -U4
dream> !fix ./outputs/img-samples/000056.292144555.png -embiggen 1.5
The first example invokes gfpgan to fix faces and esrgan to upscale.
The second example invokes codeformer to fix faces, no upscaling
The third example uses esrgan to upscale 4X
The four example runs embiggen to enlarge 1.5X
- This is very preliminary work. There are some anomalies to note:
1. The syntax is non-obvious. I would prefer something like:
!fix esrgan,gfpgan
!fix esrgan
!fix embiggen,codeformer
However, this will require refactoring the gfpgan and embiggen
code.
2. Images generated using gfpgan, esrgan or codeformer all are named
"xxxxxx.xxxxxx.postprocessed.png" and the original is saved.
However, the prefix is a new one that is not related to the
original.
3. Images generated using embiggen are named "xxxxx.xxxxxxx.png",
and once again the prefix is new. I'm not sure whether the
prefix should be aligned with the original file's prefix or not.
Probably not, but opinions welcome.
2022-09-18 21:26:09 +00:00
|
|
|
operation = 'generate' # default operation, alternative is 'postprocess'
|
|
|
|
|
2022-08-17 01:34:37 +00:00
|
|
|
try:
|
2022-08-28 18:20:34 +00:00
|
|
|
command = get_next_command(infile)
|
2022-08-17 01:34:37 +00:00
|
|
|
except EOFError:
|
2022-08-19 03:03:22 +00:00
|
|
|
done = True
|
2022-09-11 14:02:44 +00:00
|
|
|
continue
|
2022-09-11 19:47:12 +00:00
|
|
|
|
2022-08-26 11:10:37 +00:00
|
|
|
# skip empty lines
|
|
|
|
if not command.strip():
|
|
|
|
continue
|
2022-08-23 04:30:06 +00:00
|
|
|
|
2022-08-26 07:15:42 +00:00
|
|
|
if command.startswith(('#', '//')):
|
2022-08-23 04:30:06 +00:00
|
|
|
continue
|
|
|
|
|
2022-09-17 02:12:35 +00:00
|
|
|
if len(command.strip()) == 1 and command.startswith('q'):
|
2022-08-19 03:03:22 +00:00
|
|
|
done = True
|
|
|
|
break
|
2022-08-23 01:01:06 +00:00
|
|
|
|
2022-09-16 17:09:04 +00:00
|
|
|
if command.startswith(
|
2022-08-26 07:15:42 +00:00
|
|
|
'!dream'
|
|
|
|
): # in case a stored prompt still contains the !dream command
|
add ability to post-process images from the CLI
- supports gfpgan, esrgan, codeformer and embiggen
- To use:
dream> !fix ./outputs/img-samples/000056.292144555.png -ft gfpgan -U2 -G0.8
dream> !fix ./outputs/img-samples/000056.292144555.png -ft codeformer -G 0.8
dream> !fix ./outputs/img-samples/000056.29214455.png -U4
dream> !fix ./outputs/img-samples/000056.292144555.png -embiggen 1.5
The first example invokes gfpgan to fix faces and esrgan to upscale.
The second example invokes codeformer to fix faces, no upscaling
The third example uses esrgan to upscale 4X
The four example runs embiggen to enlarge 1.5X
- This is very preliminary work. There are some anomalies to note:
1. The syntax is non-obvious. I would prefer something like:
!fix esrgan,gfpgan
!fix esrgan
!fix embiggen,codeformer
However, this will require refactoring the gfpgan and embiggen
code.
2. Images generated using gfpgan, esrgan or codeformer all are named
"xxxxxx.xxxxxx.postprocessed.png" and the original is saved.
However, the prefix is a new one that is not related to the
original.
3. Images generated using embiggen are named "xxxxx.xxxxxxx.png",
and once again the prefix is new. I'm not sure whether the
prefix should be aligned with the original file's prefix or not.
Probably not, but opinions welcome.
2022-09-18 21:26:09 +00:00
|
|
|
command = command.replace('!dream ','',1)
|
2022-08-17 16:35:49 +00:00
|
|
|
|
add ability to post-process images from the CLI
- supports gfpgan, esrgan, codeformer and embiggen
- To use:
dream> !fix ./outputs/img-samples/000056.292144555.png -ft gfpgan -U2 -G0.8
dream> !fix ./outputs/img-samples/000056.292144555.png -ft codeformer -G 0.8
dream> !fix ./outputs/img-samples/000056.29214455.png -U4
dream> !fix ./outputs/img-samples/000056.292144555.png -embiggen 1.5
The first example invokes gfpgan to fix faces and esrgan to upscale.
The second example invokes codeformer to fix faces, no upscaling
The third example uses esrgan to upscale 4X
The four example runs embiggen to enlarge 1.5X
- This is very preliminary work. There are some anomalies to note:
1. The syntax is non-obvious. I would prefer something like:
!fix esrgan,gfpgan
!fix esrgan
!fix embiggen,codeformer
However, this will require refactoring the gfpgan and embiggen
code.
2. Images generated using gfpgan, esrgan or codeformer all are named
"xxxxxx.xxxxxx.postprocessed.png" and the original is saved.
However, the prefix is a new one that is not related to the
original.
3. Images generated using embiggen are named "xxxxx.xxxxxxx.png",
and once again the prefix is new. I'm not sure whether the
prefix should be aligned with the original file's prefix or not.
Probably not, but opinions welcome.
2022-09-18 21:26:09 +00:00
|
|
|
if command.startswith(
|
|
|
|
'!fix'
|
|
|
|
):
|
|
|
|
command = command.replace('!fix ','',1)
|
|
|
|
operation = 'postprocess'
|
|
|
|
|
2022-09-17 04:57:35 +00:00
|
|
|
if opt.parse_cmd(command) is None:
|
2022-08-17 16:35:49 +00:00
|
|
|
continue
|
2022-08-26 07:15:42 +00:00
|
|
|
if len(opt.prompt) == 0:
|
2022-09-17 02:12:35 +00:00
|
|
|
print('\nTry again with a prompt!')
|
2022-08-17 16:35:49 +00:00
|
|
|
continue
|
2022-09-14 11:02:31 +00:00
|
|
|
|
2022-09-17 05:14:00 +00:00
|
|
|
# width and height are set by model if not specified
|
|
|
|
if not opt.width:
|
|
|
|
opt.width = model_config.width
|
|
|
|
if not opt.height:
|
|
|
|
opt.height = model_config.height
|
|
|
|
|
add ability to post-process images from the CLI
- supports gfpgan, esrgan, codeformer and embiggen
- To use:
dream> !fix ./outputs/img-samples/000056.292144555.png -ft gfpgan -U2 -G0.8
dream> !fix ./outputs/img-samples/000056.292144555.png -ft codeformer -G 0.8
dream> !fix ./outputs/img-samples/000056.29214455.png -U4
dream> !fix ./outputs/img-samples/000056.292144555.png -embiggen 1.5
The first example invokes gfpgan to fix faces and esrgan to upscale.
The second example invokes codeformer to fix faces, no upscaling
The third example uses esrgan to upscale 4X
The four example runs embiggen to enlarge 1.5X
- This is very preliminary work. There are some anomalies to note:
1. The syntax is non-obvious. I would prefer something like:
!fix esrgan,gfpgan
!fix esrgan
!fix embiggen,codeformer
However, this will require refactoring the gfpgan and embiggen
code.
2. Images generated using gfpgan, esrgan or codeformer all are named
"xxxxxx.xxxxxx.postprocessed.png" and the original is saved.
However, the prefix is a new one that is not related to the
original.
3. Images generated using embiggen are named "xxxxx.xxxxxxx.png",
and once again the prefix is new. I'm not sure whether the
prefix should be aligned with the original file's prefix or not.
Probably not, but opinions welcome.
2022-09-18 21:26:09 +00:00
|
|
|
# retrieve previous value of init image if requested
|
2022-09-11 19:47:12 +00:00
|
|
|
if opt.init_img is not None and re.match('^-\\d+$', opt.init_img):
|
2022-09-11 14:52:19 +00:00
|
|
|
try:
|
|
|
|
opt.init_img = last_results[int(opt.init_img)][0]
|
|
|
|
print(f'>> Reusing previous image {opt.init_img}')
|
|
|
|
except IndexError:
|
2022-09-11 19:47:12 +00:00
|
|
|
print(
|
|
|
|
f'>> No previous initial image at position {opt.init_img} found')
|
2022-09-11 14:52:19 +00:00
|
|
|
opt.init_img = None
|
|
|
|
continue
|
2022-09-11 19:47:12 +00:00
|
|
|
|
add ability to post-process images from the CLI
- supports gfpgan, esrgan, codeformer and embiggen
- To use:
dream> !fix ./outputs/img-samples/000056.292144555.png -ft gfpgan -U2 -G0.8
dream> !fix ./outputs/img-samples/000056.292144555.png -ft codeformer -G 0.8
dream> !fix ./outputs/img-samples/000056.29214455.png -U4
dream> !fix ./outputs/img-samples/000056.292144555.png -embiggen 1.5
The first example invokes gfpgan to fix faces and esrgan to upscale.
The second example invokes codeformer to fix faces, no upscaling
The third example uses esrgan to upscale 4X
The four example runs embiggen to enlarge 1.5X
- This is very preliminary work. There are some anomalies to note:
1. The syntax is non-obvious. I would prefer something like:
!fix esrgan,gfpgan
!fix esrgan
!fix embiggen,codeformer
However, this will require refactoring the gfpgan and embiggen
code.
2. Images generated using gfpgan, esrgan or codeformer all are named
"xxxxxx.xxxxxx.postprocessed.png" and the original is saved.
However, the prefix is a new one that is not related to the
original.
3. Images generated using embiggen are named "xxxxx.xxxxxxx.png",
and once again the prefix is new. I'm not sure whether the
prefix should be aligned with the original file's prefix or not.
Probably not, but opinions welcome.
2022-09-18 21:26:09 +00:00
|
|
|
# retrieve previous valueof seed if requested
|
|
|
|
if opt.seed is not None and opt.seed < 0:
|
2022-08-26 06:17:14 +00:00
|
|
|
try:
|
2022-09-11 14:52:19 +00:00
|
|
|
opt.seed = last_results[opt.seed][1]
|
|
|
|
print(f'>> Reusing previous seed {opt.seed}')
|
2022-08-26 06:17:14 +00:00
|
|
|
except IndexError:
|
2022-09-11 14:52:19 +00:00
|
|
|
print(f'>> No previous seed at position {opt.seed} found')
|
2022-08-26 06:17:14 +00:00
|
|
|
opt.seed = None
|
2022-09-11 14:52:19 +00:00
|
|
|
continue
|
2022-08-26 07:15:42 +00:00
|
|
|
|
2022-09-16 17:09:04 +00:00
|
|
|
# TODO - move this into a module
|
2022-09-01 02:31:52 +00:00
|
|
|
if opt.with_variations is not None:
|
|
|
|
# shotgun parsing, woo
|
|
|
|
parts = []
|
2022-09-11 19:47:12 +00:00
|
|
|
broken = False # python doesn't have labeled loops...
|
2022-09-03 06:36:44 +00:00
|
|
|
for part in opt.with_variations.split(','):
|
|
|
|
seed_and_weight = part.split(':')
|
2022-09-01 02:31:52 +00:00
|
|
|
if len(seed_and_weight) != 2:
|
|
|
|
print(f'could not parse with_variation part "{part}"')
|
|
|
|
broken = True
|
|
|
|
break
|
|
|
|
try:
|
|
|
|
seed = int(seed_and_weight[0])
|
|
|
|
weight = float(seed_and_weight[1])
|
|
|
|
except ValueError:
|
|
|
|
print(f'could not parse with_variation part "{part}"')
|
|
|
|
broken = True
|
|
|
|
break
|
|
|
|
parts.append([seed, weight])
|
|
|
|
if broken:
|
|
|
|
continue
|
|
|
|
if len(parts) > 0:
|
|
|
|
opt.with_variations = parts
|
|
|
|
else:
|
|
|
|
opt.with_variations = None
|
2022-08-25 04:42:37 +00:00
|
|
|
|
2022-09-18 12:37:08 +00:00
|
|
|
if opt.prompt_as_dir:
|
2022-08-30 00:10:15 +00:00
|
|
|
# sanitize the prompt to a valid folder name
|
|
|
|
subdir = path_filter.sub('_', opt.prompt)[:name_max].rstrip(' .')
|
2022-08-29 16:54:40 +00:00
|
|
|
|
|
|
|
# truncate path to maximum allowed length
|
2022-08-30 00:10:15 +00:00
|
|
|
# 27 is the length of '######.##########.##.png', plus two separators and a NUL
|
2022-09-16 17:09:04 +00:00
|
|
|
subdir = subdir[:(path_max - 27 - len(os.path.abspath(opt.outdir)))]
|
2022-09-17 02:12:35 +00:00
|
|
|
current_outdir = os.path.join(opt.outdir, subdir)
|
2022-08-29 16:54:40 +00:00
|
|
|
|
2022-09-11 19:47:12 +00:00
|
|
|
print('Writing files to directory: "' + current_outdir + '"')
|
2022-08-29 16:54:40 +00:00
|
|
|
|
|
|
|
# make sure the output directory exists
|
|
|
|
if not os.path.exists(current_outdir):
|
|
|
|
os.makedirs(current_outdir)
|
2022-08-28 19:54:12 +00:00
|
|
|
else:
|
2022-09-18 12:37:08 +00:00
|
|
|
if not os.path.exists(opt.outdir):
|
|
|
|
os.makedirs(opt.outdir)
|
2022-09-17 02:12:35 +00:00
|
|
|
current_outdir = opt.outdir
|
2022-08-24 23:47:59 +00:00
|
|
|
|
2022-08-28 17:05:01 +00:00
|
|
|
# Here is where the images are actually generated!
|
2022-09-11 14:52:19 +00:00
|
|
|
last_results = []
|
2022-08-24 13:22:04 +00:00
|
|
|
try:
|
2022-09-19 18:54:52 +00:00
|
|
|
file_writer = PngWriter(current_outdir)
|
|
|
|
prefix = file_writer.unique_prefix()
|
|
|
|
results = [] # list of filename, prompt pairs
|
2022-09-17 15:59:47 +00:00
|
|
|
grid_images = dict() # seed -> Image, only used if `opt.grid`
|
|
|
|
prior_variations = opt.with_variations or []
|
|
|
|
first_seed = opt.seed
|
2022-09-11 19:47:12 +00:00
|
|
|
|
2022-08-31 04:21:04 +00:00
|
|
|
def image_writer(image, seed, upscaled=False):
|
2022-09-12 11:28:58 +00:00
|
|
|
path = None
|
2022-09-17 15:59:47 +00:00
|
|
|
nonlocal first_seed
|
|
|
|
nonlocal prior_variations
|
2022-09-14 11:02:31 +00:00
|
|
|
if opt.grid:
|
2022-08-31 04:21:04 +00:00
|
|
|
grid_images[seed] = image
|
|
|
|
else:
|
2022-09-19 18:54:52 +00:00
|
|
|
if operation == 'postprocess':
|
|
|
|
filename = choose_postprocess_name(opt.prompt)
|
|
|
|
elif upscaled and opt.save_original:
|
2022-08-31 04:21:04 +00:00
|
|
|
filename = f'{prefix}.{seed}.postprocessed.png'
|
|
|
|
else:
|
|
|
|
filename = f'{prefix}.{seed}.png'
|
2022-09-01 02:31:52 +00:00
|
|
|
if opt.variation_amount > 0:
|
2022-09-17 15:59:47 +00:00
|
|
|
first_seed = first_seed or seed
|
|
|
|
this_variation = [[seed, opt.variation_amount]]
|
|
|
|
opt.with_variations = prior_variations + this_variation
|
|
|
|
formatted_dream_prompt = opt.dream_prompt_str(seed=first_seed)
|
|
|
|
elif len(prior_variations) > 0:
|
|
|
|
formatted_dream_prompt = opt.dream_prompt_str(seed=first_seed)
|
2022-09-19 18:54:52 +00:00
|
|
|
elif operation == 'postprocess':
|
|
|
|
formatted_dream_prompt = '!fix '+opt.dream_prompt_str(seed=seed)
|
2022-09-01 02:31:52 +00:00
|
|
|
else:
|
2022-09-16 17:09:04 +00:00
|
|
|
formatted_dream_prompt = opt.dream_prompt_str(seed=seed)
|
2022-09-11 19:47:12 +00:00
|
|
|
path = file_writer.save_image_and_prompt_to_png(
|
2022-09-16 17:09:04 +00:00
|
|
|
image = image,
|
|
|
|
dream_prompt = formatted_dream_prompt,
|
2022-09-17 17:28:37 +00:00
|
|
|
metadata = metadata_dumps(
|
2022-09-16 17:09:04 +00:00
|
|
|
opt,
|
|
|
|
seeds = [seed],
|
|
|
|
model_hash = gen.model_hash,
|
|
|
|
),
|
|
|
|
name = filename,
|
|
|
|
)
|
2022-08-31 04:21:04 +00:00
|
|
|
if (not upscaled) or opt.save_original:
|
|
|
|
# only append to results if we didn't overwrite an earlier output
|
2022-09-16 17:09:04 +00:00
|
|
|
results.append([path, formatted_dream_prompt])
|
2022-09-11 19:47:12 +00:00
|
|
|
last_results.append([path, seed])
|
2022-08-31 04:21:04 +00:00
|
|
|
|
add ability to post-process images from the CLI
- supports gfpgan, esrgan, codeformer and embiggen
- To use:
dream> !fix ./outputs/img-samples/000056.292144555.png -ft gfpgan -U2 -G0.8
dream> !fix ./outputs/img-samples/000056.292144555.png -ft codeformer -G 0.8
dream> !fix ./outputs/img-samples/000056.29214455.png -U4
dream> !fix ./outputs/img-samples/000056.292144555.png -embiggen 1.5
The first example invokes gfpgan to fix faces and esrgan to upscale.
The second example invokes codeformer to fix faces, no upscaling
The third example uses esrgan to upscale 4X
The four example runs embiggen to enlarge 1.5X
- This is very preliminary work. There are some anomalies to note:
1. The syntax is non-obvious. I would prefer something like:
!fix esrgan,gfpgan
!fix esrgan
!fix embiggen,codeformer
However, this will require refactoring the gfpgan and embiggen
code.
2. Images generated using gfpgan, esrgan or codeformer all are named
"xxxxxx.xxxxxx.postprocessed.png" and the original is saved.
However, the prefix is a new one that is not related to the
original.
3. Images generated using embiggen are named "xxxxx.xxxxxxx.png",
and once again the prefix is new. I'm not sure whether the
prefix should be aligned with the original file's prefix or not.
Probably not, but opinions welcome.
2022-09-18 21:26:09 +00:00
|
|
|
if operation == 'generate':
|
|
|
|
catch_ctrl_c = infile is None # if running interactively, we catch keyboard interrupts
|
|
|
|
gen.prompt2image(
|
|
|
|
image_callback=image_writer,
|
|
|
|
catch_interrupts=catch_ctrl_c,
|
|
|
|
**vars(opt)
|
|
|
|
)
|
|
|
|
elif operation == 'postprocess':
|
|
|
|
print(f'>> fixing {opt.prompt}')
|
|
|
|
do_postprocess(gen,opt,image_writer)
|
2022-08-31 04:21:04 +00:00
|
|
|
|
2022-09-14 11:02:31 +00:00
|
|
|
if opt.grid and len(grid_images) > 0:
|
2022-09-12 11:28:58 +00:00
|
|
|
grid_img = make_grid(list(grid_images.values()))
|
|
|
|
grid_seeds = list(grid_images.keys())
|
2022-09-11 14:52:19 +00:00
|
|
|
first_seed = last_results[0][1]
|
2022-09-16 17:09:04 +00:00
|
|
|
filename = f'{prefix}.{first_seed}.png'
|
|
|
|
formatted_dream_prompt = opt.dream_prompt_str(seed=first_seed,grid=True,iterations=len(grid_images))
|
|
|
|
formatted_dream_prompt += f' # {grid_seeds}'
|
2022-09-18 19:03:11 +00:00
|
|
|
metadata = metadata_dumps(
|
2022-09-16 17:09:04 +00:00
|
|
|
opt,
|
|
|
|
seeds = grid_seeds,
|
|
|
|
weights = gen.weights,
|
|
|
|
model_hash = gen.model_hash
|
|
|
|
)
|
2022-08-31 04:21:04 +00:00
|
|
|
path = file_writer.save_image_and_prompt_to_png(
|
2022-09-16 17:09:04 +00:00
|
|
|
image = grid_img,
|
|
|
|
dream_prompt = formatted_dream_prompt,
|
|
|
|
metadata = metadata,
|
|
|
|
name = filename
|
2022-08-26 07:15:42 +00:00
|
|
|
)
|
2022-09-16 17:09:04 +00:00
|
|
|
results = [[path, formatted_dream_prompt]]
|
2022-08-25 04:42:37 +00:00
|
|
|
|
2022-08-24 13:22:04 +00:00
|
|
|
except AssertionError as e:
|
|
|
|
print(e)
|
|
|
|
continue
|
2022-08-24 15:42:44 +00:00
|
|
|
|
2022-08-25 21:26:48 +00:00
|
|
|
except OSError as e:
|
|
|
|
print(e)
|
|
|
|
continue
|
|
|
|
|
2022-09-11 19:47:12 +00:00
|
|
|
print('Outputs:')
|
2022-08-29 00:34:55 +00:00
|
|
|
log_path = os.path.join(current_outdir, 'dream_log.txt')
|
2022-09-01 02:31:52 +00:00
|
|
|
write_log_message(results, log_path)
|
2022-09-11 21:19:46 +00:00
|
|
|
print()
|
2022-08-26 07:15:42 +00:00
|
|
|
|
2022-09-11 19:47:12 +00:00
|
|
|
print('goodbye!')
|
2022-08-19 03:23:44 +00:00
|
|
|
|
add ability to post-process images from the CLI
- supports gfpgan, esrgan, codeformer and embiggen
- To use:
dream> !fix ./outputs/img-samples/000056.292144555.png -ft gfpgan -U2 -G0.8
dream> !fix ./outputs/img-samples/000056.292144555.png -ft codeformer -G 0.8
dream> !fix ./outputs/img-samples/000056.29214455.png -U4
dream> !fix ./outputs/img-samples/000056.292144555.png -embiggen 1.5
The first example invokes gfpgan to fix faces and esrgan to upscale.
The second example invokes codeformer to fix faces, no upscaling
The third example uses esrgan to upscale 4X
The four example runs embiggen to enlarge 1.5X
- This is very preliminary work. There are some anomalies to note:
1. The syntax is non-obvious. I would prefer something like:
!fix esrgan,gfpgan
!fix esrgan
!fix embiggen,codeformer
However, this will require refactoring the gfpgan and embiggen
code.
2. Images generated using gfpgan, esrgan or codeformer all are named
"xxxxxx.xxxxxx.postprocessed.png" and the original is saved.
However, the prefix is a new one that is not related to the
original.
3. Images generated using embiggen are named "xxxxx.xxxxxxx.png",
and once again the prefix is new. I'm not sure whether the
prefix should be aligned with the original file's prefix or not.
Probably not, but opinions welcome.
2022-09-18 21:26:09 +00:00
|
|
|
def do_postprocess (gen, opt, callback):
|
|
|
|
file_path = opt.prompt # treat the prompt as the file pathname
|
|
|
|
if os.path.dirname(file_path) == '': #basename given
|
|
|
|
file_path = os.path.join(opt.outdir,file_path)
|
|
|
|
if not os.path.exists(file_path):
|
|
|
|
print(f'* file {file_path} does not exist')
|
|
|
|
return
|
|
|
|
|
|
|
|
tool = opt.facetool if opt.gfpgan_strength > 0 else ('embiggen' if opt.embiggen else 'upscale')
|
|
|
|
opt.save_original = True # do not overwrite old image!
|
|
|
|
return gen.apply_postprocessor(
|
|
|
|
image_path = opt.prompt,
|
|
|
|
tool = tool,
|
|
|
|
gfpgan_strength = opt.gfpgan_strength,
|
|
|
|
codeformer_fidelity = opt.codeformer_fidelity,
|
|
|
|
save_original = opt.save_original,
|
|
|
|
upscale = opt.upscale,
|
|
|
|
callback = callback,
|
|
|
|
opt = opt,
|
|
|
|
)
|
|
|
|
|
2022-09-19 18:54:52 +00:00
|
|
|
def choose_postprocess_name(original_filename):
|
|
|
|
basename,_ = os.path.splitext(os.path.basename(original_filename))
|
|
|
|
if re.search('\d+\.\d+$',basename):
|
|
|
|
return f'{basename}.fixed.png'
|
|
|
|
match = re.search('(\d+\.\d+)\.fixed(-(\d+))?$',basename)
|
|
|
|
if match:
|
|
|
|
counter = match.group(3) or 0
|
|
|
|
return '{prefix}-{counter:02d}.png'.format(prefix=match.group(1), counter=int(counter)+1)
|
|
|
|
else:
|
|
|
|
return f'{basename}.fixed.png'
|
|
|
|
|
|
|
|
|
2022-09-11 19:47:12 +00:00
|
|
|
def get_next_command(infile=None) -> str: # command string
|
2022-08-28 18:20:34 +00:00
|
|
|
if infile is None:
|
2022-09-11 19:47:12 +00:00
|
|
|
command = input('dream> ')
|
2022-08-28 18:20:34 +00:00
|
|
|
else:
|
|
|
|
command = infile.readline()
|
|
|
|
if not command:
|
|
|
|
raise EOFError
|
|
|
|
else:
|
|
|
|
command = command.strip()
|
2022-09-14 11:02:31 +00:00
|
|
|
if len(command)>0:
|
|
|
|
print(f'#{command}')
|
2022-08-28 18:20:34 +00:00
|
|
|
return command
|
2022-08-29 02:57:13 +00:00
|
|
|
|
2022-09-13 17:17:14 +00:00
|
|
|
def dream_server_loop(gen, host, port, outdir, gfpgan):
|
2022-08-28 20:37:27 +00:00
|
|
|
print('\n* --web was specified, starting web server...')
|
|
|
|
# Change working directory to the stable-diffusion directory
|
|
|
|
os.chdir(
|
|
|
|
os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
|
|
|
)
|
2022-08-26 02:57:30 +00:00
|
|
|
|
2022-08-28 20:37:27 +00:00
|
|
|
# Start server
|
2022-09-14 11:02:31 +00:00
|
|
|
DreamServer.model = gen # misnomer in DreamServer - this is not the model you are looking for
|
2022-09-06 00:35:04 +00:00
|
|
|
DreamServer.outdir = outdir
|
2022-09-13 17:17:14 +00:00
|
|
|
DreamServer.gfpgan_model_exists = False
|
|
|
|
if gfpgan is not None:
|
|
|
|
DreamServer.gfpgan_model_exists = gfpgan.gfpgan_model_exists
|
|
|
|
|
2022-09-02 14:43:32 +00:00
|
|
|
dream_server = ThreadingDreamServer((host, port))
|
2022-09-03 13:33:02 +00:00
|
|
|
print(">> Started Stable Diffusion dream server!")
|
2022-09-02 18:56:52 +00:00
|
|
|
if host == '0.0.0.0':
|
2022-09-11 19:47:12 +00:00
|
|
|
print(
|
|
|
|
f"Point your browser at http://localhost:{port} or use the host's DNS name or IP address.")
|
2022-09-02 18:56:52 +00:00
|
|
|
else:
|
2022-09-03 13:33:02 +00:00
|
|
|
print(">> Default host address now 127.0.0.1 (localhost). Use --host 0.0.0.0 to bind any address.")
|
|
|
|
print(f">> Point your browser at http://{host}:{port}.")
|
2022-08-28 20:37:27 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
dream_server.serve_forever()
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
pass
|
|
|
|
|
|
|
|
dream_server.server_close()
|
2022-08-26 07:15:42 +00:00
|
|
|
|
2022-08-22 02:48:40 +00:00
|
|
|
|
2022-09-01 02:31:52 +00:00
|
|
|
def write_log_message(results, log_path):
|
2022-08-29 00:34:55 +00:00
|
|
|
"""logs the name of the output image, prompt, and prompt args to the terminal and log file"""
|
2022-09-09 22:49:51 +00:00
|
|
|
global output_cntr
|
2022-09-01 02:31:52 +00:00
|
|
|
log_lines = [f'{path}: {prompt}\n' for path, prompt in results]
|
2022-09-09 22:49:51 +00:00
|
|
|
for l in log_lines:
|
|
|
|
output_cntr += 1
|
2022-09-13 17:17:14 +00:00
|
|
|
print(f'[{output_cntr}] {l}', end='')
|
2022-08-22 02:48:40 +00:00
|
|
|
|
2022-08-29 02:57:13 +00:00
|
|
|
with open(log_path, 'a', encoding='utf-8') as file:
|
2022-08-26 11:22:53 +00:00
|
|
|
file.writelines(log_lines)
|
2022-08-22 02:48:40 +00:00
|
|
|
|
2022-08-26 07:15:42 +00:00
|
|
|
if __name__ == '__main__':
|
2022-08-17 01:34:37 +00:00
|
|
|
main()
|