Nodes/ComfyUI-Tensor-Operations/Image Match Normalize
ComfyUI Node

Image Match Normalize

Give one image another image's colors, no sliders required

By ttulttul·Created 3 years ago·Updated 2 years ago· 8
Image Match Normalize
  • source_image
  • target_image
  • IMAGE

You've got one image whose content you like and another whose color grade you love, and you want the first to wear the second's look. That's all this node does - two images in, one image out - and it's refreshingly dumb about it. It rescales the target image's brightness and contrast per color channel to match the source image's, then hands the result back. Same composition, same edges, same detail; only the color and lighting change.

Why you'd reach for it: every time you want a fresh generation to inherit the mood of a reference - sunset warmth from a photo, the teal-and-orange grade of a film still, the flat studio lighting of a product shot. It also shines in multi-pass workflows: upscale a render, and instead of wrestling with the upscaled copy's slightly-off saturation, just normalize it against the original so the two passes sit together. Because the stats are computed per frame in a batch, it works on video too, no extra wiring.

How it works

The mechanism is the classic first-two-moments color transfer. For each image, the node computes the mean and standard deviation of each color channel, then rescales the target so it ends up with the source's mean and std:

(target - target_mean) / target_std * source_std + source_mean

That's the whole recipe, computed under torch.no_grad() on whatever device the target already lives on. It's cheap, it's deterministic, and there's nothing to tune.

The inputs and outputs that matter

There are exactly two:

  • source_image - the image whose colors you want to borrow. It defines the target mean and contrast per channel.
  • target_image - the image whose content you want to keep. Its color statistics get overwritten with the source's.

Output is a single IMAGE, ready to wire into a VAE encode, a sampler, or a save node.

Where people get burned

  • The images must be the same size. The node doesn't resize anything - it expects matching dimensions and will throw a tensor shape error otherwise. Drop a Resize node in front of both if they differ, and remember this before blaming the node.
  • It matches the average, not the character. Mean/std transfer is blunt: if your source has a strongly bimodal palette, the target gets the arithmetic middle, not the two distinct moods. It's a color grade matcher, not a relight - for harder transfers you'd want a histogram-matching node.
  • It doesn't clamp to 0–1. If the source has much more contrast than the target, the output can push values past white or below black. ComfyUI's preview will clamp on save, but if you see blown highlights, run the result through a levels/clamp node before it goes downstream.

Installing it

It ships in the tiny, dependency-free ComfyUI-Tensor-Operations pack - no requirements.txt, no model downloads, just the code and PIL/torch, which ComfyUI already has. Either way, restart ComfyUI after installing.

cd ComfyUI/custom_nodes
git clone https://github.com/ttulttul/ComfyUI-Tensor-Operations

Or in ComfyUI Manager: search ComfyUI-Tensor-Operations and hit install. It's a small pack by the same author as the Iterative Mixing KSampler - a few utility nodes, easy to eyeball the source if you're the sort who reads before installing.

Categorytensor_ops

Inputs (2)

NameTypeDefaultDescription
source_imageIMAGE
target_imageIMAGE

Outputs (1)

NameTypeDescription
IMAGEIMAGE