Nodes/ComfyUI CV/cv2.xphoto.applyChannelGains
ComfyUI Node

cv2.xphoto.applyChannelGains

ApplyChannelGains ships with the gains at 0 — i.e. it hands you a black image

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
cv2.xphoto.applyChannelGains
  • src
  • result
◄gainB0.0000►
◄gainG0.0000►
◄gainR0.0000►

Three numbers, one multiply. xphoto.applyChannelGains scales each colour channel of an image independently - which is manual white balance, a tint, and exposure trim all at once, and it's as deterministic as an arithmetic op gets. The thing to know before you wire it: all three gains default to 0, so an untouched node multiplies your image by zero and returns black. Set them to 1.0 for the identity.

Why you'd reach for it

Because colour fixes do not need a diffusion pass. The case for deterministic colour work makes itself - the failure mode it exists to prevent is re-rolling an image because "the colours are off" when a per-channel multiply would have settled it in a millisecond. Concretely:

  • Match a composited subject to its new background. Cool background, warm subject: drop gainB a touch, raise gainR.
  • Neutralise a cast. A tungsten-lit frame is the classic: blue channel starved, so gainB goes up.
  • Dial in a stylised grade you then reuse as a fixed recipe - because the gains are numbers in a widget, they're reproducible across a whole batch in a way a prompt never is.

OpenCV's own xphoto module pairs this with white-balance estimators; those are class APIs (createSimpleWB, createGrayworldWB) that the pack's node generator doesn't reach, so here you supply the gains yourself. That's not a limitation so much as a division of labour: measure with something else, apply with this.

How it works

For each channel: dst = src * gain, saturating at 255 for 8-bit input. It's applied per frame. The upstream contract says the input is a three-channel BGR image (CV_8UC3 or CV_16UC3), and the pack respects that - feed it a mask or a single-channel array and it's promoted to three channels first, since a per-channel gain is meaningless on a grayscale canvas.

The inputs that matter

  • src - the image. NPARRAY, IMAGE or MASK; a batch is handled frame by frame.
  • gainB, gainG, gainR - the multipliers, in that order. 1.0 = no change. Note the order: blue first. If you're coming from an RGB slider mental model you will get a colour-shifted result and blame the node.
  • Green is the reference channel in practice. The pack's own tooltip says as much - leave gainG at 1.0 and balance the other two against it, rather than nudging all three and chasing your tail.

Output: result, a single socket echoing src's format. It's a low-level wrapper, so an IMAGE link still comes back as NPARRAY - convert with CV Array → Image (or the wildcard bridge, * → CV Array, on the way in).

Install

ComfyUI Manager → search ComfyUI CV, or:

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv

Restart afterwards. The pack needs opencv-contrib-python-headless~=5.0.0.93 - xphoto is a contrib submodule, so this node exists only on a contrib install. All four OpenCV wheel flavours share one site-packages/cv2, and installing a non-contrib wheel over a contrib one quietly wipes the submodules; tools/repair_opencv_contrib.py --check diagnoses it. You also need Python 3.12+ and a ComfyUI new enough for the V3 node API - on an older build the pack won't import at all.

Traps and gotchas

  • Gains of 0 → black image. Not a crash, not a warning. Just a black frame, and a confused ten minutes. Start at 1/1/1.
  • Gains above 1 clip. There's no headroom in an 8-bit tensor: raising gainB to fix a dark blue channel will also flatten every blue highlight in the frame. If you need range, convert the image to float first rather than pushing harder here.
  • It's linear, so it is not a colour correction. A gain is one number per channel; it can't fix a hue rotation, and it can't fix a photometric response that isn't a multiply. If you want a proper fit against a known reference, that's what the pack's colour-correction nodes (Create CCM Model → Apply Color Correction) are for, and Color Match Image in the ecosystem still does the mean/std transfer trick. This node is the dumb, instant, always-works version.
  • Gains are not measured for you. Eyeball them, then write them down - a saved workflow with the numbers in it is the deliverable.
Categoryimage/CV/low-level/xphoto

Inputs (4)

NameTypeDefaultDescription
srcCOMFY_MATCHTYPE_V3Input three-channel image in the BGR color space (either CV_8UC3 or CV_16UC3) The image output(s) echo this input's format. Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size.
gainBFLOAT0.0000-1e+38–1e+38gain for the B channel
gainGFLOAT0.0000-1e+38–1e+38gain for the G channel
gainRFLOAT0.0000-1e+38–1e+38gain for the R channel

Outputs (1)

NameTypeDescription
resultCOMFY_MATCHTYPE_V3Echoes the 'src' input's format: an IMAGE link comes back as IMAGE, MASK as MASK, NPARRAY stays NPARRAY.