π¨ Latent Color Match
Grade one render to match another without ever leaving latent space
- latent
- reference
- LATENT
You've got an image whose color grade you love, and another one that misses it. Standard answer: VAE-decode the first one, run something like cubiq's ImageColorMatch, re-encode - a full round trip through a lossy codec, which the KB keeps reminding us costs a little every time. π¨ Latent Color Match skips all that: it takes two latents straight off the sampler and shifts one's color statistics to match the other's, in place, before you've ever decoded a single pixel.
Before you get too excited, the honest caveat: latent channels are not RGB. This node grabs the first three channels of whatever your model's VAE uses (4 on SD 1.5/SDXL, 16 on Flux) and pretends they're red, green, and blue. It produces results that survive decode and look like a color grade, but it's a heuristic, not a true color match. The README's "Quality Loss: None" table is overselling it. Still, as a way to nudge a render toward a reference grade without paying for an encode/decode cycle, it works - and it's genuinely fast, because all of this runs on the latent tensor.
How it works
The core is cubiq's mean/std transfer, which this node adapts from image space. It converts the first three latent channels into your chosen color space via kornia - LAB, YCbCr, LUV, YUV, XYZ, or plain RGB - computes per-channel mean and standard deviation over the spatial dimensions for both images, then normalizes your source by its own stats and rescales it to the reference's. Convert back, and you have a matched tensor.
The factor then blends that result with the original, and here's a fun quirk: it's non-linear. The code applies factor ** 0.5, so a factor of 0.25 already delivers half the effect. That means even small values visibly do something - bump to 1.5β3.0 if you want it slammed all the way toward the reference.
The six advanced methods - mkl, hm, reinhard, mvgd, hm-mvgd-hm, hm-mkl-hm - hand the normalized pseudo-RGB off to the color-matcher library instead. These are the "professional" algorithms (MongeβKantorovich, histogram matching, Reinhard). A word of caution: if color-matcher isn't installed, these don't error - they silently fall back to LAB. You might think you're running hm-mkl-hm when you're actually running plain LAB.
The inputs that matter
latent- your source latent, straight from the KSampler. This is what gets recolored.reference- the latent whose color you're stealing. Also from a sampler (or a VAE-encoded image).method- pick your algorithm.LABis the sensible default;YCbCrseparates luminance from chrominance and tends to play nicer with skin tones.factor- 0β3, how strongly the match applies. Remember the square root, so it bites early.
device (auto/cpu/gpu) and batch_size (0 = all) are there for edge cases; defaults are fine. The single output is a LATENT, wired straight into VAEDecode - same latent format in, same latent format out.
Installing it
This ships in the Latent Color Tools pack (DenRakEiw/Latent_Nodes). Easiest path is ComfyUI Manager - search "Latent Color Tools" and install. Manual route:
cd ComfyUI/custom_nodes/
git clone https://github.com/DenRakEiw/Latent_Nodes
cd Latent_Nodes
pip install -r requirements.txt
Then restart ComfyUI. requirements.txt is just kornia>=0.6.0 and color-matcher>=0.2.0; torch and numpy come with ComfyUI. No model files to download. One trap: the pack's own README has a placeholder URL (yourusername/ComfyUI-Latent-Color-Tools) in its install snippet - ignore it, the real repo is the one above.
Common issues
- Weak or invisible effect. Crank
factorto 1.5β3.0. Some content types resist statistical matching; a flat, low-contrast reference will do little. - "Advanced" methods behaving like LAB. You're missing
color-matcher, and the fallback is silent.pip install color-matcher>=0.2.0, or just useLABdeliberately. - Console spam. This node prints a small essay of debug lines every run. Cosmetic, but expect it in your terminal.
- Reference size mismatch. If shapes differ, the node bicubic-upscales the reference to the latent's resolution automatically. Usually fine.
- Result looks slightly soft. The code applies a light 5% box-blur to smooth latent-space discontinuities. Barely visible, but it's there.
If the whole "color match in latent space" idea feels too clever for your use case, the classic KJNodes/Easy-Use image-space color match still exists - it just costs you the decode/encode round trip. For a quick grade transfer mid-workflow, this is the one I'd reach for.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| latent | LATENT | β | |
| reference | LATENT | β | |
| method | COMBO | LAB | 12 options: LAB, YCbCr, RGB, LUV, YUV, XYZ, +6 |
| factor | FLOAT | 1.000β3 | β |
| device | COMBO | 3 options: auto, cpu, gpu | |
| batch_size | INT | 00β1024 | β |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| LATENT | LATENT | β |