Add files via upload
This commit is contained in:
1
example_workflows/LTX Director MSR_Ghost_V5.json
Normal file
1
example_workflows/LTX Director MSR_Ghost_V5.json
Normal file
File diff suppressed because one or more lines are too long
837
ltx_director.py
837
ltx_director.py
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,7 @@ class LTXDirectorGuide(LTXVAddGuide):
|
|||||||
GuideData.Input("guide_data", tooltip="Guide data produced by Prompt Relay Encode (Timeline)."),
|
GuideData.Input("guide_data", tooltip="Guide data produced by Prompt Relay Encode (Timeline)."),
|
||||||
io.Float.Input("scale_by", default=1.0, min=0.01, max=8.0, step=0.01, tooltip="Scale the latent by this factor."),
|
io.Float.Input("scale_by", default=1.0, min=0.01, max=8.0, step=0.01, tooltip="Scale the latent by this factor."),
|
||||||
io.Combo.Input("upscale_method", options=["nearest-exact", "bilinear", "area", "bicubic", "bislerp"], default="bicubic", tooltip="Method used to upscale/downscale the latent."),
|
io.Combo.Input("upscale_method", options=["nearest-exact", "bilinear", "area", "bicubic", "bislerp"], default="bicubic", tooltip="Method used to upscale/downscale the latent."),
|
||||||
|
io.Float.Input("msr_strength", default=0.0, min=0.0, max=1.0, step=0.05, tooltip="Licon MSR only: per-stage reference strength override. 0 = use the Director's value (full pull, right for stage 1). On a refinement/upscale stage set ~0.4 to hold detail without the references repainting the opening (fixes stage-2 mist/ghosting)."),
|
||||||
],
|
],
|
||||||
outputs=[
|
outputs=[
|
||||||
io.Conditioning.Output(display_name="positive"),
|
io.Conditioning.Output(display_name="positive"),
|
||||||
@@ -33,7 +34,7 @@ class LTXDirectorGuide(LTXVAddGuide):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def execute(cls, positive, negative, vae, latent, guide_data, scale_by=1.0, upscale_method="bicubic") -> io.NodeOutput:
|
def execute(cls, positive, negative, vae, latent, guide_data, scale_by=1.0, upscale_method="bicubic", msr_strength=0.0) -> io.NodeOutput:
|
||||||
scale_factors = vae.downscale_index_formula
|
scale_factors = vae.downscale_index_formula
|
||||||
|
|
||||||
# Clone latents to avoid mutating upstream nodes
|
# Clone latents to avoid mutating upstream nodes
|
||||||
@@ -68,6 +69,12 @@ class LTXDirectorGuide(LTXVAddGuide):
|
|||||||
|
|
||||||
_, _, latent_length, latent_height, latent_width = latent_image.shape
|
_, _, latent_length, latent_height, latent_width = latent_image.shape
|
||||||
|
|
||||||
|
# MSR mode: the Director handed us the raw references; inject them at THIS node's
|
||||||
|
# resolution (so a half-res Stage 1 and a full-res Stage 2 each stay self-consistent).
|
||||||
|
msr = guide_data.get("msr")
|
||||||
|
if msr is not None:
|
||||||
|
return cls._inject_msr(positive, negative, vae, latent_image, noise_mask, msr, msr_strength)
|
||||||
|
|
||||||
images = guide_data.get("images", [])
|
images = guide_data.get("images", [])
|
||||||
insert_frames = guide_data.get("insert_frames", [])
|
insert_frames = guide_data.get("insert_frames", [])
|
||||||
strengths = guide_data.get("strengths", [])
|
strengths = guide_data.get("strengths", [])
|
||||||
@@ -87,4 +94,61 @@ class LTXDirectorGuide(LTXVAddGuide):
|
|||||||
positive, negative, frame_idx, latent_image, noise_mask, t, strength, scale_factors,
|
positive, negative, frame_idx, latent_image, noise_mask, t, strength, scale_factors,
|
||||||
)
|
)
|
||||||
|
|
||||||
return io.NodeOutput(positive, negative, {"samples": latent_image, "noise_mask": noise_mask})
|
return io.NodeOutput(positive, negative, {"samples": latent_image, "noise_mask": noise_mask})
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _inject_msr(cls, positive, negative, vae, latent_image, noise_mask, msr, msr_strength=0.0):
|
||||||
|
"""Inject the Licon MSR references at this node's current resolution."""
|
||||||
|
from nodes import NODE_CLASS_MAPPINGS
|
||||||
|
from .ltx_director import _execute_comfy_node, _unpack
|
||||||
|
|
||||||
|
IC = NODE_CLASS_MAPPINGS.get("LTXAddVideoICLoRAGuide")
|
||||||
|
AG = NODE_CLASS_MAPPINGS.get("LTXVAddGuide")
|
||||||
|
if IC is None or AG is None:
|
||||||
|
raise ValueError("MSR mode needs LTXAddVideoICLoRAGuide (ComfyUI-LTXVideo) and LTXVAddGuide (core LTXV nodes).")
|
||||||
|
|
||||||
|
def clamp01(v):
|
||||||
|
return max(0.0, min(1.0, float(v)))
|
||||||
|
|
||||||
|
slideshow = msr["slideshow"]
|
||||||
|
keyframes = msr["keyframes"]
|
||||||
|
prefix_latents = int(msr["prefix_latents"])
|
||||||
|
# Per-stage override: msr_strength > 0 wins (set it low on a refinement stage so the
|
||||||
|
# references hold detail without repainting the opening); 0 = use the Director's value.
|
||||||
|
strength = clamp01(msr_strength) if float(msr_strength) > 0.0 else clamp01(msr["strength"])
|
||||||
|
downscale = float(msr["downscale"])
|
||||||
|
|
||||||
|
# Pad the generated region so (pad + keyframes) == prefix_latents, so the downstream crop
|
||||||
|
# (which trims the slideshow's temporal footprint) lands on the true clean length.
|
||||||
|
pad_latents = max(0, prefix_latents - len(keyframes))
|
||||||
|
if pad_latents > 0:
|
||||||
|
B, C, F, H, W = latent_image.shape
|
||||||
|
latent_image = torch.cat(
|
||||||
|
[latent_image, torch.zeros((B, C, pad_latents, H, W), dtype=latent_image.dtype, device=latent_image.device)],
|
||||||
|
dim=2,
|
||||||
|
)
|
||||||
|
mb, mc, mf, mh, mw = noise_mask.shape
|
||||||
|
noise_mask = torch.cat(
|
||||||
|
[noise_mask, torch.ones((mb, mc, pad_latents, mh, mw), dtype=noise_mask.dtype, device=noise_mask.device)],
|
||||||
|
dim=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
latent = {"samples": latent_image, "noise_mask": noise_mask}
|
||||||
|
|
||||||
|
# CONDITIONING PATH: slideshow -> IC-LoRA -> keep conditioning, discard latent.
|
||||||
|
cp, cn, _ = _unpack(_execute_comfy_node(
|
||||||
|
IC, positive=positive, negative=negative, vae=vae, latent=latent, image=slideshow,
|
||||||
|
frame_idx=0, strength=strength, latent_downscale_factor=downscale,
|
||||||
|
crop="center", use_tiled_encode=False, tile_size=256, tile_overlap=64,
|
||||||
|
))
|
||||||
|
|
||||||
|
positive, negative = cp, cn
|
||||||
|
|
||||||
|
# LATENT PATH: one frozen keyframe per reference at frame 0 -> keep latent, drop cond.
|
||||||
|
for kf in keyframes:
|
||||||
|
_p, _n, latent = _unpack(_execute_comfy_node(
|
||||||
|
AG, positive=positive, negative=negative, vae=vae, latent=latent,
|
||||||
|
image=kf, frame_idx=0, strength=strength,
|
||||||
|
))
|
||||||
|
|
||||||
|
return io.NodeOutput(positive, negative, latent)
|
||||||
Reference in New Issue
Block a user