Skip to content

Commit 8323169

Browse files
authored
Dynamic prompt generation script for parameter scans (#2831)
# Programatically generate a large number of images varying by prompt and other image generation parameters This is a little standalone script named `dynamic_prompting.py` that enables the generation of dynamic prompts. Using YAML syntax, you specify a template of prompt phrases and lists of generation parameters, and the script will generate a cross product of prompts and generation settings for you. You can save these prompts to disk for later use, or pipe them to the invokeai CLI to generate the images on the fly. Typical uses are testing step and CFG values systematically while holding the seed and prompt constant, testing out various artist's styles, and comparing the results of the same prompt across different models. A typical template will look like this: ``` model: stable-diffusion-1.5 steps: 30;50;10 seed: 50 dimensions: 512x512 cfg: - 7 - 12 sampler: - k_euler_a - k_lms prompt: style: - greg rutkowski - gustav klimt location: - the mountains - a desert object: - luxurious dwelling - crude tent template: a {object} in {location}, in the style of {style} ``` This will generate 96 different images, each of which varies by one of the dimensions specified in the template. For example, the prompt axis will generate a cross product list like: ``` a luxurious dwelling in the mountains, in the style of greg rutkowski a luxurious dwelling in the mountains, in the style of gustav klimt a luxious dwelling in a desert, in the style of greg rutkowski ... etc ``` A typical usage would be: ``` python scripts/dynamic_prompts.py --invoke --outdir=/tmp/scanning my_template.yaml ``` This will populate `/tmp/scanning` with each of the requested images, and also generate a `log.md` file which you can open with an e-book reader to show something like this: ![image](https://user-images.githubusercontent.com/111189/221970165-4bbd9070-3f32-4d89-8ff2-b03a82ada575.png) Full instructions can be obtained using the `--instructions` switch, and an example template can be printed out using `--example`: ``` python scripts/dynamic_prompts.py --instructions python scripts/dynamic_prompts.py --example > my_first_template.yaml ```
2 parents c9db01e + bf5cd1b commit 8323169

File tree

8 files changed

+588
-21
lines changed

8 files changed

+588
-21
lines changed

invokeai/frontend/stats.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

ldm/generate.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ def __init__(
200200
# it wasn't actually doing anything. This logic could be reinstated.
201201
self.device = torch.device(choose_torch_device())
202202
print(f">> Using device_type {self.device.type}")
203+
if self.device.type == 'cuda':
204+
print(f">> CUDA device '{torch.cuda.get_device_name(torch.cuda.current_device())}' (GPU {os.environ.get('CUDA_VISIBLE_DEVICES') or 0})")
203205
if full_precision:
204206
if self.precision != "auto":
205207
raise ValueError("Remove --full_precision / -F if using --precision")

ldm/invoke/CLI.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ def image_writer(
389389
prior_variations,
390390
postprocessed,
391391
first_seed,
392+
gen.model_name,
392393
)
393394
path = file_writer.save_image_and_prompt_to_png(
394395
image=image,
@@ -402,6 +403,7 @@ def image_writer(
402403
else first_seed
403404
],
404405
model_hash=gen.model_hash,
406+
model_id=gen.model_name,
405407
),
406408
name=filename,
407409
compress_level=opt.png_compression,
@@ -941,21 +943,24 @@ def add_postprocessing_to_metadata(opt, original_file, new_file, tool, command):
941943

942944

943945
def prepare_image_metadata(
944-
opt,
945-
prefix,
946-
seed,
947-
operation="generate",
948-
prior_variations=[],
949-
postprocessed=False,
950-
first_seed=None,
946+
opt,
947+
prefix,
948+
seed,
949+
operation="generate",
950+
prior_variations=[],
951+
postprocessed=False,
952+
first_seed=None,
953+
model_id='unknown',
951954
):
952955
if postprocessed and opt.save_original:
953956
filename = choose_postprocess_name(opt, prefix, seed)
954957
else:
955958
wildcards = dict(opt.__dict__)
956959
wildcards["prefix"] = prefix
957960
wildcards["seed"] = seed
961+
wildcards["model_id"] = model_id
958962
try:
963+
print(f'DEBUG: fnformat={opt.fnformat}')
959964
filename = opt.fnformat.format(**wildcards)
960965
except KeyError as e:
961966
print(
@@ -972,18 +977,17 @@ def prepare_image_metadata(
972977
first_seed = first_seed or seed
973978
this_variation = [[seed, opt.variation_amount]]
974979
opt.with_variations = prior_variations + this_variation
975-
formatted_dream_prompt = opt.dream_prompt_str(seed=first_seed)
980+
formatted_dream_prompt = opt.dream_prompt_str(seed=first_seed,model_id=model_id)
976981
elif len(prior_variations) > 0:
977-
formatted_dream_prompt = opt.dream_prompt_str(seed=first_seed)
982+
formatted_dream_prompt = opt.dream_prompt_str(seed=first_seed,model_id=model_id)
978983
elif operation == "postprocess":
979984
formatted_dream_prompt = "!fix " + opt.dream_prompt_str(
980-
seed=seed, prompt=opt.input_file_path
985+
seed=seed, prompt=opt.input_file_path, model_id=model_id,
981986
)
982987
else:
983-
formatted_dream_prompt = opt.dream_prompt_str(seed=seed)
988+
formatted_dream_prompt = opt.dream_prompt_str(seed=seed,model_id=model_id)
984989
return filename, formatted_dream_prompt
985990

986-
987991
def choose_postprocess_name(opt, prefix, seed) -> str:
988992
match = re.search("postprocess:(\w+)", opt.last_operation)
989993
if match:

ldm/invoke/args.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ def dream_prompt_str(self,**kwargs):
333333
switches.append(f'-V {formatted_variations}')
334334
if 'variations' in a and len(a['variations'])>0:
335335
switches.append(f'-V {a["variations"]}')
336-
return ' '.join(switches)
336+
return ' '.join(switches) + f' # model_id={kwargs.get("model_id","unknown model")}'
337337

338338
def __getattribute__(self,name):
339339
'''
@@ -878,7 +878,7 @@ def _create_dream_cmd_parser(self):
878878
)
879879
render_group.add_argument(
880880
'--fnformat',
881-
default='{prefix}.{seed}.png',
881+
default=None,
882882
type=str,
883883
help='Overwrite the filename format. You can use any argument as wildcard enclosed in curly braces. Default is {prefix}.{seed}.png',
884884
)
@@ -1155,6 +1155,7 @@ def format_metadata(**kwargs):
11551155
def metadata_dumps(opt,
11561156
seeds=[],
11571157
model_hash=None,
1158+
model_id=None,
11581159
postprocessing=None):
11591160
'''
11601161
Given an Args object, returns a dict containing the keys and
@@ -1167,7 +1168,7 @@ def metadata_dumps(opt,
11671168
# top-level metadata minus `image` or `images`
11681169
metadata = {
11691170
'model' : 'stable diffusion',
1170-
'model_id' : opt.model,
1171+
'model_id' : model_id or opt.model,
11711172
'model_hash' : model_hash,
11721173
'app_id' : ldm.invoke.__app_id__,
11731174
'app_version' : ldm.invoke.__version__,

0 commit comments

Comments
 (0)