CV Scalar
The tiny constant-vector node the raw cv2 wrappers can't live without
- nparray
cv2.inRange needs an [low, high] bound pair. cv2.randn needs a mean and a stddev vector. cv2.compare needs a src2. All of those are "a short list of numbers", and in ComfyUI a node input can't just be a list - sockets carry typed values, and there is no native array literal to type (35, 60, 60) into. So the pack ships this: one STRING widget parsed into an NPARRAY, so you can hand a constant vector to any NPARRAY-typed input.
That's the whole job. It's the least glamorous node in a pack of 780-odd nodes, and it appears in twenty of the pack's example workflows, which tells you how often you actually need it.
How to write the literal
values takes a number or a tuple literal. (35, 60, 60) gives you three elements - one per channel, which is the shape the bounds, the means and the compare arguments want. A bare single number like 128 broadcasts to four scalar slots, emitting (128, 128, 128, 128), which is OpenCV's own Scalar convention and not a typo.
The tooltip gives the polynomial case directly: (1.3, -0.6, 0.3) as coefficients for CV Polynomial Radial Map. Up to 32 elements, so arbitrary coefficient arrays are fine.
dtype defaults to float64 (cv2 Scalar default), which is what OpenCV expects for Scalar-like arguments - and it's worth leaving there unless you have a specific reason. Feeding a float64 array into something that wanted uint8 bounds is the kind of mismatch that produces a range that selects nothing, with no error to point at.
Output is nparray, a 1-D constant array. Wire it into anything.
Where you'll actually use it
cv2_inRangelowerb / upperb - the classic HSV colour-range node. Two of these, one for each bound, and you have a colour keyer.workflows/20_color_range_playground.jsondoes exactly that.cv2_randnmean / stddev - noise generation with control over per-channel level, which is how you add sensor noise without touching a model.cv2_comparesrc2 - threshold or equality against a constant vector.- Coefficient inputs - the radial map family, filters, anywhere the API takes a short numeric vector.
- Sensible defaults for pixel constants -
workflows/17_seamless_clone.json,87_latent_cv_playground.jsonand a dozen others use it for mask fills and border values.
It's the twin of CV Array Size, which does the same job for dsize-style Size parameters: where OpenCV wants a small fixed-shape vector and ComfyUI gives you sockets, the pack gives you a literal node.
One design consequence worth noticing
By living on the NPARRAY socket type rather than a FLOAT list, this composes with the rest of the pack for free. The same type is used by images-as-arrays, point sets, maps, matrices and feature tables - so a scalar array can be concatenated (CV Concat Arrays), reshaped (CV Reshape Array), cast (CV Cast Array), inspected (CV Inspect CV Data) or previewed like anything else. If you need a constant row in a feature matrix rather than a constant vector for a cv2 call, that combination is how you get it.
Install
Ships in ComfyUI CV (bmad4ever/comfyui_cv), GPL-3.0 fork of opencv-comfyui:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
# restart ComfyUI
Manager users: search the pack title. Python ≥ 3.12 and a ComfyUI on the V3 node API - if the whole pack is invisible, that's the first thing to check, before you start suspecting the OpenCV wheel.
The catch, and it's a fair one
It's a STRING widget parsed at execution time. So a malformed literal doesn't fail at graph-load time with a red border; it fails when the graph runs, and the message comes from the parser rather than from the node you wired it into. If a cv2_* wrapper is complaining about shapes, check the literals feeding it first - a stray bracket or a comma in the wrong place is a much more common culprit than the wrapper itself. And because it's a constant, there is no seeding, no randomness and no caching subtlety: the same string always gives the same array, which is exactly what you want from a node whose entire purpose is "hold this number".
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| values | STRING | (0, 0, 0) | Number or tuple literal, e.g. '128' or '(35, 60, 60)'. One element per channel for bounds/means; a bare single number broadcasts to four scalar slots. For polynomial radial-map coefficients use e.g. '(1.3, -0.6, 0.3)'. |
| dtype | COMBO | float64 (cv2 Scalar default) | Element type of the emitted array. float64 is what cv2 expects for Scalar-like arguments. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| nparray | NPARRAY | 1-D constant array. A bare number N emits (N, N, N, N); tuple literals emit one element per entry. |