369 lines
12 KiB
Python
369 lines
12 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
from string import Template
|
|
from pathlib import Path
|
|
import math
|
|
|
|
import gradio as gr
|
|
|
|
import modules.scripts as scripts
|
|
from modules.processing import process_images, fix_seed, Processed
|
|
from modules.shared import opts, OptionInfo
|
|
from modules.devices import get_optimal_device
|
|
|
|
from dynamicprompts.wildcardmanager import WildcardManager
|
|
from prompts.uicreation import UiCreation
|
|
|
|
|
|
from dynamicprompts.generators.promptgenerator import GeneratorException
|
|
from ui import constants
|
|
from prompts.utils import slugify, get_unique_path
|
|
from prompts import prompt_writer
|
|
from prompts.generator_builder import GeneratorBuilder
|
|
|
|
from ui import wildcards_tab, save_params
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
logger.setLevel(logging.INFO)
|
|
|
|
is_debug = getattr(opts, "is_debug", False)
|
|
|
|
if is_debug:
|
|
logger.setLevel(logging.DEBUG)
|
|
|
|
base_dir = Path(scripts.basedir())
|
|
|
|
wildcard_dir = getattr(opts, "wildcard_dir", None)
|
|
|
|
|
|
if wildcard_dir is None:
|
|
WILDCARD_DIR = base_dir / "wildcards"
|
|
else:
|
|
WILDCARD_DIR = Path(wildcard_dir)
|
|
|
|
VERSION = "2.2.3"
|
|
|
|
|
|
wildcard_manager = WildcardManager(WILDCARD_DIR)
|
|
wildcards_tab.initialize(wildcard_manager)
|
|
save_params.initialize()
|
|
|
|
device = 0 if get_optimal_device() == "cuda" else -1
|
|
|
|
generator_builder = GeneratorBuilder(wildcard_manager)
|
|
|
|
|
|
class Script(scripts.Script):
|
|
|
|
def title(self):
|
|
return f"Dynamic Prompts v{VERSION}"
|
|
|
|
def show(self, is_img2img):
|
|
return scripts.AlwaysVisible
|
|
|
|
def ui(self, is_img2img):
|
|
ui_creation = UiCreation(wildcard_manager)
|
|
wildcard_html = ui_creation.probe()
|
|
|
|
html_path = base_dir / "helptext.html"
|
|
html = html_path.open().read()
|
|
html = Template(html).substitute(
|
|
wildcard_html=wildcard_html, WILDCARD_DIR=WILDCARD_DIR
|
|
)
|
|
|
|
jinja_html_path = base_dir / "jinja_help.html"
|
|
jinja_help = jinja_html_path.open().read()
|
|
|
|
with gr.Group(elem_id="dynamic-prompting"):
|
|
with gr.Accordion("Dynamic Prompts", open=False):
|
|
is_enabled = gr.Checkbox(
|
|
label="Dynamic Prompts enabled",
|
|
value=True,
|
|
elem_id="dynamic-prompts-enabled",
|
|
)
|
|
|
|
with gr.Group():
|
|
is_combinatorial = gr.Checkbox(
|
|
label="Combinatorial generation",
|
|
value=False,
|
|
elem_id="is-combinatorial",
|
|
)
|
|
combinatorial_batches = gr.Slider(
|
|
label="Combinatorial batches",
|
|
min=1,
|
|
max=10,
|
|
step=1,
|
|
value=1,
|
|
elem_id="combinatorial-times",
|
|
)
|
|
|
|
with gr.Box():
|
|
with gr.Group():
|
|
is_magic_prompt = gr.Checkbox(
|
|
label="Magic prompt", value=False, elem_id="is-magicprompt"
|
|
)
|
|
magic_prompt_length = gr.Slider(
|
|
label="Max magic prompt length",
|
|
value=100,
|
|
minimum=30,
|
|
maximum=300,
|
|
step=10,
|
|
)
|
|
magic_temp_value = gr.Slider(
|
|
label="Magic prompt creativity",
|
|
value=0.7,
|
|
minimum=0.1,
|
|
maximum=3.0,
|
|
step=0.10,
|
|
)
|
|
|
|
is_feeling_lucky = gr.Checkbox(
|
|
label="I'm feeling lucky",
|
|
value=False,
|
|
elem_id="is-feelinglucky",
|
|
)
|
|
|
|
is_attention_grabber = gr.Checkbox(
|
|
label="Attention grabber",
|
|
value=False,
|
|
elem_id="is-attention-grabber",
|
|
)
|
|
|
|
min_attention = gr.Slider(
|
|
label="Minimum attention",
|
|
value=1.1,
|
|
minimum=-1,
|
|
maximum=2,
|
|
step=0.1,
|
|
)
|
|
|
|
max_attention = gr.Slider(
|
|
label="Maximum attention",
|
|
value=1.5,
|
|
minimum=-1,
|
|
maximum=2,
|
|
step=0.1,
|
|
)
|
|
|
|
write_prompts = gr.Checkbox(
|
|
label="Write prompts to file", value=False, elem_id="write-prompts"
|
|
)
|
|
|
|
no_image_generation = gr.Checkbox(
|
|
label="Don't generate images",
|
|
value=False,
|
|
elem_id="no-image-generation",
|
|
)
|
|
|
|
with gr.Accordion("Help", open=False):
|
|
info = gr.HTML(html)
|
|
|
|
with gr.Group():
|
|
with gr.Accordion("Jinja2 templates", open=False):
|
|
enable_jinja_templates = gr.Checkbox(
|
|
label="Enable Jinja2 templates",
|
|
value=False,
|
|
elem_id="enable-jinja-templates",
|
|
)
|
|
|
|
with gr.Accordion("Help for Jinja2 templates", open=False):
|
|
jinja_info = gr.HTML(jinja_help)
|
|
|
|
with gr.Group():
|
|
with gr.Accordion("Advanced options", open=False):
|
|
unlink_seed_from_prompt = gr.Checkbox(
|
|
label="Unlink seed from prompt",
|
|
value=False,
|
|
elem_id="unlink-seed-from-prompt",
|
|
)
|
|
|
|
disable_negative_prompt = gr.Checkbox(
|
|
label="Disable negative prompt",
|
|
value=False,
|
|
elem_id="disable-negative-prompt",
|
|
)
|
|
|
|
use_fixed_seed = gr.Checkbox(
|
|
label="Fixed seed", value=False, elem_id="is-fixed-seed"
|
|
)
|
|
|
|
ignore_whitespace = gr.Checkbox(
|
|
label="Ignore whitespace",
|
|
value=False,
|
|
elem_id="ignore_whitespace",
|
|
)
|
|
|
|
write_raw_template = gr.Checkbox(
|
|
label="Write raw prompt to image",
|
|
value=False,
|
|
elem_id="write-raw-template",
|
|
)
|
|
|
|
return [
|
|
info,
|
|
is_enabled,
|
|
is_combinatorial,
|
|
combinatorial_batches,
|
|
is_magic_prompt,
|
|
is_feeling_lucky,
|
|
is_attention_grabber,
|
|
min_attention,
|
|
max_attention,
|
|
magic_prompt_length,
|
|
magic_temp_value,
|
|
use_fixed_seed,
|
|
write_prompts,
|
|
unlink_seed_from_prompt,
|
|
disable_negative_prompt,
|
|
enable_jinja_templates,
|
|
no_image_generation,
|
|
ignore_whitespace,
|
|
write_raw_template
|
|
]
|
|
|
|
def process(
|
|
self,
|
|
p,
|
|
info,
|
|
is_enabled,
|
|
is_combinatorial,
|
|
combinatorial_batches,
|
|
is_magic_prompt,
|
|
is_feeling_lucky,
|
|
is_attention_grabber,
|
|
min_attention,
|
|
max_attention,
|
|
magic_prompt_length,
|
|
magic_temp_value,
|
|
use_fixed_seed,
|
|
write_prompts,
|
|
unlink_seed_from_prompt,
|
|
disable_negative_prompt,
|
|
enable_jinja_templates,
|
|
no_image_generation,
|
|
ignore_whitespace,
|
|
write_raw_template
|
|
):
|
|
|
|
if not is_enabled:
|
|
logger.debug("Dynamic prompts disabled - exiting")
|
|
return p
|
|
|
|
self._p = p
|
|
context = p
|
|
|
|
option = OptionInfo(write_raw_template, "Write raw template to image")
|
|
opts.add_option(constants.OPTION_WRITE_RAW_TEMPLATE, option)
|
|
|
|
fix_seed(p)
|
|
|
|
original_prompt = p.all_prompts[0] if len(p.all_prompts) > 0 else p.prompt
|
|
original_negative_prompt = (
|
|
p.all_negative_prompts[0]
|
|
if len(p.all_negative_prompts) > 0
|
|
else p.negative_prompt
|
|
)
|
|
|
|
original_seed = p.seed
|
|
num_images = p.n_iter * p.batch_size
|
|
|
|
try:
|
|
combinatorial_batches = int(combinatorial_batches)
|
|
if combinatorial_batches < 1:
|
|
combinatorial_batches = 1
|
|
except (ValueError, TypeError):
|
|
combinatorial_batches = 1
|
|
|
|
try:
|
|
logger.debug("Creating generator")
|
|
generator_builder = (
|
|
GeneratorBuilder(wildcard_manager, ignore_whitespace=ignore_whitespace)
|
|
.set_is_feeling_lucky(is_feeling_lucky)
|
|
.set_is_attention_grabber(
|
|
is_attention_grabber, min_attention, max_attention
|
|
)
|
|
.set_is_jinja_template(enable_jinja_templates)
|
|
.set_is_combinatorial(is_combinatorial, combinatorial_batches)
|
|
.set_is_magic_prompt(
|
|
is_magic_prompt, magic_prompt_length, magic_temp_value
|
|
)
|
|
.set_is_dummy(False)
|
|
)
|
|
|
|
generator = generator_builder.create_generator(
|
|
original_seed, context, unlink_seed_from_prompt
|
|
)
|
|
|
|
all_prompts = generator.generate(original_prompt, num_images)
|
|
total_prompts = len(all_prompts)
|
|
|
|
if disable_negative_prompt:
|
|
all_negative_prompts = [""]
|
|
else:
|
|
all_negative_prompts = generator.generate(
|
|
original_negative_prompt, num_images
|
|
)
|
|
|
|
if len(all_negative_prompts) < total_prompts:
|
|
all_negative_prompts = all_negative_prompts * (
|
|
total_prompts // len(all_negative_prompts) + 1
|
|
)
|
|
|
|
all_negative_prompts = all_negative_prompts[:total_prompts]
|
|
|
|
except GeneratorException as e:
|
|
logger.exception(e)
|
|
all_prompts = [str(e)]
|
|
all_negative_prompts = [str(e)]
|
|
|
|
updated_count = len(all_prompts)
|
|
p.n_iter = math.ceil(updated_count / p.batch_size)
|
|
|
|
if use_fixed_seed:
|
|
all_seeds = [original_seed] * updated_count
|
|
else:
|
|
all_seeds = [
|
|
int(p.seed) + (x if p.subseed_strength == 0 else 0)
|
|
for x in range(updated_count)
|
|
]
|
|
|
|
logger.info(
|
|
f"Prompt matrix will create {updated_count} images in a total of {p.n_iter} batches."
|
|
)
|
|
|
|
try:
|
|
|
|
if write_prompts:
|
|
prompt_filename = get_unique_path(
|
|
Path(p.outpath_samples), slugify(original_prompt), suffix="csv"
|
|
)
|
|
prompt_writer.write_prompts(
|
|
prompt_filename,
|
|
original_prompt,
|
|
original_negative_prompt,
|
|
all_prompts,
|
|
all_negative_prompts,
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Failed to write prompts to file: {e}")
|
|
|
|
p.all_prompts = all_prompts
|
|
p.all_negative_prompts = all_negative_prompts
|
|
if no_image_generation:
|
|
logger.debug("No image generation requested - exiting")
|
|
# Need a minimum of batch size images to avoid errors
|
|
p.batch_size = 1
|
|
p.all_prompts = all_prompts[0:1]
|
|
|
|
p.all_seeds = all_seeds
|
|
|
|
p.prompt_for_display = original_prompt
|
|
|
|
p.prompt = original_prompt
|
|
p.seed = original_seed
|
|
|
|
|
|
wildcard_manager.ensure_directory()
|