Nodes/ComfyUI CV/cv2.mean
ComfyUI Node

cv2.mean

The fastest sanity check in the pack — and yes, those numbers are BGR

By bmad4ever·Created 4 months ago·Updated 15 days ago· 1
cv2.mean
  • src
  • mask
  • mean

cv2.mean returns the average value of each channel of an array. It is not a filter, it produces no picture, and it has no parameters worth arguing about. What it has is enormous practical value as a measurement: the mean of a frame is the single number that tells you whether a grade went the way you think it did, whether a mask is actually where you believe it is, and whether that black frame is black or just very dark.

It's the node you wire when you're debugging rather than generating. The whole output is one string, and that string is a per-channel average.

What comes out, exactly

The output socket is called mean and it's typed STRING, carrying a cv2 Scalar in the pack's literal spelling: "(c0, c1, c2, c3)" - one value per channel, and the same format every colour and borderValue input in this pack accepts. So the output isn't just for reading; you can wire it straight into a node that wants a colour literal.

Two details that decide whether the numbers make sense to you.

The channel order is BGR, and the scale is 0–255. When you link a ComfyUI IMAGE directly into src, the pack converts it to uint8 BGR before handing it to cv2. That's why a neutral grey reads back as three equal numbers (order doesn't matter) and a warm image reads back with a lower first number than last - first is blue, last is red.

If you want 0–1 and RGB, take the long way. Put Image → CV Array in front of the node with dtype: float32 (0-1) and color_format: RGB, and the mean comes back in 0–1 with red first. Different numbers, different convention, same node - which is why people compare two "mean" values across workflows and conclude one of them is broken.

Only the first four channels are reported; cv2 stuffs the result into a 4-element Scalar, so channels past the fourth are ignored. A single-channel MASK gives you its value in the first slot with zeros after.

The optional mask

mask is the reason to keep this node around after you've learned your image's average. With a mask, cv2 averages only the pixels the mask flags - so you can ask "what is the mean colour inside the face region" or "is the subject region darker than the background". That's the same statistic the KB's colour-match discussion is built on: mean and standard deviation are what a Reinhard-style transfer moves from a reference onto your image, and this node hands you the mean of a region without leaving the graph.

It takes NPARRAY, IMAGE/MASK or NPARRAY links, and the usual convention applies: a single-channel mask is a mask for all channels. The tooltip notes CV_8U, CV_8S or CV_Bool.

Installing the pack

ComfyUI CV (bmad4ever/comfyui_cv) - search "comfyui_cv" in ComfyUI Manager, or:

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"

Restart ComfyUI. Python ≥ 3.12, V3-API build, one pinned contrib OpenCV wheel, no downloads. cv2.mean is core OpenCV.

Where people get burned

The numbers look like they're in the wrong order. They're BGR. Every colour socket in this pack is BGR; get used to it.

The number is 0.5 and you expected 128. You're on the float32 0–1 path (via Image → CV Array) and your downstream assumption was 8-bit. Pick a scale and stick to it across the workflow.

Masked mean looks absurd. A mask with nothing flagged averages nothing; a mask with everything flagged is just the image mean. Preview the mask before you measure through it.

Trying to reduce a matrix. Nope - this is a scalar-per-channel reduction, not a spatial statistic. For "the mean of this array's elements" use CV Array Statistic; for the peak, cv2.minMaxLoc.

The pack-wide gotcha: install a non-contrib opencv-python over the contrib wheel and the contrib submodules empty out, so the contrib-backed nodes silently disappear from the menu (all four opencv-* wheels share one site-packages/cv2). python tools/repair_opencv_contrib.py --check then --apply fixes it. Keeping the average of a channel was never the risky part of this pack - the author's own README notes it was written with heavy LLM assistance and isn't recommended for production without reading the source.

Categoryimage/CV/low-level/cv2 M

Inputs (2)

NameTypeDefaultDescription
srcNPARRAY,IMAGE,MASKinput array that should have from 1 to 4 channels so that the result can be stored in Scalar_ . 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.
maskoptNPARRAY,IMAGE,MASKoptional operation mask ot type CV_8U, CV_8S or CV_Bool. 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.

Outputs (1)

NameTypeDescription
meanSTRINGoptional operation mask ot type CV_8U, CV_8S or CV_Bool. A cv2 Scalar as the pack's literal spelling "(c0, c1, c2, c3)" - one value per channel, and exactly what every colour / borderValue input takes.