aimusic / ComfyUI /custom_nodes /koolook /docs /designs /easy-image-batch-behavior.md
vidfom's picture
Upload folder using huggingface_hub (part 7)
e4ab0d4 verified
|
Raw
History Blame Contribute Delete
8.4 kB
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
# 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`](../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/`](../workflows/kforge-labs-workflows/),
from [#248](https://github.com/malkuthro/ComfyUI-Koolook/pull/248)). It
supersedes the generated demo removed here.
This captures the agreed changes:
1. `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).
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.