Paint by Numbers: Calculate numbers
The node that actually draws the numbers on the map
- preprocessed_image
- lineart_image
- hex_stack
- numbers_image
- color_palette
Paint by Numbers: Calculate numbers is the heart of this pack - the node that turns a flat posterized image into an actual numbered coloring sheet. The preprocessor gives you the color regions; this node gives you the legend and the numbers. If you've ever stared at a paint-by-numbers kit and wondered how anyone decides where the "7" goes, it's this algorithm.
It takes two images: the preprocessed color-flattened image and a lineart version of it. It figures out the palette, finds every region of every color, drops a number somewhere sensible in each one, and avoids dropping numbers on top of the lineart. It also spits out a printer-friendly palette sheet so you know what color each number means.
How it works
A step at a time:
- Extract the palette. If you've wired in a hex stack, it uses your colors directly (sorted by brightness so number 1 is always the darkest). Otherwise it runs K-means on the preprocessed image, then merges near-identical shades - anything within
color_merge_thresholdof each other becomes one color. This is why you can end up with fewer actual regions thannum_colors. - Map every pixel to its nearest palette color, producing a 1-indexed color map.
- Build a line exclusion zone. It converts your lineart to grayscale, treats everything darker than 128 as line, and dilates it - so numbers are kept away from lines, not just dumped on them.
- Find connected regions per color (scipy's
ndimage.label), skipping anything smaller thanmin_region_size. - Place numbers. By default, one number per region at the centroid. With
numbers_densityabove 0, it scatters extra numbers across big regions so huge color fields don't end up with a single lonely digit. - Draw the numbers in black with a white outline for legibility, on a white background.
The inputs that matter
- preprocessed_image - feed it the output of the K-Mean preprocessor. The README is explicit: run the preprocessed image through this node, and separately through a lineart converter (any ControlNet lineart will do, invert it) for the lineart input.
- lineart_image - required, not optional. This is what the node uses to keep numbers off the outlines.
- num_colors (default 20, range 2–1000) - the palette size. Note the ceiling: the preprocessor stops at 50, but this node goes to 1000 if you want a genuinely detailed sheet.
- font_size (default 14) - the size of the numbers. Small for dense images, bigger for chunky regions.
- min_region_size (default 50) - regions tinier than this don't get numbered. The README calls this "tiny-blob cleanup."
- numbers_density (default 0) - 0 means one number per region. Bump it up to sprinkle more digits across large regions.
- color_merge_threshold (default 5) - how aggressively to merge near-identical shades.
The outputs
- numbers_image - a black-on-white sheet of just the numbers. This is what goes into the overlay node.
- color_palette - a swatch chart with the number on each color and its RGB/hex below it. This is the printable legend; if you connected a hex stack, it labels each swatch with your exact hex codes so you can match paint to number. Print this and the overlaid sheet and you have a complete kit.
Installing and common snags
Install with the pack - ComfyUI Manager, search "Painting-by-colors-generator", or:
cd ComfyUI/custom_nodes
git clone https://github.com/Chrisvenator/ComfyUI-Painting-by-colors-generator
then restart. No models to download.
The classic failure mode here: numbers sitting on top of lines. The exclusion mask only grabs pixels darker than 128 and dilates them slightly - a thin, faint lineart won't create enough of a no-fly zone. Re-run your lineart step with thicker lines, or invert it correctly, and the numbers will slot into their regions. Also, if numbers seem to be missing entirely, check min_region_size - on a low-resolution preprocessed image, most regions can be under the threshold and quietly skipped.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| preprocessed_image | IMAGE | — | |
| lineart_image | IMAGE | — | |
| num_colors | INT | 202–1000 | — |
| font_size | INT | 142–100 | — |
| min_region_size | INT | 500–1000 | — |
| numbers_density | FLOAT | 0.00000–1 | — |
| color_merge_threshold | FLOAT | 5.05–50 | — |
| hex_stackopt | HEX_STACK | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| numbers_image | IMAGE | — |
| color_palette | IMAGE | — |