cv2.magnitude
Turning a Sobel pair into one gradient map
- 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.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| x | NPARRAY,IMAGE,MASK | floating-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. | |
| y | NPARRAY,IMAGE,MASK | floating-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)
| Name | Type | Description |
|---|---|---|
| nparray | NPARRAY | — |