cv2.multiply
The scalar multiply you'll actually reach for
- src1
- src2
- result
cv2.multiply is element-wise multiplication of two arrays, with an optional scale factor. Humble, and it covers three jobs you'd otherwise chain nodes for: dimming or gain-scalping an image, applying a mask as a multiplier rather than as an alpha blend, and multiplying two score/weight maps together. The KB's post-processing doc makes the general case well - brightness and contrast are curves, not model passes - and this is the raw pixel-arithmetic version of that argument.
How it behaves
The math is result = src1 * src2 * scale, cast to the output depth. Two consequences matter in practice.
First, saturation. On the default "same as input" depth with a uint8 IMAGE, 200 × 2 lands on 255, not 400. Multiplying a picture to brighten it clips highlights and flattens the top end; if you want a smooth gain, cast to CV_32F with the dtype dropdown first, or use the scale parameter as the gain and keep the data in float.
Second, the scalar trick. src2 will happily take a constant array - the pack has a scalar-array node for authoring one - and the bundled subgraphs cv2.multiply (by scalar) and the Fourier playground both do exactly this: a constant float array in src2, scale left at 1. But you can also skip the array entirely for a pure gain: a constant array of 1s times a scale of 0.3 is the same as 0.3× the image, and the second form is one fewer node. For a scalar multiple, scale is the honest knob.
Third, single-channel broadcasting. Multiply a 3-channel IMAGE by a MASK and OpenCV applies the one-channel operand across the colour channels, which is the "cut the brightness down where the mask is" idiom. That's a genuine way to darken a background behind a subject - just remember it darkens toward black multiplicatively, so a 0.5 mask halves every channel rather than fading to a blur.
Inputs and outputs
src1 decides the output's format: an IMAGE comes back an IMAGE, a MASK a MASK, an NPARRAY stays an NPARRAY. src2 accepts NPARRAY, IMAGE, MASK or LATENT and doesn't affect the echo. scale (default 1.0) multiplies the whole product. dtype defaults to "same as input" - the depth dropdown is there when you want the result in CV_32F so it stops clipping.
The LATENT support is real and worth knowing about: arithmetic ops in this pack accept a full latent batch ({samples: [B, C, H, W]}) and the whole batch flows through when both inputs have the same batch size. That's latent-space arithmetic with the values untouched - no uint8 round trip - which is the correct way to scale a sigma/latent field. Keep both sides in the same space, though: mixing a 0–255 BGR IMAGE with a float latent array is numerically fine and semantically nonsense.
Install
ComfyUI Manager → search ComfyUI CV → install → restart, or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
Python ≥ 3.12 and a recent ComfyUI on the V3 API. No contrib submodule needed, no models.
Where it bites
- Black pixels stay black. Multiplicative changes can't lift a 0 - that's what
cv2_addis for. Reach forcv2_addWeightedwhen you want a proper blend of two images; plain multiply is for gain and masking. - Shape mismatch. Both inputs must be the same size and type (the single-channel-operand case aside), and the batch sizes have to agree for the batched path. The error is usually a silently odd result or a cv2 assertion, not a helpful message.
- Mixing a MASK with an IMAGE gives you a dimmed image, not a cut-out with transparency. If you want transparency, you want a 4-channel composite, not a multiply.
- The pack's low-level nodes exist for exactly this kind of raw arithmetic, but the README is upfront that they're auto-generated and uncurated - check the result visually the first time rather than trusting the wiring.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| src1 | COMFY_MATCHTYPE_V3 | first input array. The image output(s) echo this input's format. A LATENT link is processed in latent space: frame 0 becomes a float32 [H,W,C] array (any channel count), values untouched. Arithmetic ops (add, multiply, etc.) also accept a full LATENT batch ({samples: [B,C,H,W]}) — the whole batch flows through when both inputs have the same batch size. 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. | |
| src2 | NPARRAY,IMAGE,MASK,LATENT | second input array of the same size and the same type as src1. A LATENT link is processed in latent space: frame 0 becomes a float32 [H,W,C] array (any channel count), values untouched. Arithmetic ops (add, multiply, etc.) also accept a full LATENT batch ({samples: [B,C,H,W]}) — the whole batch flows through when both inputs have the same batch size. 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. | |
| scaleopt | FLOAT | 1.0000-1e+38–1e+38 | optional scale factor. Preset to the OpenCV default (1.0). |
| dtypeopt | COMBO | same as input | optional depth of the output array |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| result | COMFY_MATCHTYPE_V3 | Echoes the 'src1' input's format: an IMAGE link comes back as IMAGE, MASK as MASK, NPARRAY stays NPARRAY. |