Color Matcher Region (LAB)
Match the skin tone without touching the background
- image_target
- image_reference
- target_mask
- reference_mask
- image_matched
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.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| image_target | IMAGE | — | |
| image_reference | IMAGE | — | |
| channels | COMBO | ab | ab = match chroma only and preserve target luminance. lab = match L + a + b for full tone transfer. |
| strength | FLOAT | 1.000–1 | Blend factor. 0 = bypass, 1 = full region match. |
| target_maskopt | MASK | Optional target region. It controls target stats and where the matched result is applied. Unconnected = full target. | |
| reference_maskopt | MASK | Optional reference sampling region. Unconnected = full reference. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image_matched | IMAGE | — |