2022-09-16 17:09:04 +00:00
""" Helper class for dealing with image generation arguments.
The Args class parses both the command line ( shell ) arguments , as well as the
command string passed at the dream > prompt . It serves as the definitive repository
2022-09-17 17:28:37 +00:00
of all the arguments used by Generate and their default values , and implements the
preliminary metadata standards discussed here :
https : / / github . com / lstein / stable - diffusion / issues / 266
2022-09-16 17:09:04 +00:00
To use :
opt = Args ( )
# Read in the command line options:
# this returns a namespace object like the underlying argparse library)
# You do not have to use the return value, but you can check it against None
# to detect illegal arguments on the command line.
args = opt . parse_args ( )
if not args :
print ( ' oops ' )
sys . exit ( - 1 )
# read in a command passed to the dream> prompt:
opts = opt . parse_cmd ( ' do androids dream of electric sheep? -H256 -W1024 -n4 ' )
# The Args object acts like a namespace object
print ( opt . model )
You can set attributes in the usual way , use vars ( ) , etc . :
opt . model = ' something-else '
do_something ( * * vars ( a ) )
It is helpful in saving metadata :
# To get a json representation of all the values, allowing
# you to override any values dynamically
j = opt . json ( seed = 42 )
# To get the prompt string with the switches, allowing you
# to override any values dynamically
j = opt . dream_prompt_str ( seed = 42 )
If you want to access the namespace objects from the shell args or the
parsed command directly , you may use the values returned from the
original calls to parse_args ( ) and parse_cmd ( ) , or get them later
using the _arg_switches and _cmd_switches attributes . This can be
useful if both the args and the command contain the same attribute and
you wish to apply logic as to which one to use . For example :
a = Args ( )
args = a . parse_args ( )
opts = a . parse_cmd ( string )
do_grid = args . grid or opts . grid
To add new attributes , edit the _create_arg_parser ( ) and
_create_dream_cmd_parser ( ) methods .
2022-09-17 17:28:37 +00:00
* * Generating and retrieving sd - metadata * *
To generate a dict representing RFC266 metadata :
metadata = metadata_dumps ( opt , < seeds , model_hash , postprocesser > )
This will generate an RFC266 dictionary that can then be turned into a JSON
and written to the PNG file . The optional seeds , weights , model_hash and
postprocesser arguments are not available to the opt object and so must be
provided externally . See how dream . py does it .
Note that this function was originally called format_metadata ( ) and a wrapper
is provided that issues a deprecation notice .
To retrieve a ( series of ) opt objects corresponding to the metadata , do this :
opt_list = metadata_loads ( metadata )
The metadata should be pulled out of the PNG image . pngwriter has a method
2022-09-21 06:44:46 +00:00
retrieve_metadata that will do this , or you can do it in one swell foop
with metadata_from_png ( ) :
2022-09-17 17:28:37 +00:00
2022-09-21 06:44:46 +00:00
opt_list = metadata_from_png ( ' /path/to/image_file.png ' )
2022-09-16 17:09:04 +00:00
"""
import argparse
2022-09-28 15:57:59 +00:00
from argparse import Namespace , RawTextHelpFormatter
2022-09-16 17:09:04 +00:00
import shlex
import json
import hashlib
import os
2022-09-26 08:18:23 +00:00
import re
2022-09-16 17:09:04 +00:00
import copy
2022-09-17 12:39:20 +00:00
import base64
2022-09-27 18:27:55 +00:00
import functools
2022-09-21 06:44:46 +00:00
import ldm . dream . pngwriter
2022-09-16 17:09:04 +00:00
from ldm . dream . conditioning import split_weighted_subprompts
SAMPLER_CHOICES = [
' ddim ' ,
' k_dpm_2_a ' ,
' k_dpm_2 ' ,
' k_euler_a ' ,
' k_euler ' ,
' k_heun ' ,
' k_lms ' ,
' plms ' ,
]
2022-09-17 17:56:25 +00:00
PRECISION_CHOICES = [
' auto ' ,
' float32 ' ,
' autocast ' ,
' float16 ' ,
]
2022-09-16 17:09:04 +00:00
# is there a way to pick this up during git commits?
APP_ID = ' lstein/stable-diffusion '
APP_VERSION = ' v1.15 '
class Args ( object ) :
def __init__ ( self , arg_parser = None , cmd_parser = None ) :
'''
Initialize new Args class . It takes two optional arguments , an argparse
parser for switches given on the shell command line , and an argparse
parser for switches given on the dream > CLI line . If one or both are
missing , it creates appropriate parsers internally .
'''
self . _arg_parser = arg_parser or self . _create_arg_parser ( )
self . _cmd_parser = cmd_parser or self . _create_dream_cmd_parser ( )
self . _arg_switches = self . parse_cmd ( ' ' ) # fill in defaults
self . _cmd_switches = self . parse_cmd ( ' ' ) # fill in defaults
def parse_args ( self ) :
''' Parse the shell switches and store. '''
try :
self . _arg_switches = self . _arg_parser . parse_args ( )
return self . _arg_switches
except :
return None
def parse_cmd ( self , cmd_string ) :
''' Parse a dream>-style command string '''
command = cmd_string . replace ( " ' " , " \\ ' " )
try :
elements = shlex . split ( command )
except ValueError :
2022-09-17 02:12:35 +00:00
import sys , traceback
2022-09-16 17:09:04 +00:00
print ( traceback . format_exc ( ) , file = sys . stderr )
return
switches = [ ' ' ]
switches_started = False
for element in elements :
if element [ 0 ] == ' - ' and not switches_started :
switches_started = True
if switches_started :
switches . append ( element )
else :
switches [ 0 ] + = element
switches [ 0 ] + = ' '
switches [ 0 ] = switches [ 0 ] [ : len ( switches [ 0 ] ) - 1 ]
try :
self . _cmd_switches = self . _cmd_parser . parse_args ( switches )
return self . _cmd_switches
except :
return None
def json ( self , * * kwargs ) :
return json . dumps ( self . to_dict ( * * kwargs ) )
def to_dict ( self , * * kwargs ) :
a = vars ( self )
a . update ( kwargs )
return a
# Isn't there a more automated way of doing this?
# Ideally we get the switch strings out of the argparse objects,
# but I don't see a documented API for this.
def dream_prompt_str ( self , * * kwargs ) :
""" Normalized dream_prompt. """
a = vars ( self )
a . update ( kwargs )
switches = list ( )
2022-09-17 14:18:55 +00:00
switches . append ( f ' " { a [ " prompt " ] } " ' )
2022-09-16 17:09:04 +00:00
switches . append ( f ' -s { a [ " steps " ] } ' )
2022-09-18 19:08:56 +00:00
switches . append ( f ' -S { a [ " seed " ] } ' )
2022-09-16 17:09:04 +00:00
switches . append ( f ' -W { a [ " width " ] } ' )
switches . append ( f ' -H { a [ " height " ] } ' )
switches . append ( f ' -C { a [ " cfg_scale " ] } ' )
if a [ ' grid ' ] :
switches . append ( ' --grid ' )
if a [ ' seamless ' ] :
switches . append ( ' --seamless ' )
2022-09-19 07:38:56 +00:00
# img2img generations have parameters relevant only to them and have special handling
2022-09-16 17:09:04 +00:00
if a [ ' init_img ' ] and len ( a [ ' init_img ' ] ) > 0 :
switches . append ( f ' -I { a [ " init_img " ] } ' )
2022-09-19 07:38:56 +00:00
switches . append ( f ' -A ddim ' ) # TODO: FIX ME WHEN IMG2IMG SUPPORTS ALL SAMPLERS
if a [ ' fit ' ] :
switches . append ( f ' --fit ' )
if a [ ' init_mask ' ] and len ( a [ ' init_mask ' ] ) > 0 :
switches . append ( f ' -M { a [ " init_mask " ] } ' )
if a [ ' init_color ' ] and len ( a [ ' init_color ' ] ) > 0 :
switches . append ( f ' --init_color { a [ " init_color " ] } ' )
if a [ ' strength ' ] and a [ ' strength ' ] > 0 :
switches . append ( f ' -f { a [ " strength " ] } ' )
else :
switches . append ( f ' -A { a [ " sampler_name " ] } ' )
# gfpgan-specific parameters
2022-09-16 17:09:04 +00:00
if a [ ' gfpgan_strength ' ] :
switches . append ( f ' -G { a [ " gfpgan_strength " ] } ' )
2022-09-19 07:38:56 +00:00
# esrgan-specific parameters
2022-09-16 17:09:04 +00:00
if a [ ' upscale ' ] :
switches . append ( f ' -U { " " . join ( [ str ( u ) for u in a [ " upscale " ] ] ) } ' )
2022-09-21 06:44:46 +00:00
# embiggen parameters
2022-09-16 17:09:04 +00:00
if a [ ' embiggen ' ] :
switches . append ( f ' --embiggen { " " . join ( [ str ( u ) for u in a [ " embiggen " ] ] ) } ' )
if a [ ' embiggen_tiles ' ] :
switches . append ( f ' --embiggen_tiles { " " . join ( [ str ( u ) for u in a [ " embiggen_tiles " ] ] ) } ' )
2022-09-21 06:44:46 +00:00
# outpainting parameters
if a [ ' out_direction ' ] :
switches . append ( f ' -D { " " . join ( [ str ( u ) for u in a [ " out_direction " ] ] ) } ' )
2022-09-27 18:27:55 +00:00
# LS: slight semantic drift which needs addressing in the future:
# 1. Variations come out of the stored metadata as a packed string with the keyword "variations"
# 2. However, they come out of the CLI (and probably web) with the keyword "with_variations" and
# in broken-out form. Variation (1) should be changed to comply with (2)
2022-09-16 17:09:04 +00:00
if a [ ' with_variations ' ] :
2022-09-27 18:27:55 +00:00
formatted_variations = ' , ' . join ( f ' { seed } : { weight } ' for seed , weight in ( a [ " variations " ] ) )
switches . append ( f ' -V { a [ " formatted_variations " ] } ' )
if ' variations ' in a :
switches . append ( f ' -V { a [ " variations " ] } ' )
2022-09-16 17:09:04 +00:00
return ' ' . join ( switches )
def __getattribute__ ( self , name ) :
'''
Returns union of command - line arguments and dream_prompt arguments ,
with the latter superseding the former .
'''
cmd_switches = None
arg_switches = None
try :
cmd_switches = object . __getattribute__ ( self , ' _cmd_switches ' )
arg_switches = object . __getattribute__ ( self , ' _arg_switches ' )
except AttributeError :
pass
if cmd_switches and arg_switches and name == ' __dict__ ' :
2022-09-17 04:57:35 +00:00
return self . _merge_dict (
arg_switches . __dict__ ,
cmd_switches . __dict__ ,
)
2022-09-16 17:09:04 +00:00
try :
return object . __getattribute__ ( self , name )
except AttributeError :
pass
if not hasattr ( cmd_switches , name ) and not hasattr ( arg_switches , name ) :
raise AttributeError
value_arg , value_cmd = ( None , None )
try :
value_cmd = getattr ( cmd_switches , name )
except AttributeError :
pass
try :
value_arg = getattr ( arg_switches , name )
except AttributeError :
pass
# here is where we can pick and choose which to use
# default behavior is to choose the dream_command value over
# the arg value. For example, the --grid and --individual options are a little
# funny because of their push/pull relationship. This is how to handle it.
if name == ' grid ' :
2022-09-16 23:58:16 +00:00
return not cmd_switches . individual and value_arg # arg supersedes cmd
2022-09-17 04:57:35 +00:00
return value_cmd if value_cmd is not None else value_arg
2022-09-16 17:09:04 +00:00
def __setattr__ ( self , name , value ) :
if name . startswith ( ' _ ' ) :
object . __setattr__ ( self , name , value )
else :
self . _cmd_switches . __dict__ [ name ] = value
2022-09-17 04:57:35 +00:00
def _merge_dict ( self , dict1 , dict2 ) :
new_dict = { }
for k in set ( list ( dict1 . keys ( ) ) + list ( dict2 . keys ( ) ) ) :
value1 = dict1 . get ( k , None )
value2 = dict2 . get ( k , None )
new_dict [ k ] = value2 if value2 is not None else value1
return new_dict
2022-09-16 17:09:04 +00:00
def _create_arg_parser ( self ) :
'''
This defines all the arguments used on the command line when you launch
the CLI or web backend .
'''
parser = argparse . ArgumentParser (
description =
"""
Generate images using Stable Diffusion .
Use - - web to launch the web interface .
Use - - from_file to load prompts from a file path or standard input ( " - " ) .
Otherwise you will be dropped into an interactive command prompt ( type - h for help . )
Other command - line arguments are defaults that can usually be overridden
prompt the command prompt .
""" ,
)
model_group = parser . add_argument_group ( ' Model selection ' )
file_group = parser . add_argument_group ( ' Input/output ' )
web_server_group = parser . add_argument_group ( ' Web server ' )
render_group = parser . add_argument_group ( ' Rendering ' )
postprocessing_group = parser . add_argument_group ( ' Postprocessing ' )
deprecated_group = parser . add_argument_group ( ' Deprecated options ' )
deprecated_group . add_argument ( ' --laion400m ' )
deprecated_group . add_argument ( ' --weights ' ) # deprecated
model_group . add_argument (
' --conf ' ,
' -c ' ,
' -conf ' ,
dest = ' conf ' ,
default = ' ./configs/models.yaml ' ,
help = ' Path to configuration file for alternate models. ' ,
)
model_group . add_argument (
' --model ' ,
default = ' stable-diffusion-1.4 ' ,
help = ' Indicates which diffusion model to load. (currently " stable-diffusion-1.4 " (default) or " laion400m " ) ' ,
)
2022-09-17 02:12:35 +00:00
model_group . add_argument (
' --sampler ' ,
' -A ' ,
' -m ' ,
dest = ' sampler_name ' ,
type = str ,
choices = SAMPLER_CHOICES ,
metavar = ' SAMPLER_NAME ' ,
help = f ' Switch to a different sampler. Supported samplers: { " , " . join ( SAMPLER_CHOICES ) } ' ,
default = ' k_lms ' ,
)
2022-09-16 17:09:04 +00:00
model_group . add_argument (
' -F ' ,
' --full_precision ' ,
dest = ' full_precision ' ,
action = ' store_true ' ,
2022-09-17 17:56:25 +00:00
help = ' Deprecated way to set --precision=float32 ' ,
)
2022-09-24 08:29:06 +00:00
model_group . add_argument (
' --free_gpu_mem ' ,
dest = ' free_gpu_mem ' ,
action = ' store_true ' ,
help = ' Force free gpu memory before final decoding ' ,
)
2022-09-17 17:56:25 +00:00
model_group . add_argument (
' --precision ' ,
dest = ' precision ' ,
type = str ,
choices = PRECISION_CHOICES ,
metavar = ' PRECISION ' ,
help = f ' Set model precision. Defaults to auto selected based on device. Options: { " , " . join ( PRECISION_CHOICES ) } ' ,
default = ' auto ' ,
2022-09-16 17:09:04 +00:00
)
file_group . add_argument (
' --from_file ' ,
dest = ' infile ' ,
type = str ,
help = ' If specified, load prompts from this file ' ,
)
file_group . add_argument (
' --outdir ' ,
' -o ' ,
type = str ,
help = ' Directory to save generated images and a log of prompts and seeds. Default: outputs/img-samples ' ,
default = ' outputs/img-samples ' ,
)
file_group . add_argument (
' --prompt_as_dir ' ,
' -p ' ,
action = ' store_true ' ,
help = ' Place images in subdirectories named after the prompt. ' ,
)
render_group . add_argument (
' --grid ' ,
' -g ' ,
action = ' store_true ' ,
help = ' generate a grid '
)
render_group . add_argument (
' --embedding_path ' ,
type = str ,
help = ' Path to a pre-trained embedding manager checkpoint - can only be set on command line ' ,
)
2022-09-13 17:17:14 +00:00
# Restoration related args
2022-09-16 17:09:04 +00:00
postprocessing_group . add_argument (
2022-09-19 17:59:43 +00:00
' --no_restore ' ,
dest = ' restore ' ,
action = ' store_false ' ,
help = ' Disable face restoration with GFPGAN or codeformer ' ,
2022-09-13 17:17:14 +00:00
)
postprocessing_group . add_argument (
2022-09-19 17:59:43 +00:00
' --no_upscale ' ,
dest = ' esrgan ' ,
action = ' store_false ' ,
help = ' Disable upscaling with ESRGAN ' ,
2022-09-16 17:09:04 +00:00
)
postprocessing_group . add_argument (
2022-09-13 17:17:14 +00:00
' --esrgan_bg_tile ' ,
2022-09-16 17:09:04 +00:00
type = int ,
default = 400 ,
help = ' Tile size for background sampler, 0 for no tile during testing. Default: 400. ' ,
)
postprocessing_group . add_argument (
' --gfpgan_model_path ' ,
type = str ,
2022-09-23 10:20:05 +00:00
default = ' experiments/pretrained_models/GFPGANv1.4.pth ' ,
2022-09-16 17:09:04 +00:00
help = ' Indicates the path to the GFPGAN model, relative to --gfpgan_dir. ' ,
)
postprocessing_group . add_argument (
' --gfpgan_dir ' ,
type = str ,
default = ' ./src/gfpgan ' ,
help = ' Indicates the directory containing the GFPGAN code. ' ,
)
web_server_group . add_argument (
' --web ' ,
dest = ' web ' ,
action = ' store_true ' ,
help = ' Start in web server mode. ' ,
)
2022-09-26 20:15:32 +00:00
web_server_group . add_argument (
' --web_develop ' ,
dest = ' web_develop ' ,
action = ' store_true ' ,
help = ' Start in web server development mode. ' ,
)
web_server_group . add_argument (
" --web_verbose " ,
action = " store_true " ,
help = " Enables verbose logging " ,
)
web_server_group . add_argument (
" --cors " ,
nargs = " * " ,
type = str ,
help = " Additional allowed origins, comma-separated " ,
)
2022-09-16 17:09:04 +00:00
web_server_group . add_argument (
' --host ' ,
type = str ,
default = ' 127.0.0.1 ' ,
help = ' Web server: Host or IP to listen on. Set to 0.0.0.0 to accept traffic from other devices on your network. '
)
web_server_group . add_argument (
' --port ' ,
type = int ,
default = ' 9090 ' ,
help = ' Web server: Port to listen on '
)
return parser
# This creates the parser that processes commands on the dream> command line
def _create_dream_cmd_parser ( self ) :
parser = argparse . ArgumentParser (
2022-09-28 15:57:59 +00:00
formatter_class = RawTextHelpFormatter ,
description =
"""
2022-09-28 15:48:11 +00:00
* Image generation : *
2022-09-28 17:00:54 +00:00
dream > a fantastic alien landscape - W576 - H512 - s60 - n4
2022-09-28 15:48:11 +00:00
* postprocessing *
2022-09-28 17:00:54 +00:00
! fix applies upscaling / facefixing to a previously - generated image .
dream > ! fix 0000045.4829112 . png - G1 - U4 - ft codeformer
2022-09-28 15:48:11 +00:00
* History manipulation *
2022-09-28 17:00:54 +00:00
! fetch retrieves the command used to generate an earlier image .
dream > ! fetch 0000015.8929913 . png
dream > a fantastic alien landscape - W 576 - H 512 - s 60 - A plms - C 7.5
2022-09-28 15:48:11 +00:00
2022-09-28 17:00:54 +00:00
! history lists all the commands issued during the current session .
2022-09-28 15:48:11 +00:00
2022-09-28 17:00:54 +00:00
! NN retrieves the NNth command from the history
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
"""
2022-09-16 17:09:04 +00:00
)
render_group = parser . add_argument_group ( ' General rendering ' )
img2img_group = parser . add_argument_group ( ' Image-to-image and inpainting ' )
variation_group = parser . add_argument_group ( ' Creating and combining variations ' )
postprocessing_group = parser . add_argument_group ( ' Post-processing ' )
special_effects_group = parser . add_argument_group ( ' Special effects ' )
render_group . add_argument ( ' prompt ' )
render_group . add_argument (
' -s ' ,
' --steps ' ,
type = int ,
default = 50 ,
help = ' Number of steps '
)
render_group . add_argument (
' -S ' ,
' --seed ' ,
type = int ,
default = None ,
help = ' Image seed; a +ve integer, or use -1 for the previous seed, -2 for the one before that, etc ' ,
)
render_group . add_argument (
' -n ' ,
' --iterations ' ,
type = int ,
default = 1 ,
help = ' Number of samplings to perform (slower, but will provide seeds for individual images) ' ,
)
render_group . add_argument (
' -W ' ,
' --width ' ,
type = int ,
help = ' Image width, multiple of 64 ' ,
)
render_group . add_argument (
' -H ' ,
' --height ' ,
type = int ,
help = ' Image height, multiple of 64 ' ,
)
render_group . add_argument (
' -C ' ,
' --cfg_scale ' ,
default = 7.5 ,
type = float ,
help = ' Classifier free guidance (CFG) scale - higher numbers cause generator to " try " harder. ' ,
)
2022-09-28 23:47:36 +00:00
render_group . add_argument (
' --threshold ' ,
default = 0.0 ,
type = float ,
help = ' Latent threshold for classifier free guidance (CFG) - prevent generator from " trying " too hard. Use positive values, 0 disables. ' ,
)
render_group . add_argument (
' --perlin ' ,
default = 0.0 ,
type = float ,
help = ' Perlin noise scale (0.0 - 1.0) - add perlin noise to the initialization instead of the usual gaussian noise. ' ,
)
2022-09-16 17:09:04 +00:00
render_group . add_argument (
' --grid ' ,
' -g ' ,
action = ' store_true ' ,
help = ' generate a grid '
)
render_group . add_argument (
' -i ' ,
2022-09-16 23:58:16 +00:00
' --individual ' ,
2022-09-16 17:09:04 +00:00
action = ' store_true ' ,
help = ' override command-line --grid setting and generate individual images '
)
render_group . add_argument (
' -x ' ,
' --skip_normalize ' ,
action = ' store_true ' ,
help = ' Skip subprompt weight normalization ' ,
)
render_group . add_argument (
' -A ' ,
' -m ' ,
' --sampler ' ,
dest = ' sampler_name ' ,
type = str ,
choices = SAMPLER_CHOICES ,
metavar = ' SAMPLER_NAME ' ,
help = f ' Switch to a different sampler. Supported samplers: { " , " . join ( SAMPLER_CHOICES ) } ' ,
)
render_group . add_argument (
' -t ' ,
' --log_tokenization ' ,
action = ' store_true ' ,
help = ' shows how the prompt is split into tokens '
)
render_group . add_argument (
' --outdir ' ,
' -o ' ,
type = str ,
help = ' Directory to save generated images and a log of prompts and seeds ' ,
)
2022-09-29 22:58:06 +00:00
render_group . add_argument (
' --hires_fix ' ,
action = ' store_true ' ,
dest = ' hires_fix ' ,
2022-10-01 19:21:57 +00:00
help = ' Create hires image using img2img to prevent duplicated objects '
2022-09-29 22:58:06 +00:00
)
2022-09-16 17:09:04 +00:00
img2img_group . add_argument (
' -I ' ,
' --init_img ' ,
type = str ,
help = ' Path to input image for img2img mode (supersedes width and height) ' ,
)
img2img_group . add_argument (
' -M ' ,
' --init_mask ' ,
type = str ,
help = ' Path to input mask for inpainting mode (supersedes width and height) ' ,
)
2022-09-18 13:47:57 +00:00
img2img_group . add_argument (
' --init_color ' ,
type = str ,
help = ' Path to reference image for color correction (used for repeated img2img and inpainting) '
)
2022-09-16 17:09:04 +00:00
img2img_group . add_argument (
' -T ' ,
' -fit ' ,
' --fit ' ,
action = ' store_true ' ,
help = ' If specified, will resize the input image to fit within the dimensions of width x height (512x512 default) ' ,
)
img2img_group . add_argument (
' -f ' ,
' --strength ' ,
type = float ,
help = ' Strength for noising/unnoising. 0.0 preserves image exactly, 1.0 replaces it completely ' ,
default = 0.75 ,
)
2022-09-21 06:44:46 +00:00
img2img_group . add_argument (
' -D ' ,
' --out_direction ' ,
nargs = ' + ' ,
type = str ,
metavar = ( ' direction ' , ' pixels ' ) ,
help = ' Direction to extend the given image (left|right|top|bottom). If a distance pixel value is not specified it defaults to half the image size '
)
2022-09-18 19:01:05 +00:00
postprocessing_group . add_argument (
' -ft ' ,
' --facetool ' ,
type = str ,
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
default = ' gfpgan ' ,
2022-09-18 19:01:05 +00:00
help = ' Select the face restoration AI to use: gfpgan, codeformer ' ,
)
2022-09-16 17:09:04 +00:00
postprocessing_group . add_argument (
' -G ' ,
' --gfpgan_strength ' ,
type = float ,
2022-09-21 07:00:19 +00:00
help = ' The strength at which to apply the face restoration to the result. ' ,
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
default = 0.0 ,
2022-09-16 17:09:04 +00:00
)
2022-09-18 19:01:05 +00:00
postprocessing_group . add_argument (
' -cf ' ,
' --codeformer_fidelity ' ,
type = float ,
2022-09-21 07:00:19 +00:00
help = ' Used along with CodeFormer. Takes values between 0 and 1. 0 produces high quality but low accuracy. 1 produces high accuracy but low quality. ' ,
2022-09-18 19:01:05 +00:00
default = 0.75
)
2022-09-16 17:09:04 +00:00
postprocessing_group . add_argument (
' -U ' ,
' --upscale ' ,
nargs = ' + ' ,
type = float ,
2022-09-25 22:11:59 +00:00
help = ' Scale factor (1, 2, 3, 4, etc..) for upscaling final output followed by upscaling strength (0-1.0). If strength not specified, defaults to 0.75 ' ,
2022-09-16 17:09:04 +00:00
default = None ,
)
postprocessing_group . add_argument (
' --save_original ' ,
' -save_orig ' ,
action = ' store_true ' ,
help = ' Save original. Use it when upscaling to save both versions. ' ,
)
postprocessing_group . add_argument (
' --embiggen ' ,
' -embiggen ' ,
nargs = ' + ' ,
type = float ,
2022-09-28 17:00:54 +00:00
help = ' Arbitrary upscaling using img2img. Provide scale factor (0.75), optionally followed by strength (0.75) and tile overlap proportion (0.25). ' ,
2022-09-16 17:09:04 +00:00
default = None ,
)
postprocessing_group . add_argument (
' --embiggen_tiles ' ,
' -embiggen_tiles ' ,
nargs = ' + ' ,
type = int ,
2022-09-28 17:00:54 +00:00
help = ' For embiggen, provide list of tiles to process and replace onto the image e.g. `1 3 5`. ' ,
2022-09-16 17:09:04 +00:00
default = None ,
)
special_effects_group . add_argument (
' --seamless ' ,
action = ' store_true ' ,
help = ' Change the model to seamless tiling (circular) mode ' ,
)
variation_group . add_argument (
' -v ' ,
' --variation_amount ' ,
default = 0.0 ,
type = float ,
help = ' If > 0, generates variations on the initial seed instead of random seeds per iteration. Must be between 0 and 1. Higher values will be more different. '
)
variation_group . add_argument (
' -V ' ,
' --with_variations ' ,
default = None ,
type = str ,
help = ' list of variations to apply, in the format `seed:weight,seed:weight,... '
)
return parser
2022-09-17 17:28:37 +00:00
def format_metadata ( * * kwargs ) :
print ( f ' format_metadata() is deprecated. Please use metadata_dumps() ' )
return metadata_dumps ( kwargs )
def metadata_dumps ( opt ,
seeds = [ ] ,
model_hash = None ,
postprocessing = None ) :
2022-09-16 17:09:04 +00:00
'''
2022-09-17 17:28:37 +00:00
Given an Args object , returns a dict containing the keys and
structure of the proposed stable diffusion metadata standard
https : / / github . com / lstein / stable - diffusion / discussions / 392
This is intended to be turned into JSON and stored in the
" sd
2022-09-16 17:09:04 +00:00
'''
2022-09-18 23:09:11 +00:00
# top-level metadata minus `image` or `images`
metadata = {
' model ' : ' stable diffusion ' ,
' model_id ' : opt . model ,
' model_hash ' : model_hash ,
' app_id ' : APP_ID ,
' app_version ' : APP_VERSION ,
}
2022-09-16 17:09:04 +00:00
# add some RFC266 fields that are generated internally, and not as
# user args
image_dict = opt . to_dict (
postprocessing = postprocessing
)
2022-09-19 07:38:56 +00:00
# 'postprocessing' is either null or an array of postprocessing metadatal
if postprocessing :
# TODO: This is just a hack until postprocessing pipeline work completed
image_dict [ ' postprocessing ' ] = [ ]
if image_dict [ ' gfpgan_strength ' ] and image_dict [ ' gfpgan_strength ' ] > 0 :
image_dict [ ' postprocessing ' ] . append ( ' GFPGAN (not RFC compliant) ' )
if image_dict [ ' upscale ' ] and image_dict [ ' upscale ' ] [ 0 ] > 0 :
image_dict [ ' postprocessing ' ] . append ( ' ESRGAN (not RFC compliant) ' )
else :
image_dict [ ' postprocessing ' ] = None
2022-09-16 17:09:04 +00:00
# remove any image keys not mentioned in RFC #266
rfc266_img_fields = [ ' type ' , ' postprocessing ' , ' sampler ' , ' prompt ' , ' seed ' , ' variations ' , ' steps ' ,
2022-09-28 23:47:36 +00:00
' cfg_scale ' , ' threshold ' , ' perlin ' , ' step_number ' , ' width ' , ' height ' , ' extra ' , ' strength ' ]
2022-09-16 17:09:04 +00:00
rfc_dict = { }
2022-09-19 07:38:56 +00:00
2022-09-16 17:09:04 +00:00
for item in image_dict . items ( ) :
key , value = item
if key in rfc266_img_fields :
rfc_dict [ key ] = value
# semantic drift
rfc_dict [ ' sampler ' ] = image_dict . get ( ' sampler_name ' , None )
2022-09-19 07:38:56 +00:00
2022-09-16 17:09:04 +00:00
# display weighted subprompts (liable to change)
if opt . prompt :
subprompts = split_weighted_subprompts ( opt . prompt )
subprompts = [ { ' prompt ' : x [ 0 ] , ' weight ' : x [ 1 ] } for x in subprompts ]
rfc_dict [ ' prompt ' ] = subprompts
2022-09-19 07:38:56 +00:00
# 'variations' should always exist and be an array, empty or consisting of {'seed': seed, 'weight': weight} pairs
rfc_dict [ ' variations ' ] = [ { ' seed ' : x [ 0 ] , ' weight ' : x [ 1 ] } for x in opt . with_variations ] if opt . with_variations else [ ]
2022-09-16 17:09:04 +00:00
if opt . init_img :
rfc_dict [ ' type ' ] = ' img2img '
rfc_dict [ ' strength_steps ' ] = rfc_dict . pop ( ' strength ' )
2022-09-17 12:39:20 +00:00
rfc_dict [ ' orig_hash ' ] = calculate_init_img_hash ( opt . init_img )
2022-09-19 07:38:56 +00:00
rfc_dict [ ' sampler ' ] = ' ddim ' # TODO: FIX ME WHEN IMG2IMG SUPPORTS ALL SAMPLERS
2022-09-16 17:09:04 +00:00
else :
rfc_dict [ ' type ' ] = ' txt2img '
2022-09-19 07:38:56 +00:00
rfc_dict . pop ( ' strength ' )
2022-09-16 17:09:04 +00:00
2022-09-17 17:44:39 +00:00
if len ( seeds ) == 0 and opt . seed :
seeds = [ seed ]
2022-09-16 17:09:04 +00:00
2022-09-18 23:09:11 +00:00
if opt . grid :
images = [ ]
for seed in seeds :
rfc_dict [ ' seed ' ] = seed
images . append ( copy . copy ( rfc_dict ) )
metadata [ ' images ' ] = images
else :
# there should only ever be a single seed if we did not generate a grid
assert len ( seeds ) == 1 , ' Expected a single seed '
rfc_dict [ ' seed ' ] = seeds [ 0 ]
metadata [ ' image ' ] = rfc_dict
return metadata
2022-09-16 17:09:04 +00:00
2022-09-27 18:27:55 +00:00
@functools.lru_cache ( maxsize = 50 )
2022-09-26 08:18:23 +00:00
def metadata_from_png ( png_file_path ) - > Args :
2022-09-21 06:44:46 +00:00
'''
Given the path to a PNG file created by dream . py , retrieves
2022-09-26 08:18:23 +00:00
an Args object containing the image metadata . Note that this
returns a single Args object , not multiple .
2022-09-21 06:44:46 +00:00
'''
meta = ldm . dream . pngwriter . retrieve_metadata ( png_file_path )
2022-09-26 08:18:23 +00:00
if ' sd-metadata ' in meta and len ( meta [ ' sd-metadata ' ] ) > 0 :
return metadata_loads ( meta ) [ 0 ]
else :
return legacy_metadata_load ( meta , png_file_path )
2022-09-21 06:44:46 +00:00
2022-09-27 18:27:55 +00:00
def dream_cmd_from_png ( png_file_path ) :
opt = metadata_from_png ( png_file_path )
return opt . dream_prompt_str ( )
def metadata_loads ( metadata ) - > list :
2022-09-17 17:28:37 +00:00
'''
Takes the dictionary corresponding to RFC266 ( https : / / github . com / lstein / stable - diffusion / issues / 266 )
2022-09-26 08:18:23 +00:00
and returns a series of opt objects for each of the images described in the dictionary . Note that this
returns a list , and not a single object . See metadata_from_png ( ) for a more convenient function for
files that contain a single image .
2022-09-17 17:28:37 +00:00
'''
results = [ ]
try :
2022-09-19 01:53:47 +00:00
if ' grid ' in metadata [ ' sd-metadata ' ] :
images = metadata [ ' sd-metadata ' ] [ ' images ' ]
else :
images = [ metadata [ ' sd-metadata ' ] [ ' image ' ] ]
2022-09-17 17:28:37 +00:00
for image in images :
# repack the prompt and variations
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 ' prompt ' in image :
image [ ' prompt ' ] = ' , ' . join ( [ ' : ' . join ( [ x [ ' prompt ' ] , str ( x [ ' weight ' ] ) ] ) for x in image [ ' prompt ' ] ] )
if ' variations ' in image :
image [ ' variations ' ] = ' , ' . join ( [ ' : ' . join ( [ str ( x [ ' seed ' ] ) , str ( x [ ' weight ' ] ) ] ) for x in image [ ' variations ' ] ] )
2022-09-18 12:37:08 +00:00
# fix a bit of semantic drift here
image [ ' sampler_name ' ] = image . pop ( ' sampler ' )
2022-09-17 17:28:37 +00:00
opt = Args ( )
opt . _cmd_switches = Namespace ( * * image )
results . append ( opt )
except KeyError as e :
import sys , traceback
print ( ' >> badly-formatted metadata ' , file = sys . stderr )
print ( traceback . format_exc ( ) , file = sys . stderr )
return results
2022-09-17 12:39:20 +00:00
# image can either be a file path on disk or a base64-encoded
# representation of the file's contents
def calculate_init_img_hash ( image_string ) :
prefix = ' data:image/png;base64, '
hash = None
if image_string . startswith ( prefix ) :
imagebase64 = image_string [ len ( prefix ) : ]
imagedata = base64 . b64decode ( imagebase64 )
with open ( ' outputs/test.png ' , ' wb ' ) as file :
file . write ( imagedata )
sha = hashlib . sha256 ( )
sha . update ( imagedata )
hash = sha . hexdigest ( )
else :
hash = sha256 ( image_string )
return hash
2022-09-16 17:09:04 +00:00
# Bah. This should be moved somewhere else...
def sha256 ( path ) :
sha = hashlib . sha256 ( )
with open ( path , ' rb ' ) as f :
while True :
data = f . read ( 65536 )
if not data :
break
sha . update ( data )
return sha . hexdigest ( )
2022-09-26 08:18:23 +00:00
def legacy_metadata_load ( meta , pathname ) - > Args :
if ' Dream ' in meta and len ( meta [ ' Dream ' ] ) > 0 :
dream_prompt = meta [ ' Dream ' ]
opt = Args ( )
opt . parse_cmd ( dream_prompt )
return opt
else : # if nothing else, we can get the seed
match = re . search ( ' \ d+ \ .( \ d+) ' , pathname )
if match :
seed = match . groups ( ) [ 0 ]
opt = Args ( )
opt . seed = seed
return opt
return None