Skip to content

Commit f80423e

Browse files
authored
Merge pull request #886 from rewbs/allow-parseq-to-override-fps-cadence-max-frames
Allow Parseq users to use the FPS, cadence and max_frames specified in their Parseq doc (instead of having to duplicate them).
2 parents 72579ed + c933577 commit f80423e

File tree

10 files changed

+167
-35
lines changed

10 files changed

+167
-35
lines changed

scripts/default_settings.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@
143143
"hybrid_comp_save_extra_frames": false,
144144
"parseq_manifest": "",
145145
"parseq_use_deltas": true,
146+
"parseq_non_schedule_overrides": true,
146147
"use_looper": false,
147148
"init_images": "{\n \"0\": \"https://deforum.github.io/a1/Gi1.png\",\n \"max_f/4-5\": \"https://deforum.github.io/a1/Gi2.png\",\n \"max_f/2-10\": \"https://deforum.github.io/a1/Gi3.png\",\n \"3*max_f/4-15\": \"https://deforum.github.io/a1/Gi4.jpg\",\n \"max_f-20\": \"https://deforum.github.io/a1/Gi1.png\"\n}",
148149
"image_strength_schedule": "0:(0.75)",

scripts/deforum_helpers/args.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -967,16 +967,23 @@ def LoopArgs():
967967
def ParseqArgs():
968968
return {
969969
"parseq_manifest": {
970-
"label": "Parseq Manifest (JSON or URL)",
970+
"label": "Parseq manifest (JSON or URL)",
971971
"type": "textbox",
972972
"lines": 4,
973973
"value": None,
974974
},
975975
"parseq_use_deltas": {
976-
"label": "Use delta values for movement parameters",
976+
"label": "Use delta values for movement parameters (recommended)",
977977
"type": "checkbox",
978978
"value": True,
979-
}
979+
"info": "Recommended. If you uncheck this, Parseq keyframe values as are treated as relative movement values instead of absolute."
980+
},
981+
"parseq_non_schedule_overrides": {
982+
"label": "Use FPS, max_frames and cadence from the Parseq manifest, if present (recommended)",
983+
"type": "checkbox",
984+
"value": True,
985+
"info": "Recommended. If you uncheck this, the FPS, max_frames and cadence in the Parseq doc are ignored, and the values in the A1111 UI are used instead."
986+
}
980987
}
981988

982989
def DeforumOutputArgs():

