cv2.Laplacian
The edge map that comes back pure black until you fix ddepth
- src
- nparray
cv2.Laplacian is a raw wrapper of the OpenCV function of the same name: a second-derivative edge detector. Where Canny gives you a clean binary line drawing, the Laplacian gives you a response map - every place the image's brightness is bending, both up and down. It's the classic tool for high-pass detail extraction, for a "sharpness score" (variance of the Laplacian is a whole research field in photography), and for a structural edge map that isn't shaped like Canny.
The reason people bounce off it is one widget. A Laplacian response is signed: bright edges come out positive, dark edges negative. Ask OpenCV for the result in the same 8-bit depth as your input and every negative value clips to zero. You get a black image and conclude the node is broken. It isn't; you asked it to throw away half the answer.
How it works
With ksize at 1, OpenCV convolves a 3×3 kernel - the four-neighbour version of the discrete Laplacian - which is cheap and very noisy. Set ksize to 3, 5 or 7 and it instead builds second-derivative operators from the Sobel machinery, which is smoother and better behaved on real photos. scale multiplies the result before it's stored, delta is added to it, and then everything is cast to ddepth.
But the pack's own node supplies ddepth as a dropdown - same as input, CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F, CV_64F. same as input is what the node presets, and for an IMAGE arriving as uint8 that means 8-bit unsigned, i.e. clipping. Reach for CV_32F and keep the sign.
The inputs and outputs that matter
src takes a ComfyUI IMAGE or MASK directly, or an NPARRAY. An IMAGE gets converted to uint8 BGR 0-255 for you, and because this function is on the pack's frame-batch list, a whole IMAGE batch is processed frame by frame rather than just frame 0.
Then the three you'll actually touch: ddepth (float, as above), ksize (must be positive and odd - 1, 3, 5, 7), and scale/delta (leave at 1.0 and 0.0 unless you're compensating for something specific). borderType defaults to BORDER_DEFAULT, and the tooltip warns BORDER_WRAP isn't supported.
One output, an NPARRAY called nparray. Note it's not an IMAGE even when you fed an IMAGE in - the pack deliberately refuses to format-echo functions whose output is a signed or out-of-range score map, because the conversion back to IMAGE min-max normalizes and would silently destroy the numbers. So send it through cv2.convertScaleAbs (which does echo: IMAGE in, IMAGE out, absolute value taken for you) if you want a normal picture, or Preview CV Array with render: normalize when you just want to look at it - turn on color_scale there and you'll see the actual value range instead of guessing.
Installing the pack
This node ships in ComfyUI CV (bmad4ever/comfyui_cv), ~470 auto-generated cv2.* wrappers plus curated nodes. Search "comfyui_cv" in ComfyUI Manager, or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
Then restart ComfyUI. The pack needs Python ≥ 3.12 and a recent ComfyUI built on the V3 node API, and its one real dependency is that pinned contrib OpenCV wheel. No model downloads for this node - it's pure CPU image math.
Where people get burned
A black output. Nine times out of ten it's ddepth left on same as input with a uint8 source. Flip it to CV_32F and re-run.
Noise becomes a wall of speckle. The Laplacian is a second derivative, so it amplifies high-frequency noise harder than any first-derivative filter. If your source is a JPEG or a video frame, blur it first - cv2.medianBlur is the right choice here, not a Gaussian, because it kills isolated bad pixels without smearing the edges you're trying to find.
You wanted edges, you got a grey soup. The raw Laplacian isn't binary. convertScaleAbs → cv2.threshold is the two-step that turns it into a real edge map; the answer rarely looks like Canny, which is either the point or the reason to use Canny.
Missing contrib nodes. Every low-level node is probed against your installed OpenCV at startup, and all four opencv-* wheels share one site-packages/cv2. Installing plain opencv-python on top of the contrib wheel silently empties the contrib submodules. The pack ships a fixer:
python tools/repair_opencv_contrib.py --check
python tools/repair_opencv_contrib.py --apply
Last thing, because the author says it about his own code: this pack was written with heavy LLM assistance and is explicitly not recommended for production without you reading the source. For a Laplace kernel that's fine. Just don't wire it into anything you can't afford to re-check.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| src | NPARRAY,IMAGE,MASK | Source 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. | |
| ddepth | COMBO | same as input | Desired depth of the destination image, see "combinations". |
| ksizeopt | INT | 1-2147483648–2147483647 | Aperture size used to compute the second-derivative filters. See #getDerivKernels for details. The size must be positive and odd. Preset to the OpenCV default (1). |
| scaleopt | FLOAT | 1.0000-1e+38–1e+38 | Optional scale factor for the computed Laplacian values. By default, no scaling is applied. See #getDerivKernels for details. Preset to the OpenCV default (1.0). |
| deltaopt | FLOAT | 0.0000-1e+38–1e+38 | Optional delta value that is added to the results prior to storing them in dst . Preset to the OpenCV default (0.0). |
| borderTypeopt | COMBO | BORDER_DEFAULT | Pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| nparray | NPARRAY | — |