d64b451681
added eta parameter to parameters output for generated images split eta settings into ancestral and ddim (because they have different default values)
264 lines
9.6 KiB
Python
264 lines
9.6 KiB
Python
from collections import namedtuple
|
|
from copy import copy
|
|
import random
|
|
|
|
from PIL import Image
|
|
import numpy as np
|
|
|
|
import modules.scripts as scripts
|
|
import gradio as gr
|
|
|
|
from modules import images
|
|
from modules.processing import process_images, Processed
|
|
from modules.shared import opts, cmd_opts, state
|
|
import modules.shared as shared
|
|
import modules.sd_samplers
|
|
import modules.sd_models
|
|
import re
|
|
|
|
|
|
def apply_field(field):
|
|
def fun(p, x, xs):
|
|
setattr(p, field, x)
|
|
|
|
return fun
|
|
|
|
|
|
def apply_prompt(p, x, xs):
|
|
p.prompt = p.prompt.replace(xs[0], x)
|
|
p.negative_prompt = p.negative_prompt.replace(xs[0], x)
|
|
|
|
|
|
samplers_dict = {}
|
|
for i, sampler in enumerate(modules.sd_samplers.samplers):
|
|
samplers_dict[sampler.name.lower()] = i
|
|
for alias in sampler.aliases:
|
|
samplers_dict[alias.lower()] = i
|
|
|
|
|
|
def apply_sampler(p, x, xs):
|
|
sampler_index = samplers_dict.get(x.lower(), None)
|
|
if sampler_index is None:
|
|
raise RuntimeError(f"Unknown sampler: {x}")
|
|
|
|
p.sampler_index = sampler_index
|
|
|
|
|
|
def apply_checkpoint(p, x, xs):
|
|
applicable = [info for info in modules.sd_models.checkpoints_list.values() if x in info.title]
|
|
assert len(applicable) > 0, f'Checkpoint {x} for found'
|
|
|
|
info = applicable[0]
|
|
|
|
modules.sd_models.reload_model_weights(shared.sd_model, info)
|
|
|
|
|
|
def format_value_add_label(p, opt, x):
|
|
if type(x) == float:
|
|
x = round(x, 8)
|
|
|
|
return f"{opt.label}: {x}"
|
|
|
|
|
|
def format_value(p, opt, x):
|
|
if type(x) == float:
|
|
x = round(x, 8)
|
|
|
|
return x
|
|
|
|
def do_nothing(p, x, xs):
|
|
pass
|
|
|
|
def format_nothing(p, opt, x):
|
|
return ""
|
|
|
|
|
|
AxisOption = namedtuple("AxisOption", ["label", "type", "apply", "format_value"])
|
|
AxisOptionImg2Img = namedtuple("AxisOptionImg2Img", ["label", "type", "apply", "format_value"])
|
|
|
|
|
|
axis_options = [
|
|
AxisOption("Nothing", str, do_nothing, format_nothing),
|
|
AxisOption("Seed", int, apply_field("seed"), format_value_add_label),
|
|
AxisOption("Var. seed", int, apply_field("subseed"), format_value_add_label),
|
|
AxisOption("Var. strength", float, apply_field("subseed_strength"), format_value_add_label),
|
|
AxisOption("Steps", int, apply_field("steps"), format_value_add_label),
|
|
AxisOption("CFG Scale", float, apply_field("cfg_scale"), format_value_add_label),
|
|
AxisOption("Prompt S/R", str, apply_prompt, format_value),
|
|
AxisOption("Sampler", str, apply_sampler, format_value),
|
|
AxisOption("Checkpoint name", str, apply_checkpoint, format_value),
|
|
AxisOption("Sigma Churn", float, apply_field("s_churn"), format_value_add_label),
|
|
AxisOption("Sigma min", float, apply_field("s_tmin"), format_value_add_label),
|
|
AxisOption("Sigma max", float, apply_field("s_tmax"), format_value_add_label),
|
|
AxisOption("Sigma noise", float, apply_field("s_noise"), format_value_add_label),
|
|
AxisOption("Eta", float, apply_field("eta"), format_value_add_label),
|
|
AxisOptionImg2Img("Denoising", float, apply_field("denoising_strength"), format_value_add_label), # as it is now all AxisOptionImg2Img items must go after AxisOption ones
|
|
]
|
|
|
|
|
|
def draw_xy_grid(p, xs, ys, x_labels, y_labels, cell, draw_legend):
|
|
res = []
|
|
|
|
ver_texts = [[images.GridAnnotation(y)] for y in y_labels]
|
|
hor_texts = [[images.GridAnnotation(x)] for x in x_labels]
|
|
|
|
first_pocessed = None
|
|
|
|
state.job_count = len(xs) * len(ys) * p.n_iter
|
|
|
|
for iy, y in enumerate(ys):
|
|
for ix, x in enumerate(xs):
|
|
state.job = f"{ix + iy * len(xs) + 1} out of {len(xs) * len(ys)}"
|
|
|
|
processed = cell(x, y)
|
|
if first_pocessed is None:
|
|
first_pocessed = processed
|
|
|
|
try:
|
|
res.append(processed.images[0])
|
|
except:
|
|
res.append(Image.new(res[0].mode, res[0].size))
|
|
|
|
grid = images.image_grid(res, rows=len(ys))
|
|
if draw_legend:
|
|
grid = images.draw_grid_annotations(grid, res[0].width, res[0].height, hor_texts, ver_texts)
|
|
|
|
first_pocessed.images = [grid]
|
|
|
|
return first_pocessed
|
|
|
|
|
|
re_range = re.compile(r"\s*([+-]?\s*\d+)\s*-\s*([+-]?\s*\d+)(?:\s*\(([+-]\d+)\s*\))?\s*")
|
|
re_range_float = re.compile(r"\s*([+-]?\s*\d+(?:.\d*)?)\s*-\s*([+-]?\s*\d+(?:.\d*)?)(?:\s*\(([+-]\d+(?:.\d*)?)\s*\))?\s*")
|
|
|
|
re_range_count = re.compile(r"\s*([+-]?\s*\d+)\s*-\s*([+-]?\s*\d+)(?:\s*\[(\d+)\s*\])?\s*")
|
|
re_range_count_float = re.compile(r"\s*([+-]?\s*\d+(?:.\d*)?)\s*-\s*([+-]?\s*\d+(?:.\d*)?)(?:\s*\[(\d+(?:.\d*)?)\s*\])?\s*")
|
|
|
|
class Script(scripts.Script):
|
|
def title(self):
|
|
return "X/Y plot"
|
|
|
|
def ui(self, is_img2img):
|
|
current_axis_options = [x for x in axis_options if type(x) == AxisOption or type(x) == AxisOptionImg2Img and is_img2img]
|
|
|
|
with gr.Row():
|
|
x_type = gr.Dropdown(label="X type", choices=[x.label for x in current_axis_options], value=current_axis_options[1].label, visible=False, type="index", elem_id="x_type")
|
|
x_values = gr.Textbox(label="X values", visible=False, lines=1)
|
|
|
|
with gr.Row():
|
|
y_type = gr.Dropdown(label="Y type", choices=[x.label for x in current_axis_options], value=current_axis_options[4].label, visible=False, type="index", elem_id="y_type")
|
|
y_values = gr.Textbox(label="Y values", visible=False, lines=1)
|
|
|
|
draw_legend = gr.Checkbox(label='Draw legend', value=True)
|
|
no_fixed_seeds = gr.Checkbox(label='Keep -1 for seeds', value=False)
|
|
|
|
return [x_type, x_values, y_type, y_values, draw_legend, no_fixed_seeds]
|
|
|
|
def run(self, p, x_type, x_values, y_type, y_values, draw_legend, no_fixed_seeds):
|
|
modules.processing.fix_seed(p)
|
|
p.batch_size = 1
|
|
|
|
def process_axis(opt, vals):
|
|
valslist = [x.strip() for x in vals.split(",")]
|
|
|
|
if opt.type == int:
|
|
valslist_ext = []
|
|
|
|
for val in valslist:
|
|
m = re_range.fullmatch(val)
|
|
mc = re_range_count.fullmatch(val)
|
|
if m is not None:
|
|
|
|
start = int(m.group(1))
|
|
end = int(m.group(2))+1
|
|
step = int(m.group(3)) if m.group(3) is not None else 1
|
|
|
|
valslist_ext += list(range(start, end, step))
|
|
elif mc is not None:
|
|
start = int(mc.group(1))
|
|
end = int(mc.group(2))
|
|
num = int(mc.group(3)) if mc.group(3) is not None else 1
|
|
|
|
valslist_ext += [int(x) for x in np.linspace(start=start, stop=end, num=num).tolist()]
|
|
else:
|
|
valslist_ext.append(val)
|
|
|
|
valslist = valslist_ext
|
|
elif opt.type == float:
|
|
valslist_ext = []
|
|
|
|
for val in valslist:
|
|
m = re_range_float.fullmatch(val)
|
|
mc = re_range_count_float.fullmatch(val)
|
|
if m is not None:
|
|
start = float(m.group(1))
|
|
end = float(m.group(2))
|
|
step = float(m.group(3)) if m.group(3) is not None else 1
|
|
|
|
valslist_ext += np.arange(start, end + step, step).tolist()
|
|
elif mc is not None:
|
|
start = float(mc.group(1))
|
|
end = float(mc.group(2))
|
|
num = int(mc.group(3)) if mc.group(3) is not None else 1
|
|
|
|
valslist_ext += np.linspace(start=start, stop=end, num=num).tolist()
|
|
else:
|
|
valslist_ext.append(val)
|
|
|
|
valslist = valslist_ext
|
|
|
|
valslist = [opt.type(x) for x in valslist]
|
|
|
|
return valslist
|
|
|
|
x_opt = axis_options[x_type]
|
|
xs = process_axis(x_opt, x_values)
|
|
|
|
y_opt = axis_options[y_type]
|
|
ys = process_axis(y_opt, y_values)
|
|
|
|
def fix_axis_seeds(axis_opt, axis_list):
|
|
if axis_opt.label == 'Seed':
|
|
return [int(random.randrange(4294967294)) if val is None or val == '' or val == -1 else val for val in axis_list]
|
|
else:
|
|
return axis_list
|
|
|
|
if not no_fixed_seeds:
|
|
xs = fix_axis_seeds(x_opt, xs)
|
|
ys = fix_axis_seeds(y_opt, ys)
|
|
|
|
if x_opt.label == 'Steps':
|
|
total_steps = sum(xs) * len(ys)
|
|
elif y_opt.label == 'Steps':
|
|
total_steps = sum(ys) * len(xs)
|
|
else:
|
|
total_steps = p.steps * len(xs) * len(ys)
|
|
|
|
print(f"X/Y plot will create {len(xs) * len(ys) * p.n_iter} images on a {len(xs)}x{len(ys)} grid. (Total steps to process: {total_steps * p.n_iter})")
|
|
shared.total_tqdm.updateTotal(total_steps * p.n_iter)
|
|
|
|
def cell(x, y):
|
|
pc = copy(p)
|
|
x_opt.apply(pc, x, xs)
|
|
y_opt.apply(pc, y, ys)
|
|
|
|
return process_images(pc)
|
|
|
|
processed = draw_xy_grid(
|
|
p,
|
|
xs=xs,
|
|
ys=ys,
|
|
x_labels=[x_opt.format_value(p, x_opt, x) for x in xs],
|
|
y_labels=[y_opt.format_value(p, y_opt, y) for y in ys],
|
|
cell=cell,
|
|
draw_legend=draw_legend
|
|
)
|
|
|
|
if opts.grid_save:
|
|
images.save_image(processed.images[0], p.outpath_grids, "xy_grid", prompt=p.prompt, seed=processed.seed, grid=True, p=p)
|
|
|
|
# restore checkpoint in case it was changed by axes
|
|
modules.sd_models.reload_model_weights(shared.sd_model)
|
|
|
|
return processed
|