ComfyUI Node

Image Array

Turn a pile of images into a labeled comparison grid

By rjgoif·Created 12 months ago·Updated 13 days ago· 6
Image Array
  • images
  • array
  • images
◄backgroundwhite►
◄shapehorizontal►
◄smart_ratio►
◄forced_N1►
◄resize_anytrue►
◄size_guidelargest►
◄match_N1►
◄size_methodpad small dim►
◄labels►
◄label_endloop►
◄label_locationbottom►
◄label_size32►
◄fontarial.ttf►
◄spacing5►
◄label_input—►

Image Array is the flagship of this pack and probably why you're here. You feed it a batch of images and it returns a single, neatly labeled grid - a contact sheet, a comparison panel, whatever you want to call it - ready to drop into a Reddit post or a Discord channel. The community has a whole genre of posts that boil down to "look, I labeled it properly," and this is the node that gets you there without leaving ComfyUI.

The pipeline is equalize → label → arrange. It makes all the images a uniform size, stamps labels under (or over, or beside) each one, and lays them out in a grid you control. That "make it presentable" step is exactly what r/StableDiffusion's comparison posts are begging for, and it's the whole point of this pack.

How it works

Three stages under one hood. First your images are equalized (the same logic as the Image Equalizer node) so they fit together. Then each cell gets a label. Finally the layout engine picks rows and columns from your shape choice:

  • horizontal / vertical - one row, one column.
  • square - the N×M grid closest to square that minimizes empty cells.
  • smart_square / smart_landscape / smart_portrait - pick the grid whose cell aspect ratio best fits 1:1, 3:2, or 2:3. This is the one you want for mixed portrait/landscape photo galleries, because a grid sized for the wrong aspect leaves ugly blank strips.

background (white or black) decides the canvas color and the label scheme - the opposite color becomes the label background, so you always get readable contrast. spacing puts a pixel border around each cell (5–10 reads much cleaner than 0).

The inputs that matter

  • images - your batch. It's a list-aware node, so a Python list of tensors works too.
  • labels - multiline text, one label per line. This is where people get burned: pressing Enter makes a new label, so to get a line break inside one label you type a literal \n. Semicolons split labels too, and numbers get auto-formatted (decimals truncated to 5 places, trailing zeros dropped).
  • label_input - optional dynamic input. Connect a string, number, or list from another node and it overrides the labels widget. Handy when labels are themselves generated, like prompt text or a seed counter.
  • label_end - loop repeats your label list if you have more images than labels; end just stops labeling.
  • resize / size_method / pad - the equalizer stage. pad (default) letterboxes to a uniform size; stretch distorts; crop_center fills by cropping. resize: grow upscales small images rather than shrinking big ones.

Two outputs: array (the single combined grid image) and images (a list of the individually labeled images, if you want them separately).

Install

ComfyUI Manager → search "Img Label Tools" → Install, or:

cd ComfyUI/custom_nodes
git clone https://github.com/rjgoif/ComfyUI-Img-Label-Tools

Restart ComfyUI after either. No models, no heavy deps - Pillow and PyTorch are already there. For custom fonts, drop a .ttf into a fonts folder at your ComfyUI root and it'll show up in the font dropdown; otherwise it falls back to Arial.

Gotchas

The \n-vs-Enter label thing is the #1 confusion, so read the labels section twice. Also, the equalizer is CPU-bound and grow mode scales to the largest image in the batch - one oversized input makes the whole grid balloon, so normalize your inputs first if memory complains.

CategoryImage Label Tools

Inputs (16)

NameTypeDefaultDescription
imagesIMAGEImages to arrange. Every image from every connected batch or list is used, in input order.
backgroundCOMBOwhiteCanvas color. Also used for the gaps from spacing and for padding added by size_method. Label boxes use the opposite color, with their text in the background color.
shapeCOMBOhorizontalGrid layout. horizontal: 1 row. vertical: 1 column. square: closest to square. smart_square / smart_landscape (3:2) / smart_portrait (2:3): pick the grid whose overall canvas shape is closest to the target. force_rows: output has forced_N rows. force_cols: output has forced_N columns. Images always fill left to right, then top to bottom.
smart_ratioSTRINGCustom W:H target for smart layout. Accepts 16:9, 16/9, 1.78, 3Wx1H, etc. Overrides the shape picklist when filled, except force_rows / force_cols, which take priority.
forced_NINT11–256Only used when shape is force_rows or force_cols. force_rows: number of rows in the output (columns are worked out from it). force_cols: number of columns in the output (rows are worked out from it). Fill order stays left to right, then top to bottom, so 14 images with 3 rows or 5 columns give the same 3x5 grid. Capped at the image count. With force_rows, if the exact row count would leave an empty row, the closest row count without one is used.
resize_anyBOOLEANtrueON: resize all images to one common size (see size_guide and size_method) before arranging. OFF: keep native sizes; each grid cell is as large as the largest image and smaller images are centered in it.
size_guideCOMBOlargestTarget size when resize_any is ON. largest / smallest: match the largest or smallest image. match: match the size of image number match_N. Ignored when resize_any is OFF.
match_NINT11–9999Only used when size_guide is match. Which image (counting from 1, in input order) sets the target size. If there are fewer images than this number, the last image is used, so a large value such as 9999 always means the last image.
size_methodCOMBOpad small dimHow each image is fitted to the target size when resize_any is ON. pad small dim: scale to fit inside the target and fill the rest with the background color. stretch: resize to the exact target size, ignoring aspect ratio. crop_center: scale to fill the target, then cut the overflow equally from both sides. crop large dim: scale to fit inside the target with no padding and no cropping (one side matches the target and the other is smaller, so image sizes can differ).
labelsSTRINGOne label per image, in order. Put each label on its own line (press Enter) or separate labels with semicolons. Type \n inside a label for a line break within that label. Ignored if label_input is connected.
label_endCOMBOloopWhat happens when there are more images than labels. loop: start over from the first label. end: images after the last label get a blank label (the label space is still reserved).
label_locationCOMBObottomWhere each label goes relative to its image. top, bottom: text above or below. left_hor, right_hor: horizontal text beside the image. left_vert, right_vert: rotated text beside the image. Every image gets the same amount of label space (sized for the longest label), so cells stay aligned.
label_sizeINT320–200Label font size in pixels. 0 turns labels off completely (no label space is added).
fontCOMBOarial.ttfLabel font. Lists the files in the fonts folder of your ComfyUI install. Without that folder, Arial is used. If the font cannot be loaded, a small built-in font is used and label_size has no effect.
spacingINT50–100Gap in pixels added around every image (after its label), filled with the background color. The gap between two neighbors is double this value.
label_inputoptSTRINGOptional text input. When connected, it replaces the labels box. Accepts a string, a number, or a list of them (one label per image).

Outputs (2)

NameTypeDescription
arrayIMAGEThe finished grid as one image.
imagesIMAGEEach image after resizing and labeling (before spacing is added), as a list.