|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import re |
|
import numexpr |
|
|
|
def check_is_number(value): |
|
float_pattern = r'^(?=.)([+-]?([0-9]*)(\.([0-9]+))?)$' |
|
return re.match(float_pattern, value) |
|
|
|
def parse_weight(match, frame=0, max_frames=0) -> float: |
|
w_raw = match.group("weight") |
|
max_f = max_frames |
|
if w_raw is None: |
|
return 1 |
|
if check_is_number(w_raw): |
|
return float(w_raw) |
|
else: |
|
t = frame |
|
if len(w_raw) < 3: |
|
print('the value inside `-characters cannot represent a math function') |
|
return 1 |
|
return float(numexpr.evaluate(w_raw[1:-1])) |
|
|
|
def split_weighted_subprompts(text, frame=0, max_frames=0): |
|
""" |
|
splits the prompt based on deforum webui implementation, moved from generate.py |
|
""" |
|
math_parser = re.compile("(?P<weight>(`[\S\s]*?`))", re.VERBOSE) |
|
|
|
parsed_prompt = re.sub(math_parser, lambda m: str(parse_weight(m, frame)), text) |
|
|
|
negative_prompts = [] |
|
positive_prompts = [] |
|
|
|
prompt_split = parsed_prompt.split("--neg") |
|
if len(prompt_split) > 1: |
|
positive_prompts, negative_prompts = parsed_prompt.split("--neg") |
|
else: |
|
positive_prompts = prompt_split[0] |
|
negative_prompts = "" |
|
|
|
return positive_prompts, negative_prompts |
|
|
|
def interpolate_prompts(animation_prompts, max_frames): |
|
import numpy as np |
|
import pandas as pd |
|
|
|
max_f = max_frames |
|
parsed_animation_prompts = {} |
|
for key, value in animation_prompts.items(): |
|
if check_is_number(key): |
|
parsed_animation_prompts[key] = value |
|
else: |
|
parsed_animation_prompts[int(numexpr.evaluate(key))] = value |
|
|
|
sorted_prompts = sorted(parsed_animation_prompts.items(), key=lambda item: int(item[0])) |
|
|
|
|
|
prompt_series = pd.Series([np.nan for a in range(max_frames)]) |
|
|
|
|
|
for i in range(0, len(sorted_prompts) - 1): |
|
|
|
current_frame = int(sorted_prompts[i][0]) |
|
next_frame = int(sorted_prompts[i + 1][0]) |
|
|
|
|
|
|
|
if current_frame >= next_frame: |
|
print(f"WARNING: Sequential prompt keyframes {i}:{current_frame} and {i + 1}:{next_frame} are not monotonously increasing; skipping interpolation.") |
|
continue |
|
|
|
|
|
current_prompt = sorted_prompts[i][1] |
|
next_prompt = sorted_prompts[i + 1][1] |
|
current_positive, current_negative, *_ = current_prompt.split("--neg") + [None] |
|
next_positive, next_negative, *_ = next_prompt.split("--neg") + [None] |
|
|
|
weight_step = 1 / (next_frame - current_frame) |
|
|
|
|
|
|
|
|
|
for f in range(current_frame, next_frame): |
|
next_weight = weight_step * (f - current_frame) |
|
current_weight = 1 - next_weight |
|
|
|
|
|
prompt_series[f] = '' |
|
|
|
|
|
if current_positive: |
|
prompt_series[f] += f" ({current_positive}):{current_weight}" |
|
if current_positive and next_positive: |
|
prompt_series[f] += f" AND " |
|
if next_positive: |
|
prompt_series[f] += f" ({next_positive}):{next_weight}" |
|
|
|
|
|
if len(current_negative) > 1 or len(next_negative) > 1: |
|
prompt_series[f] += " --neg " |
|
if len(current_negative) > 1: |
|
prompt_series[f] += f" ({current_negative}):{current_weight}" |
|
if len(current_negative) > 1 and len(next_negative) > 1: |
|
prompt_series[f] += f" AND " |
|
if len(next_negative) > 1: |
|
prompt_series[f] += f" ({next_negative}):{next_weight}" |
|
|
|
|
|
|
|
|
|
for i, prompt in parsed_animation_prompts.items(): |
|
prompt_series[int(i)] = prompt |
|
if ' AND ' in prompt: |
|
print(f"WARNING: keyframe {i}'s prompt is using composable diffusion (aka the 'AND' keyword). This will cause unexpected behaviour with interpolation.") |
|
|
|
|
|
return prompt_series.ffill().bfill() |
|
|
|
def prepare_prompt(prompt_series, max_frames, seed, frame_idx): |
|
max_f = max_frames - 1 |
|
pattern = r'`.*?`' |
|
regex = re.compile(pattern) |
|
prompt_parsed = prompt_series |
|
for match in regex.finditer(prompt_parsed): |
|
matched_string = match.group(0) |
|
parsed_string = matched_string.replace('t', f'{frame_idx}').replace("max_f", f"{max_f}").replace('`', '') |
|
parsed_value = numexpr.evaluate(parsed_string) |
|
prompt_parsed = prompt_parsed.replace(matched_string, str(parsed_value)) |
|
|
|
prompt_to_print, *after_neg = prompt_parsed.strip().split("--neg") |
|
prompt_to_print = prompt_to_print.strip() |
|
after_neg = "".join(after_neg).strip() |
|
|
|
print(f"\033[32mSeed: \033[0m{seed}") |
|
print(f"\033[35mPrompt: \033[0m{prompt_to_print}") |
|
if after_neg and after_neg.strip(): |
|
print(f"\033[91mNeg Prompt: \033[0m{after_neg}") |
|
prompt_to_print += f"--neg {after_neg}" |
|
|
|
|
|
return prompt_to_print |
|
|