Support interleave up to 6 inputs

This commit is contained in:
Kangle
2026-01-30 11:36:55 +00:00
parent 146f359c2c
commit e577e2819f
+71 -35
View File
@@ -26,21 +26,32 @@ def _is_video_input(value: Any) -> bool:
return hasattr(value, "get_stream_source") and hasattr(value, "get_components")
def _interleave_tensors(batch_a: torch.Tensor, batch_b: torch.Tensor) -> torch.Tensor:
if batch_a.dim() < 1 or batch_b.dim() < 1:
raise ValueError("Inputs must include a batch dimension.")
if batch_a.shape[0] != batch_b.shape[0]:
def _interleave_many_tensors(batches: list[torch.Tensor]) -> torch.Tensor:
if len(batches) < 2:
raise ValueError("At least two batches are required.")
for batch in batches:
if batch.dim() < 1:
raise ValueError("Inputs must include a batch dimension.")
base_shape = batches[0].shape[1:]
for batch in batches[1:]:
if batch.shape[1:] != base_shape:
raise ValueError("All inputs must have the same shape (except batch size).")
lengths = [batch.shape[0] for batch in batches]
if len(set(lengths)) > 1:
logging.warning(
"Interleave Frames: batch sizes differ (%s vs %s); truncating.",
batch_a.shape[0],
batch_b.shape[0],
"Interleave Frames: batch sizes differ %s; truncating to shortest.",
lengths,
)
count = min(batch_a.shape[0], batch_b.shape[0])
count = min(lengths)
if count == 0:
raise ValueError("Inputs must contain at least one frame.")
batch_a = batch_a[:count]
batch_b = batch_b[:count]
return torch.stack([batch_a, batch_b], dim=1).reshape(2 * count, *batch_a.shape[1:])
trimmed = [batch[:count] for batch in batches]
return torch.stack(trimmed, dim=1).reshape(count * len(trimmed), *base_shape)
def _interleave_tensors(batch_a: torch.Tensor, batch_b: torch.Tensor) -> torch.Tensor:
return _interleave_many_tensors([batch_a, batch_b])
def _detect_batch_type(value: Any) -> str:
@@ -53,22 +64,29 @@ def _detect_batch_type(value: Any) -> str:
raise ValueError("Input must be IMAGE or LATENT.")
def _interleave_latents(batch_a: dict, batch_b: dict) -> dict:
samples_a = batch_a["samples"]
samples_b = batch_b["samples"]
if not isinstance(samples_a, torch.Tensor) or not isinstance(samples_b, torch.Tensor):
raise ValueError("LATENT samples must be torch tensors.")
def _interleave_latent_batches(batches: list[dict]) -> dict:
samples = [batch["samples"] for batch in batches]
for sample in samples:
if not isinstance(sample, torch.Tensor):
raise ValueError("LATENT samples must be torch tensors.")
output = {"samples": _interleave_tensors(samples_a, samples_b)}
for key, value_a in batch_a.items():
if key == "samples" or key not in batch_b:
output = {"samples": _interleave_many_tensors(samples)}
common_keys = set(batches[0].keys())
for batch in batches[1:]:
common_keys &= set(batch.keys())
common_keys.discard("samples")
for key in common_keys:
values = [batch[key] for batch in batches]
if not all(isinstance(value, torch.Tensor) for value in values):
continue
value_b = batch_b[key]
if not isinstance(value_a, torch.Tensor) or not isinstance(value_b, torch.Tensor):
sample_lengths = [sample.shape[0] for sample in samples]
if any(value.shape[0] != length for value, length in zip(values, sample_lengths)):
continue
if value_a.shape[0] != samples_a.shape[0] or value_b.shape[0] != samples_b.shape[0]:
try:
output[key] = _interleave_many_tensors(values)
except ValueError:
continue
output[key] = _interleave_tensors(value_a, value_b)
return output
@@ -214,8 +232,14 @@ class InterleaveFrames:
def INPUT_TYPES(cls):
return {
"required": {
"batch_a": ("IMAGE,LATENT", {"tooltip": "First batch to interleave."}),
"batch_b": ("IMAGE,LATENT", {"tooltip": "Second batch to interleave."}),
"batch1": ("IMAGE,LATENT", {"tooltip": "First batch to interleave."}),
"batch2": ("IMAGE,LATENT", {"tooltip": "Second batch to interleave."}),
},
"optional": {
"batch3": ("IMAGE,LATENT", {"tooltip": "Third batch to interleave."}),
"batch4": ("IMAGE,LATENT", {"tooltip": "Fourth batch to interleave."}),
"batch5": ("IMAGE,LATENT", {"tooltip": "Fifth batch to interleave."}),
"batch6": ("IMAGE,LATENT", {"tooltip": "Sixth batch to interleave."}),
}
}
@@ -224,17 +248,29 @@ class InterleaveFrames:
FUNCTION = "interleave"
CATEGORY = "ffmpeg"
def interleave(self, batch_a, batch_b):
type_a = _detect_batch_type(batch_a)
type_b = _detect_batch_type(batch_b)
if type_a != type_b:
raise ValueError("Input types must match (IMAGE+IMAGE or LATENT+LATENT).")
def interleave(
self,
batch1,
batch2,
batch3=None,
batch4=None,
batch5=None,
batch6=None,
):
batches = [batch for batch in [batch1, batch2, batch3, batch4, batch5, batch6] if batch is not None]
if len(batches) < 2:
raise ValueError("At least two batches must be provided.")
if type_a == "IMAGE":
if not isinstance(batch_a, torch.Tensor) or not isinstance(batch_b, torch.Tensor):
types = {_detect_batch_type(batch) for batch in batches}
if len(types) != 1:
raise ValueError("All inputs must have the same type (IMAGE or LATENT).")
batch_type = types.pop()
if batch_type == "IMAGE":
if not all(isinstance(batch, torch.Tensor) for batch in batches):
raise ValueError("IMAGE inputs must be torch tensors.")
return (_interleave_tensors(batch_a, batch_b),)
return (_interleave_many_tensors(batches),)
if not isinstance(batch_a, dict) or not isinstance(batch_b, dict):
if not all(isinstance(batch, dict) for batch in batches):
raise ValueError("LATENT inputs must be dicts.")
return (_interleave_latents(batch_a, batch_b),)
return (_interleave_latent_batches(batches),)