Nodes/ComfyUI-ImageBag/Enhanced Image Colour Transfer
ComfyUI Node

Enhanced Image Colour Transfer

Steal any photo's color grade, no LUT required

By NHLStenden·Created about a year ago·Updated 8 months ago· 1
Enhanced Image Colour Transfer
  • source_image
  • target_image
  • IMAGE
cross_covariance_limit0.50
reshaping_iteration1
modified_val0.50
extra_shadingtrue
shader_val0.50
scale_vs_clip1.00
tint_val1.00
saturation_val-1.00

What it actually does

You feed it two images: a source (the look you want - a film still, a photo with a mood) and a target (your render). The output is your render recolored to match the source's overall color statistics - brightness, contrast, color cast, saturation. This is the classic Reinhard colour transfer from the 2001 paper, the same algorithm behind every "make my image look like this movie's palette" script that predates modern AI. No LUT, no training, no model: it's pure statistics on the two images you give it.

The mechanism, plain

Reinhard's trick is doing the match in a clever color space. The source code is explicit that this uses the Lαβ space derived from log-LMS, not CIELAB. The point of that space: luminance (L) is fully separated from the two chroma channels (α, β), and those channels are decorrelated - so you can match the target's mean and standard deviation to the source's per channel, and brightness, contrast, and color each follow independently without dragging the others along. Match means and standard deviations, and the target inherits the source's overall grade while keeping its own content.

The "enhanced" part is everything layered on top of that vanilla mean/std match: iterative reshaping of the chroma distribution (which handles sources with nonlinear color stats that plain Reinhard mangles), cross-covariance limiting so α and β don't get tangled, a saturation pass in HSV, an optional shading pass, and tint plus scale-vs-clip handling at the end. It's an aggressively knobby node - nine controls, none of them documented with tooltips.

The inputs you'll actually touch

Two are non-negotiable, and getting them backwards is the classic mistake:

  • source_image - the reference, the look to steal.
  • target_image - the image being recolored. The output is always the target wearing the source's statistics.

After that, honestly, one knob matters for most people: modified_val (default 0.5) blends the result back toward the original target, so it's your "how aggressive is this transfer" dial - 0 keeps your image untouched, 1 is full transfer. shader_val (default 0.5) controls how much of the source's brightness bleeds in; if the output comes out too dark or too washed out, that's the knob. The rest - cross_covariance_limit, reshaping_iteration, extra_shading, scale_vs_clip, tint_val, saturation_val - exist for fine control, and the defaults (0.5, 1, on, 1, 1, auto) work for a first pass. One trap: saturation_val defaults to -1, which isn't "no saturation," it's "auto" - the node computes a saturation factor from the images instead of applying a fixed value. Read it as a plain slider and you'll be confused why nothing happens.

Where people get burned

The big conceptual one: this is a global match. It changes the grade and mood of the whole frame, not the actual hue of specific objects. If you want "make this red car blue," this is the wrong tool - that's IP-Adapter or ControlNet territory. It's a grading tool, and a great pairing is to run it first, then finish with the film-grain node from the same pack, because palette alone doesn't give you the captured-on-film look.

Then there's the tuning problem: nine parameters, zero tooltips, and a pack this small has no community consensus to copy from. Change one thing at a time, preview against your target, and lean on modified_val and shader_val before touching anything else. The output is always an IMAGE at the target's resolution, so wire it into a Save Image or into your next post-processing step.

Install

Same pack as the grain node - it ships in the same ComfyUI-ImageBag repo:

cd ComfyUI/custom_nodes
git clone https://github.com/NHLStenden/ComfyUI-ImageBag.git

Restart ComfyUI. Or search "ImageBag" in ComfyUI Manager if it's in the catalog. Dependencies are torch and numpy, both already in every ComfyUI environment, and there are no model downloads. It's a pure function of the two input images, runs under torch.no_grad(), and works fine on CPU if you're testing - so the only real cost is your patience with the parameter list.

Categoryimage/preprocessors

Inputs (10)

NameTypeDefaultDescription
source_imageIMAGE
target_imageIMAGE
cross_covariance_limitFLOAT0.500–1
reshaping_iterationINT11–10
modified_valFLOAT0.500–1.5
extra_shadingBOOLEANtrue
shader_valFLOAT0.500–1.5
scale_vs_clipFLOAT1.000–1
tint_valFLOAT1.000–1.5
saturation_valFLOAT-1.00-1–2

Outputs (1)

NameTypeDescription
IMAGEIMAGE