Optimize long videos ref bookkeeping
This commit is contained in:
+48
-20
@@ -4422,10 +4422,10 @@ def _named_refs_for_text(text, ref_slots, kinds=None):
|
|||||||
return matched
|
return matched
|
||||||
|
|
||||||
|
|
||||||
def _matched_reference_slots(text, ref_slots):
|
def _matched_reference_slots(text, ref_slots, normalized_refs=None):
|
||||||
matched = []
|
matched = []
|
||||||
seen = set()
|
seen = set()
|
||||||
normalized_slots = _normalized_ref_slots(ref_slots)
|
normalized_slots = normalized_refs if normalized_refs is not None else _normalized_ref_slots(ref_slots)
|
||||||
for slot_number, ref in (
|
for slot_number, ref in (
|
||||||
_slot_refs_for_text(text, normalized_slots)
|
_slot_refs_for_text(text, normalized_slots)
|
||||||
+ _named_refs_for_text(text, normalized_slots, kinds=("character", "location"))
|
+ _named_refs_for_text(text, normalized_slots, kinds=("character", "location"))
|
||||||
@@ -4437,9 +4437,16 @@ def _matched_reference_slots(text, ref_slots):
|
|||||||
return matched
|
return matched
|
||||||
|
|
||||||
|
|
||||||
def _reference_context_for_text(text, ref_slots, include_character_wardrobe=True):
|
def _reference_context_for_text(
|
||||||
|
text,
|
||||||
|
ref_slots,
|
||||||
|
include_character_wardrobe=True,
|
||||||
|
normalized_refs=None,
|
||||||
|
):
|
||||||
parts = []
|
parts = []
|
||||||
for slot_number, ref in _matched_reference_slots(text, ref_slots):
|
if normalized_refs is None:
|
||||||
|
normalized_refs = _normalized_ref_slots(ref_slots)
|
||||||
|
for slot_number, ref in _matched_reference_slots(text, ref_slots, normalized_refs=normalized_refs):
|
||||||
label = _reference_text(ref.get("name")) or _reference_text(ref.get("id")) or f"reference {slot_number}"
|
label = _reference_text(ref.get("name")) or _reference_text(ref.get("id")) or f"reference {slot_number}"
|
||||||
description = _reference_sentence(ref.get("description"))
|
description = _reference_sentence(ref.get("description"))
|
||||||
wardrobe = (_reference_sentence(ref.get("wardrobe"))
|
wardrobe = (_reference_sentence(ref.get("wardrobe"))
|
||||||
@@ -4480,10 +4487,12 @@ def _inject_reference_context(block, context):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _reference_character_memory(ref_slots):
|
def _reference_character_memory(ref_slots, normalized_refs=None):
|
||||||
lines = []
|
lines = []
|
||||||
seen = set()
|
seen = set()
|
||||||
for slot_number, ref in enumerate(_normalized_ref_slots(ref_slots), 1):
|
if normalized_refs is None:
|
||||||
|
normalized_refs = _normalized_ref_slots(ref_slots)
|
||||||
|
for slot_number, ref in enumerate(normalized_refs, 1):
|
||||||
if ref is None:
|
if ref is None:
|
||||||
continue
|
continue
|
||||||
if ref.get("kind") != "character":
|
if ref.get("kind") != "character":
|
||||||
@@ -4504,12 +4513,14 @@ def _reference_character_memory(ref_slots):
|
|||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
def _connected_refs(ref_slots):
|
def _connected_refs(ref_slots, normalized_refs=None):
|
||||||
"""Connected refs only, preserving slot order and skipping empty sockets."""
|
"""Connected refs only, preserving slot order and skipping empty sockets."""
|
||||||
return [ref for ref in _normalized_ref_slots(ref_slots) if _reference_image(ref) is not None]
|
if normalized_refs is None:
|
||||||
|
normalized_refs = _normalized_ref_slots(ref_slots)
|
||||||
|
return [ref for ref in normalized_refs if _reference_image(ref) is not None]
|
||||||
|
|
||||||
|
|
||||||
def resolve_tagged_refs(text, ref_list):
|
def resolve_tagged_refs(text, ref_list, normalized_refs=None):
|
||||||
"""(rewritten text, images, dropped) for the <Picture N> tags in ONE shot.
|
"""(rewritten text, images, dropped) for the <Picture N> tags in ONE shot.
|
||||||
|
|
||||||
The tokenizer numbers references by their position in the list it is handed, so
|
The tokenizer numbers references by their position in the list it is handed, so
|
||||||
@@ -4521,7 +4532,8 @@ def resolve_tagged_refs(text, ref_list):
|
|||||||
A tag naming a slot with no image connected refers to nothing at all, so it is
|
A tag naming a slot with no image connected refers to nothing at all, so it is
|
||||||
removed from the text rather than left to confuse the encoder, and reported."""
|
removed from the text rather than left to confuse the encoder, and reported."""
|
||||||
wanted = picture_tags(text)
|
wanted = picture_tags(text)
|
||||||
normalized_refs = _normalized_ref_slots(ref_list)
|
if normalized_refs is None:
|
||||||
|
normalized_refs = _normalized_ref_slots(ref_list)
|
||||||
live = [n for n in wanted if 1 <= n <= len(normalized_refs) and _reference_image(normalized_refs[n - 1]) is not None]
|
live = [n for n in wanted if 1 <= n <= len(normalized_refs) and _reference_image(normalized_refs[n - 1]) is not None]
|
||||||
dropped = [n for n in wanted if n not in live]
|
dropped = [n for n in wanted if n not in live]
|
||||||
renumber = {old: new for new, old in enumerate(live, 1)}
|
renumber = {old: new for new, old in enumerate(live, 1)}
|
||||||
@@ -4538,7 +4550,7 @@ def resolve_tagged_refs(text, ref_list):
|
|||||||
return out.strip(), [normalized_refs[n - 1] for n in live], dropped
|
return out.strip(), [normalized_refs[n - 1] for n in live], dropped
|
||||||
|
|
||||||
|
|
||||||
def resolve_prompt_refs(text, ref_list, include_named=True):
|
def resolve_prompt_refs(text, ref_list, include_named=True, normalized_refs=None):
|
||||||
"""(rewritten text, refs, dropped) for the refs a shot actually carries.
|
"""(rewritten text, refs, dropped) for the refs a shot actually carries.
|
||||||
|
|
||||||
Explicit <Picture N> tags still decide which slot numbers the prompt points at,
|
Explicit <Picture N> tags still decide which slot numbers the prompt points at,
|
||||||
@@ -4546,8 +4558,9 @@ def resolve_prompt_refs(text, ref_list, include_named=True):
|
|||||||
that split, a shot could inherit the facts/context for "Mara" and "Jon" while
|
that split, a shot could inherit the facts/context for "Mara" and "Jon" while
|
||||||
only carrying a tagged location image, which reads exactly like the names were
|
only carrying a tagged location image, which reads exactly like the names were
|
||||||
understood but the faces were ignored."""
|
understood but the faces were ignored."""
|
||||||
normalized_refs = _normalized_ref_slots(ref_list)
|
if normalized_refs is None:
|
||||||
rewritten, tagged_refs, dropped = resolve_tagged_refs(text, normalized_refs)
|
normalized_refs = _normalized_ref_slots(ref_list)
|
||||||
|
rewritten, tagged_refs, dropped = resolve_tagged_refs(text, normalized_refs, normalized_refs=normalized_refs)
|
||||||
refs = list(tagged_refs)
|
refs = list(tagged_refs)
|
||||||
seen = {id(ref) for ref in refs}
|
seen = {id(ref) for ref in refs}
|
||||||
if include_named:
|
if include_named:
|
||||||
@@ -4560,7 +4573,7 @@ def resolve_prompt_refs(text, ref_list, include_named=True):
|
|||||||
return rewritten, refs, dropped
|
return rewritten, refs, dropped
|
||||||
|
|
||||||
|
|
||||||
def shot_references(ref_list, ref_mode, shot_index, handoff):
|
def shot_references(ref_list, ref_mode, shot_index, handoff, connected_refs=None):
|
||||||
"""Pure: which reference images shot `shot_index` is conditioned on, or [] when
|
"""Pure: which reference images shot `shot_index` is conditioned on, or [] when
|
||||||
the shot should use the keyframe handoff instead.
|
the shot should use the keyframe handoff instead.
|
||||||
|
|
||||||
@@ -4586,7 +4599,7 @@ def shot_references(ref_list, ref_mode, shot_index, handoff):
|
|||||||
back as a soft signal (the model is shown where the last
|
back as a soft signal (the model is shown where the last
|
||||||
shot ended rather than told to start exactly there), and it
|
shot ended rather than told to start exactly there), and it
|
||||||
stays a single ref2va task, so nothing conflicts."""
|
stays a single ref2va task, so nothing conflicts."""
|
||||||
refs = _connected_refs(ref_list)
|
refs = connected_refs if connected_refs is not None else _connected_refs(ref_list)
|
||||||
if not refs:
|
if not refs:
|
||||||
return []
|
return []
|
||||||
if ref_mode == "auto ref2v":
|
if ref_mode == "auto ref2v":
|
||||||
@@ -6397,7 +6410,9 @@ class H3LongVideos:
|
|||||||
), legacy_ref_slots)
|
), legacy_ref_slots)
|
||||||
)
|
)
|
||||||
ref_slots = direct_ref_slots
|
ref_slots = direct_ref_slots
|
||||||
direct_ref_count = len(_connected_refs(direct_ref_slots))
|
normalized_ref_slots = _normalized_ref_slots(ref_slots)
|
||||||
|
connected_refs = [ref for ref in normalized_ref_slots if _reference_image(ref) is not None]
|
||||||
|
direct_ref_count = len(connected_refs)
|
||||||
explicit_character_memory = (character_memory or "").strip()
|
explicit_character_memory = (character_memory or "").strip()
|
||||||
derived_character_memory = _reference_character_memory(ref_slots)
|
derived_character_memory = _reference_character_memory(ref_slots)
|
||||||
effective_character_memory = explicit_character_memory or derived_character_memory
|
effective_character_memory = explicit_character_memory or derived_character_memory
|
||||||
@@ -6620,6 +6635,7 @@ class H3LongVideos:
|
|||||||
block,
|
block,
|
||||||
ref_slots,
|
ref_slots,
|
||||||
include_character_wardrobe=not bool(explicit_character_memory),
|
include_character_wardrobe=not bool(explicit_character_memory),
|
||||||
|
normalized_refs=normalized_ref_slots,
|
||||||
)
|
)
|
||||||
if context:
|
if context:
|
||||||
block = _inject_reference_context(block, context)
|
block = _inject_reference_context(block, context)
|
||||||
@@ -6696,7 +6712,7 @@ class H3LongVideos:
|
|||||||
else "prompt/soundscape silencing only"))
|
else "prompt/soundscape silencing only"))
|
||||||
# Same reference accounting the render reports: which shots lose the
|
# Same reference accounting the render reports: which shots lose the
|
||||||
# handoff is a composition decision, so it belongs in the preview.
|
# handoff is a composition decision, so it belongs in the preview.
|
||||||
n_refs = len(_connected_refs(ref_slots))
|
n_refs = len(connected_refs)
|
||||||
plan_ref = ""
|
plan_ref = ""
|
||||||
if n_refs:
|
if n_refs:
|
||||||
# Mirror the render's placement exactly: 'where tagged' reads the
|
# Mirror the render's placement exactly: 'where tagged' reads the
|
||||||
@@ -6709,7 +6725,12 @@ class H3LongVideos:
|
|||||||
for shot_index, gen in enumerate(gens):
|
for shot_index, gen in enumerate(gens):
|
||||||
shot_mode = beat_ref_mode_directive(beats[shot_index] if shot_index < len(beats) else "") or ref_mode
|
shot_mode = beat_ref_mode_directive(beats[shot_index] if shot_index < len(beats) else "") or ref_mode
|
||||||
if shot_mode in ("where tagged", "auto ref2v") and any_tags_anywhere:
|
if shot_mode in ("where tagged", "auto ref2v") and any_tags_anywhere:
|
||||||
if resolve_prompt_refs(gen, ref_slots, include_named=(shot_mode == "auto ref2v"))[1]:
|
if resolve_prompt_refs(
|
||||||
|
gen,
|
||||||
|
ref_slots,
|
||||||
|
include_named=(shot_mode == "auto ref2v"),
|
||||||
|
normalized_refs=normalized_ref_slots,
|
||||||
|
)[1]:
|
||||||
on.append(shot_index + 1)
|
on.append(shot_index + 1)
|
||||||
tagged_used = True
|
tagged_used = True
|
||||||
else:
|
else:
|
||||||
@@ -6768,7 +6789,7 @@ class H3LongVideos:
|
|||||||
latent_chunks = [] # per-shot sampled latents, pre-decode
|
latent_chunks = [] # per-shot sampled latents, pre-decode
|
||||||
mouth_settled = [] # shots seeded from a settled (closed) mouth
|
mouth_settled = [] # shots seeded from a settled (closed) mouth
|
||||||
handoff, sr = first_frame, None
|
handoff, sr = first_frame, None
|
||||||
connected_ref_count = len(_connected_refs(ref_slots))
|
connected_ref_count = len(connected_refs)
|
||||||
ref_shots = [] # which shots ended up ref-conditioned
|
ref_shots = [] # which shots ended up ref-conditioned
|
||||||
ref_missing = [] # <Picture N> tags naming an unconnected slot
|
ref_missing = [] # <Picture N> tags naming an unconnected slot
|
||||||
ref_carried = [] # tagged shots that kept continuity as an extra ref
|
ref_carried = [] # tagged shots that kept continuity as an extra ref
|
||||||
@@ -6807,12 +6828,19 @@ class H3LongVideos:
|
|||||||
gen_prompt,
|
gen_prompt,
|
||||||
ref_slots,
|
ref_slots,
|
||||||
include_named=(shot_mode == "auto ref2v"),
|
include_named=(shot_mode == "auto ref2v"),
|
||||||
|
normalized_refs=normalized_ref_slots,
|
||||||
)
|
)
|
||||||
for n in dropped:
|
for n in dropped:
|
||||||
if n not in ref_missing:
|
if n not in ref_missing:
|
||||||
ref_missing.append(n)
|
ref_missing.append(n)
|
||||||
else:
|
else:
|
||||||
shot_refs = shot_references(ref_slots, shot_mode_eff, i, handoff)
|
shot_refs = shot_references(
|
||||||
|
ref_slots,
|
||||||
|
shot_mode_eff,
|
||||||
|
i,
|
||||||
|
handoff,
|
||||||
|
connected_refs=connected_refs,
|
||||||
|
)
|
||||||
# A shot that follows a strip starts FRESH. Continuing from a frame that
|
# A shot that follows a strip starts FRESH. Continuing from a frame that
|
||||||
# still shows the garment is how it reappears -- the picture outvotes the
|
# still shows the garment is how it reappears -- the picture outvotes the
|
||||||
# text every time. Costs a cut exactly where the state changes, which is
|
# text every time. Costs a cut exactly where the state changes, which is
|
||||||
|
|||||||
Reference in New Issue
Block a user