Nodes/painting-by-colors-generator/Paint by Numbers: Calculate numbers
ComfyUI Node

Paint by Numbers: Calculate numbers

The node that actually draws the numbers on the map

By Chrisvenator·Created about a year ago·Updated about a year ago· 3
Paint by Numbers: Calculate numbers
  • preprocessed_image
  • lineart_image
  • hex_stack
  • numbers_image
  • color_palette
num_colors20
font_size14
min_region_size50
numbers_density0.0000
color_merge_threshold5.0

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:

  1. 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_threshold of each other becomes one color. This is why you can end up with fewer actual regions than num_colors.
  2. Map every pixel to its nearest palette color, producing a 1-indexed color map.
  3. 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.
  4. Find connected regions per color (scipy's ndimage.label), skipping anything smaller than min_region_size.
  5. Place numbers. By default, one number per region at the centroid. With numbers_density above 0, it scatters extra numbers across big regions so huge color fields don't end up with a single lonely digit.
  6. 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.

Categoryimage/artistic

Inputs (8)

NameTypeDefaultDescription
preprocessed_imageIMAGE
lineart_imageIMAGE
num_colorsINT202–1000
font_sizeINT142–100
min_region_sizeINT500–1000
numbers_densityFLOAT0.00000–1
color_merge_thresholdFLOAT5.05–50
hex_stackoptHEX_STACK

Outputs (2)

NameTypeDescription
numbers_imageIMAGE
color_paletteIMAGE