Nodes/ComfyUI-JH-PixelPro/Color Matcher Region (LAB)
ComfyUI Node

Color Matcher Region (LAB)

Match the skin tone without touching the background

By jetthuangai·Created 5 months ago·Updated 4 months ago· 5
Color Matcher Region (LAB)
  • image_target
  • image_reference
  • target_mask
  • reference_mask
  • image_matched
channelsab
strength1.00

JHPixelProColorMatcherRegion is the answer to a question its sibling JHPixelProColorMatcher can't quite handle: what if you only want to match part of the image - and leave the rest pixel-for-pixel alone?

Same Reinhard LAB color-transfer engine underneath, but this version takes two optional masks, and here's the difference that matters:

  • target_mask - gates both the target's statistics and where the matched result gets applied. Unconnected = full target. This is the key upgrade over the plain ColorMatcher, whose mask only gates the statistics and always applies to the whole frame.
  • reference_mask - restricts which pixels of the reference image feed the color statistics. Unconnected = full reference.

So the workflow writes itself: you want the skin tone of an AI-refined portrait to match the original, but the background must not shift even one RGB value. Wire a skin/face mask into target_mask, connect the pre-AI source as image_reference, and the match lands only on skin while the background stays bit-identical. That's a genuinely useful property, because most "fix the color drift" jobs in ComfyUI are actually "fix the subject's color drift" - the background was fine.

The other trick this unlocks: sampling stats from a specific region of the reference. Say your reference has a properly-white-balanced face but a wildly cast background - with reference_mask, you can feed the match only the face pixels from the reference, so the background cast can't poison the color statistics. Same channels (ab = chroma only, the default and usual choice; lab = full tone) and strength (0–1, typical 0.6–0.8) controls as its sibling.

Practical notes

The reference still needs to match the target's H×W (raise otherwise), and both masks live in the target/reference image coordinate space respectively. One subtlety worth internalizing: this node's target_mask is doing double duty - it restricts stats and application. If you want the old "stats gated, application full-frame" behavior, that's the plain JHPixelProColorMatcher with its mask input. Don't go looking for a third node; the choice is intentional. If you need an applied region match with full-frame stats, that's a two-node combo: ColorMatcher for stats, then an ImageBlend masked composite for the region.

Output is image_matched, same shape as the target.

Install

ComfyUI Manager → search ComfyUI-JH-PixelPro, or:

cd ComfyUI/custom_nodes
git clone https://github.com/jetthuangai/ComfyUI-JH-PixelPro.git
cd ComfyUI-JH-PixelPro
pip install -r requirements.txt

Restart ComfyUI; it's under ComfyUI-JH-PixelPro/color. The pack needs ComfyUI ≥ 0.43.x, Python ≥ 3.10, and installs kornia, mediapipe, opencv-python-headless, and scipy.

It's a niche node in the sense that it only earns its keep when you genuinely need region-limited matching. But if you're doing skin-tone unification across a batch of portraits, or you've hit the "the whole frame turns the wrong color" wall that Reinhard transfer loves to create, the region version is the one that stops being a compromise and starts being the right tool. Pair the mask with anything from the pack's mask group - an edge-refined skin mask works beautifully here.

CategoryComfyUI-JH-PixelPro/color

Inputs (6)

NameTypeDefaultDescription
image_targetIMAGE
image_referenceIMAGE
channelsCOMBOabab = match chroma only and preserve target luminance. lab = match L + a + b for full tone transfer.
strengthFLOAT1.000–1Blend factor. 0 = bypass, 1 = full region match.
target_maskoptMASKOptional target region. It controls target stats and where the matched result is applied. Unconnected = full target.
reference_maskoptMASKOptional reference sampling region. Unconnected = full reference.

Outputs (1)

NameTypeDescription
image_matchedIMAGE