Files
malkuthro-ComfyUI-Koolook/docs/designs/easy-image-batch-behavior.md
T
88dbfedf86 feat(nodes): Easy Image Batch keyframes_insert + insert-over-source +… (#246)
* feat(nodes): Easy Image Batch keyframes_insert + insert-over-source + clean batch

- Rename optional input keyframe_batch -> keyframes_insert (node ID unchanged).
- Insert-over-source mode: when keyframes_insert AND source_batch are both
  connected, composite the inserts over the source cut window (instead of the
  placeholder background). keyframes_insert alone keeps the offset/reconstruct
  behaviour over placeholder.
- No image source connected -> clean placeholder batch sized by new
  width/height fallback widgets, instead of raising. width/height are appended
  LAST so older saved workflows keep their positional widgets_values alignment
  (fixes a mid-list insertion that shifted placeholder_color into source_frames).
- alpha_batch marks only inserted/placed positions; source background is not
  counted as placed.
- New pure planner plan_source_base_fill (torch-free) alongside
  plan_offset_placements; both unit-tested in CI.
- Docs: behavior matrix spec, interactive HTML demo, and a generated
  "all versions" ComfyUI demo workflow under docs/designs/.

Not yet implemented (specced as DRAFT in docs/designs, pending maintainer
approval + tie-break decision): image1-4 "super-overwrite" layer across all
modes, and removal of the Mode 2 unconnected-slot source_batch fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(deps): raise aiohttp [test] floor to >=3.14.1

The constraints lock already pins aiohttp 3.14.1 (the CVE-2026-54273..54280
fix line), but pyproject's [test] extra still declared aiohttp>=3.9.
Dependabot keys off pyproject, not the lock, so the open declared range kept
eight range-based alerts open even though the installed version is patched.
Raising the floor to >=3.14.1 aligns the declared range with the lock and
clears the alerts once this reaches the default branch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(nodes): Easy Image Batch image1-4 super-overwrite + source passthrough

Make the four manual slots the top compositing layer in every mode (select
and insert): a connected imageN overwrites output frame imageN_frame on top
of the sequence/inserts and the background. Higher slot wins a collision
(image4 > image1); a slot beats a sequence pick / insert at the same position.
Extracted as the torch-free plan_slot_overwrites planner + a shared
_apply_slot_overwrites helper used by both code paths.

Two model fixes ride along, per the finalised behaviour matrix:
- An unconnected slot now contributes nothing -- imageN_frame is only a
  placement target for a wired image, never a source_batch pick. The old
  unwired-slot fallback (which surfaced seemingly-random frames) is removed.
- source_batch + an empty frame list (no insert) now passes the source cut
  window straight through instead of a placeholder batch, matching the
  insert-mode empty-list passthrough.

Tests: new plan_slot_overwrites planner cases + fake-tensor behaviour tests
covering slot-beats-pick/insert, higher-slot-wins, passthrough-not-placed, and
the removed phantom fallback. Docstring, tooltips, user guide, design spec
(promoted DRAFT -> canonical), and CHANGELOG updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(nodes): assert selected_image_batch = picks/inserts + slots; drop matrix HTML demo

selected_image_batch already packs every placed position -- both the
source_frames picks / inserts and the manual-slot overwrites, with slots
winning on overlap. Add explicit select- and insert-mode tests asserting the
composition and ascending order, locking the behaviour in.

Also delete docs/designs/easy-image-batch-matrix.html (stale interactive demo
built against an earlier draft) and drop its companion reference from the
design spec.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(nodes): Easy Image Batch passthrough inpaint mask + frame-range syntax

Two refinements to the Easy Image Batch passthrough/list behaviour:

- Short-source passthrough is now an extend/inpaint mask. When source_batch is
  connected and the frame list is empty, the covered source frames are kept
  content (alpha 0.0) and the uncovered tail becomes the selection
  (selected_image_batch / selected_frames) with alpha 1.0 -- "keep the first N,
  generate the rest". Full coverage -> no gap, empty selection. The selection
  is the gap here (inverse of select mode); insert mode is unchanged.
- The source_frames field accepts inclusive ranges: "1-5, 7, 9, 14-17" expands
  to 1,2,3,4,5,7,9,14,15,16,17, in both select and insert, via a shared
  torch-free parse_frame_tokens (descending/non-integer tokens warn).

Tests cover the gap selection (short/full/cut-start-offset/slot-in-gap), the
parser (ranges, bad tokens), and range syntax end-to-end. Docstring, tooltip,
user guide, design spec, and CHANGELOG updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(designs): remove draft-era Easy Image Batch demo workflow + builder

Delete easy-image-batch-demo-setup.json and build_easy_image_batch_demo.py
(the leftover draft-era demo, sibling to the already-removed matrix HTML) and
drop the companion reference from the behavior spec. A purpose-built demo will
replace them later; the spec matrix + the node remain the canonical reference.
Also drop the now-stale "(proposed)" from the spec title (it is canonical).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(user-guide): remove stale v0.1.5 Easy Image Batch screenshot

The screenshot predates the node's keyframes_insert / super-overwrite /
passthrough rework and is unreferenced. Removing it to be re-created fresh.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs: point Easy Image Batch docs at the bundled Select-and-Rebuild demo

Note the runnable demo workflow ("Easy Image Batch - Select and Rebuild",
bundled into the Kforge Labs Workflows sidebar folder via #248) in both the
user guide and the design spec, replacing the generated demo removed earlier.
Referenced by path (not a clickable link) since the workflow lands via #248.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(nodes): address PR review — keyframe_batch alias + input guards

- Restore keyframe_batch as a deprecated alias of keyframes_insert so
  pre-rename workflows (origin/main exposes the input) keep loading; an explicit
  keyframes_insert wins. Follows docs/maintainers/node-versioning.md
  (alias-then-deprecate); removal deferred to a release boundary.
- Warn instead of silently returning a clean batch when source_frames is
  non-empty but no image source is connected.
- Cap frame-list range expansion at 8192 frames/token to guard against typos
  like 1-99999999.
- Docs: reframe the bundled demo-workflow note as forthcoming (the JSON lands
  via #248, not on this branch); document the alias, cap, and no-source guard in
  the user guide / design spec / CHANGELOG.

Tests: alias routing + precedence, no-source warning, range cap + boundary
(46 node tests). Fresh-eyes review: no correctness/compat issues (the IMAGE
alias input adds no widget-position shift).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs: point Easy Image Batch docs at the now-merged Select-and-Rebuild demo

#248 merged to main and is now in this branch (via the main merge), so the
demo workflow exists at docs/workflows/kforge-labs-workflows/. Repoint the user
guide and design spec at it directly (present tense, real folder link) instead
of the earlier "coming soon / pending" framing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: AI Machine <ai.machine@x.baconcph.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 00:51:16 +02:00

8.2 KiB
Raw Blame History

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:

  1. keyframe_batch → keyframes_insert (rename; the old name is kept as a deprecated alias input so pre-rename workflows keep loading).
  2. No image source connected → clean placeholder batch sized by new width/height widgets (no error).
  3. Insert-over-source mode — keyframes_insert + source_batch composite the inserts over the source cut window.
  4. image1–image4 are "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 from source_batch via imageN_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 descending M-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_batch shorter 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)

  1. Tie-break direction — higher-numbered slot wins (image4 > image1); a slot beats a sequence pick / insert at the same index (Layer 3 > Layer 2).
  2. Empty-list passthrough (select) — source_batch + an empty list passes the source cut window through (row 5). Covered source frames are kept content (alpha 0.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.
  3. Mode-2 fallback removed — an unconnected slot never pulls from source_batch; imageN_frame is only a placement target for a wired image.
  4. Range syntax — the frame list accepts inclusive ranges (14-17 → 14,15,16,17) in both select and insert; descending/non-integer tokens warn.