feat: split frame strength into high/low noise strength per frame

Each frame path now has two strength fields instead of one:
'start frame high strength', 'start frame low strength' (and same
for middle/end). Migration splits old single 'X frame strength' into
both new keys using the same value.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-04 14:34:03 +02:00
parent 4b40d0f50c
commit c771fa3451
3 changed files with 36 additions and 17 deletions
+11 -4
View File
@@ -96,9 +96,12 @@ def _get_data(name: str, file_name: str, seq: int = Query(default=1)) -> dict[st
result = dict(match) result = dict(match)
# Inject strength defaults if not yet saved to JSON # Inject strength defaults if not yet saved to JSON
for key, default in ( for key, default in (
("start frame strength", 1.0), ("start frame high strength", 1.0),
("middle frame strength", 1.0), ("start frame low strength", 1.0),
("end frame strength", 1.0), ("middle frame high strength", 1.0),
("middle frame low strength", 1.0),
("end frame high strength", 1.0),
("end frame low strength", 1.0),
): ):
result.setdefault(key, default) result.setdefault(key, default)
# Computed stem names from frame paths # Computed stem names from frame paths
@@ -133,7 +136,11 @@ def _get_keys(name: str, file_name: str, seq: int = Query(default=1)) -> dict[st
else: else:
types.append("STRING") types.append("STRING")
# Injected defaults — always present even if not yet saved to JSON # Injected defaults — always present even if not yet saved to JSON
for key in ("start frame strength", "middle frame strength", "end frame strength"): for key in (
"start frame high strength", "start frame low strength",
"middle frame high strength", "middle frame low strength",
"end frame high strength", "end frame low strength",
):
if key not in match: if key not in match:
keys.append(key) keys.append(key)
types.append("FLOAT") types.append("FLOAT")
+12 -10
View File
@@ -317,9 +317,9 @@ def render_batch_processor(state: AppState):
'seed', 'camera', KEY_SEQUENCE_NUMBER, 'seed', 'camera', KEY_SEQUENCE_NUMBER,
'frame_to_skip', 'logic index', 'transition', 'vace_length', 'frame_to_skip', 'logic index', 'transition', 'vace_length',
'input_a_frames', 'input_b_frames', 'reference switch', 'vace schedule', 'input_a_frames', 'input_b_frames', 'reference switch', 'vace schedule',
'start frame path', 'start frame strength', 'start frame path', 'start frame high strength', 'start frame low strength',
'middle frame path', 'middle frame strength', 'middle frame path', 'middle frame high strength', 'middle frame low strength',
'end frame path', 'end frame strength', 'end frame path', 'end frame high strength', 'end frame low strength',
'video file path', 'video file path',
} }
standard_keys.update(lora_keys) standard_keys.update(lora_keys)
@@ -560,10 +560,10 @@ def _render_sequence_card(i, seq, batch_list, data, file_path, state,
# --- Frame paths (start / middle / end) --- # --- Frame paths (start / middle / end) ---
logic_val = int(seq.get('logic index', 0)) logic_val = int(seq.get('logic index', 0))
for bit, img_label, img_key, str_key in [ for bit, img_label, img_key, hi_key, lo_key in [
(0, 'Start Frame', 'start frame path', 'start frame strength'), (0, 'Start Frame', 'start frame path', 'start frame high strength', 'start frame low strength'),
(1, 'Middle Frame', 'middle frame path', 'middle frame strength'), (1, 'Middle Frame', 'middle frame path', 'middle frame high strength', 'middle frame low strength'),
(2, 'End Frame', 'end frame path', 'end frame strength'), (2, 'End Frame', 'end frame path', 'end frame high strength', 'end frame low strength'),
]: ]:
ui.label(img_label).classes('text-caption text-weight-bold q-mt-sm') ui.label(img_label).classes('text-caption text-weight-bold q-mt-sm')
with ui.row().classes('w-full items-center no-wrap q-mt-xs'): with ui.row().classes('w-full items-center no-wrap q-mt-xs'):
@@ -581,11 +581,13 @@ def _render_sequence_card(i, seq, batch_list, data, file_path, state,
f'style="width:36px;height:36px;object-fit:cover;' f'style="width:36px;height:36px;object-fit:cover;'
f'border-radius:4px;cursor:pointer;flex-shrink:0">' f'border-radius:4px;cursor:pointer;flex-shrink:0">'
).on('click', img_dlg.open) ).on('click', img_dlg.open)
str_inp = dict_number('Strength', seq, str_key, default=1.0,
step=0.05, format='%.2f').style(
'width:80px').props('outlined dense')
sw = ui.switch(value=bool((logic_val >> bit) & 1)) sw = ui.switch(value=bool((logic_val >> bit) & 1))
frame_switches.append(sw) frame_switches.append(sw)
with ui.row().classes('w-full no-wrap q-mt-xs q-gutter-xs'):
dict_number('High', seq, hi_key, default=1.0,
step=0.05, format='%.2f').classes('col').props('outlined dense')
dict_number('Low', seq, lo_key, default=1.0,
step=0.05, format='%.2f').classes('col').props('outlined dense')
with splitter.after: with splitter.after:
# Mode # Mode
+13 -3
View File
@@ -44,11 +44,14 @@ DEFAULTS = {
"reference switch": 1, "reference switch": 1,
"video file path": "", "video file path": "",
"start frame path": "", "start frame path": "",
"start frame strength": 1.0, "start frame high strength": 1.0,
"start frame low strength": 1.0,
"middle frame path": "", "middle frame path": "",
"middle frame strength": 1.0, "middle frame high strength": 1.0,
"middle frame low strength": 1.0,
"end frame path": "", "end frame path": "",
"end frame strength": 1.0, "end frame high strength": 1.0,
"end frame low strength": 1.0,
# --- LoRAs (name as STRING, strength as FLOAT) --- # --- LoRAs (name as STRING, strength as FLOAT) ---
"lora 1 high": "", "lora 1 high": "",
@@ -173,6 +176,13 @@ def _migrate_key_renames(data: dict) -> None:
item['end frame path'] = item.pop('flf image path') item['end frame path'] = item.pop('flf image path')
if 'reference image path' in item and 'start frame path' not in item: if 'reference image path' in item and 'start frame path' not in item:
item['start frame path'] = item.pop('reference image path') item['start frame path'] = item.pop('reference image path')
# Split old single strength into high+low
for prefix in ('start frame', 'middle frame', 'end frame'):
old_key = f'{prefix} strength'
if old_key in item:
val = item.pop(old_key)
item.setdefault(f'{prefix} high strength', val)
item.setdefault(f'{prefix} low strength', val)
def _migrate_lora_keys(data: dict) -> None: def _migrate_lora_keys(data: dict) -> None: