Nodes/aioli-nodes/๐ŸŽจ Inpaint Color Fix
ComfyUI Node

๐ŸŽจ Inpaint Color Fix

When your inpaint comes back the right shape but the wrong colour

By aiolicollectiveยทCreated 6 months agoยทUpdated 2 months agoยท 0
๐ŸŽจ Inpaint Color Fix
  • original_crop
  • inpainted_crop
  • mask
  • image_corrected
  • correction_mask
โ—„delta_e_threshold-1โ–บ
โ—„blend_strength1.00โ–บ
โ—„feather_radius0โ–บ

Every crop-and-stitch inpaint has a moment where the shape is perfect but the colours don't match. The model rendered the region slightly too saturated, or a touch too warm, and pasting it back makes the whole area look wrong even though the edges line up. Flux 2 Klein in particular is known for saturation drift on inpaint. A naive global colour-match would fix the tone and simultaneously flatten the content you actually asked for. This node threads that needle: it colour-matches only the pixels that haven't really changed, and leaves genuinely creative pixels alone.

It slots in right after VAE Decode, before ImageResize+ / ImageCompositeMasked - precisely the point where the inpainted crop is about to be pasted back and its colour shift becomes visible.

How it works

Both crops (the original from BBoxMultipleFix.image_cropped and the fresh one from VAE Decode) get converted to LAB colour space - pure numpy, no external dependencies - and the node computes per-pixel Delta-E between them. Delta-E is the perceptual colour-difference metric: how different two colours look to a human, not just their RGB distance.

Then it applies the key heuristic. Pixels where Delta-E is below the threshold are "similar" - the model kept them roughly intact but shifted the colour - so they get colour-matched back toward the original. Pixels where Delta-E is above the threshold are "creative" - the model genuinely changed the content (new object, different clothing, restyled hair) - and they're left exactly as generated. That's the whole trick, and it's why the threshold matters so much. A global colour match would have to choose, and would get it wrong; Delta-E lets each pixel decide.

Two controls refine it: blend_strength (0 = no correction, 1 = full match on similar zones) and feather_radius, a Gaussian blur on the correction mask for soft transitions between corrected and intact areas. There's also an optional mask input that bypasses Delta-E entirely - if you know exactly which region to fix, feed it a mask and the node corrects exactly that, nothing else.

Inputs and outputs

  • original_crop - image_cropped from BBoxMultipleFix (before the KSampler).
  • inpainted_crop - the IMAGE from VAE Decode.
  • delta_e_threshold - -1 auto (the recommended start), 5โ€“10 strict (corrects almost everything except clearly creative pixels), 15โ€“20 balanced, 25โ€“35 loose (only near-identical pixels), 50+ near-global match.
  • blend_strength, feather_radius, and the optional mask override.

Outputs: image_corrected (connect to ImageResize+) and correction_mask, a debug view where white = corrected and black = creative/intact. Run it once and stare at correction_mask before trusting the numbers - it tells you exactly what the node decided to touch.

Installing it

Part of the Aioli Nodes pack. ComfyUI Manager โ†’ search "Aioli Nodes", or:

cd ComfyUI/custom_nodes
git clone https://github.com/aiolicollective/aioli-nodes

Restart ComfyUI, find it under Aioli Nodes. Zero extra dependencies - pure numpy and torch, both already in ComfyUI.

Gotchas

The trap is setting the threshold too low: then "similar" includes pixels you wanted changed, and the correction quietly undoes part of your edit. When in doubt, start at -1 (auto) and watch the correction_mask output. It also handles the awkward inputs gracefully - greyscale images and RGBA PNGs (nano-banana / Gemini output with an alpha channel) are normalised to RGB before the LAB pass, dropping the alpha. And since it's a young pack from a small studio, don't expect a big thread history of issues - the GitHub issues page is the honest support channel if this misbehaves.

CategoryAioli Nodes

Inputs (6)

NameTypeDefaultDescription
original_cropIMAGEโ€”
inpainted_cropIMAGEโ€”
delta_e_thresholdFLOAT-1-1โ€“100โ€”
blend_strengthFLOAT1.000โ€“1โ€”
feather_radiusINT00โ€“64โ€”
maskoptMASKโ€”

Outputs (2)

NameTypeDescription
image_correctedIMAGEโ€”
correction_maskMASKโ€”