Nodes/ComfyUI CV/cv2.magnitude
ComfyUI Node

cv2.magnitude

Turning a Sobel pair into one gradient map

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
cv2.magnitude
  • x
  • y
  • nparray

cv2.magnitude(x, y) computes sqrt(x² + y²) for every element. That's the entire function. It takes two arrays and gives you one.

The reason it exists is the two-pass edge detector: to get a real gradient magnitude you run cv2.Sobel twice, once with dx: 1, dy: 0 and once with dx: 0, dy: 1, then combine the horizontal and vertical responses. cv2.magnitude is the combine step. It's also the last step of a DFT magnitude spectrum (given the real and imaginary halves) and of any optical-flow magnitude you ever want to look at.

Compared to the Laplacian, a gradient magnitude is a well-behaved edge map: it's non-negative by construction and it stays strong on edges in any direction. It's also what most "gradient" or "soft edge" preprocessors are approximating when they don't want Canny's binary look - the KB's ControlNet preprocessor notes are all about that trade (Canny's clean thin lines for architecture, softedge's forgiving gradients for organic subjects), and this is the deterministic, model-free end of the same spectrum.

How it works, and what it needs

It's a float operation. OpenCV's own docs describe both inputs as floating-point arrays of vector coordinates, and the whole point is to keep the sub-pixel structure of the gradient. So the honest workflow is: convert once to float with Image → CV Array (dtype: float32 (0-1)), or keep your Sobel outputs in float by setting ddepth: CV_32F on them, and feed those to x and y.

Both sockets are the same shape: NPARRAY, IMAGE/MASK, or an NPARRAY link. They must be the same size. There are no optional parameters - no scale, no normalisation, no threshold. If you want it as a displayable picture, the output NPARRAY goes to Preview CV Array (render: normalize) or cv2.convertScaleAbs / cv2.normalize on the way to an IMAGE.

The output socket is nparray, and it is deliberately not format-echoing. A gradient magnitude is a float score map, and the pack refuses to hand score maps back as IMAGEs because the conversion min-max normalizes and would wipe out the magnitudes. Live with the extra conversion node; it's protecting you from a silent lie.

A working chain

Load Image
  └─ Image → CV Array (dtype: float32 (0-1), color_format: GRAY)
       ├─ cv2.Sobel  (ddepth: CV_32F, dx: 1, dy: 0, ksize: 3)
       └─ cv2.Sobel  (ddepth: CV_32F, dx: 0, dy: 1, ksize: 3)
            └─ cv2.magnitude (x, y)
                 └─ Preview CV Array (render: normalize)

Gray first, or you'll be adding three channels of gradient together and wondering why the map is soft.

Installing the pack

ComfyUI CV (bmad4ever/comfyui_cv) - 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 ComfyUI afterwards. Python ≥ 3.12 and a V3-API ComfyUI build; one pinned contrib OpenCV wheel; nothing to download. cv2.magnitude works on a core-only install, but the pack assumes contrib.

Where people get burned

Eight-bit inputs. Feed it two uint8 IMAGEs linked straight in - the pack converts them to uint8 BGR 0–255 - and you're running a float-shaped function on integer data with the sign and precision already thrown away, plus three channels of BGR you didn't want. Convert to float32 GRAY first.

Size mismatch. x and y have to be the same dimensions and the same type, exactly. No broadcasting, no rescaling. If your two Sobels came off differently-sized branches you'll get a cv2 error that doesn't explain itself.

A "gradient magnitude" that's all one flat grey. That's the display normalization doing its job on a map whose real range is tiny - use Preview CV Array with color_scale on and read the numbers, or apply cv2.normalize explicitly so you know what scale you're looking at.

Missing contrib nodes after installing another vision pack. All four opencv-* distributions share one site-packages/cv2; a non-contrib wheel installed over the contrib one silently empties the contrib submodules and the affected nodes stop appearing at all. python tools/repair_opencv_contrib.py --check then --apply is the fix.

Categoryimage/CV/low-level/cv2 M

Inputs (2)

NameTypeDefaultDescription
xNPARRAY,IMAGE,MASKfloating-point array of x-coordinates of the vectors. 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.
yNPARRAY,IMAGE,MASKfloating-point array of y-coordinates of the vectors; it must have the same size as x. 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
nparrayNPARRAY—