🖼️AGSoft Contact Sheet
Turn a 24-image KSampler sweep into one image you can actually read
- images
- sheet
- labeled
If you have ever run a 24-item sampler sweep, you already know the problem: the batch is sitting in your temp folder as 24 files named ComfyUI_00041_ through ComfyUI_00064_, and you have no idea which one was steps=12 and which was steps=16. 🖼️AGSoft Contact Sheet exists to kill that step. It burns a label into each frame and pastes the whole batch into one NxM grid, so the sweep is a single image you can scroll, save, or drop into Discord.
What it actually does
Two things, in this order. First it labels: it takes the batch on images, splits current_str on newlines, and burns line N into frame N. Second it grids: it lays the labeled frames out into a contact sheet and returns that as one IMAGE.
The labeling is plain Pillow compositing, not a magic text engine. For each frame it draws a rounded rectangle at your chosen corner (box_color, box_opacity, box_radius), alpha-composites it over the frame, then draws the text on top (text_color, text_opacity). Box and text have separate opacity, which is the thing most label nodes get wrong - with box_opacity at its 0.6 default, white text stays legible over a bright frame, and setting it to 0 gives you a bare label with no plate behind it.
The grid pass is where the layout decisions happen. columns = 0 means auto, which the source computes as ceil(sqrt(n)) - so 9 frames become 3×3 and 16 become 4×4. Cell size is the largest frame in the batch, the canvas is filled black, and every frame is pasted at its native size with padding between cells.
You get two outputs, and the second one is the useful surprise: sheet (the single grid image) and labeled (the same batch, labels burned in, original order). So you can preview the sheet for eyeballing while still piping labeled into a video node or Save Image Plus with the parameter text baked into the pixels.
The inputs worth touching
Everything except images is optional, and three of them do 90% of the work:
images- the batch, typically straight off AGSoft KSampler's decodedimagesoutput.current_str- the string output ofoptions_single,options_dualoroptions_lora. Wire it here and line N lands on frame N. Leave it unconnected and you still get a clean grid, just no captions.columns- 0 for auto, or set it explicitly (e.g. 4) when you want a fixed row width for a post. Max 16.
Then font_size (24 default), padding, position (six corner/edge choices), and the four look-and-feel knobs. box_radius is clamped to half the box size, so cranking it to 128 just gives you a pill.
Install
Same as the rest of the pack, and this node needs nothing extra - no models, no downloads:
cd ComfyUI/custom_nodes
git clone https://github.com/Art-xmaster/comfyui-AGSoft
# restart ComfyUI
Or in ComfyUI Manager, search comfyui-AGSoft and install. The pack's requirements.txt is numpy, opencv-python and translators; none of it is this node, and all of it is normally already present in a working ComfyUI.
Where people get burned
Captions missing. Either current_str isn't connected, or it's connected to the wrong thing. It wants the current_str socket of an options node, not the KSampler's options output - that one carries a dict of the last run, not one line per frame.
Fewer lines than frames. The code reads lines[idx] if idx < len(lines), so frames past the last line come out unlabeled. Silent, not an error. If your series was in current mode you'll only have one line, and only frame 1 gets a caption.
Tiny, unreadable labels on Linux boxes and minimal Docker images. The node looks for arial.ttf or DejaVuSans.ttf on the system (plus the Windows font path) and falls back to Pillow's default bitmap font if neither exists - and the default font ignores font_size entirely. If your labels are legible-but-microscopic, install a font rather than turning the size up:
sudo apt install fonts-dejavu-core # Debian/Ubuntu
Black gaps in the grid. The canvas is filled black and each frame is pasted at its own size, so a batch with mixed resolutions leaves holes. Keep one resolution per sweep.
Nothing loads at all? The pack's __init__.py imports every .py file in the folder and skips any module that raises on import, logging Import error in module '<name>'. If the node isn't in the menu, check the ComfyUI console - the missing node will be named there.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | Image batch (e.g. series result from AGSoft KSampler). --- Батч изображений (например результат серии из AGSoft KSampler). | |
| current_stropt | STRING | Connect the current_str output of options_single / options_dual / options_lora here: one line per frame (line N is burned into frame N). If not connected or empty — no labels. --- Подключи сюда выход current_str нод options_single / options_dual / options_lora: по одной строке на кадр (строка N вжигается в кадр N). Не подключено или пусто — без подписей. | |
| columnsopt | INT | 00–16 | Grid columns; 0 = auto (square-ish). --- Колонок в сетке; 0 = авто (примерно квадрат). |
| font_sizeopt | INT | 248–128 | Label font size. --- Размер шрифта подписи. |
| paddingopt | INT | 80–64 | Grid padding and label inset from the frame edge. --- Отступы сетки и отступ подписи от края кадра. |
| positionopt | COMBO | top_left | Label position: top or bottom, left / center / right. --- Позиция подписи: сверху или снизу, слева / по центру / справа. |
| text_opacityopt | FLOAT | 1.000–1 | Text opacity (0 = invisible, 1 = solid). --- Прозрачность текста (0 = невидимый, 1 = сплошной). |
| box_opacityopt | FLOAT | 0.600–1 | Background box opacity behind the text (0 = no box). --- Прозрачность плашки под текстом (0 = без плашки). |
| text_coloropt | COMBO | white | Text color preset (24 templates). --- Шаблон цвета текста (24 варианта). |
| box_coloropt | COMBO | black | Background box color preset (24 templates). --- Шаблон цвета плашки (24 варианта). |
| box_radiusopt | INT | 100–128 | Box corner radius in pixels; 0 = straight corners. Auto-clamped to half of the box size. --- Радиус скругления углов плашки в пикселях; 0 = прямые углы. Автоматически ограничивается половиной размера плашки. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| sheet | IMAGE | Single NxM contact sheet image. Одно изображение-сетка NxM. |
| labeled | IMAGE | Batch with labels burned in (same order). Батч с вжжёнными подписями (тот же порядок). |