2022-10-11 02:50:11 +00:00
import logging
feature: prompt expansion (#51)
You can use `{}` to randomly pull values from lists. A list of values separated by `|` and enclosed in `{ }` will be randomly drawn from in a non-repeating fashion. Values that are surrounded by `_ _` will pull from a phrase list of the same name. Folders containing .txt phraselist files may be specified via
`--prompt_library_path`. The option may be specified multiple times. Built-in categories:
3d-term, adj-architecture, adj-beauty, adj-detailed, adj-emotion, adj-general, adj-horror, animal, art-movement,
art-site, artist, artist-botanical, artist-surreal, aspect-ratio, bird, body-of-water, body-pose, camera-brand,
camera-model, color, cosmic-galaxy, cosmic-nebula, cosmic-star, cosmic-term, dinosaur, eyecolor, f-stop,
fantasy-creature, fantasy-setting, fish, flower, focal-length, food, fruit, games, gen-modifier, hair, hd,
iso-stop, landscape-type, national-park, nationality, neg-weight, noun-beauty, noun-fantasy, noun-general,
noun-horror, occupation, photo-term, pop-culture, pop-location, punk-style, quantity, rpg-item, scenario-desc,
skin-color, spaceship, style, tree-species, trippy, world-heritage-site
Examples:
`imagine "a {red|black} dog" -r 2 --seed 0` will generate both "a red dog" and "a black dog"
`imagine "a {_color_} dog" -r 4 --seed 0` will generate four, different colored dogs. The colors will eb pulled from an included
phraselist of colors.
`imagine "a {_spaceship_|_fruit_|hot air balloon}. low-poly" -r 4 --seed 0` will generate images of spaceships or fruits or a hot air balloon
Credit to [noodle-soup-prompts](https://github.com/WASasquatch/noodle-soup-prompts/) where most, but not all, of the wordlists originate.
2022-10-09 01:34:35 +00:00
import math
2022-09-09 05:22:55 +00:00
import click
2023-01-31 04:18:23 +00:00
from click_help_colors import HelpColorsCommand , HelpColorsMixin
from click_shell import Shell
2023-02-03 05:43:04 +00:00
from imaginairy import config
2022-09-11 06:27:22 +00:00
logger = logging . getLogger ( __name__ )
2023-01-31 04:18:23 +00:00
common_options = [
click . option (
" --negative-prompt " ,
2023-02-05 15:32:21 +00:00
default = None ,
show_default = False ,
2023-01-31 04:18:23 +00:00
help = " Negative prompt. Things to try and exclude from images. Same negative prompt will be used for all images. " ,
2022-09-24 05:58:48 +00:00
) ,
2023-01-31 04:18:23 +00:00
click . option (
" --prompt-strength " ,
default = 7.5 ,
show_default = True ,
help = " How closely to follow the prompt. Image looks unnatural at higher values " ,
2023-01-17 06:48:27 +00:00
) ,
2023-01-31 04:18:23 +00:00
click . option (
" --init-image " ,
metavar = " PATH|URL " ,
help = " Starting image. " ,
2023-02-05 15:32:21 +00:00
multiple = True ,
2023-01-31 04:18:23 +00:00
) ,
click . option (
" --init-image-strength " ,
default = None ,
show_default = False ,
type = float ,
help = " Starting image strength. Between 0 and 1. " ,
) ,
click . option (
" --outdir " ,
default = " ./outputs " ,
show_default = True ,
type = click . Path ( ) ,
help = " Where to write results to. " ,
) ,
2023-02-05 15:43:53 +00:00
click . option (
" --output-file-extension " ,
default = " jpg " ,
show_default = True ,
type = click . Choice ( [ " jpg " , " png " ] ) ,
help = " Where to write results to. " ,
) ,
2023-01-31 04:18:23 +00:00
click . option (
" -r " ,
" --repeats " ,
default = 1 ,
show_default = True ,
type = int ,
help = " How many times to repeat the renders. If you provide two prompts and --repeat=3 then six images will be generated. " ,
) ,
click . option (
" -h " ,
" --height " ,
default = None ,
show_default = True ,
type = int ,
help = " Image height. Should be multiple of 8. " ,
) ,
click . option (
" -w " ,
" --width " ,
default = None ,
show_default = True ,
type = int ,
help = " Image width. Should be multiple of 8. " ,
) ,
click . option (
" --steps " ,
default = None ,
type = int ,
show_default = True ,
help = " How many diffusion steps to run. More steps, more detail, but with diminishing returns. " ,
) ,
click . option (
" --seed " ,
default = None ,
type = int ,
help = " What seed to use for randomness. Allows reproducible image renders. " ,
) ,
click . option ( " --upscale " , is_flag = True ) ,
click . option ( " --fix-faces " , is_flag = True ) ,
click . option (
" --fix-faces-fidelity " ,
default = None ,
type = float ,
help = " How faithful to the original should face enhancement be. 1 = best fidelity, 0 = best looking face. " ,
) ,
click . option (
" --sampler-type " ,
" --sampler " ,
default = config . DEFAULT_SAMPLER ,
show_default = True ,
type = click . Choice ( config . SAMPLER_TYPE_OPTIONS ) ,
help = " What sampling strategy to use. " ,
) ,
click . option (
" --log-level " ,
default = " INFO " ,
show_default = True ,
type = click . Choice ( [ " DEBUG " , " INFO " , " WARNING " , " ERROR " ] ) ,
help = " What level of logs to show. " ,
) ,
click . option (
" --quiet " ,
" -q " ,
is_flag = True ,
help = " Suppress logs. Alias of `--log-level ERROR`. " ,
) ,
click . option (
" --show-work " ,
default = False ,
is_flag = True ,
help = " Output a debug images to `steps` folder. " ,
) ,
click . option (
" --tile " ,
is_flag = True ,
help = " Any images rendered will be tileable in both X and Y directions. " ,
) ,
click . option (
" --tile-x " ,
is_flag = True ,
help = " Any images rendered will be tileable in the X direction. " ,
) ,
click . option (
" --tile-y " ,
is_flag = True ,
help = " Any images rendered will be tileable in the Y direction. " ,
) ,
2023-02-16 16:11:31 +00:00
click . option (
" --allow-compose-phase/--no-compose-phase " ,
default = True ,
help = " Allow the image to be composed at a lower resolution. " ,
) ,
2023-01-31 04:18:23 +00:00
click . option (
" --mask-image " ,
metavar = " PATH|URL " ,
help = " A mask to use for inpainting. White gets painted, Black is left alone. " ,
) ,
click . option (
" --mask-prompt " ,
help = (
" Describe what you want masked and the AI will mask it for you. "
" You can describe complex masks with AND, OR, NOT keywords and parentheses. "
" The strength of each mask can be modified with { *1.5} notation. \n \n "
" Examples: \n "
" car AND (wheels { *1.1} OR trunk OR engine OR windows OR headlights) AND NOT (truck OR headlights) { *10} \n "
" fruit|fruit stem "
) ,
) ,
click . option (
" --mask-mode " ,
default = " replace " ,
show_default = True ,
type = click . Choice ( [ " keep " , " replace " ] ) ,
help = " Should we replace the masked area or keep it? " ,
) ,
click . option (
" --mask-modify-original " ,
default = True ,
is_flag = True ,
help = " After the inpainting is done, apply the changes to a copy of the original image. " ,
) ,
click . option (
" --outpaint " ,
help = (
" Specify in what directions to expand the image. Values will be snapped such that output image size is multiples of 8. Examples \n "
" `--outpaint up10,down300,left50,right50` \n "
" `--outpaint u10,d300,l50,r50` \n "
" `--outpaint all200` \n "
" `--outpaint a200` \n "
) ,
default = " " ,
) ,
click . option (
" --caption " ,
default = False ,
is_flag = True ,
help = " Generate a text description of the generated image. " ,
) ,
click . option (
" --precision " ,
help = " Evaluate at this precision. " ,
type = click . Choice ( [ " full " , " autocast " ] ) ,
default = " autocast " ,
show_default = True ,
) ,
click . option (
" --model-weights-path " ,
" --model " ,
help = f " Model to use. Should be one of { ' , ' . join ( config . MODEL_SHORT_NAMES ) } , or a path to custom weights. " ,
show_default = True ,
default = config . DEFAULT_MODEL ,
) ,
click . option (
" --model-config-path " ,
help = " Model config file to use. If a model name is specified, the appropriate config will be used. " ,
show_default = True ,
default = None ,
) ,
click . option (
" --prompt-library-path " ,
help = " Path to folder containing phrase lists in txt files. Use txt filename in prompt: {_filename_} . " ,
type = click . Path ( exists = True ) ,
default = None ,
multiple = True ,
) ,
click . option (
" --version " ,
default = False ,
is_flag = True ,
help = " Print the version and exit. " ,
) ,
click . option (
" --gif " ,
" make_gif " ,
default = False ,
is_flag = True ,
help = " Create a gif of the generation. " ,
) ,
click . option (
" --compare-gif " ,
" make_compare_gif " ,
default = False ,
is_flag = True ,
help = " Create a gif comparing the original image to the modified one. " ,
) ,
click . option (
" --arg-schedule " ,
" arg_schedules " ,
multiple = True ,
help = " Schedule how an argument should change over several generations. Format: `--arg-schedule arg_name[start:end:increment]` or `--arg-schedule arg_name[val,val2,val3]` " ,
) ,
click . option (
" --compilation-anim " ,
" make_compilation_animation " ,
default = None ,
type = click . Choice ( [ " gif " , " mp4 " ] ) ,
help = " Generate an animation composed of all the images generated in this run. Defaults to gif but `--compilation-anim mp4` will generate an mp4 instead. " ,
) ,
2023-02-22 04:41:29 +00:00
click . option (
" --caption-text " ,
" caption_text " ,
default = None ,
help = " Specify the text to write onto the image " ,
type = str ,
) ,
2023-01-31 04:18:23 +00:00
]
def add_options ( options ) :
def _add_options ( func ) :
for option in reversed ( options ) :
func = option ( func )
return func
return _add_options
def replace_option ( options , option_name , new_option ) :
for i , option in enumerate ( options ) :
if option . name == option_name :
options [ i ] = new_option
return
raise ValueError ( f " Option { option_name } not found " )
def remove_option ( options , option_name ) :
for i , option_dec in enumerate ( options ) :
def temp_f ( ) :
return True
temp_f = option_dec ( temp_f )
option = temp_f . __click_params__ [ 0 ]
if option . name == option_name :
del options [ i ]
return
raise ValueError ( f " Option { option_name } not found " )
class ColorShell ( HelpColorsMixin , Shell ) :
pass
class ImagineColorsCommand ( HelpColorsCommand ) :
def __init__ ( self , * args , * * kwargs ) :
super ( ) . __init__ ( * args , * * kwargs )
self . help_headers_color = " yellow "
self . help_options_color = " green "
@click.command (
prompt = " 🤖🧠> " ,
intro = " Starting imaginAIry shell... " ,
help_headers_color = " yellow " ,
help_options_color = " green " ,
context_settings = { " max_content_width " : 140 } ,
cls = ColorShell ,
2023-01-29 01:16:47 +00:00
)
2022-09-20 04:15:38 +00:00
@click.pass_context
2023-01-31 04:18:23 +00:00
def aimg ( ctx ) :
"""
🤖 🧠 ImaginAIry .
Pythonic generation of images via AI
"""
import sys
is_shell = len ( sys . argv ) == 1
if is_shell :
print ( ctx . get_help ( ) )
aimg . command_class = ImagineColorsCommand
@click.command ( context_settings = { " max_content_width " : 140 } , cls = ImagineColorsCommand )
@click.argument ( " prompt_texts " , nargs = - 1 )
@add_options ( common_options )
2023-02-12 07:42:19 +00:00
@click.option (
" --control-image " ,
metavar = " PATH|URL " ,
help = (
" Image used for control signal in image generation. "
" For example if control-mode is depth, then the generated image will match the depth map "
" extracted from the control image. "
" Defaults to the `--init-image` "
) ,
multiple = False ,
)
@click.option (
" --control-image-raw " ,
metavar = " PATH|URL " ,
help = (
" Preprocessed image used for control signal in image generation. Like `--control-image` but "
" expects the already extracted signal. For example the raw control image would be a depth map or "
" pose information. "
) ,
multiple = False ,
)
@click.option (
" --control-mode " ,
default = None ,
show_default = False ,
type = click . Choice ( [ " " , " canny " , " depth " , " normal " , " hed " , " openpose " ] ) ,
help = " how the control image is used as signal " ,
)
2023-01-31 04:18:23 +00:00
@click.pass_context
2022-09-09 05:22:55 +00:00
def imagine_cmd (
2022-09-20 04:15:38 +00:00
ctx ,
2022-09-09 05:22:55 +00:00
prompt_texts ,
2022-12-02 09:49:13 +00:00
negative_prompt ,
2022-09-12 01:00:40 +00:00
prompt_strength ,
init_image ,
init_image_strength ,
2022-09-09 05:22:55 +00:00
outdir ,
2023-02-05 15:43:53 +00:00
output_file_extension ,
2022-09-09 05:22:55 +00:00
repeats ,
height ,
width ,
steps ,
seed ,
2022-09-13 07:27:53 +00:00
upscale ,
fix_faces ,
2022-10-03 17:26:08 +00:00
fix_faces_fidelity ,
2022-09-09 05:22:55 +00:00
sampler_type ,
2022-09-11 06:27:22 +00:00
log_level ,
2022-09-24 08:56:19 +00:00
quiet ,
2022-09-11 06:27:22 +00:00
show_work ,
2022-09-11 20:56:41 +00:00
tile ,
2022-12-20 05:32:41 +00:00
tile_x ,
tile_y ,
2023-02-16 16:11:31 +00:00
allow_compose_phase ,
2022-09-18 13:07:07 +00:00
mask_image ,
mask_prompt ,
mask_mode ,
2022-09-24 18:21:53 +00:00
mask_modify_original ,
2023-01-17 06:48:27 +00:00
outpaint ,
2022-09-20 04:15:38 +00:00
caption ,
2022-09-22 05:03:12 +00:00
precision ,
2022-10-06 04:50:20 +00:00
model_weights_path ,
2023-01-01 22:54:49 +00:00
model_config_path ,
feature: prompt expansion (#51)
You can use `{}` to randomly pull values from lists. A list of values separated by `|` and enclosed in `{ }` will be randomly drawn from in a non-repeating fashion. Values that are surrounded by `_ _` will pull from a phrase list of the same name. Folders containing .txt phraselist files may be specified via
`--prompt_library_path`. The option may be specified multiple times. Built-in categories:
3d-term, adj-architecture, adj-beauty, adj-detailed, adj-emotion, adj-general, adj-horror, animal, art-movement,
art-site, artist, artist-botanical, artist-surreal, aspect-ratio, bird, body-of-water, body-pose, camera-brand,
camera-model, color, cosmic-galaxy, cosmic-nebula, cosmic-star, cosmic-term, dinosaur, eyecolor, f-stop,
fantasy-creature, fantasy-setting, fish, flower, focal-length, food, fruit, games, gen-modifier, hair, hd,
iso-stop, landscape-type, national-park, nationality, neg-weight, noun-beauty, noun-fantasy, noun-general,
noun-horror, occupation, photo-term, pop-culture, pop-location, punk-style, quantity, rpg-item, scenario-desc,
skin-color, spaceship, style, tree-species, trippy, world-heritage-site
Examples:
`imagine "a {red|black} dog" -r 2 --seed 0` will generate both "a red dog" and "a black dog"
`imagine "a {_color_} dog" -r 4 --seed 0` will generate four, different colored dogs. The colors will eb pulled from an included
phraselist of colors.
`imagine "a {_spaceship_|_fruit_|hot air balloon}. low-poly" -r 4 --seed 0` will generate images of spaceships or fruits or a hot air balloon
Credit to [noodle-soup-prompts](https://github.com/WASasquatch/noodle-soup-prompts/) where most, but not all, of the wordlists originate.
2022-10-09 01:34:35 +00:00
prompt_library_path ,
2023-01-18 17:00:25 +00:00
version , # noqa
2023-01-22 01:36:47 +00:00
make_gif ,
2023-01-29 01:16:47 +00:00
make_compare_gif ,
arg_schedules ,
make_compilation_animation ,
2023-02-22 04:41:29 +00:00
caption_text ,
2023-02-12 07:42:19 +00:00
control_image ,
control_image_raw ,
control_mode ,
2023-01-21 21:23:48 +00:00
) :
2023-01-31 04:18:23 +00:00
"""
Generate images via AI .
Can be invoked via either ` aimg imagine ` or just ` imagine ` .
"""
2023-01-21 21:23:48 +00:00
return _imagine_cmd (
ctx ,
prompt_texts ,
negative_prompt ,
prompt_strength ,
init_image ,
init_image_strength ,
outdir ,
2023-02-05 15:43:53 +00:00
output_file_extension ,
2023-01-21 21:23:48 +00:00
repeats ,
height ,
width ,
steps ,
seed ,
upscale ,
fix_faces ,
fix_faces_fidelity ,
sampler_type ,
log_level ,
quiet ,
show_work ,
tile ,
tile_x ,
tile_y ,
2023-02-16 16:11:31 +00:00
allow_compose_phase ,
2023-01-21 21:23:48 +00:00
mask_image ,
mask_prompt ,
mask_mode ,
mask_modify_original ,
outpaint ,
caption ,
precision ,
model_weights_path ,
model_config_path ,
prompt_library_path ,
version , # noqa
2023-01-22 16:23:59 +00:00
make_gif ,
2023-01-29 01:16:47 +00:00
make_compare_gif ,
arg_schedules ,
make_compilation_animation ,
2023-02-22 04:41:29 +00:00
caption_text ,
2023-02-12 07:42:19 +00:00
control_image ,
control_image_raw ,
control_mode ,
2023-01-21 21:23:48 +00:00
)
2023-02-05 15:32:21 +00:00
@aimg.command ( " edit-demo " )
@click.argument ( " image_paths " , metavar = " PATH|URL " , required = True , nargs = - 1 )
@click.option (
" --outdir " ,
default = " ./outputs " ,
show_default = True ,
type = click . Path ( ) ,
help = " Where to write results to. " ,
)
@click.option (
" -h " ,
" --height " ,
default = 512 ,
show_default = True ,
type = int ,
help = " Image height. Should be multiple of 8. " ,
)
@click.option (
" -w " ,
" --width " ,
default = 512 ,
show_default = True ,
type = int ,
help = " Image width. Should be multiple of 8. " ,
)
def edit_demo ( image_paths , outdir , height , width ) :
2023-02-05 17:25:53 +00:00
""" Make some fun pre-set edits to input photos. """
2023-01-31 04:18:23 +00:00
2023-02-05 15:32:21 +00:00
from imaginairy . log_utils import configure_logging
from imaginairy . surprise_me import create_surprise_me_images
configure_logging ( )
for image_path in image_paths :
create_surprise_me_images (
image_path , outdir = outdir , make_gif = True , width = width , height = height
)
edit_options = common_options . copy ( )
remove_option ( edit_options , " model_weights_path " )
2023-01-31 04:18:23 +00:00
remove_option ( edit_options , " init_image " )
2023-02-05 15:32:21 +00:00
remove_option ( edit_options , " init_image_strength " )
remove_option ( edit_options , " negative_prompt " )
2023-02-17 21:05:49 +00:00
remove_option ( edit_options , " allow_compose_phase " )
2023-02-23 09:28:40 +00:00
2023-02-12 07:42:19 +00:00
# remove_option(edit_options, "control_mode")
# remove_option(edit_options, "control_image")
# remove_option(edit_options, "control_image_raw")
2023-01-31 04:18:23 +00:00
@aimg.command ( " edit " )
2023-02-05 15:32:21 +00:00
@click.argument ( " image_paths " , metavar = " PATH|URL " , required = True , nargs = - 1 )
2023-01-22 01:36:47 +00:00
@click.option (
2023-02-05 15:32:21 +00:00
" --image-strength " ,
default = 1 ,
show_default = False ,
type = float ,
help = " Starting image strength. Between 0 and 1. " ,
2023-01-22 01:36:47 +00:00
)
2023-02-05 15:32:21 +00:00
@click.option ( " --prompt " , " -p " , required = True , multiple = True )
@click.option (
" --model-weights-path " ,
" --model " ,
help = f " Model to use. Should be one of { ' , ' . join ( config . MODEL_SHORT_NAMES ) } , or a path to custom weights. " ,
show_default = True ,
default = " edit " ,
)
@click.option (
" --negative-prompt " ,
default = None ,
show_default = False ,
help = " Negative prompt. Things to try and exclude from images. Same negative prompt will be used for all images. A default negative prompt is used if none is selected. " ,
)
@add_options ( edit_options )
2023-01-21 21:23:48 +00:00
@click.pass_context
2023-01-22 01:36:47 +00:00
def edit_image ( # noqa
2023-01-21 21:23:48 +00:00
ctx ,
2023-02-05 15:32:21 +00:00
image_paths ,
image_strength ,
prompt ,
2023-01-21 21:23:48 +00:00
negative_prompt ,
prompt_strength ,
outdir ,
2023-02-05 15:43:53 +00:00
output_file_extension ,
2023-01-21 21:23:48 +00:00
repeats ,
height ,
width ,
steps ,
seed ,
upscale ,
fix_faces ,
fix_faces_fidelity ,
sampler_type ,
log_level ,
quiet ,
show_work ,
tile ,
tile_x ,
tile_y ,
mask_image ,
mask_prompt ,
mask_mode ,
mask_modify_original ,
outpaint ,
caption ,
precision ,
model_weights_path ,
model_config_path ,
prompt_library_path ,
version , # noqa
2023-01-22 01:36:47 +00:00
make_gif ,
2023-01-29 01:16:47 +00:00
make_compare_gif ,
arg_schedules ,
make_compilation_animation ,
2023-02-22 04:41:29 +00:00
caption_text ,
2023-01-21 21:23:48 +00:00
) :
2023-01-31 04:18:23 +00:00
"""
Edit an image via AI .
2023-02-05 15:32:21 +00:00
Provide paths or URLs to images and directions on how to alter them .
2023-01-22 01:36:47 +00:00
2023-02-05 15:32:21 +00:00
Example : aimg edit - - prompt " make the dog red " my - dog . jpg my - dog2 . jpg
2023-01-22 01:36:47 +00:00
2023-02-05 15:32:21 +00:00
Same as calling ` aimg imagine - - model edit - - init - image my - dog . jpg - - init - image - strength 1 ` except this command
can batch edit images .
"""
2023-02-17 21:36:53 +00:00
allow_compose_phase = False
2023-01-21 21:23:48 +00:00
return _imagine_cmd (
ctx ,
2023-02-05 15:32:21 +00:00
prompt ,
2023-01-21 21:23:48 +00:00
negative_prompt ,
prompt_strength ,
2023-02-05 15:32:21 +00:00
image_paths ,
image_strength ,
2023-01-21 21:23:48 +00:00
outdir ,
2023-02-05 15:43:53 +00:00
output_file_extension ,
2023-01-21 21:23:48 +00:00
repeats ,
height ,
width ,
steps ,
seed ,
upscale ,
fix_faces ,
fix_faces_fidelity ,
sampler_type ,
log_level ,
quiet ,
show_work ,
tile ,
tile_x ,
tile_y ,
2023-02-17 21:36:53 +00:00
allow_compose_phase ,
2023-01-21 21:23:48 +00:00
mask_image ,
mask_prompt ,
mask_mode ,
mask_modify_original ,
outpaint ,
caption ,
precision ,
model_weights_path ,
model_config_path ,
prompt_library_path ,
version , # noqa
2023-01-22 01:36:47 +00:00
make_gif ,
2023-01-29 01:16:47 +00:00
make_compare_gif ,
arg_schedules ,
make_compilation_animation ,
2023-02-22 04:41:29 +00:00
caption_text ,
2023-01-21 21:23:48 +00:00
)
def _imagine_cmd (
ctx ,
prompt_texts ,
negative_prompt ,
prompt_strength ,
init_image ,
init_image_strength ,
outdir ,
2023-02-05 15:43:53 +00:00
output_file_extension ,
2023-01-21 21:23:48 +00:00
repeats ,
height ,
width ,
steps ,
seed ,
upscale ,
fix_faces ,
fix_faces_fidelity ,
sampler_type ,
log_level ,
quiet ,
show_work ,
tile ,
tile_x ,
tile_y ,
2023-02-16 16:11:31 +00:00
allow_compose_phase ,
2023-01-21 21:23:48 +00:00
mask_image ,
mask_prompt ,
mask_mode ,
mask_modify_original ,
outpaint ,
caption ,
precision ,
model_weights_path ,
model_config_path ,
prompt_library_path ,
2023-01-22 01:36:47 +00:00
version = False , # noqa
make_gif = False ,
2023-01-29 01:16:47 +00:00
make_compare_gif = False ,
arg_schedules = None ,
make_compilation_animation = False ,
2023-02-22 04:41:29 +00:00
caption_text = " " ,
2023-02-12 07:42:19 +00:00
control_image = None ,
control_image_raw = None ,
control_mode = " " ,
2022-09-09 05:22:55 +00:00
) :
2023-01-02 04:14:22 +00:00
""" Have the AI generate images. alias:imagine. """
2023-02-03 05:43:04 +00:00
2022-09-20 04:15:38 +00:00
if ctx . invoked_subcommand is not None :
return
2022-10-11 02:50:11 +00:00
2023-01-18 17:00:25 +00:00
if version :
2023-02-03 05:43:04 +00:00
from imaginairy . version import get_version
print ( get_version ( ) )
2023-01-18 17:00:25 +00:00
return
2022-09-24 08:56:19 +00:00
if quiet :
log_level = " ERROR "
2023-01-31 04:18:23 +00:00
import sys
if len ( sys . argv ) > 1 :
msg = (
" ✨ Generate images faster using a persistent shell session. Just run `aimg` to start. "
" This makes generation and editing much quicker since the model can stay loaded in memory. \n "
)
print ( msg )
from imaginairy . log_utils import configure_logging
2022-09-11 06:27:22 +00:00
configure_logging ( log_level )
2022-09-11 20:56:41 +00:00
2023-02-05 15:32:21 +00:00
if isinstance ( init_image , str ) :
init_images = [ init_image ]
else :
init_images = init_image
2023-02-05 15:43:53 +00:00
total_image_count = len ( prompt_texts ) * max ( len ( init_images ) , 1 ) * repeats
2022-09-11 06:27:22 +00:00
logger . info (
2023-02-05 15:43:53 +00:00
f " Received { len ( prompt_texts ) } prompt(s) and { len ( init_images ) } input image(s). Will repeat the generations { repeats } times to create { total_image_count } images. "
2022-09-11 06:27:22 +00:00
)
2023-01-31 04:18:23 +00:00
from imaginairy import ImaginePrompt , LazyLoadingImage , imagine_image_files
2023-02-12 07:42:19 +00:00
if control_image and control_image . startswith ( " http " ) :
control_image = LazyLoadingImage ( url = control_image )
if control_image_raw and control_image_raw . startswith ( " http " ) :
control_image_raw = LazyLoadingImage ( url = control_image_raw )
2023-02-05 15:32:21 +00:00
new_init_images = [ ]
2023-02-05 15:46:27 +00:00
for _init_image in init_images :
if _init_image and _init_image . startswith ( " http " ) :
_init_image = LazyLoadingImage ( url = _init_image )
new_init_images . append ( _init_image )
2023-02-05 15:32:21 +00:00
init_images = new_init_images
2023-02-05 15:43:53 +00:00
if not init_images :
init_images = [ None ]
2022-09-16 06:06:59 +00:00
2022-09-24 05:58:48 +00:00
if mask_image and mask_image . startswith ( " http " ) :
mask_image = LazyLoadingImage ( url = mask_image )
2022-11-25 09:45:07 +00:00
2022-09-09 05:22:55 +00:00
prompts = [ ]
feature: prompt expansion (#51)
You can use `{}` to randomly pull values from lists. A list of values separated by `|` and enclosed in `{ }` will be randomly drawn from in a non-repeating fashion. Values that are surrounded by `_ _` will pull from a phrase list of the same name. Folders containing .txt phraselist files may be specified via
`--prompt_library_path`. The option may be specified multiple times. Built-in categories:
3d-term, adj-architecture, adj-beauty, adj-detailed, adj-emotion, adj-general, adj-horror, animal, art-movement,
art-site, artist, artist-botanical, artist-surreal, aspect-ratio, bird, body-of-water, body-pose, camera-brand,
camera-model, color, cosmic-galaxy, cosmic-nebula, cosmic-star, cosmic-term, dinosaur, eyecolor, f-stop,
fantasy-creature, fantasy-setting, fish, flower, focal-length, food, fruit, games, gen-modifier, hair, hd,
iso-stop, landscape-type, national-park, nationality, neg-weight, noun-beauty, noun-fantasy, noun-general,
noun-horror, occupation, photo-term, pop-culture, pop-location, punk-style, quantity, rpg-item, scenario-desc,
skin-color, spaceship, style, tree-species, trippy, world-heritage-site
Examples:
`imagine "a {red|black} dog" -r 2 --seed 0` will generate both "a red dog" and "a black dog"
`imagine "a {_color_} dog" -r 4 --seed 0` will generate four, different colored dogs. The colors will eb pulled from an included
phraselist of colors.
`imagine "a {_spaceship_|_fruit_|hot air balloon}. low-poly" -r 4 --seed 0` will generate images of spaceships or fruits or a hot air balloon
Credit to [noodle-soup-prompts](https://github.com/WASasquatch/noodle-soup-prompts/) where most, but not all, of the wordlists originate.
2022-10-09 01:34:35 +00:00
prompt_expanding_iterators = { }
2023-01-31 04:18:23 +00:00
from imaginairy . enhancers . prompt_expansion import expand_prompts
2022-09-09 05:22:55 +00:00
for _ in range ( repeats ) :
for prompt_text in prompt_texts :
feature: prompt expansion (#51)
You can use `{}` to randomly pull values from lists. A list of values separated by `|` and enclosed in `{ }` will be randomly drawn from in a non-repeating fashion. Values that are surrounded by `_ _` will pull from a phrase list of the same name. Folders containing .txt phraselist files may be specified via
`--prompt_library_path`. The option may be specified multiple times. Built-in categories:
3d-term, adj-architecture, adj-beauty, adj-detailed, adj-emotion, adj-general, adj-horror, animal, art-movement,
art-site, artist, artist-botanical, artist-surreal, aspect-ratio, bird, body-of-water, body-pose, camera-brand,
camera-model, color, cosmic-galaxy, cosmic-nebula, cosmic-star, cosmic-term, dinosaur, eyecolor, f-stop,
fantasy-creature, fantasy-setting, fish, flower, focal-length, food, fruit, games, gen-modifier, hair, hd,
iso-stop, landscape-type, national-park, nationality, neg-weight, noun-beauty, noun-fantasy, noun-general,
noun-horror, occupation, photo-term, pop-culture, pop-location, punk-style, quantity, rpg-item, scenario-desc,
skin-color, spaceship, style, tree-species, trippy, world-heritage-site
Examples:
`imagine "a {red|black} dog" -r 2 --seed 0` will generate both "a red dog" and "a black dog"
`imagine "a {_color_} dog" -r 4 --seed 0` will generate four, different colored dogs. The colors will eb pulled from an included
phraselist of colors.
`imagine "a {_spaceship_|_fruit_|hot air balloon}. low-poly" -r 4 --seed 0` will generate images of spaceships or fruits or a hot air balloon
Credit to [noodle-soup-prompts](https://github.com/WASasquatch/noodle-soup-prompts/) where most, but not all, of the wordlists originate.
2022-10-09 01:34:35 +00:00
if prompt_text not in prompt_expanding_iterators :
prompt_expanding_iterators [ prompt_text ] = expand_prompts (
n = math . inf ,
prompt_text = prompt_text ,
prompt_library_paths = prompt_library_path ,
)
prompt_iterator = prompt_expanding_iterators [ prompt_text ]
2022-12-20 05:32:41 +00:00
if tile :
_tile_mode = " xy "
elif tile_x :
_tile_mode = " x "
elif tile_y :
_tile_mode = " y "
else :
_tile_mode = " "
2023-02-05 15:32:21 +00:00
for _init_image in init_images :
prompt = ImaginePrompt (
next ( prompt_iterator ) ,
negative_prompt = negative_prompt ,
prompt_strength = prompt_strength ,
init_image = _init_image ,
init_image_strength = init_image_strength ,
2023-02-12 07:42:19 +00:00
control_image = control_image ,
control_image_raw = control_image_raw ,
control_mode = control_mode ,
2023-02-05 15:32:21 +00:00
seed = seed ,
sampler_type = sampler_type ,
steps = steps ,
height = height ,
width = width ,
mask_image = mask_image ,
mask_prompt = mask_prompt ,
mask_mode = mask_mode ,
mask_modify_original = mask_modify_original ,
outpaint = outpaint ,
upscale = upscale ,
fix_faces = fix_faces ,
fix_faces_fidelity = fix_faces_fidelity ,
tile_mode = _tile_mode ,
2023-02-16 16:11:31 +00:00
allow_compose_phase = allow_compose_phase ,
2023-02-05 15:32:21 +00:00
model = model_weights_path ,
model_config_path = model_config_path ,
2023-02-22 04:41:29 +00:00
caption_text = caption_text ,
2023-02-05 15:32:21 +00:00
)
from imaginairy . prompt_schedules import (
parse_schedule_strs ,
prompt_mutator ,
)
2022-12-20 05:32:41 +00:00
2023-02-05 15:32:21 +00:00
if arg_schedules :
schedules = parse_schedule_strs ( arg_schedules )
for new_prompt in prompt_mutator ( prompt , schedules ) :
prompts . append ( new_prompt )
else :
prompts . append ( prompt )
2022-09-09 05:22:55 +00:00
2023-01-29 01:16:47 +00:00
filenames = imagine_image_files (
2022-09-09 05:22:55 +00:00
prompts ,
outdir = outdir ,
2022-09-24 18:21:53 +00:00
record_step_images = show_work ,
2023-02-05 15:43:53 +00:00
output_file_extension = output_file_extension ,
2022-09-20 04:15:38 +00:00
print_caption = caption ,
2022-09-22 05:03:12 +00:00
precision = precision ,
2023-01-28 01:18:42 +00:00
make_gif = make_gif ,
2023-01-29 01:16:47 +00:00
make_compare_gif = make_compare_gif ,
2022-09-09 05:22:55 +00:00
)
2023-01-29 01:16:47 +00:00
if make_compilation_animation :
2023-01-31 04:18:23 +00:00
import os . path
2023-01-29 01:16:47 +00:00
ext = make_compilation_animation
compilation_outdir = os . path . join ( outdir , " compilations " )
2023-02-17 17:53:31 +00:00
os . makedirs ( compilation_outdir , exist_ok = True )
2023-01-29 01:16:47 +00:00
base_count = len ( os . listdir ( compilation_outdir ) )
new_filename = os . path . join (
compilation_outdir , f " { base_count : 04d } _compilation. { ext } "
)
comp_imgs = [ LazyLoadingImage ( filepath = f ) for f in filenames ]
2023-01-29 05:32:56 +00:00
comp_imgs . reverse ( )
2023-01-31 04:18:23 +00:00
from imaginairy . animations import make_bounce_animation
2023-01-29 05:32:56 +00:00
make_bounce_animation (
outpath = new_filename ,
imgs = comp_imgs ,
start_pause_duration_ms = 1500 ,
end_pause_duration_ms = 1000 ,
)
2023-01-29 01:16:47 +00:00
logger . info ( f " [compilation] saved to: { new_filename } " )
2022-09-09 05:22:55 +00:00
2023-01-18 17:00:25 +00:00
@aimg.command ( )
def version ( ) :
""" Print the version. """
2023-02-03 05:43:04 +00:00
from imaginairy . version import get_version
print ( get_version ( ) )
2023-01-18 17:00:25 +00:00
2023-01-28 07:57:31 +00:00
@click.argument ( " image_filepaths " , nargs = - 1 )
@click.option (
" --outdir " ,
default = " ./outputs/upscaled " ,
show_default = True ,
type = click . Path ( ) ,
help = " Where to write results to. " ,
)
@aimg.command ( " upscale " )
def upscale_cmd ( image_filepaths , outdir ) :
"""
Upscale an image 4 x using AI .
"""
2023-02-03 05:43:04 +00:00
import os . path
from tqdm import tqdm
from imaginairy import LazyLoadingImage
from imaginairy . enhancers . upscale_realesrgan import upscale_image
2023-01-28 07:57:31 +00:00
os . makedirs ( outdir , exist_ok = True )
for p in tqdm ( image_filepaths ) :
savepath = os . path . join ( outdir , os . path . basename ( p ) )
if p . startswith ( " http " ) :
img = LazyLoadingImage ( url = p )
else :
img = LazyLoadingImage ( filepath = p )
logger . info (
2023-02-03 05:43:04 +00:00
f " Upscaling { p } from { img . width } x { img . height } to { img . width * 4 } x { img . height * 4 } and saving it to { savepath } "
2023-01-28 07:57:31 +00:00
)
img = upscale_image ( img )
img . save ( os . path . join ( outdir , os . path . basename ( p ) ) )
2023-02-23 09:28:40 +00:00
@click.argument ( " image_filepaths " , nargs = - 1 )
@click.option (
" --outdir " ,
default = " ./outputs/colorized " ,
show_default = True ,
type = click . Path ( ) ,
help = " Where to write results to. " ,
)
@click.option (
" -r " ,
" --repeats " ,
default = 1 ,
show_default = True ,
type = int ,
help = " How many times to repeat the renders. If you provide two prompts and --repeat=3 then six images will be generated. " ,
)
@aimg.command ( " colorize " )
def colorize_cmd ( image_filepaths , outdir , repeats ) :
"""
Colorize images using AI . Doesn ' t work very well yet.
"""
import os . path
from tqdm import tqdm
from imaginairy import LazyLoadingImage
from imaginairy . colorize import colorize_img
from imaginairy . log_utils import configure_logging
configure_logging ( )
os . makedirs ( outdir , exist_ok = True )
base_count = len ( os . listdir ( outdir ) )
for _ in range ( repeats ) :
for p in tqdm ( image_filepaths ) :
base_count + = 1
filename = f " { base_count : 06d } _ { os . path . basename ( p ) } " . lower ( )
savepath = os . path . join ( outdir , filename )
if p . startswith ( " http " ) :
img = LazyLoadingImage ( url = p )
elif os . path . isdir ( p ) :
print ( f " Skipping directory: { p } " )
continue
else :
img = LazyLoadingImage ( filepath = p )
logger . info ( f " Colorizing { p } and saving it to { savepath } " )
img = colorize_img ( img )
img . save ( savepath )
2022-09-20 04:15:38 +00:00
@click.argument ( " image_filepaths " , nargs = - 1 )
@aimg.command ( )
def describe ( image_filepaths ) :
2023-01-02 04:14:22 +00:00
""" Generate text descriptions of images. """
2023-02-23 09:28:40 +00:00
import os
2023-02-03 05:43:04 +00:00
from imaginairy import LazyLoadingImage
from imaginairy . enhancers . describe_image_blip import generate_caption
2022-09-20 04:15:38 +00:00
imgs = [ ]
for p in image_filepaths :
2023-02-23 09:28:40 +00:00
2022-09-20 04:15:38 +00:00
if p . startswith ( " http " ) :
img = LazyLoadingImage ( url = p )
2023-02-23 09:28:40 +00:00
elif os . path . isdir ( p ) :
print ( f " Skipping directory: { p } " )
continue
2022-09-20 04:15:38 +00:00
else :
img = LazyLoadingImage ( filepath = p )
imgs . append ( img )
for img in imgs :
print ( generate_caption ( img . copy ( ) ) )
2023-01-01 22:54:49 +00:00
@click.option (
" --concept-label " ,
help = (
' The concept you are training on. Usually " a photo of [person or thing] [classname] " is what you should use. '
) ,
required = True ,
)
@click.option (
" --concept-images-dir " ,
type = click . Path ( ) ,
required = True ,
help = " Where to find the pre-processed concept images to train on. " ,
)
@click.option (
" --class-label " ,
help = (
' What class of things does the concept belong to. For example, if you are training on " a painting of a George Washington " , '
' you might use " a painting of a man " as the class label. We use this to prevent the model from overfitting. '
) ,
default = " a photo of * " ,
)
@click.option (
" --class-images-dir " ,
type = click . Path ( ) ,
required = True ,
help = " Where to find the pre-processed class images to train on. " ,
)
@click.option (
" --n-class-images " ,
type = int ,
default = 300 ,
help = " Number of class images to generate. " ,
)
@click.option (
" --model-weights-path " ,
" --model " ,
" model " ,
2023-01-18 17:00:25 +00:00
help = f " Model to use. Should be one of { ' , ' . join ( config . MODEL_SHORT_NAMES ) } , or a path to custom weights. " ,
2023-01-01 22:54:49 +00:00
show_default = True ,
default = config . DEFAULT_MODEL ,
)
@click.option (
" --person " ,
" is_person " ,
is_flag = True ,
help = " Set if images are of a person. Will use face detection and enhancement. " ,
)
@click.option (
" -y " ,
" preconfirmed " ,
is_flag = True ,
default = False ,
help = " Bypass input confirmations. " ,
)
@click.option (
" --skip-prep " ,
is_flag = True ,
default = False ,
help = " Skip the image preparation step. " ,
)
@click.option (
" --skip-class-img-gen " ,
is_flag = True ,
default = False ,
help = " Skip the class image generation step. " ,
)
@aimg.command ( )
def train_concept (
concept_label ,
concept_images_dir ,
class_label ,
class_images_dir ,
n_class_images ,
model ,
is_person ,
preconfirmed ,
skip_prep ,
skip_class_img_gen ,
) :
"""
Teach the model a new concept ( a person , thing , style , etc ) .
Provided a directory of concept images , a concept token , and a class token , this command will train the model
to generate images of that concept .
\b
This happens in a 3 - step process :
1. Cropping and resizing your training images . If - - person is set we crop to include the face .
2. Generating a set of class images to train on . This helps prevent overfitting .
3. Training the model on the concept and class images .
The output of this command is a new model weights file that you can use with the - - model option .
\b
## Instructions
1. Gather a set of images of the concept you want to train on . They should show the subject from a variety of angles
and in a variety of situations .
2. Train the model .
- Concept label : For a person , firstnamelastname should be fine .
- If all the training images are photos you should add " a photo of " to the beginning of the concept label .
- Class label : This is the category of the things beings trained on . For people this is typically " person " , " man "
or " woman " .
- If all the training images are photos you should add " a photo of " to the beginning of the class label .
- CLass images will be generated for you if you do not provide them .
3. Stop training before it overfits . I haven ' t figured this out yet.
For example , if you were training on photos of a man named bill hamilton you could run the following :
\b
aimg train - concept \\
- - person \\
- - concept - label " photo of billhamilton man " \\
- - concept - images - dir . / images / billhamilton \\
- - class - label " photo of a man " \\
- - class - images - dir . / images / man
When you use the model you should prompt with ` firstnamelastname classname ` ( e . g . ` billhamilton man ` ) .
You can find a lot of relevant instructions here : https : / / github . com / JoePenna / Dreambooth - Stable - Diffusion
"""
2023-02-05 00:08:05 +00:00
from imaginairy . utils import get_device
if " mps " in get_device ( ) :
click . secho (
" ⚠️ MPS (MacOS) is not supported for training. Please use a GPU or CPU. " ,
fg = " yellow " ,
)
return
2023-02-03 05:43:04 +00:00
import os . path
from imaginairy . train import train_diffusion_model
from imaginairy . training_tools . image_prep import (
create_class_images ,
get_image_filenames ,
prep_images ,
)
2023-01-01 22:54:49 +00:00
target_size = 512
# Step 1. Crop and enhance the training images
prepped_images_path = os . path . join ( concept_images_dir , " prepped-images " )
image_filenames = get_image_filenames ( concept_images_dir )
click . secho (
f ' \n 🤖🧠 Training " { concept_label } " based on { len ( image_filenames ) } images. \n '
)
if not skip_prep :
msg = (
f " Creating cropped copies of the { len ( image_filenames ) } concept images \n "
f " Is Person: { is_person } \n "
f " Source: { concept_images_dir } \n "
f " Dest: { prepped_images_path } \n "
)
logger . info ( msg )
if not is_person :
click . secho ( " ⚠️ the --person flag was not set. " , fg = " yellow " )
if not preconfirmed and not click . confirm ( " Continue? " ) :
return
prep_images (
images_dir = concept_images_dir , is_person = is_person , target_size = target_size
)
concept_images_dir = prepped_images_path
if not skip_class_img_gen :
# Step 2. Generate class images
class_image_filenames = get_image_filenames ( class_images_dir )
images_needed = max ( n_class_images - len ( class_image_filenames ) , 0 )
logger . info ( f " Generating { n_class_images } class images in { class_images_dir } " )
logger . info (
f " { len ( class_image_filenames ) } existing class images found so only generating { images_needed } . "
)
if not preconfirmed and not click . confirm ( " Continue? " ) :
return
create_class_images (
class_description = class_label ,
output_folder = class_images_dir ,
num_images = n_class_images ,
)
logger . info ( " Training the model... " )
if not preconfirmed and not click . confirm ( " Continue? " ) :
return
# Step 3. Train the model
train_diffusion_model (
concept_label = concept_label ,
concept_images_dir = concept_images_dir ,
class_label = class_label ,
class_images_dir = class_images_dir ,
weights_location = model ,
logdir = " logs " ,
learning_rate = 1e-6 ,
accumulate_grad_batches = 32 ,
)
@click.argument (
" images_dir " ,
required = True ,
)
@click.option (
" --person " ,
" is_person " ,
is_flag = True ,
help = " Set if images are of a person. Will use face detection and enhancement. " ,
)
@click.option (
" --target-size " ,
default = 512 ,
type = int ,
show_default = True ,
)
@aimg.command ( " prep-images " )
def prepare_images ( images_dir , is_person , target_size ) :
"""
Prepare a folder of images for training .
Prepped images will be written to the ` prepped - images ` subfolder .
All images will be cropped and resized to ( default ) 512 x512 .
Upscaling and face enhancement will be applied as needed to smaller images .
Examples :
aimg prep - images - - person . / images / selfies
aimg prep - images . / images / toy - train
"""
2023-02-03 05:43:04 +00:00
from imaginairy . training_tools . image_prep import prep_images
2023-01-01 22:54:49 +00:00
prep_images ( images_dir = images_dir , is_person = is_person , target_size = target_size )
2023-02-23 09:28:40 +00:00
@click.option (
" --target-size " ,
default = 512 ,
type = int ,
show_default = True ,
)
@aimg.command ( " prep-images " )
def colorize_image_cmd ( images_dir , is_person , target_size ) :
from imaginairy . training_tools . image_prep import prep_images
prep_images ( images_dir = images_dir , is_person = is_person , target_size = target_size )
2023-01-01 22:54:49 +00:00
@click.argument ( " ckpt_paths " , nargs = - 1 )
@aimg.command ( " prune-ckpt " )
def prune_ckpt ( ckpt_paths ) :
"""
Prune a checkpoint file .
This will remove the optimizer state from the checkpoint file .
This is useful if you want to use the checkpoint file for inference and save a lot of disk space
Example :
aimg prune - ckpt . / path / to / checkpoint . ckpt
"""
2023-02-03 05:43:04 +00:00
from imaginairy . training_tools . prune_model import prune_diffusion_ckpt
2023-01-01 22:54:49 +00:00
click . secho ( " Pruning checkpoint files... " )
for p in ckpt_paths :
prune_diffusion_ckpt ( p )
2023-01-27 05:41:52 +00:00
@aimg.command ( " system-info " )
def system_info ( ) :
"""
Display system information . Submit this when reporting bugs .
"""
2023-02-03 05:43:04 +00:00
from imaginairy . debug_info import get_debug_info
2023-01-27 05:41:52 +00:00
for k , v in get_debug_info ( ) . items ( ) :
k + = " : "
click . secho ( f " { k : <30 } { v } " )
2022-09-24 08:56:19 +00:00
aimg . add_command ( imagine_cmd , name = " imagine " )
2022-09-20 04:15:38 +00:00
2022-09-09 05:22:55 +00:00
if __name__ == " __main__ " :
2022-09-11 20:56:41 +00:00
imagine_cmd ( ) # noqa
2022-12-18 08:00:38 +00:00
# from cProfile import Profile
# from pyprof2calltree import convert, visualize
# profiler = Profile()
# profiler.runctx("imagine_cmd.main(standalone_mode=False)", locals(), globals())
# convert(profiler.getstats(), 'imagine.kgrind')
# visualize(profiler.getstats())