Nodes/ComfyUI CV/CV Histogram
ComfyUI Node

CV Histogram

Histogram anything in a ComfyUI graph — including just the part you painted

By bmad4ever·Created 4 months ago·Updated 15 days ago· 1
CV Histogram
  • image
  • mask
  • histogram
  • pixels
◄channels0►
◄bins32►
◄ranges0, 256►
◄normalizenone (raw counts)►

cv2.calcHist with the awkward parts sanded off: named channels, comma-separated bins and ranges instead of numpy tuples, an optional mask, and a normalisation dropdown. Three things people actually use it for - checking a render's tonal distribution, building the reference histogram that CV Back Project needs, and reading the colour of a specific region you masked out, which is the trick that makes colour matching work.

Histogramming a whole image tells you what the image is like. Histogramming a face tells you what the skin tone is. Feed the second into back-projection and you've got a region finder tuned to this shot.

How it works

It counts how often each value occurs, per channel: pick one or more channels, split each channel's range into bins, get the counts back as an NPARRAY - 1-D for one channel, 2-D for two (a hue × saturation histogram is a genuinely useful thing and almost nobody builds one, because it's fiddly in raw OpenCV and trivial here).

One space gotcha before anything else: a ComfyUI IMAGE arrives as BGR uint8, and raw BGR histograms are dominated by whatever the scene's overall colour cast is. Convert with cv2_cvtColor first - HSV for anything colour-matching related, or greyscale if you just want a tonal curve.

Inputs

  • image - the array to histogram, in whatever space you want to count. An IMAGE batch uses frame 0.
  • channels - comma-separated channel indices: 0 (hue of an HSV array), or 0, 1 for hue + saturation.
  • bins - bins per channel, comma-separated, one count broadcasts. 180 for every 8-bit hue level, or 30, 32 for a 2-D hue × saturation histogram. Fewer bins means smoother, more tolerant matching - this is the knob that decides whether back-projection is fussy or forgiving.
  • ranges - lo, hi pairs, flattened: 0, 180 for 8-bit hue, 0, 180, 0, 256 for hue + saturation. The upper bound is exclusive (full 8-bit = 0, 256).
  • normalize - raw counts; peak stretched to 255, which is precisely what cv2.calcBackProject wants; or divided by the total so the bins sum to 1. An all-zero histogram stays zero in every mode.
  • mask (optional) - the region to count. Non-zero pixels are counted, everything else ignored, and a mask of a different size gets resized to fit. This takes an NPARRAY or a core MASK, so the polygon you drew with CV Annotate Points drops right in. Omit it and you histogram the whole image.

Outputs

histogram - float32 bin counts, one axis per channel. Wire it into CV Back Project with the same channels and ranges, or preview 1-D/2-D histograms with the pack's array preview (normalize or heatmap mode; a raw count array as an image is nearly useless otherwise).

pixels - how many pixels were counted, inside both the mask and the ranges. That output is the one that saves you time: pixels = 0 means the histogram is empty, and an empty mask yields an all-zero histogram and pixels=0 rather than an error. So when back-projection comes out black, check pixels first - nine times out of ten your mask or your ranges are the problem, not the tracking node downstream.

Install

Manager → ComfyUI CV, 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, recent ComfyUI on the V3 node API.

Where people get burned

Hue is 0–180 in OpenCV, not 0–360, and it's the single most common wrong range in any histogram pipeline. Second: a mask that's slightly off - the mask resizes silently to the image, so a mask built at a different aspect ratio will still run and just measure the wrong region. Keep masks and images in the same space.

Categoryimage/CV/segmentation

Inputs (6)

NameTypeDefaultDescription
imageNPARRAY,IMAGEArray to histogram, in whatever color space you want to count (convert with cv2_cvtColor first). A ComfyUI IMAGE arrives as BGR uint8. 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.
channelsSTRING0Channel indices to histogram, comma-separated, e.g. '0' (hue of an HSV array) or '0, 1' (hue + saturation). Must exist in the image.
binsSTRING32Bin count per channel, comma-separated (a single count broadcasts), e.g. '180' for every 8-bit hue or '30, 32' for a 2-D hue x saturation histogram. Fewer bins = smoother, more tolerant matching.
rangesSTRING0, 256Value range per channel as 'lo, hi' pairs, comma-separated and flattened, e.g. '0, 180' for 8-bit hue or '0, 180, 0, 256' for hue + saturation. A single pair broadcasts to every channel. 'hi' is EXCLUSIVE (8-bit full range = 0, 256).
normalizeCOMBOnone (raw counts)Post-scaling: raw counts; peak stretched to 255 (what cv2.calcBackProject expects); or divided by the total so the bins sum to 1. An all-zero histogram stays zero in every mode.
maskoptNPARRAY,MASKOptional region mask: non-zero pixels are counted, the rest ignored. Resized to the image if needed. Omit to histogram the whole image. 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 (2)

NameTypeDescription
histogramNPARRAYfloat32 bin counts, one axis per channel (shape (bins,) for one channel, (bins0, bins1) for two). Feed to 'CV Back Project' with the SAME channels + ranges.
pixelsINTHow many pixels were counted (inside the mask and the ranges) - 0 means the histogram is empty.