Nodes/ComfyUI CV/cv2.randu
ComfyUI Node

cv2.randu

Uniform noise and random test fields, bounded by two array inputs

By bmad4ever·Created 4 months ago·Updated 15 days ago· 1
cv2.randu
  • dst
  • low
  • high
  • result

Where cv2.randn gives you bell-shaped noise around a mean, cv2.randu gives you values spread evenly between a lower and an upper bound. That difference matters more than it sounds: uniform noise has no centre, so nothing gets crushed by a Gaussian's tails, and "95% of pixels below this value" becomes a property you can actually compute and rely on - which is why uniform fields are what you threshold when you want an exactly 3% speckle pattern.

What it's used for

Two jobs, and both are about test data and effects rather than real imagery:

  • Synthetic fields. A uniform random array thresholded at both ends is the classic salt-and-pepper recipe - the pack's own CV Add salt & pepper subgraph does exactly that, turning values above 247 into salt and below 8 into pepper for about 3% of each. Per-channel uniform noise is also the basis for chroma noise passes and dithering patterns.
  • Randomised parameters and inputs. Filling an array with values in a range is how you generate randomised initialisations, dummy data for a plumbing test, or randomised patch positions.

The mechanism, and the part people trip on

Like its sibling, randu writes into a pre-allocated array: dst defines the shape, channel count and dtype of the result, and its pixel values are irrelevant. The wrapper passes cv2 a private copy, so nothing upstream is modified, and the output comes out of this node.

low and high are NPARRAY sockets, not number widgets - one value per channel, or a single value that broadcasts. Type them with CV Scalar: (0, 0, 0) and (255, 255, 255) for a full-range uint8 field, or (0, 255) style per-channel bounds. The interval is half-open: low is included, high is excluded, so (0, 255) on a uint8 canvas gives you 0–254. Nobody notices until they need a 255.

And the third thing: the random source is cv2's single global RNG. Two nodes that draw from it interact through execution order, which follows the graph's links rather than the canvas. If you need the same speckle pattern twice, put CV Random Seed on the wire feeding this node - it seeds the global generator and passes a value through, which is the only reliable ordering constraint you have. A seed widget floating on its own may run after the thing it was meant to seed.

Inputs and outputs

dst (IMAGE/MASK/NPARRAY - used as a shape spec), low, high (both NPARRAY vectors, typed via CV Scalar). Output result, echoing dst's format: IMAGE in gives IMAGE out, ready for a preview. The node is in the pack's per-frame batch set, so a batch is filled frame by frame.

A practical sequencing note: the natural follow-up is cv2.threshold or cv2.compare to turn the uniform field into a two-level effect, then cv2.add / cv2.addWeighted to blend it into your image. In a uint8 pipeline, remember the noise is 0–255 in value, not "deviation from the image" - offset or blend rather than adding raw, or you'll bias the frame brighter.

Install

Part of ComfyUI CV (bmad4ever). ComfyUI Manager, search 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"

Restart. Python ≥ 3.12, recent V3-API ComfyUI. No models.

Common issues

"I can't type a bound." low and high are array sockets. CV Scalar is the node that fills them.

"My salt-and-pepper is all salt / all pepper." Your bounds are asymmetric or your threshold sits at an edge of the range. Values at exactly high never occur (half-open interval), so a threshold at 255 matches nothing.

"Same pattern every run / different pattern every run." Both are the global RNG's doing, and both are fixed by putting CV Random Seed in the data path.

"It's only affecting the first frame." It shouldn't - randu is batch-aware here. If a clip is half-done, the collapsing node is elsewhere in the chain.

Install friction. The pack pins opencv-contrib-python-headless~=5.0.0.93 and needs Python ≥ 3.12 plus a V3-API ComfyUI. Old embedded Python environments won't load it at all, and an existing numpy-1.x-pinned pack will conflict with OpenCV 5's numpy 2.x - resolve that before you debug the node.

Categoryimage/CV/low-level/cv2 R

Inputs (3)

NameTypeDefaultDescription
dstCOMFY_MATCHTYPE_V3output array of random numbers; the array must be pre-allocated. The image output(s) echo this input's format. The low-level cv2 function writes its result into this array in place, but this wrapper passes cv2 a private copy, so your input array is never modified. 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.
lowNPARRAYinclusive lower boundary of the generated random numbers. A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here.
highNPARRAYexclusive upper boundary of the generated random numbers. A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here.

Outputs (1)

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