scripts/deforum_helpers/general_utils.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,4 +141,7 @@ def download_file_with_checksum(url, expected_checksum, dest_folder, dest_filena
141141
if not os.path.exists(expected_full_path) and not os.path.isdir(expected_full_path):
142142
load_file_from_url(url=url, model_dir=dest_folder, file_name=dest_filename, progress=True)
143143
if checksum(expected_full_path) != expected_checksum:
144-
raise Exception(f"Error while downloading {dest_filename}.]nPlease manually download from: {url}\nAnd place it in: {dest_folder}")
144+
raise Exception(f"Error while downloading {dest_filename}.]nPlease manually download from: {url}\nAnd place it in: {dest_folder}")
145+
146+
def tickOrCross(value):
147+
return "✅" if value else "❌"

scripts/deforum_helpers/parseq_adapter.py

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import requests
2525
from .animation_key_frames import DeformAnimKeys, ControlNetKeys, LooperAnimKeys
2626
from .rich import console
27+
from .general_utils import tickOrCross
2728

2829
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
2930

@@ -35,11 +36,31 @@ def __init__(self, parseq_args, anim_args, video_args, controlnet_args, loop_arg
3536
# Basic data extraction
3637
self.use_parseq = parseq_args.parseq_manifest and parseq_args.parseq_manifest.strip()
3738
self.use_deltas = parseq_args.parseq_use_deltas
39+
self.non_schedule_overrides = parseq_args.parseq_non_schedule_overrides
40+
41+
self.video_args = video_args
42+
self.anim_args = anim_args
3843

3944
self.parseq_json = self.load_manifest(parseq_args) if self.use_parseq else json.loads('{ "rendered_frames": [{"frame": 0}] }')
4045
self.rendered_frames = self.parseq_json['rendered_frames']
41-
self.max_frame = self.get_max('frame')
42-
self.required_frames = anim_args.max_frames
46+
47+
# Store the settings from the UI in case we override them, so we can report clearly on what's going on.
48+
self.a1111_fps = video_args.fps
49+
self.a1111_cadence = anim_args.diffusion_cadence
50+
self.a1111_frame_count = anim_args.max_frames
51+
52+
# These options in the Parseq manifest will override the options used by Deforum _if and only if_ parseq_non_schedule_overrides is true.
53+
# Warning: we mutate fields on the actual anim_args and video_args objects
54+
if (self.use_parseq):
55+
self.fps = self.parseq_json['options']['output_fps'] if 'output_fps' in self.parseq_json['options'] else None
56+
self.cadence = self.parseq_json['options']['cadence'] if 'cadence' in self.parseq_json['options'] else None
57+
self.frame_count = len(self.rendered_frames)
58+
if self.manages_max_frames():
59+
anim_args.max_frames = self.frame_count
60+
if self.manages_cadence():
61+
anim_args.diffusion_cadence = self.cadence
62+
if self.manages_fps():
63+
video_args.fps = self.fps
4364

4465
# Wrap the original schedules with Parseq decorators, so that Parseq values will override the original values IFF appropriate.
4566
self.anim_keys = ParseqAnimKeysDecorator(self, DeformAnimKeys(anim_args))
@@ -49,12 +70,10 @@ def __init__(self, parseq_args, anim_args, video_args, controlnet_args, loop_arg
4970

5071
# Validation
5172
if (self.use_parseq):
52-
self.required_fps = video_args.fps
53-
self.config_output_fps = self.parseq_json['options']['output_fps']
54-
count_defined_frames = len(self.rendered_frames)
55-
expected_defined_frames = self.max_frame+1 # frames are 0-indexed
56-
if (expected_defined_frames != count_defined_frames):
57-
logging.warning(f"There may be duplicated or missing frame data in the Parseq input: expected {expected_defined_frames} frames including frame 0 because the highest frame number is {self.max_frame}, but there are {count_defined_frames} frames defined.")
73+
max_frame = self.get_max('frame')
74+
expected_frame_count = max_frame+1
75+
if (self.frame_count != expected_frame_count):
76+
logging.warning(f"There may be duplicated or missing frame data in the Parseq input: expected {expected_frame_count} frames including frame 0 because the highest frame number is {max_frame}, but there are {self.frame_count} frames defined.")
5877
if not mute:
5978
self.print_parseq_table()
6079

@@ -98,24 +117,34 @@ def print_parseq_table(self):
98117
table.add_column("Parseq", style="cyan")
99118
table.add_column("Deforum", style="green")
100119

101-
table.add_row("Animation", '\n'.join(self.anim_keys.managed_fields()), '\n'.join(self.anim_keys.unmanaged_fields()))
120+
table.add_row("Animation", '\n'.join(self.anim_keys.managed_fields()), '\n'.join(self.anim_keys.unmanaged_fields()))
102121
if self.cn_keys:
103-
table.add_row("ControlNet", '\n'.join(self.cn_keys.managed_fields()), '\n'.join(self.cn_keys.unmanaged_fields()))
122+
table.add_row("ControlNet", '\n'.join(self.cn_keys.managed_fields()), '\n'.join(self.cn_keys.unmanaged_fields()))
104123
if self.looper_keys:
105-
table.add_row("Guided Images", '\n'.join(self.looper_keys.managed_fields()), '\n'.join(self.looper_keys.unmanaged_fields()))
106-
table.add_row("Prompts", "✅" if self.manages_prompts() else "❌", "✅" if not self.manages_prompts() else "❌")
107-
table.add_row("Frames", str(len(self.rendered_frames)), str(self.required_frames) + (" ⚠️" if str(self.required_frames) != str(len(self.rendered_frames))+"" else ""))
108-
table.add_row("FPS", str(self.config_output_fps), str(self.required_fps) + (" ⚠️" if str(self.required_fps) != str(self.config_output_fps) else ""))
124+
table.add_row("Guided Images", '\n'.join(self.looper_keys.managed_fields()), '\n'.join(self.looper_keys.unmanaged_fields()))
125+
table.add_row("Prompts", tickOrCross(self.manages_prompts()), tickOrCross(not self.manages_prompts()))
126+
table.add_row("Frames", str(self.frame_count) + tickOrCross(self.manages_max_frames()), str(self.a1111_frame_count) + tickOrCross(not self.manages_max_frames()))
127+
table.add_row("FPS", str(self.fps) + tickOrCross(self.manages_fps()), str(self.a1111_fps) + tickOrCross(not self.manages_fps()))
128+
table.add_row("Cadence", str(self.cadence) + tickOrCross(self.manages_cadence()), str(self.a1111_cadence) + tickOrCross(not self.manages_cadence()))
109129

110130
console.print("\nUse this table to validate your Parseq & Deforum setup:")
111131
console.print(table)
112132

113133
def manages_prompts(self):
114134
return self.use_parseq and 'deforum_prompt' in self.rendered_frames[0].keys()
115-
135+
116136
def manages_seed(self):
117137
return self.use_parseq and 'seed' in self.rendered_frames[0].keys()
118138

139+
def manages_cadence(self):
140+
return self.use_parseq and self.non_schedule_overrides and self.cadence
141+
142+
def manages_fps(self):
143+
return self.use_parseq and self.non_schedule_overrides and self.fps
144+
145+
def manages_max_frames(self):
146+
return self.use_parseq and self.non_schedule_overrides and self.frame_count
147+
119148
def get_max(self, seriesName):
120149
return max(self.rendered_frames, key=itemgetter(seriesName))[seriesName]
121150

@@ -135,19 +164,21 @@ def parseq_to_series(self, seriesName):
135164
logging.debug(f"Found {seriesName} in first frame of Parseq data. Assuming it's defined.")
136165
except KeyError:
137166
return None
167+
168+
required_frames = self.adapter.anim_args.max_frames
138169

139-
key_frame_series = pd.Series([np.nan for a in range(self.adapter.required_frames)])
170+
key_frame_series = pd.Series([np.nan for a in range(required_frames)])
140171

141172
for frame in self.adapter.rendered_frames:
142173
frame_idx = frame['frame']
143-
if frame_idx < self.adapter.required_frames:
174+
if frame_idx < required_frames:
144175
if not np.isnan(key_frame_series[frame_idx]):
145176
logging.warning(f"Duplicate frame definition {frame_idx} detected for data {seriesName}. Latest wins.")
146177
key_frame_series[frame_idx] = frame[seriesName]
147178

148179
# If the animation will have more frames than Parseq defines,
149180
# duplicate final value to match the required frame count.
150-
while (frame_idx < self.adapter.required_frames):
181+
while (frame_idx < required_frames):
151182
key_frame_series[frame_idx] = operator.itemgetter(-1)(self.adapter.rendered_frames)[seriesName]
152183
frame_idx += 1
153184

scripts/deforum_helpers/parseq_adapter_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434

3535
def buildParseqAdapter(parseq_use_deltas, parseq_manifest, setup_args=DEFAULT_ARGS):
36-
return ParseqAdapter(SimpleNamespace(parseq_use_deltas=parseq_use_deltas, parseq_manifest=parseq_manifest),
36+
return ParseqAdapter(SimpleNamespace(parseq_use_deltas=parseq_use_deltas, parseq_non_schedule_overrides=False, parseq_manifest=parseq_manifest),
3737
setup_args.anim_args, setup_args.video_args, setup_args.controlnet_args, setup_args.loop_args)
3838

3939
class TestParseqAnimKeys(unittest.TestCase):

scripts/deforum_helpers/render.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@
5353
from deforum_api import JobStatusTracker
5454

5555
def render_animation(args, anim_args, video_args, parseq_args, loop_args, controlnet_args, root):
56+
57+
# initialise Parseq adapter
58+
parseq_adapter = ParseqAdapter(parseq_args, anim_args, video_args, controlnet_args, loop_args)
59+
5660
if opts.data.get("deforum_save_gen_info_as_srt", False): # create .srt file and set timeframe mechanism using FPS
5761
srt_filename = os.path.join(args.outdir, f"{root.timestring}.srt")
5862
srt_frame_duration = init_srt_file(srt_filename, video_args.fps)
@@ -80,9 +84,6 @@ def render_animation(args, anim_args, video_args, parseq_args, loop_args, contro
8084
if is_controlnet_enabled(controlnet_args):
8185
unpack_controlnet_vids(args, anim_args, controlnet_args)
8286

83-
# initialise Parseq adapter
84-
parseq_adapter = ParseqAdapter(parseq_args, anim_args, video_args, controlnet_args, loop_args)
85-
8687
# expand key frame strings to values
8788
keys = DeformAnimKeys(anim_args, args.seed) if not parseq_adapter.use_parseq else parseq_adapter.anim_keys
8889
loopSchedulesAndData = LooperAnimKeys(loop_args, anim_args, args.seed) if not parseq_adapter.use_parseq else parseq_adapter.looper_keys

scripts/deforum_helpers/ui_elements.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,8 @@ def get_tab_init(d, da, dp):
352352
gr.HTML(value=get_gradio_html('parseq'))
353353
with FormRow():
354354
parseq_manifest = create_gr_elem(dp.parseq_manifest)
355+
with FormRow():
356+
parseq_non_schedule_overrides = create_gr_elem(dp.parseq_non_schedule_overrides)
355357
with FormRow():
356358
parseq_use_deltas = create_gr_elem(dp.parseq_use_deltas)
357359
return {k: v for k, v in {**locals(), **vars()}.items()}

0 commit comments

Comments
 (0)