Easy Image Batch β behavior matrix
Status: Canonical (approved 2026-06-18). Implemented in
k_easy_image_batch.py and mirrored in
docs/user_guide/nodes/koolook_image/easy_image_batch.md.
Runnable demo: the Easy Image Batch β Select and Rebuild workflow bundled in
the Kforge Labs Workflows sidebar folder
(docs/workflows/kforge-labs-workflows/,
from #248). It
supersedes the generated demo removed here.
This captures the agreed changes:
keyframe_batchβkeyframes_insert(rename; the old name remains as a deprecated alias input so workflows saved on 0.4.1/0.4.2 keep loading).- No image source connected β clean placeholder batch sized by new
width/heightwidgets (no error). - Insert-over-source mode β
keyframes_insert+source_batchcomposite the inserts over the source cut window. image1βimage4are "super-overwrites" β when connected they replace whatever is at their index in every mode (top priority). When not connected they do nothing (the old "pull fromsource_batchviaimageN_frame" fallback β Mode 2 β is removed).
The layered model (single source of truth)
The output is built in three layers, lowest first. A later layer overwrites an earlier one at the same output index.
output index i β VFX frame v = cut_start_frame + i (window: i in [0, total_frames))
ββ Layer 3 β SUPER-OVERWRITE (image1βimage4) βββββββββββββββββ highest priority
β Each CONNECTED imageN β placed at index (imageN_frame β
β β cut_start_frame). Wins over everything. Unconnected β
β slots contribute NOTHING. (imageN_frame matters only β
β when imageN is connected.) β
ββ Layer 2 β SEQUENCE ββββββββββββββββββββββββββββββββββββββββ€
β β’ keyframes_insert connected (INSERT): i-th incoming frame β
β β i-th source_frames position (ascending, deduped). β
β β’ else (SELECT): each source_frames number N picks β
β source_batch[Nβ1] β index (N β cut_start_frame). β
ββ Layer 1 β BACKGROUND ββββββββββββββββββββββββββββββββββββββ€ lowest priority
β β’ insert-over-source (keyframes_insert AND source_batch): β
β source_batch cut window (index i β source_batch[vβ1]; β
β frames beyond the source fall back to placeholder). β
β β’ otherwise: placeholder_color everywhere. β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Background rule. Layer 1 is the source cut window whenever source_batch
is connected and either an insert is active (keyframes_insert) or the
source_frames list is empty; otherwise it is placeholder_color.
What counts as "placed" (drives alpha_batch, selected_image_batch,
selected_frames): normally the Layer 2 placements and Layer 3 overwrites;
the Layer 1 source_batch backdrop is not placed (just the background). So
alpha_batch is 0.0 at sequence inserts/picks and connected-slot overwrites,
1.0 everywhere else (flipped by invert_alpha).
Select passthrough is the exception (row 5). When the source passes through
with an empty list, the covered source frames are kept content (alpha
0.0), and the uncovered tail becomes the selection (alpha 1.0,
emitted in selected_*) β an extend-a-clip inpaint mask. The selection here is
the gap, the inverse of every other row. Insert-mode passthrough (row 9)
keeps the plain backdrop semantics (nothing placed).
Resolution / dtype come from the first connected image (insert: from
keyframes_insert; select: image1 β source_batch β image2β¦4). With no
image connected at all, the output is sized by the width/height widgets
(channels = 3), on CPU/float β a clean placeholder batch.
Tie-break: if two connected slots target the same index, the
higher-numbered slot wins (image4 > β¦ > image1). Sequence + slot at the
same index β the slot wins (Layer 3 > Layer 2).
Mode matrix
Legend β keyframes_insert = KI, source_batch = SB, source_frames
list = list, connected image1β4 = slots.
| # | KI | SB | list | slots | Background | Placed (alpha 0.0) | selected_* |
Mode label |
|---|---|---|---|---|---|---|---|---|
| 1 | β | β | β | none | placeholder (widthΓheight) |
nothing | empty | clean batch |
| 2 | β | β | has | none | placeholder | source picks at listed positions | the picks | select |
| 3 | β | β | β | some | placeholder | connected slots | the slots | select (slots) |
| 4 | β | β | has | some | placeholder | source picks + slots (slots win on overlap) | picks + slots | select |
| 5 | β | β | empty | none | source cut window | covered source (kept) | the uncovered gap | source passthrough / extend |
| 6 | β | β | has | none | placeholder | scattered inserts | the inserts | offset / reconstruct |
| 7 | β | β | empty | none | placeholder | nothing | empty | clean (nothing inserted) |
| 8 | β | β | has | none | source cut window | scattered inserts (overwrite source) | the inserts | insert-over-source |
| 9 | β | β | empty | none | source cut window | nothing | empty | source passthrough |
| 10 | β | β | has | some | source cut window | inserts + slots (slots win) | inserts + slots | insert-over-source |
| 11 | any | any | any | set but NOT connected | (per above) | slots contribute nothing | (per above) | (per above) |
Row 11 was the bug fixed here: a slot with a non-default imageN_frame
that is not wired has zero effect on the output. The old behaviour (an
unconnected slot pulling from source_batch) is gone.
Edge cases & logging
- List ranges.
N-M(e.g.14-17) expands inclusively; a descendingM-N, an over-wide range (more than 8192 frames), or any non-integer token warns and is skipped. Applies in both modes. - List set but no image source (select): the list is ignored and a clean placeholder batch is returned, with a console warning (not a silent drop).
- List token not in
source_batch(select): warn + skip that number. - List/insert position outside the cut window: dropped, summarized once.
- More inserts than list positions / more positions than inserts: reported as "extra insert frames" / "positions with no insert frame".
source_batchshorter than the cut window (insert-over-source): the uncovered tail frames stay placeholder; count reported.- Empty list in an insert mode: clean placeholder (no SB) or clean source passthrough (with SB) β console note "frame list is empty".
- Mismatched H/W/C among connected inputs: hard
ValueError(unchanged). - Every run still prints one summary line: mode, cut window, placed count, and any of the notes above.
Decisions (resolved)
- Tie-break direction β higher-numbered slot wins (
image4>image1); a slot beats a sequence pick / insert at the same index (Layer 3 > Layer 2). - Empty-list passthrough (select) β
source_batch+ an empty list passes the source cut window through (row 5). Covered source frames are kept content (alpha0.0); a source shorter than the output leaves a placeholder tail that becomes the selection (selected_*) β an extend/inpaint mask. The selection here is the gap, the inverse of other rows. - Mode-2 fallback removed β an unconnected slot never pulls from
source_batch;imageN_frameis only a placement target for a wired image. - Range syntax β the frame list accepts inclusive ranges (
14-17β 14,15,16,17) in both select and insert; descending/non-integer tokens warn.